blob: 0f6d12a90c707c935958d79dd4d4788106f0c3ac [file] [log] [blame]
James Zernad1e1632012-01-06 14:49:06 -08001// Copyright 2011 Google Inc. All Rights Reserved.
Urvang Joshia4f32ca2011-09-30 11:07:01 +05302//
3// This code is licensed under the same terms as WebM:
4// Software License Agreement: http://www.webmproject.org/license/software/
5// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
6// -----------------------------------------------------------------------------
7//
8// Simple command-line to create a WebP container file and to extract or strip
9// relevant data from the container file.
10//
James Zern04e84cf2011-11-04 15:20:08 -070011// Compile with: gcc -o webpmux webpmux.c -lwebpmux -lwebp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053012//
13//
14// Authors: Vikas (vikaas.arora@gmail.com),
15// Urvang (urvang@google.com)
16
17/* Usage examples:
18
19 Create container WebP file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053020 webpmux -tile tile_1.webp +0+0 \
21 -tile tile_2.webp +960+0 \
22 -tile tile_3.webp +0+576 \
23 -tile tile_4.webp +960+576 \
24 -o out_tile_container.webp
25
26 webpmux -frame anim_1.webp +0+0+0 \
27 -frame anim_2.webp +25+25+100 \
28 -frame anim_3.webp +50+50+100 \
29 -frame anim_4.webp +0+0+100 \
30 -loop 10 \
31 -o out_animation_container.webp
32
33 webpmux -set icc image_profile.icc in.webp -o out_icc_container.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053034 webpmux -set xmp image_metadata.xmp in.webp -o out_xmp_container.webp
35
Urvang Joshia4f32ca2011-09-30 11:07:01 +053036 Extract relevant data from WebP container file:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053037 webpmux -get tile n in.webp -o out_tile.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053038 webpmux -get frame n in.webp -o out_frame.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053039 webpmux -get icc in.webp -o image_profile.icc
Urvang Joshia4f32ca2011-09-30 11:07:01 +053040 webpmux -get xmp in.webp -o image_metadata.xmp
41
Urvang Joshia4f32ca2011-09-30 11:07:01 +053042 Strip data from WebP Container file:
James Zern04e84cf2011-11-04 15:20:08 -070043 webpmux -strip icc in.webp -o out.webp
44 webpmux -strip xmp in.webp -o out.webp
Urvang Joshia4f32ca2011-09-30 11:07:01 +053045
46 Misc:
Urvang Joshia4f32ca2011-09-30 11:07:01 +053047 webpmux -info in.webp
James Zern04e84cf2011-11-04 15:20:08 -070048 webpmux [ -h | -help ]
Urvang Joshia4f32ca2011-09-30 11:07:01 +053049*/
50
51#include <assert.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <string.h>
55#include "webp/mux.h"
56
Urvang Joshia4f32ca2011-09-30 11:07:01 +053057//------------------------------------------------------------------------------
58// Config object to parse command-line arguments.
59
60typedef enum {
61 NIL_ACTION = 0,
62 ACTION_GET,
63 ACTION_SET,
64 ACTION_STRIP,
65 ACTION_INFO,
66 ACTION_HELP
67} ActionType;
68
69typedef enum {
70 NIL_SUBTYPE = 0,
71 SUBTYPE_FRM,
72 SUBTYPE_LOOP
73} FeatureSubType;
74
75typedef struct {
76 FeatureSubType subtype_;
77 const char* filename_;
78 const char* params_;
79} FeatureArg;
80
81typedef enum {
82 NIL_FEATURE = 0,
83 FEATURE_XMP,
84 FEATURE_ICCP,
85 FEATURE_FRM,
86 FEATURE_TILE
87} FeatureType;
88
89typedef struct {
90 FeatureType type_;
91 FeatureArg* args_;
92 int arg_count_;
93} Feature;
94
95typedef struct {
96 ActionType action_type_;
97 const char* input_;
98 const char* output_;
99 Feature feature_;
100} WebPMuxConfig;
101
102//------------------------------------------------------------------------------
103// Helper functions.
104
James Zern04e84cf2011-11-04 15:20:08 -0700105static int CountOccurrences(const char* arglist[], int list_length,
106 const char* arg) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530107 int i;
108 int num_occurences = 0;
109
110 for (i = 0; i < list_length; ++i) {
111 if (!strcmp(arglist[i], arg)) {
112 ++num_occurences;
113 }
114 }
115 return num_occurences;
116}
117
118static int IsNotCompatible(int count1, int count2) {
James Zern04e84cf2011-11-04 15:20:08 -0700119 return (count1 > 0) != (count2 > 0);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530120}
121
122#define RETURN_IF_ERROR(ERR_MSG) \
123 if (err != WEBP_MUX_OK) { \
124 fprintf(stderr, ERR_MSG); \
125 return err; \
126 }
127
128#define RETURN_IF_ERROR2(ERR_MSG, FORMAT_STR) \
129 if (err != WEBP_MUX_OK) { \
130 fprintf(stderr, ERR_MSG, FORMAT_STR); \
131 return err; \
132 }
133
134#define ERROR_GOTO1(ERR_MSG, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700135 do { \
136 fprintf(stderr, ERR_MSG); \
137 ok = 0; \
138 goto LABEL; \
139 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530140
141#define ERROR_GOTO2(ERR_MSG, FORMAT_STR, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700142 do { \
143 fprintf(stderr, ERR_MSG, FORMAT_STR); \
144 ok = 0; \
145 goto LABEL; \
146 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530147
148#define ERROR_GOTO3(ERR_MSG, FORMAT_STR1, FORMAT_STR2, LABEL) \
James Zern04e84cf2011-11-04 15:20:08 -0700149 do { \
150 fprintf(stderr, ERR_MSG, FORMAT_STR1, FORMAT_STR2); \
151 ok = 0; \
152 goto LABEL; \
153 } while (0)
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530154
155static WebPMuxError DisplayInfo(const WebPMux* mux) {
156 int nFrames;
157 int nTiles;
158 const uint8_t* data = NULL;
159 uint32_t size = 0;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530160 const uint8_t* alpha_data;
161 uint32_t alpha_size;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530162 uint32_t flag;
163
164 WebPMuxError err = WebPMuxGetFeatures(mux, &flag);
165 RETURN_IF_ERROR("Failed to retrieve features\n");
166
167 if (flag == 0) {
168 fprintf(stderr, "No features present.\n");
169 return err;
170 }
171
172 // Print the features present.
173 fprintf(stderr, "Features present:");
174 if (flag & ANIMATION_FLAG) fprintf(stderr, " animation");
175 if (flag & TILE_FLAG) fprintf(stderr, " tiling");
176 if (flag & ICCP_FLAG) fprintf(stderr, " icc profile");
177 if (flag & META_FLAG) fprintf(stderr, " metadata");
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530178 if (flag & ALPHA_FLAG) fprintf(stderr, " transparency");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530179 fprintf(stderr, "\n");
180
181 if (flag & ANIMATION_FLAG) {
182 uint32_t loop_count;
183 err = WebPMuxGetLoopCount(mux, &loop_count);
184 RETURN_IF_ERROR("Failed to retrieve loop count\n");
185 fprintf(stderr, "Loop Count : %d\n", loop_count);
186
187 err = WebPMuxNumNamedElements(mux, "frame", &nFrames);
188 RETURN_IF_ERROR("Failed to retrieve number of frames\n");
189
190 fprintf(stderr, "Number of frames: %d\n", nFrames);
191 if (nFrames > 0) {
192 int i;
193 uint32_t x_offset, y_offset, duration;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530194 fprintf(stderr, "No.: x_offset y_offset duration");
195 if (flag & ALPHA_FLAG) fprintf(stderr, " alpha_size");
196 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530197 for (i = 1; i <= nFrames; i++) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530198 err = WebPMuxGetFrame(mux, i, &data, &size, &alpha_data, &alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530199 &x_offset, &y_offset, &duration);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530200 RETURN_IF_ERROR2("Failed to retrieve frame#%d\n", i);
201 fprintf(stderr, "%3d: %8d %8d %8d", i, x_offset, y_offset, duration);
202 if (flag & ALPHA_FLAG) fprintf(stderr, " %10d", alpha_size);
203 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530204 }
205 }
206 }
207
208 if (flag & TILE_FLAG) {
209 err = WebPMuxNumNamedElements(mux, "tile", &nTiles);
210 RETURN_IF_ERROR("Failed to retrieve number of tiles\n");
211
212 fprintf(stderr, "Number of tiles: %d\n", nTiles);
213 if (nTiles > 0) {
214 int i;
215 uint32_t x_offset, y_offset;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530216 fprintf(stderr, "No.: x_offset y_offset");
217 if (flag & ALPHA_FLAG) fprintf(stderr, " alpha_size");
218 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530219 for (i = 1; i <= nTiles; i++) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530220 err = WebPMuxGetTile(mux, i, &data, &size, &alpha_data, &alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530221 &x_offset, &y_offset);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530222 RETURN_IF_ERROR2("Failed to retrieve tile#%d\n", i);
223 fprintf(stderr, "%3d: %8d %8d", i, x_offset, y_offset);
224 if (flag & ALPHA_FLAG) fprintf(stderr, " %10d", alpha_size);
225 fprintf(stderr, "\n");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530226 }
227 }
228 }
229
230 if (flag & ICCP_FLAG) {
231 err = WebPMuxGetColorProfile(mux, &data, &size);
232 RETURN_IF_ERROR("Failed to retrieve the color profile\n");
233 fprintf(stderr, "Size of the color profile data: %d\n", size);
234 }
235
236 if (flag & META_FLAG) {
237 err = WebPMuxGetMetadata(mux, &data, &size);
238 RETURN_IF_ERROR("Failed to retrieve the XMP metadata\n");
239 fprintf(stderr, "Size of the XMP metadata: %d\n", size);
240 }
241
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530242 if ((flag & ALPHA_FLAG) && !(flag & (ANIMATION_FLAG | TILE_FLAG))) {
243 err = WebPMuxGetImage(mux, &data, &size, &alpha_data, &alpha_size);
244 RETURN_IF_ERROR("Failed to retrieve the image\n");
245 fprintf(stderr, "Size of the alpha data: %d\n", alpha_size);
246 }
247
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530248 return WEBP_MUX_OK;
249}
250
Pascal Massiminoabd030b2011-11-01 06:24:34 -0700251static void PrintHelp(void) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530252 fprintf(stderr, "Usage: webpmux -get GET_OPTIONS INPUT -o OUTPUT "
253 " Extract relevant data.\n");
254 fprintf(stderr, " or: webpmux -set SET_OPTIONS INPUT -o OUTPUT "
255 " Set color profile/metadata.\n");
256 fprintf(stderr, " or: webpmux -strip STRIP_OPTIONS INPUT -o OUTPUT "
257 " Strip color profile/metadata.\n");
258 fprintf(stderr, " or: webpmux [-tile TILE_OPTIONS]... -o OUTPUT "
259 " Create tiled image.\n");
260 fprintf(stderr, " or: webpmux [-frame FRAME_OPTIONS]... -loop LOOP_COUNT"
261 " -o OUTPUT Create animation.\n");
262 fprintf(stderr, " or: webpmux -info INPUT "
263 " Print info about given webp file.\n");
264 fprintf(stderr, " or: webpmux -help OR -h "
265 " Print this help.\n");
266
267 fprintf(stderr, "\n");
268 fprintf(stderr, "GET_OPTIONS:\n");
269 fprintf(stderr, " icc Get ICCP Color profile.\n");
270 fprintf(stderr, " xmp Get XMP metadata.\n");
271 fprintf(stderr, " tile n Get nth tile.\n");
272 fprintf(stderr, " frame n Get nth frame.\n");
273
274 fprintf(stderr, "\n");
275 fprintf(stderr, "SET_OPTIONS:\n");
276 fprintf(stderr, " icc Set ICC Color profile.\n");
277 fprintf(stderr, " xmp Set XMP metadata.\n");
278
279 fprintf(stderr, "\n");
280 fprintf(stderr, "STRIP_OPTIONS:\n");
281 fprintf(stderr, " icc Strip ICCP color profile.\n");
282 fprintf(stderr, " xmp Strip XMP metadata.\n");
283
284 fprintf(stderr, "\n");
285 fprintf(stderr, "TILE_OPTIONS(i):\n");
286 fprintf(stderr, " file_i +xi+yi\n");
287 fprintf(stderr, " where: 'file_i' is the i'th tile (webp format),\n");
288 fprintf(stderr, " 'xi','yi' specify the image offset for this "
289 "tile.\n");
290
291 fprintf(stderr, "\n");
292 fprintf(stderr, "FRAME_OPTIONS(i):\n");
293 fprintf(stderr, " file_i +xi+yi+di\n");
294 fprintf(stderr, " where: 'file_i' is the i'th animation frame (webp "
295 "format),\n");
296 fprintf(stderr, " 'xi','yi' specify the image offset for this "
297 "frame.\n");
298 fprintf(stderr, " 'di' is the pause duration before next frame."
299 "\n");
300
301 fprintf(stderr, "\n");
302 fprintf(stderr, "INPUT & OUTPUT are in webp format.");
303 fprintf(stderr, "\n");
304}
305
306static int ReadData(const char* filename, void** data_ptr, uint32_t* size_ptr) {
307 void* data = NULL;
308 long size = 0;
309 int ok = 0;
310 FILE* in;
311
312 *size_ptr = 0;
313 in = fopen(filename, "rb");
314 if (!in) {
315 fprintf(stderr, "Failed to open file %s\n", filename);
316 return 0;
317 }
318 fseek(in, 0, SEEK_END);
319 size = ftell(in);
320 fseek(in, 0, SEEK_SET);
321 if (size > 0xffffffffu) {
322 fprintf(stderr, "Size (%ld bytes) is out of range for file %s\n",
323 size, filename);
324 size = 0;
325 goto Err;
326 }
327 if (size < 0) {
328 size = 0;
329 goto Err;
330 }
331 data = malloc(size);
332 if (data) {
333 if (fread(data, size, 1, in) != 1) {
334 free(data);
335 data = NULL;
336 size = 0;
337 fprintf(stderr, "Failed to read %ld bytes from file %s\n",
338 size, filename);
339 goto Err;
340 }
341 ok = 1;
342 } else {
343 fprintf(stderr, "Failed to allocate %ld bytes for reading file %s\n",
344 size, filename);
345 size = 0;
346 }
347
348 Err:
349 if (in != stdin) fclose(in);
350 *size_ptr = (uint32_t)size;
351 *data_ptr = data;
352 return ok;
353}
354
355static int ReadFile(const char* const filename, WebPMux** mux) {
356 uint32_t size = 0;
357 void* data = NULL;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530358 WebPMuxState mux_state;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530359
360 assert(mux != NULL);
361
362 if (!ReadData(filename, &data, &size)) return 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530363 *mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530364 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530365 if (*mux != NULL && mux_state == WEBP_MUX_STATE_COMPLETE) return 1;
366 fprintf(stderr, "Failed to create mux object from file %s. mux_state = %d.\n",
367 filename, mux_state);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530368 return 0;
369}
370
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530371static int ReadImage(const char* filename,
372 const uint8_t** data_ptr, uint32_t* size_ptr,
373 const uint8_t** alpha_data_ptr, uint32_t* alpha_size_ptr) {
374 void* data = NULL;
375 uint32_t size = 0;
376 const uint8_t* alpha_data = NULL;
377 uint32_t alpha_size = 0;
378 WebPMux* mux;
379 WebPMuxError err;
380 int ok = 0;
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530381 WebPMuxState mux_state;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530382
383 if (!ReadData(filename, &data, &size)) return 0;
384
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530385 mux = WebPMuxCreate((const uint8_t*)data, size, 1, &mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530386 free(data);
Urvang Joshi8d6490d2012-01-13 14:46:30 +0530387 if (mux == NULL || mux_state != WEBP_MUX_STATE_COMPLETE) {
388 fprintf(stderr,
389 "Failed to create mux object from file %s. mux_state = %d.\n",
390 filename, mux_state);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530391 return 0;
392 }
393 err = WebPMuxGetImage(mux, (const uint8_t**)&data, &size,
394 &alpha_data, &alpha_size);
395 if (err == WEBP_MUX_OK) {
James Zern2f741d12011-12-02 16:00:51 -0800396 uint8_t* const data_mem = (uint8_t*)malloc(size);
397 uint8_t* const alpha_mem = (uint8_t*)malloc(alpha_size);
398 if ((data_mem != NULL) && (alpha_mem != NULL)) {
399 memcpy(data_mem, data, size);
400 memcpy(alpha_mem, alpha_data, alpha_size);
401 *data_ptr = data_mem;
402 *size_ptr = size;
403 *alpha_data_ptr = alpha_mem;
404 *alpha_size_ptr = alpha_size;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530405 ok = 1;
406 } else {
James Zern2f741d12011-12-02 16:00:51 -0800407 free(data_mem);
408 free(alpha_mem);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530409 err = WEBP_MUX_MEMORY_ERROR;
410 fprintf(stderr, "Failed to allocate %d bytes to extract image data from"
411 " file %s. Error: %d\n", size + alpha_size, filename, err);
412 }
413 } else {
414 fprintf(stderr, "Failed to extract image data from file %s. Error: %d\n",
415 filename, err);
416 }
417 WebPMuxDelete(mux);
418 return ok;
419}
420
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530421static int WriteData(const char* filename, void* data, uint32_t size) {
422 int ok = 0;
James Zern04e84cf2011-11-04 15:20:08 -0700423 FILE* fout = strcmp(filename, "-") ? fopen(filename, "wb") : stdout;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530424 if (!fout) {
425 fprintf(stderr, "Error opening output WebP file %s!\n", filename);
426 return 0;
427 }
428 if (fwrite(data, size, 1, fout) != 1) {
429 fprintf(stderr, "Error writing file %s!\n", filename);
430 } else {
431 fprintf(stderr, "Saved file %s (%d bytes)\n", filename, size);
432 ok = 1;
433 }
434 if (fout != stdout) fclose(fout);
435 return ok;
436}
437
438static int WriteWebP(WebPMux* const mux, const char* filename) {
439 uint8_t* data = NULL;
440 uint32_t size = 0;
441 int ok;
442
443 WebPMuxError err = WebPMuxAssemble(mux, &data, &size);
444 if (err != WEBP_MUX_OK) {
445 fprintf(stderr, "Error (%d) assembling the WebP file.\n", err);
446 return 0;
447 }
448 ok = WriteData(filename, data, size);
449 free(data);
450 return ok;
451}
452
453static int ParseFrameArgs(const char* args, uint32_t* x_offset,
454 uint32_t* y_offset, uint32_t* duration) {
455 return (sscanf(args, "+%d+%d+%d", x_offset, y_offset, duration) == 3);
456}
457
458static int ParseTileArgs(const char* args, uint32_t* x_offset,
459 uint32_t* y_offset) {
460 return (sscanf(args, "+%d+%d", x_offset, y_offset) == 2);
461}
462
463//------------------------------------------------------------------------------
464// Clean-up.
465
466static void DeleteConfig(WebPMuxConfig* config) {
467 if (config != NULL) {
468 free(config->feature_.args_);
469 free(config);
470 }
471}
472
473//------------------------------------------------------------------------------
474// Parsing.
475
476// Basic syntactic checks on the command-line arguments.
477// Returns 1 on valid, 0 otherwise.
478// Also fills up num_feature_args to be number of feature arguments given.
479// (e.g. if there are 4 '-frame's and 1 '-loop', then num_feature_args = 5).
480static int ValidateCommandLine(int argc, const char* argv[],
481 int* num_feature_args) {
482 int num_frame_args;
483 int num_tile_args;
484 int num_loop_args;
485 int ok = 1;
486
487 assert(num_feature_args != NULL);
488 *num_feature_args = 0;
489
490 // Simple checks.
James Zern04e84cf2011-11-04 15:20:08 -0700491 if (CountOccurrences(argv, argc, "-get") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530492 ERROR_GOTO1("ERROR: Multiple '-get' arguments specified.\n", ErrValidate);
493 }
James Zern04e84cf2011-11-04 15:20:08 -0700494 if (CountOccurrences(argv, argc, "-set") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530495 ERROR_GOTO1("ERROR: Multiple '-set' arguments specified.\n", ErrValidate);
496 }
James Zern04e84cf2011-11-04 15:20:08 -0700497 if (CountOccurrences(argv, argc, "-strip") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530498 ERROR_GOTO1("ERROR: Multiple '-strip' arguments specified.\n", ErrValidate);
499 }
James Zern04e84cf2011-11-04 15:20:08 -0700500 if (CountOccurrences(argv, argc, "-info") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530501 ERROR_GOTO1("ERROR: Multiple '-info' arguments specified.\n", ErrValidate);
502 }
James Zern04e84cf2011-11-04 15:20:08 -0700503 if (CountOccurrences(argv, argc, "-o") > 1) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530504 ERROR_GOTO1("ERROR: Multiple output files specified.\n", ErrValidate);
505 }
506
507 // Compound checks.
James Zern04e84cf2011-11-04 15:20:08 -0700508 num_frame_args = CountOccurrences(argv, argc, "-frame");
509 num_tile_args = CountOccurrences(argv, argc, "-tile");
510 num_loop_args = CountOccurrences(argv, argc, "-loop");
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530511
512 if (num_loop_args > 1) {
513 ERROR_GOTO1("ERROR: Multiple loop counts specified.\n", ErrValidate);
514 }
515
516 if (IsNotCompatible(num_frame_args, num_loop_args)) {
517 ERROR_GOTO1("ERROR: Both frames and loop count have to be specified.\n",
518 ErrValidate);
519 }
520 if (num_frame_args > 0 && num_tile_args > 0) {
521 ERROR_GOTO1("ERROR: Only one of frames & tiles can be specified at a time."
522 "\n", ErrValidate);
523 }
524
525 assert(ok == 1);
526 if (num_frame_args == 0 && num_tile_args == 0) {
527 // Single argument ('set' action for XMP or ICCP, OR a 'get' action).
528 *num_feature_args = 1;
529 } else {
530 // Multiple arguments ('set' action for animation or tiling).
531 if (num_frame_args > 0) {
532 *num_feature_args = num_frame_args + num_loop_args;
533 } else {
534 *num_feature_args = num_tile_args;
535 }
536 }
537
538 ErrValidate:
539 return ok;
540}
541
542#define ACTION_IS_NIL (config->action_type_ == NIL_ACTION)
543
544#define FEATURETYPE_IS_NIL (feature->type_ == NIL_FEATURE)
545
546#define CHECK_NUM_ARGS_LESS(NUM, LABEL) \
547 if (argc < i + (NUM)) { \
548 fprintf(stderr, "ERROR: Too few arguments for '%s'.\n", argv[i]); \
549 goto LABEL; \
550 }
551
552#define CHECK_NUM_ARGS_NOT_EQUAL(NUM, LABEL) \
553 if (argc != i + (NUM)) { \
554 fprintf(stderr, "ERROR: Too many arguments for '%s'.\n", argv[i]); \
555 goto LABEL; \
556 }
557
558// Parses command-line arguments to fill up config object. Also performs some
559// semantic checks.
560static int ParseCommandLine(int argc, const char* argv[],
561 WebPMuxConfig* config) {
562 int i = 0;
563 int feature_arg_index = 0;
564 int ok = 1;
565
566 while (i < argc) {
567 Feature* const feature = &config->feature_;
James Zern04e84cf2011-11-04 15:20:08 -0700568 FeatureArg* const arg = &feature->args_[feature_arg_index];
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530569 if (argv[i][0] == '-') { // One of the action types or output.
570 if (!strcmp(argv[i], "-set")) {
571 if (ACTION_IS_NIL) {
572 config->action_type_ = ACTION_SET;
573 } else {
574 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
575 }
576 ++i;
577 } else if (!strcmp(argv[i], "-get")) {
578 if (ACTION_IS_NIL) {
579 config->action_type_ = ACTION_GET;
580 } else {
581 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
582 }
583 ++i;
584 } else if (!strcmp(argv[i], "-strip")) {
585 if (ACTION_IS_NIL) {
586 config->action_type_ = ACTION_STRIP;
587 feature->arg_count_ = 0;
588 } else {
589 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
590 }
591 ++i;
592 } else if (!strcmp(argv[i], "-frame")) {
593 CHECK_NUM_ARGS_LESS(3, ErrParse);
594 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
595 config->action_type_ = ACTION_SET;
596 } else {
597 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
598 }
599 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
600 feature->type_ = FEATURE_FRM;
601 } else {
602 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
603 }
604 arg->subtype_ = SUBTYPE_FRM;
605 arg->filename_ = argv[i + 1];
606 arg->params_ = argv[i + 2];
607 ++feature_arg_index;
608 i += 3;
609 } else if (!strcmp(argv[i], "-loop")) {
610 CHECK_NUM_ARGS_LESS(2, ErrParse);
611 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
612 config->action_type_ = ACTION_SET;
613 } else {
614 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
615 }
616 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_FRM) {
617 feature->type_ = FEATURE_FRM;
618 } else {
619 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
620 }
621 arg->subtype_ = SUBTYPE_LOOP;
622 arg->params_ = argv[i + 1];
623 ++feature_arg_index;
624 i += 2;
625 } else if (!strcmp(argv[i], "-tile")) {
626 CHECK_NUM_ARGS_LESS(3, ErrParse);
627 if (ACTION_IS_NIL || config->action_type_ == ACTION_SET) {
628 config->action_type_ = ACTION_SET;
629 } else {
630 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
631 }
632 if (FEATURETYPE_IS_NIL || feature->type_ == FEATURE_TILE) {
633 feature->type_ = FEATURE_TILE;
634 } else {
635 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
636 }
637 arg->filename_ = argv[i + 1];
638 arg->params_ = argv[i + 2];
639 ++feature_arg_index;
640 i += 3;
641 } else if (!strcmp(argv[i], "-o")) {
642 CHECK_NUM_ARGS_LESS(2, ErrParse);
643 config->output_ = argv[i + 1];
644 i += 2;
645 } else if (!strcmp(argv[i], "-info")) {
646 CHECK_NUM_ARGS_NOT_EQUAL(2, ErrParse);
647 if (config->action_type_ != NIL_ACTION) {
648 ERROR_GOTO1("ERROR: Multiple actions specified.\n", ErrParse);
649 } else {
650 config->action_type_ = ACTION_INFO;
651 feature->arg_count_ = 0;
652 config->input_ = argv[i + 1];
653 }
654 i += 2;
655 } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-help")) {
656 PrintHelp();
657 DeleteConfig(config);
658 exit(1);
659 } else {
660 ERROR_GOTO2("ERROR: Unknown option: '%s'.\n", argv[i], ErrParse);
661 }
662 } else { // One of the feature types or input.
663 if (ACTION_IS_NIL) {
664 ERROR_GOTO1("ERROR: Action must be specified before other arguments.\n",
665 ErrParse);
666 }
667 if (!strcmp(argv[i], "icc") || !strcmp(argv[i], "xmp")) {
668 if (FEATURETYPE_IS_NIL) {
669 feature->type_ = (!strcmp(argv[i], "icc")) ? FEATURE_ICCP :
670 FEATURE_XMP;
671 } else {
672 ERROR_GOTO1("ERROR: Multiple features specified.\n", ErrParse);
673 }
674 if (config->action_type_ == ACTION_SET) {
675 CHECK_NUM_ARGS_LESS(2, ErrParse);
676 arg->filename_ = argv[i + 1];
677 ++feature_arg_index;
678 i += 2;
679 } else {
680 ++i;
681 }
682 } else if ((!strcmp(argv[i], "frame") ||
683 !strcmp(argv[i], "tile")) &&
684 (config->action_type_ == ACTION_GET)) {
685 CHECK_NUM_ARGS_LESS(2, ErrParse);
686 feature->type_ = (!strcmp(argv[i], "frame")) ? FEATURE_FRM :
687 FEATURE_TILE;
688 arg->params_ = argv[i + 1];
689 ++feature_arg_index;
690 i += 2;
James Zern04e84cf2011-11-04 15:20:08 -0700691 } else { // Assume input file.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530692 if (config->input_ == NULL) {
693 config->input_ = argv[i];
694 } else {
695 ERROR_GOTO2("ERROR at '%s': Multiple input files specified.\n",
696 argv[i], ErrParse);
697 }
698 ++i;
699 }
700 }
701 }
702 ErrParse:
703 return ok;
704}
705
James Zern04e84cf2011-11-04 15:20:08 -0700706// Additional checks after config is filled.
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530707static int ValidateConfig(WebPMuxConfig* config) {
708 int ok = 1;
709 Feature* const feature = &config->feature_;
710
711 // Action.
712 if (ACTION_IS_NIL) {
713 ERROR_GOTO1("ERROR: No action specified.\n", ErrValidate2);
714 }
715
716 // Feature type.
717 if (FEATURETYPE_IS_NIL && config->action_type_ != ACTION_INFO) {
718 ERROR_GOTO1("ERROR: No feature specified.\n", ErrValidate2);
719 }
720
721 // Input file.
722 if (config->input_ == NULL) {
723 if (config->action_type_ != ACTION_SET) {
724 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
725 } else if (feature->type_ != FEATURE_FRM &&
726 feature->type_ != FEATURE_TILE) {
727 ERROR_GOTO1("ERROR: No input file specified.\n", ErrValidate2);
728 }
729 }
730
731 // Output file.
732 if (config->output_ == NULL && config->action_type_ != ACTION_INFO) {
733 ERROR_GOTO1("ERROR: No output file specified.\n", ErrValidate2);
734 }
735
736 ErrValidate2:
737 return ok;
738}
739
740// Create config object from command-line arguments.
741static int InitializeConfig(int argc, const char* argv[],
742 WebPMuxConfig** config) {
743 int num_feature_args = 0;
744 int ok = 1;
745
746 assert(config != NULL);
747 *config = NULL;
748
749 // Validate command-line arguments.
750 if (!ValidateCommandLine(argc, argv, &num_feature_args)) {
751 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
752 }
753
754 // Allocate memory.
755 *config = (WebPMuxConfig*)calloc(1, sizeof(**config));
756 if (*config == NULL) {
757 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
758 }
759 (*config)->feature_.arg_count_ = num_feature_args;
760 (*config)->feature_.args_ =
761 (FeatureArg*)calloc(num_feature_args, sizeof(FeatureArg));
762 if ((*config)->feature_.args_ == NULL) {
763 ERROR_GOTO1("ERROR: Memory allocation error.\n", Err1);
764 }
765
766 // Parse command-line.
James Zern04e84cf2011-11-04 15:20:08 -0700767 if (!ParseCommandLine(argc, argv, *config) ||
768 !ValidateConfig(*config)) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530769 ERROR_GOTO1("Exiting due to command-line parsing error.\n", Err1);
770 }
771
772 Err1:
773 return ok;
774}
775
776#undef ACTION_IS_NIL
777#undef FEATURETYPE_IS_NIL
778#undef CHECK_NUM_ARGS_LESS
779#undef CHECK_NUM_ARGS_MORE
780
781//------------------------------------------------------------------------------
782// Processing.
783
James Zern04e84cf2011-11-04 15:20:08 -0700784static int GetFrameTile(const WebPMux* mux,
785 const WebPMuxConfig* config, int isFrame) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530786 const uint8_t* data = NULL;
787 uint32_t size = 0;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530788 const uint8_t* alpha_data = NULL;
789 uint32_t alpha_size = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530790 uint32_t x_offset = 0;
791 uint32_t y_offset = 0;
792 uint32_t duration = 0;
793 WebPMuxError err = WEBP_MUX_OK;
794 WebPMux* mux_single = NULL;
795 long num = 0;
796 int ok = 1;
797
798 num = strtol(config->feature_.args_[0].params_, NULL, 10);
799 if (num < 0) {
800 ERROR_GOTO1("ERROR: Frame/Tile index must be non-negative.\n", ErrGet);
801 }
802
803 if (isFrame) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530804 err = WebPMuxGetFrame(mux, num, &data, &size, &alpha_data, &alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530805 &x_offset, &y_offset, &duration);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530806 if (err != WEBP_MUX_OK) {
807 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
808 }
809 } else {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530810 err = WebPMuxGetTile(mux, num, &data, &size, &alpha_data, &alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530811 &x_offset, &y_offset);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530812 if (err != WEBP_MUX_OK) {
813 ERROR_GOTO3("ERROR#%d: Could not get frame %ld.\n", err, num, ErrGet);
814 }
815 }
816
817 mux_single = WebPMuxNew();
818 if (mux_single == NULL) {
819 err = WEBP_MUX_MEMORY_ERROR;
820 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n", err, ErrGet);
821 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530822 err = WebPMuxSetImage(mux_single, data, size, alpha_data, alpha_size, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530823 if (err != WEBP_MUX_OK) {
824 ERROR_GOTO2("ERROR#%d: Could not create single image mux object.\n", err,
825 ErrGet);
826 }
827 ok = WriteWebP(mux_single, config->output_);
828
829 ErrGet:
830 WebPMuxDelete(mux_single);
831 return ok;
832}
833
834// Read and process config.
James Zern04e84cf2011-11-04 15:20:08 -0700835static int Process(const WebPMuxConfig* config) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530836 WebPMux* mux = NULL;
837 const uint8_t* data = NULL;
838 uint32_t size = 0;
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530839 const uint8_t* alpha_data = NULL;
840 uint32_t alpha_size = 0;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530841 uint32_t x_offset = 0;
842 uint32_t y_offset = 0;
843 uint32_t duration = 0;
844 uint32_t loop_count = 0;
845 WebPMuxError err = WEBP_MUX_OK;
846 long num;
847 int index = 0;
848 int ok = 1;
James Zern04e84cf2011-11-04 15:20:08 -0700849 const Feature* const feature = &config->feature_;
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530850
James Zern04e84cf2011-11-04 15:20:08 -0700851 switch (config->action_type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530852 case ACTION_GET:
853 ok = ReadFile(config->input_, &mux);
854 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700855 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530856 case FEATURE_FRM:
857 ok = GetFrameTile(mux, config, 1);
858 break;
859
860 case FEATURE_TILE:
861 ok = GetFrameTile(mux, config, 0);
862 break;
863
864 case FEATURE_ICCP:
865 err = WebPMuxGetColorProfile(mux, &data, &size);
866 if (err != WEBP_MUX_OK) {
867 ERROR_GOTO2("ERROR#%d: Could not get color profile.\n", err, Err2);
868 }
869 ok = WriteData(config->output_, (void*)data, size);
870 break;
871
872 case FEATURE_XMP:
873 err = WebPMuxGetMetadata(mux, &data, &size);
874 if (err != WEBP_MUX_OK) {
875 ERROR_GOTO2("ERROR#%d: Could not get XMP metadata.\n", err, Err2);
876 }
877 ok = WriteData(config->output_, (void*)data, size);
878 break;
879
880 default:
881 ERROR_GOTO1("ERROR: Invalid feature for action 'get'.\n", Err2);
882 break;
883 }
884 break;
885
886 case ACTION_SET:
James Zern04e84cf2011-11-04 15:20:08 -0700887 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530888 case FEATURE_FRM:
889 mux = WebPMuxNew();
890 if (mux == NULL) {
891 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
892 WEBP_MUX_MEMORY_ERROR, Err2);
893 }
894 for (index = 0; index < feature->arg_count_; ++index) {
895 if (feature->args_[index].subtype_ == SUBTYPE_LOOP) {
896 num = strtol(feature->args_[index].params_, NULL, 10);
897 if (num < 0) {
898 ERROR_GOTO1("ERROR: Loop count must be non-negative.\n", Err2);
899 } else {
900 loop_count = num;
901 }
902 err = WebPMuxSetLoopCount(mux, loop_count);
903 if (err != WEBP_MUX_OK) {
904 ERROR_GOTO2("ERROR#%d: Could not set loop count.\n", err, Err2);
905 }
906 } else if (feature->args_[index].subtype_ == SUBTYPE_FRM) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530907 ok = ReadImage(feature->args_[index].filename_,
908 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530909 if (!ok) goto Err2;
910 ok = ParseFrameArgs(feature->args_[index].params_,
911 &x_offset, &y_offset, &duration);
912 if (!ok) {
913 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530914 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530915 ERROR_GOTO1("ERROR: Could not parse frame properties.\n", Err2);
916 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530917 err = WebPMuxAddFrame(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530918 x_offset, y_offset, duration, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530919 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530920 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530921 if (err != WEBP_MUX_OK) {
922 ERROR_GOTO3("ERROR#%d: Could not add a frame at index %d.\n",
923 err, index, Err2);
924 }
925 } else {
926 ERROR_GOTO1("ERROR: Invalid subtype for 'frame'", Err2);
927 }
928 }
929 break;
930
931 case FEATURE_TILE:
932 mux = WebPMuxNew();
933 if (mux == NULL) {
934 ERROR_GOTO2("ERROR#%d: Could not allocate a mux object.\n",
935 WEBP_MUX_MEMORY_ERROR, Err2);
936 }
937 for (index = 0; index < feature->arg_count_; ++index) {
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530938 ok = ReadImage(feature->args_[index].filename_,
939 &data, &size, &alpha_data, &alpha_size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530940 if (!ok) goto Err2;
941 ok = ParseTileArgs(feature->args_[index].params_, &x_offset,
942 &y_offset);
943 if (!ok) {
944 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530945 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530946 ERROR_GOTO1("ERROR: Could not parse tile properties.\n", Err2);
947 }
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530948 err = WebPMuxAddTile(mux, 0, data, size, alpha_data, alpha_size,
Urvang Joshic398f592011-11-22 14:40:41 +0530949 x_offset, y_offset, 1);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530950 free((void*)data);
Urvang Joshicdf97aa2011-12-01 16:11:51 +0530951 free((void*)alpha_data);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530952 if (err != WEBP_MUX_OK) {
953 ERROR_GOTO3("ERROR#%d: Could not add a tile at index %d.\n",
954 err, index, Err2);
955 }
956 }
957 break;
958
959 case FEATURE_ICCP:
960 ok = ReadFile(config->input_, &mux);
961 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700962 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530963 if (!ok) goto Err2;
964 err = WebPMuxSetColorProfile(mux, data, size, 1);
965 free((void*)data);
966 if (err != WEBP_MUX_OK) {
967 ERROR_GOTO2("ERROR#%d: Could not set color profile.\n", err, Err2);
968 }
969 break;
970
971 case FEATURE_XMP:
972 ok = ReadFile(config->input_, &mux);
973 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700974 ok = ReadData(feature->args_[0].filename_, (void**)&data, &size);
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530975 if (!ok) goto Err2;
976 err = WebPMuxSetMetadata(mux, data, size, 1);
977 free((void*)data);
978 if (err != WEBP_MUX_OK) {
979 ERROR_GOTO2("ERROR#%d: Could not set XMP metadata.\n", err, Err2);
980 }
981 break;
982
983 default:
984 ERROR_GOTO1("ERROR: Invalid feature for action 'set'.\n", Err2);
985 break;
986 }
987 ok = WriteWebP(mux, config->output_);
988 break;
989
990 case ACTION_STRIP:
991 ok = ReadFile(config->input_, &mux);
992 if (!ok) goto Err2;
James Zern04e84cf2011-11-04 15:20:08 -0700993 switch (feature->type_) {
Urvang Joshia4f32ca2011-09-30 11:07:01 +0530994 case FEATURE_ICCP:
995 err = WebPMuxDeleteColorProfile(mux);
996 if (err != WEBP_MUX_OK) {
997 ERROR_GOTO2("ERROR#%d: Could not delete color profile.\n", err,
998 Err2);
999 }
1000 break;
1001 case FEATURE_XMP:
1002 err = WebPMuxDeleteMetadata(mux);
1003 if (err != WEBP_MUX_OK) {
1004 ERROR_GOTO2("ERROR#%d: Could not delete XMP metadata.\n", err,
1005 Err2);
1006 }
1007 break;
1008 default:
1009 ERROR_GOTO1("ERROR: Invalid feature for action 'strip'.\n", Err2);
1010 break;
1011 }
1012 ok = WriteWebP(mux, config->output_);
1013 break;
1014
1015 case ACTION_INFO:
1016 ok = ReadFile(config->input_, &mux);
1017 if (!ok) goto Err2;
1018 ok = (DisplayInfo(mux) == WEBP_MUX_OK);
1019 break;
1020
1021 default:
1022 assert(0); // Invalid action.
1023 break;
1024 }
1025
1026 Err2:
1027 WebPMuxDelete(mux);
1028 return ok;
1029}
1030
1031//------------------------------------------------------------------------------
1032// Main.
1033
1034int main(int argc, const char* argv[]) {
1035 int ok = 1;
1036 WebPMuxConfig* config;
1037 ok = InitializeConfig(argc-1, argv+1, &config);
1038 if (ok) {
1039 Process(config);
1040 } else {
1041 PrintHelp();
1042 }
1043 DeleteConfig(config);
1044 return ok;
1045}
1046
1047//------------------------------------------------------------------------------