blob: 86b217c0371f2a5e2d8efe128121986615da465e [file] [log] [blame]
Josh Coalsonee44de42002-11-08 06:07:20 +00001/* metaflac - Command-line FLAC metadata editor
Josh Coalson0395dac2006-04-25 06:59:33 +00002 * Copyright (C) 2001,2002,2003,2004,2005,2006 Josh Coalson
Josh Coalsonee44de42002-11-08 06:07:20 +00003 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
Josh Coalsonb1ec7962006-05-24 04:41:36 +000019#if HAVE_CONFIG_H
20# include <config.h>
21#endif
22
Josh Coalsonee44de42002-11-08 06:07:20 +000023#include "options.h"
24#include "usage.h"
25#include "utils.h"
26#include "FLAC/assert.h"
Josh Coalson83b8d282005-08-31 00:30:41 +000027#include "share/grabbag/replaygain.h"
Josh Coalsonee44de42002-11-08 06:07:20 +000028#include <ctype.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33/*
34 share__getopt format struct; note we don't use short options so we just
35 set the 'val' field to 0 everywhere to indicate a valid option.
36*/
37struct share__option long_options_[] = {
38 /* global options */
39 { "preserve-modtime", 0, 0, 0 },
40 { "with-filename", 0, 0, 0 },
41 { "no-filename", 0, 0, 0 },
42 { "no-utf8-convert", 0, 0, 0 },
43 { "dont-use-padding", 0, 0, 0 },
Josh Coalsoncfae6cd2002-11-27 04:44:54 +000044 { "no-cued-seekpoints", 0, 0, 0 },
Josh Coalsonee44de42002-11-08 06:07:20 +000045 /* shorthand operations */
46 { "show-md5sum", 0, 0, 0 },
47 { "show-min-blocksize", 0, 0, 0 },
48 { "show-max-blocksize", 0, 0, 0 },
49 { "show-min-framesize", 0, 0, 0 },
50 { "show-max-framesize", 0, 0, 0 },
51 { "show-sample-rate", 0, 0, 0 },
52 { "show-channels", 0, 0, 0 },
53 { "show-bps", 0, 0, 0 },
54 { "show-total-samples", 0, 0, 0 },
55 { "set-md5sum", 1, 0, 0 }, /* undocumented */
56 { "set-min-blocksize", 1, 0, 0 }, /* undocumented */
57 { "set-max-blocksize", 1, 0, 0 }, /* undocumented */
58 { "set-min-framesize", 1, 0, 0 }, /* undocumented */
59 { "set-max-framesize", 1, 0, 0 }, /* undocumented */
60 { "set-sample-rate", 1, 0, 0 }, /* undocumented */
61 { "set-channels", 1, 0, 0 }, /* undocumented */
62 { "set-bps", 1, 0, 0 }, /* undocumented */
Josh Coalsonb7b98be2004-09-12 22:55:38 +000063 { "set-total-samples", 1, 0, 0 }, /* undocumented */ /* WATCHOUT: used by test/test_flac.sh on windows */
Josh Coalson0ceb4492004-09-08 00:55:16 +000064 { "show-vendor-tag", 0, 0, 0 },
65 { "show-tag", 1, 0, 0 },
66 { "remove-all-tags", 0, 0, 0 },
67 { "remove-tag", 1, 0, 0 },
68 { "remove-first-tag", 1, 0, 0 },
69 { "set-tag", 1, 0, 0 },
Josh Coalson26c82352006-05-18 07:57:16 +000070 { "set-tag-from-file", 1, 0, 0 },
Josh Coalson0ceb4492004-09-08 00:55:16 +000071 { "import-tags-from", 1, 0, 0 },
72 { "export-tags-to", 1, 0, 0 },
Josh Coalson303123f2002-11-26 06:21:06 +000073 { "import-cuesheet-from", 1, 0, 0 },
74 { "export-cuesheet-to", 1, 0, 0 },
Josh Coalsonb02574e2006-09-26 00:43:48 +000075 { "import-picture", 1, 0, 0 },
Josh Coalson989f2672006-11-02 03:40:44 +000076 { "export-picture-to", 1, 0, 0 },
Josh Coalsonee44de42002-11-08 06:07:20 +000077 { "add-seekpoint", 1, 0, 0 },
78 { "add-replay-gain", 0, 0, 0 },
Josh Coalson83b8d282005-08-31 00:30:41 +000079 { "remove-replay-gain", 0, 0, 0 },
Josh Coalsonee44de42002-11-08 06:07:20 +000080 { "add-padding", 1, 0, 0 },
81 /* major operations */
82 { "help", 0, 0, 0 },
83 { "version", 0, 0, 0 },
84 { "list", 0, 0, 0 },
85 { "append", 0, 0, 0 },
86 { "remove", 0, 0, 0 },
87 { "remove-all", 0, 0, 0 },
88 { "merge-padding", 0, 0, 0 },
89 { "sort-padding", 0, 0, 0 },
90 /* major operation arguments */
91 { "block-number", 1, 0, 0 },
92 { "block-type", 1, 0, 0 },
93 { "except-block-type", 1, 0, 0 },
94 { "data-format", 1, 0, 0 },
95 { "application-data-format", 1, 0, 0 },
96 { "from-file", 1, 0, 0 },
97 {0, 0, 0, 0}
98};
99
100static FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options);
101static void append_new_operation(CommandLineOptions *options, Operation operation);
102static void append_new_argument(CommandLineOptions *options, Argument argument);
103static Operation *append_major_operation(CommandLineOptions *options, OperationType type);
104static Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type);
Josh Coalson989f2672006-11-02 03:40:44 +0000105static Argument *find_argument(CommandLineOptions *options, ArgumentType type);
Josh Coalsonee44de42002-11-08 06:07:20 +0000106static Operation *find_shorthand_operation(CommandLineOptions *options, OperationType type);
107static Argument *append_argument(CommandLineOptions *options, ArgumentType type);
108static FLAC__bool parse_md5(const char *src, FLAC__byte dest[16]);
109static FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest);
110static FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest);
Josh Coalsonb02574e2006-09-26 00:43:48 +0000111static FLAC__bool parse_string(const char *src, char **dest);
Josh Coalsonee44de42002-11-08 06:07:20 +0000112static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation);
113static FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation);
114static FLAC__bool parse_add_padding(const char *in, unsigned *out);
115static FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out);
116static FLAC__bool parse_block_type(const char *in, Argument_BlockType *out);
117static FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out);
118static FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out);
119static void undocumented_warning(const char *opt);
120
121
122void init_options(CommandLineOptions *options)
123{
124 options->preserve_modtime = false;
125
126 /* '2' is a hack to mean "use default if not forced on command line" */
127 FLAC__ASSERT(true != 2);
128 options->prefix_with_filename = 2;
129
130 options->utf8_convert = true;
131 options->use_padding = true;
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000132 options->cued_seekpoints = true;
Josh Coalsonee44de42002-11-08 06:07:20 +0000133 options->show_long_help = false;
134 options->show_version = false;
135 options->application_data_format_is_hexdump = false;
136
137 options->ops.operations = 0;
138 options->ops.num_operations = 0;
139 options->ops.capacity = 0;
140
141 options->args.arguments = 0;
142 options->args.num_arguments = 0;
143 options->args.capacity = 0;
144
145 options->args.checks.num_shorthand_ops = 0;
146 options->args.checks.num_major_ops = 0;
147 options->args.checks.has_block_type = false;
148 options->args.checks.has_except_block_type = false;
149
150 options->num_files = 0;
151 options->filenames = 0;
152}
153
154FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options)
155{
156 int ret;
157 int option_index = 1;
158 FLAC__bool had_error = false;
159
160 while ((ret = share__getopt_long(argc, argv, "", long_options_, &option_index)) != -1) {
161 switch (ret) {
162 case 0:
163 had_error |= !parse_option(option_index, share__optarg, options);
164 break;
165 case '?':
166 case ':':
167 had_error = true;
168 break;
169 default:
170 FLAC__ASSERT(0);
171 break;
172 }
173 }
174
175 if(options->prefix_with_filename == 2)
176 options->prefix_with_filename = (argc - share__optind > 1);
177
178 if(share__optind >= argc && !options->show_long_help && !options->show_version) {
179 fprintf(stderr,"ERROR: you must specify at least one FLAC file;\n");
180 fprintf(stderr," metaflac cannot be used as a pipe\n");
181 had_error = true;
182 }
183
184 options->num_files = argc - share__optind;
185
186 if(options->num_files > 0) {
187 unsigned i = 0;
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000188 if(0 == (options->filenames = (char**)malloc(sizeof(char*) * options->num_files)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000189 die("out of memory allocating space for file names list");
190 while(share__optind < argc)
191 options->filenames[i++] = local_strdup(argv[share__optind++]);
192 }
193
194 if(options->args.checks.num_major_ops > 0) {
195 if(options->args.checks.num_major_ops > 1) {
196 fprintf(stderr, "ERROR: you may only specify one major operation at a time\n");
197 had_error = true;
198 }
199 else if(options->args.checks.num_shorthand_ops > 0) {
200 fprintf(stderr, "ERROR: you may not mix shorthand and major operations\n");
201 had_error = true;
202 }
203 }
204
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000205 /* check for only one FLAC file used with --import-cuesheet-from/--export-cuesheet-to */
Josh Coalson989f2672006-11-02 03:40:44 +0000206 if(
207 (
208 0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM) ||
209 0 != find_shorthand_operation(options, OP__EXPORT_CUESHEET_TO) ||
210 0 != find_shorthand_operation(options, OP__EXPORT_PICTURE_TO)
211 ) && options->num_files > 1
212 ) {
213 fprintf(stderr, "ERROR: you may only specify one FLAC file when using '--import-cuesheet-from', '--export-cuesheet-to' or '--export-picture-to'\n");
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000214 had_error = true;
215 }
Josh Coalson303123f2002-11-26 06:21:06 +0000216
Josh Coalsonee44de42002-11-08 06:07:20 +0000217 if(options->args.checks.has_block_type && options->args.checks.has_except_block_type) {
218 fprintf(stderr, "ERROR: you may not specify both '--block-type' and '--except-block-type'\n");
219 had_error = true;
220 }
221
222 if(had_error)
223 short_usage(0);
224
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000225 /*
226 * We need to create an OP__ADD_SEEKPOINT operation if there is
227 * not one already, and --import-cuesheet-from was specified but
228 * --no-cued-seekpoints was not:
229 */
230 if(options->cued_seekpoints) {
231 Operation *op = find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM);
232 if(0 != op) {
233 Operation *op2 = find_shorthand_operation(options, OP__ADD_SEEKPOINT);
234 if(0 == op2)
235 op2 = append_shorthand_operation(options, OP__ADD_SEEKPOINT);
236 op->argument.import_cuesheet_from.add_seekpoint_link = &(op2->argument.add_seekpoint);
237 }
238 }
239
Josh Coalsonee44de42002-11-08 06:07:20 +0000240 return !had_error;
241}
242
243void free_options(CommandLineOptions *options)
244{
245 unsigned i;
246 Operation *op;
247 Argument *arg;
248
249 FLAC__ASSERT(0 == options->ops.operations || options->ops.num_operations > 0);
250 FLAC__ASSERT(0 == options->args.arguments || options->args.num_arguments > 0);
251
252 for(i = 0, op = options->ops.operations; i < options->ops.num_operations; i++, op++) {
253 switch(op->type) {
254 case OP__SHOW_VC_FIELD:
255 case OP__REMOVE_VC_FIELD:
256 case OP__REMOVE_VC_FIRSTFIELD:
257 if(0 != op->argument.vc_field_name.value)
258 free(op->argument.vc_field_name.value);
259 break;
260 case OP__SET_VC_FIELD:
261 if(0 != op->argument.vc_field.field)
262 free(op->argument.vc_field.field);
263 if(0 != op->argument.vc_field.field_name)
264 free(op->argument.vc_field.field_name);
265 if(0 != op->argument.vc_field.field_value)
266 free(op->argument.vc_field.field_value);
267 break;
268 case OP__IMPORT_VC_FROM:
269 case OP__EXPORT_VC_TO:
Josh Coalson303123f2002-11-26 06:21:06 +0000270 case OP__EXPORT_CUESHEET_TO:
271 if(0 != op->argument.filename.value)
272 free(op->argument.filename.value);
Josh Coalsonee44de42002-11-08 06:07:20 +0000273 break;
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000274 case OP__IMPORT_CUESHEET_FROM:
275 if(0 != op->argument.import_cuesheet_from.filename)
276 free(op->argument.import_cuesheet_from.filename);
277 break;
Josh Coalsonb02574e2006-09-26 00:43:48 +0000278 case OP__IMPORT_PICTURE:
279 if(0 != op->argument.specification.value)
280 free(op->argument.specification.value);
281 break;
Josh Coalson989f2672006-11-02 03:40:44 +0000282 case OP__EXPORT_PICTURE_TO:
283 if(0 != op->argument.export_picture_to.filename)
284 free(op->argument.export_picture_to.filename);
285 break;
Josh Coalsonee44de42002-11-08 06:07:20 +0000286 case OP__ADD_SEEKPOINT:
287 if(0 != op->argument.add_seekpoint.specification)
288 free(op->argument.add_seekpoint.specification);
289 break;
290 default:
291 break;
292 }
293 }
294
295 for(i = 0, arg = options->args.arguments; i < options->args.num_arguments; i++, arg++) {
296 switch(arg->type) {
297 case ARG__BLOCK_NUMBER:
298 if(0 != arg->value.block_number.entries)
299 free(arg->value.block_number.entries);
300 break;
301 case ARG__BLOCK_TYPE:
302 case ARG__EXCEPT_BLOCK_TYPE:
303 if(0 != arg->value.block_type.entries)
304 free(arg->value.block_type.entries);
305 break;
306 case ARG__FROM_FILE:
307 if(0 != arg->value.from_file.file_name)
308 free(arg->value.from_file.file_name);
309 break;
310 default:
311 break;
312 }
313 }
314
315 if(0 != options->ops.operations)
316 free(options->ops.operations);
317
318 if(0 != options->args.arguments)
319 free(options->args.arguments);
320
Josh Coalson4fa90592002-12-04 07:01:37 +0000321 if(0 != options->filenames) {
322 for(i = 0; i < options->num_files; i++) {
323 if(0 != options->filenames[i])
324 free(options->filenames[i]);
325 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000326 free(options->filenames);
Josh Coalson4fa90592002-12-04 07:01:37 +0000327 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000328}
329
330/*
331 * local routines
332 */
333
334FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options)
335{
336 const char *opt = long_options_[option_index].name;
337 Operation *op;
338 Argument *arg;
339 FLAC__bool ok = true;
340
341 if(0 == strcmp(opt, "preserve-modtime")) {
342 options->preserve_modtime = true;
343 }
344 else if(0 == strcmp(opt, "with-filename")) {
345 options->prefix_with_filename = true;
346 }
347 else if(0 == strcmp(opt, "no-filename")) {
348 options->prefix_with_filename = false;
349 }
350 else if(0 == strcmp(opt, "no-utf8-convert")) {
351 options->utf8_convert = false;
352 }
353 else if(0 == strcmp(opt, "dont-use-padding")) {
354 options->use_padding = false;
355 }
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000356 else if(0 == strcmp(opt, "no-cued-seekpoints")) {
357 options->cued_seekpoints = false;
358 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000359 else if(0 == strcmp(opt, "show-md5sum")) {
360 (void) append_shorthand_operation(options, OP__SHOW_MD5SUM);
361 }
362 else if(0 == strcmp(opt, "show-min-blocksize")) {
363 (void) append_shorthand_operation(options, OP__SHOW_MIN_BLOCKSIZE);
364 }
365 else if(0 == strcmp(opt, "show-max-blocksize")) {
366 (void) append_shorthand_operation(options, OP__SHOW_MAX_BLOCKSIZE);
367 }
368 else if(0 == strcmp(opt, "show-min-framesize")) {
369 (void) append_shorthand_operation(options, OP__SHOW_MIN_FRAMESIZE);
370 }
371 else if(0 == strcmp(opt, "show-max-framesize")) {
372 (void) append_shorthand_operation(options, OP__SHOW_MAX_FRAMESIZE);
373 }
374 else if(0 == strcmp(opt, "show-sample-rate")) {
375 (void) append_shorthand_operation(options, OP__SHOW_SAMPLE_RATE);
376 }
377 else if(0 == strcmp(opt, "show-channels")) {
378 (void) append_shorthand_operation(options, OP__SHOW_CHANNELS);
379 }
380 else if(0 == strcmp(opt, "show-bps")) {
381 (void) append_shorthand_operation(options, OP__SHOW_BPS);
382 }
383 else if(0 == strcmp(opt, "show-total-samples")) {
384 (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES);
385 }
386 else if(0 == strcmp(opt, "set-md5sum")) {
387 op = append_shorthand_operation(options, OP__SET_MD5SUM);
388 FLAC__ASSERT(0 != option_argument);
389 if(!parse_md5(option_argument, op->argument.streaminfo_md5.value)) {
390 fprintf(stderr, "ERROR (--%s): bad MD5 sum\n", opt);
391 ok = false;
392 }
393 else
394 undocumented_warning(opt);
395 }
396 else if(0 == strcmp(opt, "set-min-blocksize")) {
397 op = append_shorthand_operation(options, OP__SET_MIN_BLOCKSIZE);
398 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) {
399 fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
400 ok = false;
401 }
402 else
403 undocumented_warning(opt);
404 }
405 else if(0 == strcmp(opt, "set-max-blocksize")) {
406 op = append_shorthand_operation(options, OP__SET_MAX_BLOCKSIZE);
407 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BLOCK_SIZE || op->argument.streaminfo_uint32.value > FLAC__MAX_BLOCK_SIZE) {
408 fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
409 ok = false;
410 }
411 else
412 undocumented_warning(opt);
413 }
414 else if(0 == strcmp(opt, "set-min-framesize")) {
415 op = append_shorthand_operation(options, OP__SET_MIN_FRAMESIZE);
416 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) {
417 fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN);
418 ok = false;
419 }
420 else
421 undocumented_warning(opt);
422 }
423 else if(0 == strcmp(opt, "set-max-framesize")) {
424 op = append_shorthand_operation(options, OP__SET_MAX_FRAMESIZE);
425 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) {
426 fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN);
427 ok = false;
428 }
429 else
430 undocumented_warning(opt);
431 }
432 else if(0 == strcmp(opt, "set-sample-rate")) {
433 op = append_shorthand_operation(options, OP__SET_SAMPLE_RATE);
434 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || !FLAC__format_sample_rate_is_valid(op->argument.streaminfo_uint32.value)) {
435 fprintf(stderr, "ERROR (--%s): invalid sample rate\n", opt);
436 ok = false;
437 }
438 else
439 undocumented_warning(opt);
440 }
441 else if(0 == strcmp(opt, "set-channels")) {
442 op = append_shorthand_operation(options, OP__SET_CHANNELS);
443 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value > FLAC__MAX_CHANNELS) {
444 fprintf(stderr, "ERROR (--%s): value must be > 0 and <= %u\n", opt, FLAC__MAX_CHANNELS);
445 ok = false;
446 }
447 else
448 undocumented_warning(opt);
449 }
450 else if(0 == strcmp(opt, "set-bps")) {
451 op = append_shorthand_operation(options, OP__SET_BPS);
452 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value < FLAC__MIN_BITS_PER_SAMPLE || op->argument.streaminfo_uint32.value > FLAC__MAX_BITS_PER_SAMPLE) {
453 fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE);
454 ok = false;
455 }
456 else
457 undocumented_warning(opt);
458 }
459 else if(0 == strcmp(opt, "set-total-samples")) {
460 op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES);
461 if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) {
462 fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN);
463 ok = false;
464 }
465 else
466 undocumented_warning(opt);
467 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000468 else if(0 == strcmp(opt, "show-vendor-tag")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000469 (void) append_shorthand_operation(options, OP__SHOW_VC_VENDOR);
470 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000471 else if(0 == strcmp(opt, "show-tag")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000472 const char *violation;
473 op = append_shorthand_operation(options, OP__SHOW_VC_FIELD);
474 FLAC__ASSERT(0 != option_argument);
475 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
476 FLAC__ASSERT(0 != violation);
477 fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation);
478 ok = false;
479 }
480 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000481 else if(0 == strcmp(opt, "remove-all-tags")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000482 (void) append_shorthand_operation(options, OP__REMOVE_VC_ALL);
483 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000484 else if(0 == strcmp(opt, "remove-tag")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000485 const char *violation;
486 op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
487 FLAC__ASSERT(0 != option_argument);
488 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
489 FLAC__ASSERT(0 != violation);
490 fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation);
491 ok = false;
492 }
493 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000494 else if(0 == strcmp(opt, "remove-first-tag")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000495 const char *violation;
496 op = append_shorthand_operation(options, OP__REMOVE_VC_FIRSTFIELD);
497 FLAC__ASSERT(0 != option_argument);
498 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
499 FLAC__ASSERT(0 != violation);
500 fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n %s\n", opt, option_argument, violation);
501 ok = false;
502 }
503 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000504 else if(0 == strcmp(opt, "set-tag")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000505 const char *violation;
506 op = append_shorthand_operation(options, OP__SET_VC_FIELD);
507 FLAC__ASSERT(0 != option_argument);
Josh Coalson26c82352006-05-18 07:57:16 +0000508 op->argument.vc_field.field_value_from_file = false;
509 if(!parse_vorbis_comment_field(option_argument, &(op->argument.vc_field.field), &(op->argument.vc_field.field_name), &(op->argument.vc_field.field_value), &(op->argument.vc_field.field_value_length), &violation)) {
510 FLAC__ASSERT(0 != violation);
511 fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n %s\n", opt, option_argument, violation);
512 ok = false;
513 }
514 }
515 else if(0 == strcmp(opt, "set-tag-from-file")) {
516 const char *violation;
517 op = append_shorthand_operation(options, OP__SET_VC_FIELD);
518 FLAC__ASSERT(0 != option_argument);
519 op->argument.vc_field.field_value_from_file = true;
Josh Coalsonee44de42002-11-08 06:07:20 +0000520 if(!parse_vorbis_comment_field(option_argument, &(op->argument.vc_field.field), &(op->argument.vc_field.field_name), &(op->argument.vc_field.field_value), &(op->argument.vc_field.field_value_length), &violation)) {
521 FLAC__ASSERT(0 != violation);
522 fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n %s\n", opt, option_argument, violation);
523 ok = false;
524 }
525 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000526 else if(0 == strcmp(opt, "import-tags-from")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000527 op = append_shorthand_operation(options, OP__IMPORT_VC_FROM);
528 FLAC__ASSERT(0 != option_argument);
Josh Coalsonb02574e2006-09-26 00:43:48 +0000529 if(!parse_string(option_argument, &(op->argument.filename.value))) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000530 fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
531 ok = false;
532 }
533 }
Josh Coalson2e29c932006-09-26 01:25:52 +0000534 else if(0 == strcmp(opt, "export-tags-to")) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000535 op = append_shorthand_operation(options, OP__EXPORT_VC_TO);
536 FLAC__ASSERT(0 != option_argument);
Josh Coalsonb02574e2006-09-26 00:43:48 +0000537 if(!parse_string(option_argument, &(op->argument.filename.value))) {
Josh Coalson303123f2002-11-26 06:21:06 +0000538 fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
539 ok = false;
540 }
541 }
542 else if(0 == strcmp(opt, "import-cuesheet-from")) {
Josh Coalsoncfae6cd2002-11-27 04:44:54 +0000543 if(0 != find_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM)) {
544 fprintf(stderr, "ERROR (--%s): may be specified only once\n", opt);
545 ok = false;
546 }
Josh Coalson303123f2002-11-26 06:21:06 +0000547 op = append_shorthand_operation(options, OP__IMPORT_CUESHEET_FROM);
548 FLAC__ASSERT(0 != option_argument);
Josh Coalsonb02574e2006-09-26 00:43:48 +0000549 if(!parse_string(option_argument, &(op->argument.import_cuesheet_from.filename))) {
Josh Coalson303123f2002-11-26 06:21:06 +0000550 fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
551 ok = false;
552 }
553 }
554 else if(0 == strcmp(opt, "export-cuesheet-to")) {
555 op = append_shorthand_operation(options, OP__EXPORT_CUESHEET_TO);
556 FLAC__ASSERT(0 != option_argument);
Josh Coalsonb02574e2006-09-26 00:43:48 +0000557 if(!parse_string(option_argument, &(op->argument.filename.value))) {
Josh Coalsonee44de42002-11-08 06:07:20 +0000558 fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
559 ok = false;
560 }
561 }
Josh Coalsonb02574e2006-09-26 00:43:48 +0000562 else if(0 == strcmp(opt, "import-picture")) {
563 op = append_shorthand_operation(options, OP__IMPORT_PICTURE);
564 FLAC__ASSERT(0 != option_argument);
565 if(!parse_string(option_argument, &(op->argument.specification.value))) {
566 fprintf(stderr, "ERROR (--%s): missing specification\n", opt);
567 ok = false;
568 }
569 }
Josh Coalson989f2672006-11-02 03:40:44 +0000570 else if(0 == strcmp(opt, "export-picture-to")) {
571 const Argument *arg = find_argument(options, ARG__BLOCK_NUMBER);
572 op = append_shorthand_operation(options, OP__EXPORT_PICTURE_TO);
573 FLAC__ASSERT(0 != option_argument);
574 if(!parse_string(option_argument, &(op->argument.export_picture_to.filename))) {
575 fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
576 ok = false;
577 }
578 op->argument.export_picture_to.block_number_link = arg? &(arg->value.block_number) : 0;
579 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000580 else if(0 == strcmp(opt, "add-seekpoint")) {
581 const char *violation;
582 char *spec;
583 FLAC__ASSERT(0 != option_argument);
584 if(!parse_add_seekpoint(option_argument, &spec, &violation)) {
585 FLAC__ASSERT(0 != violation);
586 fprintf(stderr, "ERROR (--%s): malformed seekpoint specification \"%s\",\n %s\n", opt, option_argument, violation);
587 ok = false;
588 }
Josh Coalsonb0479642005-08-31 00:21:14 +0000589 else {
590 op = find_shorthand_operation(options, OP__ADD_SEEKPOINT);
591 if(0 == op)
592 op = append_shorthand_operation(options, OP__ADD_SEEKPOINT);
593 local_strcat(&(op->argument.add_seekpoint.specification), spec);
594 local_strcat(&(op->argument.add_seekpoint.specification), ";");
595 free(spec);
596 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000597 }
598 else if(0 == strcmp(opt, "add-replay-gain")) {
599 (void) append_shorthand_operation(options, OP__ADD_REPLAY_GAIN);
600 }
Josh Coalson83b8d282005-08-31 00:30:41 +0000601 else if(0 == strcmp(opt, "remove-replay-gain")) {
Josh Coalson04720582006-09-15 04:25:39 +0000602 const FLAC__byte * const tags[5] = {
603 GRABBAG__REPLAYGAIN_TAG_REFERENCE_LOUDNESS,
Josh Coalson83b8d282005-08-31 00:30:41 +0000604 GRABBAG__REPLAYGAIN_TAG_TITLE_GAIN,
605 GRABBAG__REPLAYGAIN_TAG_TITLE_PEAK,
606 GRABBAG__REPLAYGAIN_TAG_ALBUM_GAIN,
607 GRABBAG__REPLAYGAIN_TAG_ALBUM_PEAK
608 };
609 size_t i;
610 for(i = 0; i < sizeof(tags)/sizeof(tags[0]); i++) {
611 op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
612 op->argument.vc_field_name.value = local_strdup((const char *)tags[i]);
613 }
614 }
Josh Coalsonee44de42002-11-08 06:07:20 +0000615 else if(0 == strcmp(opt, "add-padding")) {
616 op = append_shorthand_operation(options, OP__ADD_PADDING);
617 FLAC__ASSERT(0 != option_argument);
618 if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) {
619 fprintf(stderr, "ERROR (--%s): illegal length \"%s\", length must be >= 0 and < 2^%u\n", opt, option_argument, FLAC__STREAM_METADATA_LENGTH_LEN);
620 ok = false;
621 }
622 }
623 else if(0 == strcmp(opt, "help")) {
624 options->show_long_help = true;
625 }
626 else if(0 == strcmp(opt, "version")) {
627 options->show_version = true;
628 }
629 else if(0 == strcmp(opt, "list")) {
630 (void) append_major_operation(options, OP__LIST);
631 }
632 else if(0 == strcmp(opt, "append")) {
633 (void) append_major_operation(options, OP__APPEND);
634 }
635 else if(0 == strcmp(opt, "remove")) {
636 (void) append_major_operation(options, OP__REMOVE);
637 }
638 else if(0 == strcmp(opt, "remove-all")) {
639 (void) append_major_operation(options, OP__REMOVE_ALL);
640 }
641 else if(0 == strcmp(opt, "merge-padding")) {
642 (void) append_major_operation(options, OP__MERGE_PADDING);
643 }
644 else if(0 == strcmp(opt, "sort-padding")) {
645 (void) append_major_operation(options, OP__SORT_PADDING);
646 }
647 else if(0 == strcmp(opt, "block-number")) {
648 arg = append_argument(options, ARG__BLOCK_NUMBER);
649 FLAC__ASSERT(0 != option_argument);
650 if(!parse_block_number(option_argument, &(arg->value.block_number))) {
651 fprintf(stderr, "ERROR: malformed block number specification \"%s\"\n", option_argument);
652 ok = false;
653 }
654 }
655 else if(0 == strcmp(opt, "block-type")) {
656 arg = append_argument(options, ARG__BLOCK_TYPE);
657 FLAC__ASSERT(0 != option_argument);
658 if(!parse_block_type(option_argument, &(arg->value.block_type))) {
659 fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
660 ok = false;
661 }
662 options->args.checks.has_block_type = true;
663 }
664 else if(0 == strcmp(opt, "except-block-type")) {
665 arg = append_argument(options, ARG__EXCEPT_BLOCK_TYPE);
666 FLAC__ASSERT(0 != option_argument);
667 if(!parse_block_type(option_argument, &(arg->value.block_type))) {
668 fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
669 ok = false;
670 }
671 options->args.checks.has_except_block_type = true;
672 }
673 else if(0 == strcmp(opt, "data-format")) {
674 arg = append_argument(options, ARG__DATA_FORMAT);
675 FLAC__ASSERT(0 != option_argument);
676 if(!parse_data_format(option_argument, &(arg->value.data_format))) {
677 fprintf(stderr, "ERROR (--%s): illegal data format \"%s\"\n", opt, option_argument);
678 ok = false;
679 }
680 }
681 else if(0 == strcmp(opt, "application-data-format")) {
682 FLAC__ASSERT(0 != option_argument);
683 if(!parse_application_data_format(option_argument, &(options->application_data_format_is_hexdump))) {
684 fprintf(stderr, "ERROR (--%s): illegal application data format \"%s\"\n", opt, option_argument);
685 ok = false;
686 }
687 }
688 else if(0 == strcmp(opt, "from-file")) {
689 arg = append_argument(options, ARG__FROM_FILE);
690 FLAC__ASSERT(0 != option_argument);
691 arg->value.from_file.file_name = local_strdup(option_argument);
692 }
693 else {
694 FLAC__ASSERT(0);
695 }
696
697 return ok;
698}
699
700void append_new_operation(CommandLineOptions *options, Operation operation)
701{
702 if(options->ops.capacity == 0) {
703 options->ops.capacity = 50;
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000704 if(0 == (options->ops.operations = (Operation*)malloc(sizeof(Operation) * options->ops.capacity)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000705 die("out of memory allocating space for option list");
706 memset(options->ops.operations, 0, sizeof(Operation) * options->ops.capacity);
707 }
708 if(options->ops.capacity <= options->ops.num_operations) {
709 unsigned original_capacity = options->ops.capacity;
710 options->ops.capacity *= 4;
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000711 if(0 == (options->ops.operations = (Operation*)realloc(options->ops.operations, sizeof(Operation) * options->ops.capacity)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000712 die("out of memory allocating space for option list");
713 memset(options->ops.operations + original_capacity, 0, sizeof(Operation) * (options->ops.capacity - original_capacity));
714 }
715
716 options->ops.operations[options->ops.num_operations++] = operation;
717}
718
719void append_new_argument(CommandLineOptions *options, Argument argument)
720{
721 if(options->args.capacity == 0) {
722 options->args.capacity = 50;
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000723 if(0 == (options->args.arguments = (Argument*)malloc(sizeof(Argument) * options->args.capacity)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000724 die("out of memory allocating space for option list");
725 memset(options->args.arguments, 0, sizeof(Argument) * options->args.capacity);
726 }
727 if(options->args.capacity <= options->args.num_arguments) {
728 unsigned original_capacity = options->args.capacity;
729 options->args.capacity *= 4;
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000730 if(0 == (options->args.arguments = (Argument*)realloc(options->args.arguments, sizeof(Argument) * options->args.capacity)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000731 die("out of memory allocating space for option list");
732 memset(options->args.arguments + original_capacity, 0, sizeof(Argument) * (options->args.capacity - original_capacity));
733 }
734
735 options->args.arguments[options->args.num_arguments++] = argument;
736}
737
738Operation *append_major_operation(CommandLineOptions *options, OperationType type)
739{
740 Operation op;
741 memset(&op, 0, sizeof(op));
742 op.type = type;
743 append_new_operation(options, op);
744 options->args.checks.num_major_ops++;
745 return options->ops.operations + (options->ops.num_operations - 1);
746}
747
748Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type)
749{
750 Operation op;
751 memset(&op, 0, sizeof(op));
752 op.type = type;
753 append_new_operation(options, op);
754 options->args.checks.num_shorthand_ops++;
755 return options->ops.operations + (options->ops.num_operations - 1);
756}
757
Josh Coalson989f2672006-11-02 03:40:44 +0000758Argument *find_argument(CommandLineOptions *options, ArgumentType type)
759{
760 unsigned i;
761 for(i = 0; i < options->args.num_arguments; i++)
762 if(options->args.arguments[i].type == type)
763 return &options->args.arguments[i];
764 return 0;
765}
766
Josh Coalsonee44de42002-11-08 06:07:20 +0000767Operation *find_shorthand_operation(CommandLineOptions *options, OperationType type)
768{
769 unsigned i;
770 for(i = 0; i < options->ops.num_operations; i++)
771 if(options->ops.operations[i].type == type)
772 return &options->ops.operations[i];
773 return 0;
774}
775
776Argument *append_argument(CommandLineOptions *options, ArgumentType type)
777{
778 Argument arg;
779 memset(&arg, 0, sizeof(arg));
780 arg.type = type;
781 append_new_argument(options, arg);
782 return options->args.arguments + (options->args.num_arguments - 1);
783}
784
785FLAC__bool parse_md5(const char *src, FLAC__byte dest[16])
786{
787 unsigned i, d;
788 int c;
789 FLAC__ASSERT(0 != src);
790 if(strlen(src) != 32)
791 return false;
792 /* strtoul() accepts negative numbers which we do not want, so we do it the hard way */
793 for(i = 0; i < 16; i++) {
794 c = (int)(*src++);
795 if(isdigit(c))
796 d = (unsigned)(c - '0');
797 else if(c >= 'a' && c <= 'f')
798 d = (unsigned)(c - 'a') + 10u;
799 else if(c >= 'A' && c <= 'F')
800 d = (unsigned)(c - 'A') + 10u;
801 else
802 return false;
803 d <<= 4;
804 c = (int)(*src++);
805 if(isdigit(c))
806 d |= (unsigned)(c - '0');
807 else if(c >= 'a' && c <= 'f')
808 d |= (unsigned)(c - 'a') + 10u;
809 else if(c >= 'A' && c <= 'F')
810 d |= (unsigned)(c - 'A') + 10u;
811 else
812 return false;
813 dest[i] = (FLAC__byte)d;
814 }
815 return true;
816}
817
818FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest)
819{
820 FLAC__ASSERT(0 != src);
821 if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
822 return false;
823 *dest = strtoul(src, 0, 10);
824 return true;
825}
826
827/* There's no stroull() in MSVC6 so we just write a specialized one */
828static FLAC__uint64 local__strtoull(const char *src)
829{
830 FLAC__uint64 ret = 0;
831 int c;
832 FLAC__ASSERT(0 != src);
833 while(0 != (c = *src++)) {
834 c -= '0';
835 if(c >= 0 && c <= 9)
836 ret = (ret * 10) + c;
837 else
838 break;
839 }
840 return ret;
841}
842
843FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest)
844{
845 FLAC__ASSERT(0 != src);
846 if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
847 return false;
848 *dest = local__strtoull(src);
849 return true;
850}
851
Josh Coalsonb02574e2006-09-26 00:43:48 +0000852FLAC__bool parse_string(const char *src, char **dest)
Josh Coalsonee44de42002-11-08 06:07:20 +0000853{
854 if(0 == src || strlen(src) == 0)
855 return false;
856 *dest = strdup(src);
857 return true;
858}
859
860FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation)
861{
862 static const char * const violations[] = {
863 "field name contains invalid character"
864 };
865
866 char *q, *s;
867
868 s = local_strdup(field_ref);
869
870 for(q = s; *q; q++) {
871 if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
872 free(s);
873 *violation = violations[0];
874 return false;
875 }
876 }
877
878 *name = s;
879
880 return true;
881}
882
883FLAC__bool parse_add_seekpoint(const char *in, char **out, const char **violation)
884{
885 static const char *garbled_ = "garbled specification";
886 const unsigned n = strlen(in);
887
888 FLAC__ASSERT(0 != in);
889 FLAC__ASSERT(0 != out);
890
891 if(n == 0) {
892 *violation = "specification is empty";
893 return false;
894 }
895
896 if(n > strspn(in, "0123456789.Xsx")) {
897 *violation = "specification contains invalid character";
898 return false;
899 }
900
901 if(in[n-1] == 'X') {
902 if(n > 1) {
903 *violation = garbled_;
904 return false;
905 }
906 }
907 else if(in[n-1] == 's') {
908 if(n-1 > strspn(in, "0123456789.")) {
909 *violation = garbled_;
910 return false;
911 }
912 }
913 else if(in[n-1] == 'x') {
914 if(n-1 > strspn(in, "0123456789")) {
915 *violation = garbled_;
916 return false;
917 }
918 }
919 else {
920 if(n > strspn(in, "0123456789")) {
921 *violation = garbled_;
922 return false;
923 }
924 }
925
926 *out = local_strdup(in);
927 return true;
928}
929
930FLAC__bool parse_add_padding(const char *in, unsigned *out)
931{
932 FLAC__ASSERT(0 != in);
933 FLAC__ASSERT(0 != out);
934 *out = (unsigned)strtoul(in, 0, 10);
935 return *out < (1u << FLAC__STREAM_METADATA_LENGTH_LEN);
936}
937
938FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out)
939{
940 char *p, *q, *s, *end;
941 long i;
942 unsigned entry;
943
944 if(*in == '\0')
945 return false;
946
947 s = local_strdup(in);
948
949 /* first count the entries */
950 for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
951 ;
952
953 /* make space */
954 FLAC__ASSERT(out->num_entries > 0);
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000955 if(0 == (out->entries = (unsigned*)malloc(sizeof(unsigned) * out->num_entries)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000956 die("out of memory allocating space for option list");
957
958 /* load 'em up */
959 entry = 0;
960 q = s;
961 while(q) {
962 FLAC__ASSERT(entry < out->num_entries);
963 if(0 != (p = strchr(q, ',')))
964 *p++ = '\0';
965 if(!isdigit((int)(*q)) || (i = strtol(q, &end, 10)) < 0 || *end) {
966 free(s);
967 return false;
968 }
969 out->entries[entry++] = (unsigned)i;
970 q = p;
971 }
972 FLAC__ASSERT(entry == out->num_entries);
973
974 free(s);
975 return true;
976}
977
978FLAC__bool parse_block_type(const char *in, Argument_BlockType *out)
979{
980 char *p, *q, *r, *s;
981 unsigned entry;
982
983 if(*in == '\0')
984 return false;
985
986 s = local_strdup(in);
987
988 /* first count the entries */
989 for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
990 ;
991
992 /* make space */
993 FLAC__ASSERT(out->num_entries > 0);
Josh Coalson2a9ae6a2004-12-30 03:55:29 +0000994 if(0 == (out->entries = (Argument_BlockTypeEntry*)malloc(sizeof(Argument_BlockTypeEntry) * out->num_entries)))
Josh Coalsonee44de42002-11-08 06:07:20 +0000995 die("out of memory allocating space for option list");
996
997 /* load 'em up */
998 entry = 0;
999 q = s;
1000 while(q) {
1001 FLAC__ASSERT(entry < out->num_entries);
1002 if(0 != (p = strchr(q, ',')))
1003 *p++ = 0;
1004 r = strchr(q, ':');
1005 if(r)
1006 *r++ = '\0';
1007 if(0 != r && 0 != strcmp(q, "APPLICATION")) {
1008 free(s);
1009 return false;
1010 }
1011 if(0 == strcmp(q, "STREAMINFO")) {
1012 out->entries[entry++].type = FLAC__METADATA_TYPE_STREAMINFO;
1013 }
1014 else if(0 == strcmp(q, "PADDING")) {
1015 out->entries[entry++].type = FLAC__METADATA_TYPE_PADDING;
1016 }
1017 else if(0 == strcmp(q, "APPLICATION")) {
1018 out->entries[entry].type = FLAC__METADATA_TYPE_APPLICATION;
1019 out->entries[entry].filter_application_by_id = (0 != r);
1020 if(0 != r) {
1021 if(strlen(r) == 4) {
1022 strcpy(out->entries[entry].application_id, r);
1023 }
1024 else if(strlen(r) == 10 && strncmp(r, "0x", 2) == 0 && strspn(r+2, "0123456789ABCDEFabcdef") == 8) {
1025 FLAC__uint32 x = strtoul(r+2, 0, 16);
1026 out->entries[entry].application_id[3] = (FLAC__byte)(x & 0xff);
1027 out->entries[entry].application_id[2] = (FLAC__byte)((x>>=8) & 0xff);
1028 out->entries[entry].application_id[1] = (FLAC__byte)((x>>=8) & 0xff);
1029 out->entries[entry].application_id[0] = (FLAC__byte)((x>>=8) & 0xff);
1030 }
1031 else {
1032 free(s);
1033 return false;
1034 }
1035 }
1036 entry++;
1037 }
1038 else if(0 == strcmp(q, "SEEKTABLE")) {
1039 out->entries[entry++].type = FLAC__METADATA_TYPE_SEEKTABLE;
1040 }
1041 else if(0 == strcmp(q, "VORBIS_COMMENT")) {
1042 out->entries[entry++].type = FLAC__METADATA_TYPE_VORBIS_COMMENT;
1043 }
Josh Coalsoncfae6cd2002-11-27 04:44:54 +00001044 else if(0 == strcmp(q, "CUESHEET")) {
1045 out->entries[entry++].type = FLAC__METADATA_TYPE_CUESHEET;
1046 }
Josh Coalsonb02574e2006-09-26 00:43:48 +00001047 else if(0 == strcmp(q, "PICTURE")) {
1048 out->entries[entry++].type = FLAC__METADATA_TYPE_PICTURE;
1049 }
Josh Coalsonee44de42002-11-08 06:07:20 +00001050 else {
1051 free(s);
1052 return false;
1053 }
1054 q = p;
1055 }
1056 FLAC__ASSERT(entry == out->num_entries);
1057
1058 free(s);
1059 return true;
1060}
1061
1062FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out)
1063{
1064 if(0 == strcmp(in, "binary"))
1065 out->is_binary = true;
1066 else if(0 == strcmp(in, "text"))
1067 out->is_binary = false;
1068 else
1069 return false;
1070 return true;
1071}
1072
1073FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out)
1074{
1075 if(0 == strcmp(in, "hexdump"))
1076 *out = true;
1077 else if(0 == strcmp(in, "text"))
1078 *out = false;
1079 else
1080 return false;
1081 return true;
1082}
1083
1084void undocumented_warning(const char *opt)
1085{
1086 fprintf(stderr, "WARNING: undocmented option --%s should be used with caution,\n only for repairing a damaged STREAMINFO block\n", opt);
1087}