fix bug where importing vorbis comments to a flac file with no vorbis comment block...
[platform/upstream/flac.git] / src / metaflac / main.c
1 /* metaflac - Command-line FLAC metadata editor
2  * Copyright (C) 2001,2002  Josh Coalson
3  *
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
19 /*@@@
20 more powerful operations yet to add:
21         add a seektable, using same args as flac
22 */
23
24 #if HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #include "FLAC/assert.h"
29 #include "FLAC/metadata.h"
30 #include "share/utf8.h"
31 #include <ctype.h>
32 #include <locale.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #if 0
39 /*[JEC] was:#if HAVE_GETOPT_LONG*/
40 /*[JEC] see flac/include/share/getopt.h as to why the change */
41 #  include <getopt.h>
42 #else
43 #  include "share/getopt.h"
44 #endif
45
46 /*
47    FLAC__share__getopt format struct; note we don't use short options so we just
48    set the 'val' field to 0 everywhere to indicate a valid option.
49 */
50 static struct FLAC__share__option long_options_[] = {
51         /* global options */
52         { "preserve-modtime", 0, 0, 0 },
53         { "with-filename", 0, 0, 0 },
54         { "no-filename", 0, 0, 0 },
55         { "no-utf8-convert", 0, 0, 0 },
56         { "dont-use-padding", 0, 0, 0 },
57         /* shorthand operations */
58         { "show-md5sum", 0, 0, 0 },
59         { "show-min-blocksize", 0, 0, 0 },
60         { "show-max-blocksize", 0, 0, 0 },
61         { "show-min-framesize", 0, 0, 0 },
62         { "show-max-framesize", 0, 0, 0 },
63         { "show-sample-rate", 0, 0, 0 },
64         { "show-channels", 0, 0, 0 },
65         { "show-bps", 0, 0, 0 },
66         { "show-total-samples", 0, 0, 0 },
67         { "set-md5sum", 1, 0, 0 }, /* undocumented */
68         { "set-min-blocksize", 1, 0, 0 }, /* undocumented */
69         { "set-max-blocksize", 1, 0, 0 }, /* undocumented */
70         { "set-min-framesize", 1, 0, 0 }, /* undocumented */
71         { "set-max-framesize", 1, 0, 0 }, /* undocumented */
72         { "set-sample-rate", 1, 0, 0 }, /* undocumented */
73         { "set-channels", 1, 0, 0 }, /* undocumented */
74         { "set-bps", 1, 0, 0 }, /* undocumented */
75         { "set-total-samples", 1, 0, 0 }, /* undocumented */
76         { "show-vc-vendor", 0, 0, 0 },
77         { "show-vc-field", 1, 0, 0 },
78         { "remove-vc-all", 0, 0, 0 },
79         { "remove-vc-field", 1, 0, 0 },
80         { "remove-vc-firstfield", 1, 0, 0 },
81         { "set-vc-field", 1, 0, 0 },
82         { "import-vc-from", 1, 0, 0 },
83         { "export-vc-to", 1, 0, 0 },
84         { "add-padding", 1, 0, 0 },
85         /* major operations */
86         { "help", 0, 0, 0 },
87         { "version", 0, 0, 0 },
88         { "list", 0, 0, 0 },
89         { "append", 0, 0, 0 },
90         { "remove", 0, 0, 0 },
91         { "remove-all", 0, 0, 0 },
92         { "merge-padding", 0, 0, 0 },
93         { "sort-padding", 0, 0, 0 },
94         /* major operation arguments */
95         { "block-number", 1, 0, 0 },
96         { "block-type", 1, 0, 0 },
97         { "except-block-type", 1, 0, 0 },
98         { "data-format", 1, 0, 0 },
99         { "application-data-format", 1, 0, 0 },
100         { "from-file", 1, 0, 0 },
101         {0, 0, 0, 0}
102 };
103
104 typedef enum {
105         OP__SHOW_MD5SUM,
106         OP__SHOW_MIN_BLOCKSIZE,
107         OP__SHOW_MAX_BLOCKSIZE,
108         OP__SHOW_MIN_FRAMESIZE,
109         OP__SHOW_MAX_FRAMESIZE,
110         OP__SHOW_SAMPLE_RATE,
111         OP__SHOW_CHANNELS,
112         OP__SHOW_BPS,
113         OP__SHOW_TOTAL_SAMPLES,
114         OP__SET_MD5SUM,
115         OP__SET_MIN_BLOCKSIZE,
116         OP__SET_MAX_BLOCKSIZE,
117         OP__SET_MIN_FRAMESIZE,
118         OP__SET_MAX_FRAMESIZE,
119         OP__SET_SAMPLE_RATE,
120         OP__SET_CHANNELS,
121         OP__SET_BPS,
122         OP__SET_TOTAL_SAMPLES,
123         OP__SHOW_VC_VENDOR,
124         OP__SHOW_VC_FIELD,
125         OP__REMOVE_VC_ALL,
126         OP__REMOVE_VC_FIELD,
127         OP__REMOVE_VC_FIRSTFIELD,
128         OP__SET_VC_FIELD,
129         OP__IMPORT_VC_FROM,
130         OP__EXPORT_VC_TO,
131         OP__ADD_PADDING,
132         OP__LIST,
133         OP__APPEND,
134         OP__REMOVE,
135         OP__REMOVE_ALL,
136         OP__MERGE_PADDING,
137         OP__SORT_PADDING
138 } OperationType;
139
140 typedef enum {
141         ARG__BLOCK_NUMBER,
142         ARG__BLOCK_TYPE,
143         ARG__EXCEPT_BLOCK_TYPE,
144         ARG__DATA_FORMAT,
145         ARG__FROM_FILE
146 } ArgumentType;
147
148 typedef struct {
149         FLAC__byte value[16];
150 } Argument_StreaminfoMD5;
151
152 typedef struct {
153         FLAC__uint32 value;
154 } Argument_StreaminfoUInt32;
155
156 typedef struct {
157         FLAC__uint64 value;
158 } Argument_StreaminfoUInt64;
159
160 typedef struct {
161         char *value;
162 } Argument_VcFieldName;
163
164 typedef struct {
165         char *field; /* the whole field as passed on the command line, i.e. "NAME=VALUE" */
166         char *field_name;
167         /* according to the vorbis spec, field values can contain \0 so simple C strings are not enough here */
168         unsigned field_value_length;
169         char *field_value;
170 } Argument_VcField;
171
172 typedef struct {
173         char *value;
174 } Argument_VcFilename;
175
176 typedef struct {
177         unsigned num_entries;
178         unsigned *entries;
179 } Argument_BlockNumber;
180
181 typedef struct {
182         FLAC__MetadataType type;
183         char application_id[4]; /* only relevant if type == FLAC__STREAM_METADATA_TYPE_APPLICATION */
184         FLAC__bool filter_application_by_id;
185 } Argument_BlockTypeEntry;
186
187 typedef struct {
188         unsigned num_entries;
189         Argument_BlockTypeEntry *entries;
190 } Argument_BlockType;
191
192 typedef struct {
193         FLAC__bool is_binary;
194 } Argument_DataFormat;
195
196 typedef struct {
197         char *file_name;
198 } Argument_FromFile;
199
200 typedef struct {
201         unsigned length;
202 } Argument_AddPadding;
203
204 typedef struct {
205         OperationType type;
206         union {
207                 Argument_StreaminfoMD5 streaminfo_md5;
208                 Argument_StreaminfoUInt32 streaminfo_uint32;
209                 Argument_StreaminfoUInt64 streaminfo_uint64;
210                 Argument_VcFieldName vc_field_name;
211                 Argument_VcField vc_field;
212                 Argument_VcFilename vc_filename;
213                 Argument_AddPadding add_padding;
214         } argument;
215 } Operation;
216
217 typedef struct {
218         ArgumentType type;
219         union {
220                 Argument_BlockNumber block_number;
221                 Argument_BlockType block_type;
222                 Argument_DataFormat data_format;
223                 Argument_FromFile from_file;
224         } value;
225 } Argument;
226
227 typedef struct {
228         FLAC__bool preserve_modtime;
229         FLAC__bool prefix_with_filename;
230         FLAC__bool utf8_convert;
231         FLAC__bool use_padding;
232         FLAC__bool show_long_help;
233         FLAC__bool show_version;
234         FLAC__bool application_data_format_is_hexdump;
235         struct {
236                 Operation *operations;
237                 unsigned num_operations;
238                 unsigned capacity;
239         } ops;
240         struct {
241                 struct {
242                         unsigned num_shorthand_ops;
243                         unsigned num_major_ops;
244                         FLAC__bool has_block_type;
245                         FLAC__bool has_except_block_type;
246                 } checks;
247                 Argument *arguments;
248                 unsigned num_arguments;
249                 unsigned capacity;
250         } args;
251         unsigned num_files;
252         char **filenames;
253 } CommandLineOptions;
254
255 static void die(const char *message);
256 static void init_options(CommandLineOptions *options);
257 static FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options);
258 static FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options);
259 static void free_options(CommandLineOptions *options);
260 static void append_new_operation(CommandLineOptions *options, Operation operation);
261 static void append_new_argument(CommandLineOptions *options, Argument argument);
262 static Operation *append_major_operation(CommandLineOptions *options, OperationType type);
263 static Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type);
264 static Argument *append_argument(CommandLineOptions *options, ArgumentType type);
265 static void show_version();
266 static int short_usage(const char *message, ...);
267 static int long_usage(const char *message, ...);
268 static char *local_strdup(const char *source);
269 static FLAC__bool parse_md5(const char *src, FLAC__byte dest[16]);
270 static FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest);
271 static FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest);
272 static FLAC__bool parse_filename(const char *src, char **dest);
273 static FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation);
274 static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation);
275 static FLAC__bool parse_add_padding(const char *in, unsigned *out);
276 static FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out);
277 static FLAC__bool parse_block_type(const char *in, Argument_BlockType *out);
278 static FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out);
279 static FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out);
280 static FLAC__bool do_operations(const CommandLineOptions *options);
281 static FLAC__bool do_major_operation(const CommandLineOptions *options);
282 static FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options);
283 static FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
284 static FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
285 static FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
286 static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
287 static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
288 static FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options);
289 static FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert);
290 static FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
291 static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
292 static FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw);
293 static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number);
294 static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application);
295 static void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f);
296 static void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f);
297 static FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write);
298 static FLAC__bool remove_vc_field(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
299 static FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
300 static FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw);
301 static FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw);
302 static FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool raw);
303 static FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry);
304 static void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent);
305 static void undocumented_warning(const char *opt);
306
307 int main(int argc, char *argv[])
308 {
309         CommandLineOptions options;
310         int ret = 0;
311
312         setlocale(LC_ALL, "");
313         init_options(&options);
314
315         if(parse_options(argc, argv, &options))
316                 ret = !do_operations(&options);
317
318         free_options(&options);
319
320         return ret;
321 }
322
323 void die(const char *message)
324 {
325         FLAC__ASSERT(0 != message);
326         fprintf(stderr, "ERROR: %s\n", message);
327         exit(1);
328 }
329
330 void init_options(CommandLineOptions *options)
331 {
332         options->preserve_modtime = false;
333
334         /* '2' is a hack to mean "use default if not forced on command line" */
335         FLAC__ASSERT(true != 2);
336         options->prefix_with_filename = 2;
337
338         options->utf8_convert = true;
339         options->use_padding = true;
340         options->show_long_help = false;
341         options->show_version = false;
342         options->application_data_format_is_hexdump = false;
343
344         options->ops.operations = 0;
345         options->ops.num_operations = 0;
346         options->ops.capacity = 0;
347
348         options->args.arguments = 0;
349         options->args.num_arguments = 0;
350         options->args.capacity = 0;
351
352         options->args.checks.num_shorthand_ops = 0;
353         options->args.checks.num_major_ops = 0;
354         options->args.checks.has_block_type = false;
355         options->args.checks.has_except_block_type = false;
356
357         options->num_files = 0;
358         options->filenames = 0;
359 }
360
361 FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options)
362 {
363         int ret;
364         int option_index = 1;
365         FLAC__bool had_error = false;
366
367         while ((ret = FLAC__share__getopt_long(argc, argv, "", long_options_, &option_index)) != -1) {
368                 switch (ret) {
369                         case 0:
370                                 had_error |= !parse_option(option_index, FLAC__share__optarg, options);
371                                 break;
372                         case '?':
373                         case ':':
374                                 had_error = true;
375                                 break;
376                         default:
377                                 FLAC__ASSERT(0);
378                                 break;
379                 }
380         }
381
382         if(options->prefix_with_filename == 2)
383                 options->prefix_with_filename = (argc - FLAC__share__optind > 1);
384
385         if(FLAC__share__optind >= argc && !options->show_long_help && !options->show_version) {
386                 fprintf(stderr,"ERROR: you must specify at least one FLAC file;\n");
387                 fprintf(stderr,"       metaflac cannot be used as a pipe\n");
388                 had_error = true;
389         }
390
391         options->num_files = argc - FLAC__share__optind;
392
393         if(options->num_files > 0) {
394                 unsigned i = 0;
395                 if(0 == (options->filenames = malloc(sizeof(char *) * options->num_files)))
396                         die("out of memory allocating space for file names list");
397                 while(FLAC__share__optind < argc)
398                         options->filenames[i++] = local_strdup(argv[FLAC__share__optind++]);
399         }
400
401         if(options->args.checks.num_major_ops > 0) {
402                 if(options->args.checks.num_major_ops > 1) {
403                         fprintf(stderr, "ERROR: you may only specify one major operation at a time\n");
404                         had_error = true;
405                 }
406                 else if(options->args.checks.num_shorthand_ops > 0) {
407                         fprintf(stderr, "ERROR: you may not mix shorthand and major operations\n");
408                         had_error = true;
409                 }
410         }
411
412         if(options->args.checks.has_block_type && options->args.checks.has_except_block_type) {
413                 fprintf(stderr, "ERROR: you may not specify both '--block-type' and '--except-block-type'\n");
414                 had_error = true;
415         }
416
417         if(had_error)
418                 short_usage(0);
419
420         return !had_error;
421 }
422
423 FLAC__bool parse_option(int option_index, const char *option_argument, CommandLineOptions *options)
424 {
425         const char *opt = long_options_[option_index].name;
426         Operation *op;
427         Argument *arg;
428         FLAC__bool ok = true;
429
430         if(0 == strcmp(opt, "preserve-modtime")) {
431                 options->preserve_modtime = true;
432         }
433         else if(0 == strcmp(opt, "with-filename")) {
434                 options->prefix_with_filename = true;
435         }
436         else if(0 == strcmp(opt, "no-filename")) {
437                 options->prefix_with_filename = false;
438         }
439         else if(0 == strcmp(opt, "no-utf8-convert")) {
440                 options->utf8_convert = false;
441         }
442         else if(0 == strcmp(opt, "dont-use-padding")) {
443                 options->use_padding = false;
444         }
445         else if(0 == strcmp(opt, "show-md5sum")) {
446                 (void) append_shorthand_operation(options, OP__SHOW_MD5SUM);
447         }
448         else if(0 == strcmp(opt, "show-min-blocksize")) {
449                 (void) append_shorthand_operation(options, OP__SHOW_MIN_BLOCKSIZE);
450         }
451         else if(0 == strcmp(opt, "show-max-blocksize")) {
452                 (void) append_shorthand_operation(options, OP__SHOW_MAX_BLOCKSIZE);
453         }
454         else if(0 == strcmp(opt, "show-min-framesize")) {
455                 (void) append_shorthand_operation(options, OP__SHOW_MIN_FRAMESIZE);
456         }
457         else if(0 == strcmp(opt, "show-max-framesize")) {
458                 (void) append_shorthand_operation(options, OP__SHOW_MAX_FRAMESIZE);
459         }
460         else if(0 == strcmp(opt, "show-sample-rate")) {
461                 (void) append_shorthand_operation(options, OP__SHOW_SAMPLE_RATE);
462         }
463         else if(0 == strcmp(opt, "show-channels")) {
464                 (void) append_shorthand_operation(options, OP__SHOW_CHANNELS);
465         }
466         else if(0 == strcmp(opt, "show-bps")) {
467                 (void) append_shorthand_operation(options, OP__SHOW_BPS);
468         }
469         else if(0 == strcmp(opt, "show-total-samples")) {
470                 (void) append_shorthand_operation(options, OP__SHOW_TOTAL_SAMPLES);
471         }
472         else if(0 == strcmp(opt, "set-md5sum")) {
473                 op = append_shorthand_operation(options, OP__SET_MD5SUM);
474                 FLAC__ASSERT(0 != option_argument);
475                 if(!parse_md5(option_argument, op->argument.streaminfo_md5.value)) {
476                         fprintf(stderr, "ERROR (--%s): bad MD5 sum\n", opt);
477                         ok = false;
478                 }
479                 else
480                         undocumented_warning(opt);
481         }
482         else if(0 == strcmp(opt, "set-min-blocksize")) {
483                 op = append_shorthand_operation(options, OP__SET_MIN_BLOCKSIZE);
484                 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) {
485                         fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
486                         ok = false;
487                 }
488                 else
489                         undocumented_warning(opt);
490         }
491         else if(0 == strcmp(opt, "set-max-blocksize")) {
492                 op = append_shorthand_operation(options, OP__SET_MAX_BLOCKSIZE);
493                 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) {
494                         fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BLOCK_SIZE, FLAC__MAX_BLOCK_SIZE);
495                         ok = false;
496                 }
497                 else
498                         undocumented_warning(opt);
499         }
500         else if(0 == strcmp(opt, "set-min-framesize")) {
501                 op = append_shorthand_operation(options, OP__SET_MIN_FRAMESIZE);
502                 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) {
503                         fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN);
504                         ok = false;
505                 }
506                 else
507                         undocumented_warning(opt);
508         }
509         else if(0 == strcmp(opt, "set-max-framesize")) {
510                 op = append_shorthand_operation(options, OP__SET_MAX_FRAMESIZE);
511                 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) {
512                         fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN);
513                         ok = false;
514                 }
515                 else
516                         undocumented_warning(opt);
517         }
518         else if(0 == strcmp(opt, "set-sample-rate")) {
519                 op = append_shorthand_operation(options, OP__SET_SAMPLE_RATE);
520                 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || !FLAC__format_sample_rate_is_valid(op->argument.streaminfo_uint32.value)) {
521                         fprintf(stderr, "ERROR (--%s): invalid sample rate\n", opt);
522                         ok = false;
523                 }
524                 else
525                         undocumented_warning(opt);
526         }
527         else if(0 == strcmp(opt, "set-channels")) {
528                 op = append_shorthand_operation(options, OP__SET_CHANNELS);
529                 if(!parse_uint32(option_argument, &(op->argument.streaminfo_uint32.value)) || op->argument.streaminfo_uint32.value > FLAC__MAX_CHANNELS) {
530                         fprintf(stderr, "ERROR (--%s): value must be > 0 and <= %u\n", opt, FLAC__MAX_CHANNELS);
531                         ok = false;
532                 }
533                 else
534                         undocumented_warning(opt);
535         }
536         else if(0 == strcmp(opt, "set-bps")) {
537                 op = append_shorthand_operation(options, OP__SET_BPS);
538                 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) {
539                         fprintf(stderr, "ERROR (--%s): value must be >= %u and <= %u\n", opt, FLAC__MIN_BITS_PER_SAMPLE, FLAC__MAX_BITS_PER_SAMPLE);
540                         ok = false;
541                 }
542                 else
543                         undocumented_warning(opt);
544         }
545         else if(0 == strcmp(opt, "set-total-samples")) {
546                 op = append_shorthand_operation(options, OP__SET_TOTAL_SAMPLES);
547                 if(!parse_uint64(option_argument, &(op->argument.streaminfo_uint64.value)) || op->argument.streaminfo_uint64.value >= (1u<<FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) {
548                         fprintf(stderr, "ERROR (--%s): value must be a %u-bit unsigned integer\n", opt, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN);
549                         ok = false;
550                 }
551                 else
552                         undocumented_warning(opt);
553         }
554         else if(0 == strcmp(opt, "show-vc-vendor")) {
555                 (void) append_shorthand_operation(options, OP__SHOW_VC_VENDOR);
556         }
557         else if(0 == strcmp(opt, "show-vc-field")) {
558                 const char *violation;
559                 op = append_shorthand_operation(options, OP__SHOW_VC_FIELD);
560                 FLAC__ASSERT(0 != option_argument);
561                 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
562                         FLAC__ASSERT(0 != violation);
563                         fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
564                         ok = false;
565                 }
566         }
567         else if(0 == strcmp(opt, "remove-vc-all")) {
568                 (void) append_shorthand_operation(options, OP__REMOVE_VC_ALL);
569         }
570         else if(0 == strcmp(opt, "remove-vc-field")) {
571                 const char *violation;
572                 op = append_shorthand_operation(options, OP__REMOVE_VC_FIELD);
573                 FLAC__ASSERT(0 != option_argument);
574                 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
575                         FLAC__ASSERT(0 != violation);
576                         fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
577                         ok = false;
578                 }
579         }
580         else if(0 == strcmp(opt, "remove-vc-firstfield")) {
581                 const char *violation;
582                 op = append_shorthand_operation(options, OP__REMOVE_VC_FIRSTFIELD);
583                 FLAC__ASSERT(0 != option_argument);
584                 if(!parse_vorbis_comment_field_name(option_argument, &(op->argument.vc_field_name.value), &violation)) {
585                         FLAC__ASSERT(0 != violation);
586                         fprintf(stderr, "ERROR (--%s): malformed vorbis comment field name \"%s\",\n       %s\n", opt, option_argument, violation);
587                         ok = false;
588                 }
589         }
590         else if(0 == strcmp(opt, "set-vc-field")) {
591                 const char *violation;
592                 op = append_shorthand_operation(options, OP__SET_VC_FIELD);
593                 FLAC__ASSERT(0 != option_argument);
594                 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)) {
595                         FLAC__ASSERT(0 != violation);
596                         fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n       %s\n", opt, option_argument, violation);
597                         ok = false;
598                 }
599         }
600         else if(0 == strcmp(opt, "import-vc-from")) {
601                 op = append_shorthand_operation(options, OP__IMPORT_VC_FROM);
602                 FLAC__ASSERT(0 != option_argument);
603                 if(!parse_filename(option_argument, &(op->argument.vc_filename.value))) {
604                         fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
605                         ok = false;
606                 }
607         }
608         else if(0 == strcmp(opt, "export-vc-to")) {
609                 op = append_shorthand_operation(options, OP__EXPORT_VC_TO);
610                 FLAC__ASSERT(0 != option_argument);
611                 if(!parse_filename(option_argument, &(op->argument.vc_filename.value))) {
612                         fprintf(stderr, "ERROR (--%s): missing filename\n", opt);
613                         ok = false;
614                 }
615         }
616         else if(0 == strcmp(opt, "add-padding")) {
617                 op = append_shorthand_operation(options, OP__ADD_PADDING);
618                 FLAC__ASSERT(0 != option_argument);
619                 if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) {
620                         fprintf(stderr, "ERROR (--%s): illegal length \"%s\", length must be >= 0 and < 2^%u\n", opt, option_argument, FLAC__STREAM_METADATA_LENGTH_LEN);
621                         ok = false;
622                 }
623         }
624         else if(0 == strcmp(opt, "help")) {
625                 options->show_long_help = true;
626         }
627         else if(0 == strcmp(opt, "version")) {
628                 options->show_version = true;
629         }
630         else if(0 == strcmp(opt, "list")) {
631                 (void) append_major_operation(options, OP__LIST);
632         }
633         else if(0 == strcmp(opt, "append")) {
634                 (void) append_major_operation(options, OP__APPEND);
635         }
636         else if(0 == strcmp(opt, "remove")) {
637                 (void) append_major_operation(options, OP__REMOVE);
638         }
639         else if(0 == strcmp(opt, "remove-all")) {
640                 (void) append_major_operation(options, OP__REMOVE_ALL);
641         }
642         else if(0 == strcmp(opt, "merge-padding")) {
643                 (void) append_major_operation(options, OP__MERGE_PADDING);
644         }
645         else if(0 == strcmp(opt, "sort-padding")) {
646                 (void) append_major_operation(options, OP__SORT_PADDING);
647         }
648         else if(0 == strcmp(opt, "block-number")) {
649                 arg = append_argument(options, ARG__BLOCK_NUMBER);
650                 FLAC__ASSERT(0 != option_argument);
651                 if(!parse_block_number(option_argument, &(arg->value.block_number))) {
652                         fprintf(stderr, "ERROR: malformed block number specification \"%s\"\n", option_argument);
653                         ok = false;
654                 }
655         }
656         else if(0 == strcmp(opt, "block-type")) {
657                 arg = append_argument(options, ARG__BLOCK_TYPE);
658                 FLAC__ASSERT(0 != option_argument);
659                 if(!parse_block_type(option_argument, &(arg->value.block_type))) {
660                         fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
661                         ok = false;
662                 }
663                 options->args.checks.has_block_type = true;
664         }
665         else if(0 == strcmp(opt, "except-block-type")) {
666                 arg = append_argument(options, ARG__EXCEPT_BLOCK_TYPE);
667                 FLAC__ASSERT(0 != option_argument);
668                 if(!parse_block_type(option_argument, &(arg->value.block_type))) {
669                         fprintf(stderr, "ERROR (--%s): malformed block type specification \"%s\"\n", opt, option_argument);
670                         ok = false;
671                 }
672                 options->args.checks.has_except_block_type = true;
673         }
674         else if(0 == strcmp(opt, "data-format")) {
675                 arg = append_argument(options, ARG__DATA_FORMAT);
676                 FLAC__ASSERT(0 != option_argument);
677                 if(!parse_data_format(option_argument, &(arg->value.data_format))) {
678                         fprintf(stderr, "ERROR (--%s): illegal data format \"%s\"\n", opt, option_argument);
679                         ok = false;
680                 }
681         }
682         else if(0 == strcmp(opt, "application-data-format")) {
683                 FLAC__ASSERT(0 != option_argument);
684                 if(!parse_application_data_format(option_argument, &(options->application_data_format_is_hexdump))) {
685                         fprintf(stderr, "ERROR (--%s): illegal application data format \"%s\"\n", opt, option_argument);
686                         ok = false;
687                 }
688         }
689         else if(0 == strcmp(opt, "from-file")) {
690                 arg = append_argument(options, ARG__FROM_FILE);
691                 FLAC__ASSERT(0 != option_argument);
692                 arg->value.from_file.file_name = local_strdup(option_argument);
693         }
694         else {
695                 FLAC__ASSERT(0);
696         }
697
698         return ok;
699 }
700
701 void free_options(CommandLineOptions *options)
702 {
703         unsigned i;
704         Operation *op;
705         Argument *arg;
706
707         FLAC__ASSERT(0 == options->ops.operations || options->ops.num_operations > 0);
708         FLAC__ASSERT(0 == options->args.arguments || options->args.num_arguments > 0);
709
710         for(i = 0, op = options->ops.operations; i < options->ops.num_operations; i++, op++) {
711                 switch(op->type) {
712                         case OP__SHOW_VC_FIELD:
713                         case OP__REMOVE_VC_FIELD:
714                         case OP__REMOVE_VC_FIRSTFIELD:
715                                 if(0 != op->argument.vc_field_name.value)
716                                         free(op->argument.vc_field_name.value);
717                                 break;
718                         case OP__SET_VC_FIELD:
719                                 if(0 != op->argument.vc_field.field)
720                                         free(op->argument.vc_field.field);
721                                 if(0 != op->argument.vc_field.field_name)
722                                         free(op->argument.vc_field.field_name);
723                                 if(0 != op->argument.vc_field.field_value)
724                                         free(op->argument.vc_field.field_value);
725                                 break;
726                         case OP__IMPORT_VC_FROM:
727                         case OP__EXPORT_VC_TO:
728                                 if(0 != op->argument.vc_filename.value)
729                                         free(op->argument.vc_filename.value);
730                                 break;
731                         default:
732                                 break;
733                 }
734         }
735
736         for(i = 0, arg = options->args.arguments; i < options->args.num_arguments; i++, arg++) {
737                 switch(arg->type) {
738                         case ARG__BLOCK_NUMBER:
739                                 if(0 != arg->value.block_number.entries)
740                                         free(arg->value.block_number.entries);
741                                 break;
742                         case ARG__BLOCK_TYPE:
743                         case ARG__EXCEPT_BLOCK_TYPE:
744                                 if(0 != arg->value.block_type.entries)
745                                         free(arg->value.block_type.entries);
746                                 break;
747                         case ARG__FROM_FILE:
748                                 if(0 != arg->value.from_file.file_name)
749                                         free(arg->value.from_file.file_name);
750                                 break;
751                         default:
752                                 break;
753                 }
754         }
755
756         if(0 != options->ops.operations)
757                 free(options->ops.operations);
758
759         if(0 != options->args.arguments)
760                 free(options->args.arguments);
761
762         if(0 != options->filenames)
763                 free(options->filenames);
764 }
765
766 void append_new_operation(CommandLineOptions *options, Operation operation)
767 {
768         if(options->ops.capacity == 0) {
769                 options->ops.capacity = 50;
770                 if(0 == (options->ops.operations = malloc(sizeof(Operation) * options->ops.capacity)))
771                         die("out of memory allocating space for option list");
772                 memset(options->ops.operations, 0, sizeof(Operation) * options->ops.capacity);
773         }
774         if(options->ops.capacity <= options->ops.num_operations) {
775                 unsigned original_capacity = options->ops.capacity;
776                 options->ops.capacity *= 4;
777                 if(0 == (options->ops.operations = realloc(options->ops.operations, sizeof(Operation) * options->ops.capacity)))
778                         die("out of memory allocating space for option list");
779                 memset(options->ops.operations + original_capacity, 0, sizeof(Operation) * (options->ops.capacity - original_capacity));
780         }
781
782         options->ops.operations[options->ops.num_operations++] = operation;
783 }
784
785 void append_new_argument(CommandLineOptions *options, Argument argument)
786 {
787         if(options->args.capacity == 0) {
788                 options->args.capacity = 50;
789                 if(0 == (options->args.arguments = malloc(sizeof(Argument) * options->args.capacity)))
790                         die("out of memory allocating space for option list");
791                 memset(options->args.arguments, 0, sizeof(Argument) * options->args.capacity);
792         }
793         if(options->args.capacity <= options->args.num_arguments) {
794                 unsigned original_capacity = options->args.capacity;
795                 options->args.capacity *= 4;
796                 if(0 == (options->args.arguments = realloc(options->args.arguments, sizeof(Argument) * options->args.capacity)))
797                         die("out of memory allocating space for option list");
798                 memset(options->args.arguments + original_capacity, 0, sizeof(Argument) * (options->args.capacity - original_capacity));
799         }
800
801         options->args.arguments[options->args.num_arguments++] = argument;
802 }
803
804 Operation *append_major_operation(CommandLineOptions *options, OperationType type)
805 {
806         Operation op;
807         memset(&op, 0, sizeof(op));
808         op.type = type;
809         append_new_operation(options, op);
810         options->args.checks.num_major_ops++;
811         return options->ops.operations + (options->ops.num_operations - 1);
812 }
813
814 Operation *append_shorthand_operation(CommandLineOptions *options, OperationType type)
815 {
816         Operation op;
817         memset(&op, 0, sizeof(op));
818         op.type = type;
819         append_new_operation(options, op);
820         options->args.checks.num_shorthand_ops++;
821         return options->ops.operations + (options->ops.num_operations - 1);
822 }
823
824 Argument *append_argument(CommandLineOptions *options, ArgumentType type)
825 {
826         Argument arg;
827         memset(&arg, 0, sizeof(arg));
828         arg.type = type;
829         append_new_argument(options, arg);
830         return options->args.arguments + (options->args.num_arguments - 1);
831 }
832
833 void show_version()
834 {
835         printf("metaflac %s\n", FLAC__VERSION_STRING);
836 }
837
838 static void usage_header(FILE *out)
839 {
840         fprintf(out, "==============================================================================\n");
841         fprintf(out, "metaflac - Command-line FLAC metadata editor version %s\n", FLAC__VERSION_STRING);
842         fprintf(out, "Copyright (C) 2001,2002  Josh Coalson\n");
843         fprintf(out, "\n");
844         fprintf(out, "This program is free software; you can redistribute it and/or\n");
845         fprintf(out, "modify it under the terms of the GNU General Public License\n");
846         fprintf(out, "as published by the Free Software Foundation; either version 2\n");
847         fprintf(out, "of the License, or (at your option) any later version.\n");
848         fprintf(out, "\n");
849         fprintf(out, "This program is distributed in the hope that it will be useful,\n");
850         fprintf(out, "but WITHOUT ANY WARRANTY; without even the implied warranty of\n");
851         fprintf(out, "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n");
852         fprintf(out, "GNU General Public License for more details.\n");
853         fprintf(out, "\n");
854         fprintf(out, "You should have received a copy of the GNU General Public License\n");
855         fprintf(out, "along with this program; if not, write to the Free Software\n");
856         fprintf(out, "Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\n");
857         fprintf(out, "==============================================================================\n");
858 }
859
860 static void usage_summary(FILE *out)
861 {
862         fprintf(out, "Usage:\n");
863         fprintf(out, "  metaflac [options] [operations] FLACfile [FLACfile ...]\n");
864         fprintf(out, "\n");
865         fprintf(out, "Use metaflac to list, add, remove, or edit metadata in one or more FLAC files.\n");
866         fprintf(out, "You may perform one major operation, or many shorthand operations at a time.\n");
867         fprintf(out, "\n");
868         fprintf(out, "Options:\n");
869         fprintf(out, "--preserve-modtime    Preserve the original modification time in spite of edits\n");
870         fprintf(out, "--with-filename       Prefix each output line with the FLAC file name\n");
871         fprintf(out, "                      (the default if more than one FLAC file is specified)\n");
872         fprintf(out, "--no-filename         Do not prefix each output line with the FLAC file name\n");
873         fprintf(out, "                      (the default if only one FLAC file is specified)\n");
874         fprintf(out, "--no-utf8-convert     Do not convert Vorbis comments from UTF-8 to local charset,\n");
875         fprintf(out, "                      or vice versa.  This is useful for scripts.\n");
876         fprintf(out, "--dont-use-padding    By default metaflac tries to use padding where possible\n");
877         fprintf(out, "                      to avoid rewriting the entire file if the metadata size\n");
878         fprintf(out, "                      changes.  Use this option to tell metaflac to not take\n");
879         fprintf(out, "                      advantage of padding this way.\n");
880 }
881
882 int short_usage(const char *message, ...)
883 {
884         va_list args;
885
886         if(message) {
887                 va_start(args, message);
888
889                 (void) vfprintf(stderr, message, args);
890
891                 va_end(args);
892
893         }
894         usage_header(stderr);
895         fprintf(stderr, "\n");
896         fprintf(stderr, "This is the short help; for full help use 'metaflac --help'\n");
897         fprintf(stderr, "\n");
898         usage_summary(stderr);
899
900         return message? 1 : 0;
901 }
902
903 int long_usage(const char *message, ...)
904 {
905         FILE *out = (message? stderr : stdout);
906         va_list args;
907
908         if(message) {
909                 va_start(args, message);
910
911                 (void) vfprintf(stderr, message, args);
912
913                 va_end(args);
914
915         }
916         usage_header(out);
917         fprintf(out, "\n");
918         usage_summary(out);
919         fprintf(out, "\n");
920         fprintf(out, "Shorthand operations:\n");
921         fprintf(out, "--show-md5sum         Show the MD5 signature from the STREAMINFO block.\n");
922         fprintf(out, "--show-min-blocksize  Show the minimum block size from the STREAMINFO block.\n");
923         fprintf(out, "--show-max-blocksize  Show the maximum block size from the STREAMINFO block.\n");
924         fprintf(out, "--show-min-framesize  Show the minimum frame size from the STREAMINFO block.\n");
925         fprintf(out, "--show-max-framesize  Show the maximum frame size from the STREAMINFO block.\n");
926         fprintf(out, "--show-sample-rate    Show the sample rate from the STREAMINFO block.\n");
927         fprintf(out, "--show-channels       Show the number of channels from the STREAMINFO block.\n");
928         fprintf(out, "--show-bps            Show the # of bits per sample from the STREAMINFO block.\n");
929         fprintf(out, "--show-total-samples  Show the total # of samples from the STREAMINFO block.\n");
930         fprintf(out, "\n");
931         fprintf(out, "--show-vc-vendor      Show the vendor string from the VORBIS_COMMENT block.\n");
932         fprintf(out, "--show-vc-field=name  Show all Vorbis comment fields where the the field name\n");
933         fprintf(out, "                      matches 'name'.\n");
934         fprintf(out, "--remove-vc-field=name\n");
935         fprintf(out, "                      Remove all Vorbis comment fields whose field name is\n");
936         fprintf(out, "                      'name'.\n");
937         fprintf(out, "--remove-vc-firstfield=name\n");
938         fprintf(out, "                      Remove first Vorbis comment field whose field name is\n");
939         fprintf(out, "                      'name'.\n");
940         fprintf(out, "--remove-vc-all       Remove all Vorbis comment fields, leaving only the\n");
941         fprintf(out, "                      vendor string in the VORBIS_COMMENT block.\n");
942         fprintf(out, "--set-vc-field=field  Add a Vorbis comment field.  The field must comply with\n");
943         fprintf(out, "                      the Vorbis comment spec, of the form \"NAME=VALUE\".  If\n");
944         fprintf(out, "                      there is currently no VORBIS_COMMENT block, one will be\n");
945         fprintf(out, "                      created.\n");
946         fprintf(out, "--import-vc-from=file Import Vorbis comments from a file.  Use '-' for stdin.\n");
947         fprintf(out, "                      Each line should be of the form NAME=VALUE.  Multi-\n");
948         fprintf(out, "                      line comments are currently not supported.  Specify\n");
949         fprintf(out, "                      --remove-vc-all and/or --no-utf8-convert before\n");
950         fprintf(out, "                      --import-vc-from if necessary.\n");
951         fprintf(out, "--export-vc-to=file   Export Vorbis comments to a file.  Use '-' for stdin.\n");
952         fprintf(out, "                      Each line will be of the form NAME=VALUE.  Specify\n");
953         fprintf(out, "                      --no-utf8-convert if necessary.\n");
954         fprintf(out, "--add-padding=length  Add a padding block of the given length (in bytes).\n");
955         fprintf(out, "                      The overall length of the new block will be 4 + length;\n");
956         fprintf(out, "                      the extra 4 bytes is for the metadata block header.\n");
957         fprintf(out, "\n");
958         fprintf(out, "Major operations:\n");
959         fprintf(out, "--version\n");
960         fprintf(out, "    Show the metaflac version number.\n");
961         fprintf(out, "--list\n");
962         fprintf(out, "    List the contents of one or more metadata blocks to stdout.  By default,\n");
963         fprintf(out, "    all metadata blocks are listed in text format.  Use the following options\n");
964         fprintf(out, "    to change this behavior:\n");
965         fprintf(out, "\n");
966         fprintf(out, "    --block-number=#[,#[...]]\n");
967         fprintf(out, "    An optional comma-separated list of block numbers to display.  The first\n");
968         fprintf(out, "    block, the STREAMINFO block, is block 0.\n");
969         fprintf(out, "\n");
970         fprintf(out, "    --block-type=type[,type[...]]\n");
971         fprintf(out, "    --except-block-type=type[,type[...]]\n");
972         fprintf(out, "    An optional comma-separated list of block types to included or ignored\n");
973         fprintf(out, "    with this option.  Use only one of --block-type or --except-block-type.\n");
974         fprintf(out, "    The valid block types are: STREAMINFO, PADDING, APPLICATION, SEEKTABLE,\n");
975         fprintf(out, "    VORBIS_COMMENT.  You may narrow down the types of APPLICATION blocks\n");
976         fprintf(out, "    displayed as follows:\n");
977         fprintf(out, "        APPLICATION:abcd        The APPLICATION block(s) whose textual repre-\n");
978         fprintf(out, "                                sentation of the 4-byte ID is \"abcd\"\n");
979         fprintf(out, "        APPLICATION:0xXXXXXXXX  The APPLICATION block(s) whose hexadecimal big-\n");
980         fprintf(out, "                                endian representation of the 4-byte ID is\n");
981         fprintf(out, "                                \"0xXXXXXXXX\".  For the example \"abcd\" above the\n");
982         fprintf(out, "                                hexadecimal equivalalent is 0x61626364\n");
983         fprintf(out, "\n");
984         fprintf(out, "    NOTE: if both --block-number and --[except-]block-type are specified,\n");
985         fprintf(out, "          the result is the logical AND of both arguments.\n");
986         fprintf(out, "\n");
987 #if 0
988         /*@@@ not implemented yet */
989         fprintf(out, "    --data-format=binary|text\n");
990         fprintf(out, "    By default a human-readable text representation of the data is displayed.\n");
991         fprintf(out, "    You may specify --data-format=binary to dump the raw binary form of each\n");
992         fprintf(out, "    metadata block.  The output can be read in using a subsequent call to\n");
993         fprintf(out, "    "metaflac --append --from-file=..."\n");
994         fprintf(out, "\n");
995 #endif
996         fprintf(out, "    --application-data-format=hexdump|text\n");
997         fprintf(out, "    If the application block you are displaying contains binary data but your\n");
998         fprintf(out, "    --data-format=text, you can display a hex dump of the application data\n");
999         fprintf(out, "    contents instead using --application-data-format=hexdump\n");
1000         fprintf(out, "\n");
1001 #if 0
1002         /*@@@ not implemented yet */
1003         fprintf(out, "--append\n");
1004         fprintf(out, "    Insert a metadata block from a file.  The input file must be in the same\n");
1005         fprintf(out, "    format as generated with --list.\n");
1006         fprintf(out, "\n");
1007         fprintf(out, "    --block-number=#\n");
1008         fprintf(out, "    Specify the insertion point (defaults to last block).  The new block will\n");
1009         fprintf(out, "    be added after the given block number.  This prevents the illegal insertion\n");
1010         fprintf(out, "    of a block before the first STREAMINFO block.  You may not --append another\n");
1011         fprintf(out, "    STREAMINFO block.\n");
1012         fprintf(out, "\n");
1013         fprintf(out, "    --from-file=filename\n");
1014         fprintf(out, "    Mandatory 'option' to specify the input file containing the block contents.\n");
1015         fprintf(out, "\n");
1016         fprintf(out, "    --data-format=binary|text\n");
1017         fprintf(out, "    By default the block contents are assumed to be in binary format.  You can\n");
1018         fprintf(out, "    override this by specifying --data-format=text\n");
1019         fprintf(out, "\n");
1020 #endif
1021         fprintf(out, "--remove\n");
1022         fprintf(out, "    Remove one or more metadata blocks from the metadata.  Unless\n");
1023         fprintf(out, "    --dont-use-padding is specified, the blocks will be replaced with padding.\n");
1024         fprintf(out, "    You may not remove the STREAMINFO block.\n");
1025         fprintf(out, "\n");
1026         fprintf(out, "    --block-number=#[,#[...]]\n");
1027         fprintf(out, "    --block-type=type[,type[...]]\n");
1028         fprintf(out, "    --except-block-type=type[,type[...]]\n");
1029         fprintf(out, "    See --list above for usage.\n");
1030         fprintf(out, "\n");
1031         fprintf(out, "    NOTE: if both --block-number and --[except-]block-type are specified,\n");
1032         fprintf(out, "          the result is the logical AND of both arguments.\n");
1033         fprintf(out, "\n");
1034         fprintf(out, "--remove-all\n");
1035         fprintf(out, "    Remove all metadata blocks (except the STREAMINFO block) from the\n");
1036         fprintf(out, "    metadata.  Unless --dont-use-padding is specified, the blocks will be\n");
1037         fprintf(out, "    replaced with padding.\n");
1038         fprintf(out, "\n");
1039         fprintf(out, "--merge-padding\n");
1040         fprintf(out, "    Merge adjacent PADDING blocks into single blocks.\n");
1041         fprintf(out, "\n");
1042         fprintf(out, "--sort-padding\n");
1043         fprintf(out, "    Move all PADDING blocks to the end of the metadata and merge them into a\n");
1044         fprintf(out, "    single block.\n");
1045
1046         return message? 1 : 0;
1047 }
1048
1049 char *local_strdup(const char *source)
1050 {
1051         char *ret;
1052         FLAC__ASSERT(0 != source);
1053         if(0 == (ret = strdup(source)))
1054                 die("out of memory during strdup()");
1055         return ret;
1056 }
1057
1058 FLAC__bool parse_md5(const char *src, FLAC__byte dest[16])
1059 {
1060         unsigned i, d;
1061         int c;
1062         FLAC__ASSERT(0 != src);
1063         if(strlen(src) != 32)
1064                 return false;
1065         /* strtoul() accepts negative numbers which we do not want, so we do it the hard way */
1066         for(i = 0; i < 16; i++) {
1067                 c = (int)(*src++);
1068                 if(isdigit(c))
1069                         d = (unsigned)(c - '0');
1070                 else if(c >= 'a' && c <= 'f')
1071                         d = (unsigned)(c - 'a') + 10u;
1072                 else if(c >= 'A' && c <= 'F')
1073                         d = (unsigned)(c - 'A') + 10u;
1074                 else
1075                         return false;
1076                 d <<= 4;
1077                 c = (int)(*src++);
1078                 if(isdigit(c))
1079                         d |= (unsigned)(c - '0');
1080                 else if(c >= 'a' && c <= 'f')
1081                         d |= (unsigned)(c - 'a') + 10u;
1082                 else if(c >= 'A' && c <= 'F')
1083                         d |= (unsigned)(c - 'A') + 10u;
1084                 else
1085                         return false;
1086                 dest[i] = (FLAC__byte)d;
1087         }
1088         return true;
1089 }
1090
1091 FLAC__bool parse_uint32(const char *src, FLAC__uint32 *dest)
1092 {
1093         FLAC__ASSERT(0 != src);
1094         if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
1095                 return false;
1096         *dest = strtoul(src, 0, 10);
1097         return true;
1098 }
1099
1100 /* There's no stroull() in MSVC6 so we just write a specialized one */
1101 static FLAC__uint64 local__strtoull(const char *src)
1102 {
1103         FLAC__uint64 ret = 0;
1104         int c;
1105         FLAC__ASSERT(0 != src);
1106         while(0 != (c = *src++)) {
1107                 c -= '0';
1108                 if(c >= 0 && c <= 9)
1109                         ret = (ret * 10) + c;
1110                 else
1111                         break;
1112         }
1113         return ret;
1114 }
1115
1116 FLAC__bool parse_uint64(const char *src, FLAC__uint64 *dest)
1117 {
1118         FLAC__ASSERT(0 != src);
1119         if(strlen(src) == 0 || strspn(src, "0123456789") != strlen(src))
1120                 return false;
1121         *dest = local__strtoull(src);
1122         return true;
1123 }
1124
1125 FLAC__bool parse_filename(const char *src, char **dest)
1126 {
1127         if(0 == src || strlen(src) == 0)
1128                 return false;
1129         *dest = strdup(src);
1130         return true;
1131 }
1132
1133 FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation)
1134 {
1135         static const char * const violations[] = {
1136                 "field name contains invalid character",
1137                 "field contains no '=' character"
1138         };
1139
1140         char *p, *q, *s;
1141
1142         if(0 != field)
1143                 *field = local_strdup(field_ref);
1144
1145         s = local_strdup(field_ref);
1146
1147         if(0 == (p = strchr(s, '='))) {
1148                 free(s);
1149                 *violation = violations[1];
1150                 return false;
1151         }
1152         *p++ = '\0';
1153
1154         for(q = s; *q; q++) {
1155                 if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
1156                         free(s);
1157                         *violation = violations[0];
1158                         return false;
1159                 }
1160         }
1161
1162         *name = local_strdup(s);
1163         *value = local_strdup(p);
1164         *length = strlen(p);
1165
1166         free(s);
1167         return true;
1168 }
1169
1170 static FLAC__bool parse_vorbis_comment_field_name(const char *field_ref, char **name, const char **violation)
1171 {
1172         static const char * const violations[] = {
1173                 "field name contains invalid character"
1174         };
1175
1176         char *q, *s;
1177
1178         s = local_strdup(field_ref);
1179
1180         for(q = s; *q; q++) {
1181                 if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
1182                         free(s);
1183                         *violation = violations[0];
1184                         return false;
1185                 }
1186         }
1187
1188         *name = s;
1189
1190         return true;
1191 }
1192
1193 FLAC__bool parse_add_padding(const char *in, unsigned *out)
1194 {
1195         *out = (unsigned)strtoul(in, 0, 10);
1196         return *out < (1u << FLAC__STREAM_METADATA_LENGTH_LEN);
1197 }
1198
1199 FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out)
1200 {
1201         char *p, *q, *s, *end;
1202         long i;
1203         unsigned entry;
1204
1205         if(*in == '\0')
1206                 return false;
1207
1208         s = local_strdup(in);
1209
1210         /* first count the entries */
1211         for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
1212                 ;
1213
1214         /* make space */
1215         FLAC__ASSERT(out->num_entries > 0);
1216         if(0 == (out->entries = malloc(sizeof(unsigned) * out->num_entries)))
1217                 die("out of memory allocating space for option list");
1218
1219         /* load 'em up */
1220         entry = 0;
1221         q = s;
1222         while(q) {
1223                 FLAC__ASSERT(entry < out->num_entries);
1224                 if(0 != (p = strchr(q, ',')))
1225                         *p++ = '\0';
1226                 if(!isdigit((int)(*q)) || (i = strtol(q, &end, 10)) < 0 || *end) {
1227                         free(s);
1228                         return false;
1229                 }
1230                 out->entries[entry++] = (unsigned)i;
1231                 q = p;
1232         }
1233         FLAC__ASSERT(entry == out->num_entries);
1234
1235         free(s);
1236         return true;
1237 }
1238
1239 FLAC__bool parse_block_type(const char *in, Argument_BlockType *out)
1240 {
1241         char *p, *q, *r, *s;
1242         unsigned entry;
1243
1244         if(*in == '\0')
1245                 return false;
1246
1247         s = local_strdup(in);
1248
1249         /* first count the entries */
1250         for(out->num_entries = 1, p = strchr(s, ','); p; out->num_entries++, p = strchr(++p, ','))
1251                 ;
1252
1253         /* make space */
1254         FLAC__ASSERT(out->num_entries > 0);
1255         if(0 == (out->entries = malloc(sizeof(Argument_BlockTypeEntry) * out->num_entries)))
1256                 die("out of memory allocating space for option list");
1257
1258         /* load 'em up */
1259         entry = 0;
1260         q = s;
1261         while(q) {
1262                 FLAC__ASSERT(entry < out->num_entries);
1263                 if(0 != (p = strchr(q, ',')))
1264                         *p++ = 0;
1265                 r = strchr(q, ':');
1266                 if(r)
1267                         *r++ = '\0';
1268                 if(0 != r && 0 != strcmp(q, "APPLICATION")) {
1269                         free(s);
1270                         return false;
1271                 }
1272                 if(0 == strcmp(q, "STREAMINFO")) {
1273                         out->entries[entry++].type = FLAC__METADATA_TYPE_STREAMINFO;
1274                 }
1275                 else if(0 == strcmp(q, "PADDING")) {
1276                         out->entries[entry++].type = FLAC__METADATA_TYPE_PADDING;
1277                 }
1278                 else if(0 == strcmp(q, "APPLICATION")) {
1279                         out->entries[entry].type = FLAC__METADATA_TYPE_APPLICATION;
1280                         out->entries[entry].filter_application_by_id = (0 != r);
1281                         if(0 != r) {
1282                                 if(strlen(r) == 4) {
1283                                         strcpy(out->entries[entry].application_id, r);
1284                                 }
1285                                 else if(strlen(r) == 10 && strncmp(r, "0x", 2) == 0 && strspn(r+2, "0123456789ABCDEFabcdef") == 8) {
1286                                         FLAC__uint32 x = strtoul(r+2, 0, 16);
1287                                         out->entries[entry].application_id[3] = (FLAC__byte)(x & 0xff);
1288                                         out->entries[entry].application_id[2] = (FLAC__byte)((x>>=8) & 0xff);
1289                                         out->entries[entry].application_id[1] = (FLAC__byte)((x>>=8) & 0xff);
1290                                         out->entries[entry].application_id[0] = (FLAC__byte)((x>>=8) & 0xff);
1291                                 }
1292                                 else {
1293                                         free(s);
1294                                         return false;
1295                                 }
1296                         }
1297                         entry++;
1298                 }
1299                 else if(0 == strcmp(q, "SEEKTABLE")) {
1300                         out->entries[entry++].type = FLAC__METADATA_TYPE_SEEKTABLE;
1301                 }
1302                 else if(0 == strcmp(q, "VORBIS_COMMENT")) {
1303                         out->entries[entry++].type = FLAC__METADATA_TYPE_VORBIS_COMMENT;
1304                 }
1305                 else {
1306                         free(s);
1307                         return false;
1308                 }
1309                 q = p;
1310         }
1311         FLAC__ASSERT(entry == out->num_entries);
1312
1313         free(s);
1314         return true;
1315 }
1316
1317 FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out)
1318 {
1319         if(0 == strcmp(in, "binary"))
1320                 out->is_binary = true;
1321         else if(0 == strcmp(in, "text"))
1322                 out->is_binary = false;
1323         else
1324                 return false;
1325         return true;
1326 }
1327
1328 FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out)
1329 {
1330         if(0 == strcmp(in, "hexdump"))
1331                 *out = true;
1332         else if(0 == strcmp(in, "text"))
1333                 *out = false;
1334         else
1335                 return false;
1336         return true;
1337 }
1338
1339 FLAC__bool do_operations(const CommandLineOptions *options)
1340 {
1341         FLAC__bool ok = true;
1342
1343         if(options->show_long_help) {
1344                 long_usage(0);
1345         }
1346         if(options->show_version) {
1347                 show_version();
1348         }
1349         else if(options->args.checks.num_major_ops > 0) {
1350                 FLAC__ASSERT(options->args.checks.num_shorthand_ops == 0);
1351                 FLAC__ASSERT(options->args.checks.num_major_ops == 1);
1352                 FLAC__ASSERT(options->args.checks.num_major_ops == options->ops.num_operations);
1353                 ok = do_major_operation(options);
1354         }
1355         else if(options->args.checks.num_shorthand_ops > 0) {
1356                 FLAC__ASSERT(options->args.checks.num_shorthand_ops == options->ops.num_operations);
1357                 ok = do_shorthand_operations(options);
1358         }
1359
1360         return ok;
1361 }
1362
1363 FLAC__bool do_major_operation(const CommandLineOptions *options)
1364 {
1365         unsigned i;
1366         FLAC__bool ok = true;
1367
1368         /*@@@ to die after first error,  v---  add '&& ok' here */
1369         for(i = 0; i < options->num_files; i++)
1370                 ok &= do_major_operation_on_file(options->filenames[i], options);
1371
1372         return ok;
1373 }
1374
1375 FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
1376 {
1377         FLAC__bool ok = true, needs_write = false;
1378         FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
1379
1380         if(0 == chain)
1381                 die("out of memory allocating chain");
1382
1383         if(!FLAC__metadata_chain_read(chain, filename)) {
1384                 fprintf(stderr, "%s: ERROR: reading metadata, status = \"%s\"\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1385                 return false;
1386         }
1387
1388         switch(options->ops.operations[0].type) {
1389                 case OP__LIST:
1390                         ok = do_major_operation__list(options->prefix_with_filename? filename : 0, chain, options);
1391                         break;
1392                 case OP__APPEND:
1393                         ok = do_major_operation__append(chain, options);
1394                         needs_write = true;
1395                         break;
1396                 case OP__REMOVE:
1397                         ok = do_major_operation__remove(chain, options);
1398                         needs_write = true;
1399                         break;
1400                 case OP__REMOVE_ALL:
1401                         ok = do_major_operation__remove_all(chain, options);
1402                         needs_write = true;
1403                         break;
1404                 case OP__MERGE_PADDING:
1405                         FLAC__metadata_chain_merge_padding(chain);
1406                         needs_write = true;
1407                         break;
1408                 case OP__SORT_PADDING:
1409                         FLAC__metadata_chain_sort_padding(chain);
1410                         needs_write = true;
1411                         break;
1412                 default:
1413                         FLAC__ASSERT(0);
1414                         return false;
1415         }
1416
1417         if(ok && needs_write) {
1418                 if(options->use_padding)
1419                         FLAC__metadata_chain_sort_padding(chain);
1420                 ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
1421                 if(!ok)
1422                         fprintf(stderr, "%s: ERROR: writing FLAC file, error = %s\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1423         }
1424
1425         FLAC__metadata_chain_delete(chain);
1426
1427         return ok;
1428 }
1429
1430 FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
1431 {
1432         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1433         FLAC__StreamMetadata *block;
1434         FLAC__bool ok = true;
1435         unsigned block_number;
1436
1437         if(0 == iterator)
1438                 die("out of memory allocating iterator");
1439
1440         FLAC__metadata_iterator_init(iterator, chain);
1441
1442         block_number = 0;
1443         do {
1444                 block = FLAC__metadata_iterator_get_block(iterator);
1445                 ok &= (0 != block);
1446                 if(!ok)
1447                         fprintf(stderr, "%s: ERROR: couldn't get block from chain\n", filename);
1448                 else if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number))
1449                         write_metadata(filename, block, block_number, !options->utf8_convert, options->application_data_format_is_hexdump);
1450                 block_number++;
1451         } while(ok && FLAC__metadata_iterator_next(iterator));
1452
1453         FLAC__metadata_iterator_delete(iterator);
1454
1455         return ok;
1456 }
1457
1458 FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
1459 {
1460         (void) chain, (void) options;
1461         fprintf(stderr, "ERROR: --append not implemented yet\n"); /*@@@*/
1462         return false;
1463 }
1464
1465 FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
1466 {
1467         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1468         FLAC__bool ok = true;
1469         unsigned block_number;
1470
1471         if(0 == iterator)
1472                 die("out of memory allocating iterator");
1473
1474         FLAC__metadata_iterator_init(iterator, chain);
1475
1476         block_number = 0;
1477         while(ok && FLAC__metadata_iterator_next(iterator)) {
1478                 block_number++;
1479                 if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
1480                         ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
1481                         if(options->use_padding)
1482                                 ok &= FLAC__metadata_iterator_next(iterator);
1483                 }
1484         }
1485
1486         FLAC__metadata_iterator_delete(iterator);
1487
1488         return ok;
1489 }
1490
1491 FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
1492 {
1493         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1494         FLAC__bool ok = true;
1495
1496         if(0 == iterator)
1497                 die("out of memory allocating iterator");
1498
1499         FLAC__metadata_iterator_init(iterator, chain);
1500
1501         while(ok && FLAC__metadata_iterator_next(iterator)) {
1502                 ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
1503                 if(options->use_padding)
1504                         ok &= FLAC__metadata_iterator_next(iterator);
1505         }
1506
1507         FLAC__metadata_iterator_delete(iterator);
1508
1509         return ok;
1510 }
1511
1512 FLAC__bool do_shorthand_operations(const CommandLineOptions *options)
1513 {
1514         unsigned i;
1515         FLAC__bool ok = true;
1516
1517         /* to die after first error,     v---  add '&& ok' here */
1518         for(i = 0; i < options->num_files; i++)
1519                 ok &= do_shorthand_operations_on_file(options->filenames[i], options);
1520
1521         return ok;
1522 }
1523
1524 FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLineOptions *options)
1525 {
1526         unsigned i;
1527         FLAC__bool ok = true, needs_write = false, use_padding = options->use_padding;
1528         FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
1529
1530         if(0 == chain)
1531                 die("out of memory allocating chain");
1532
1533         if(!FLAC__metadata_chain_read(chain, filename)) {
1534                 fprintf(stderr, "%s: ERROR: reading metadata, status = \"%s\"\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1535                 return false;
1536         }
1537
1538         for(i = 0; i < options->ops.num_operations && ok; i++) {
1539                 ok &= do_shorthand_operation(options->prefix_with_filename? filename : 0, chain, &options->ops.operations[i], &needs_write, options->utf8_convert);
1540
1541                 /* The following seems counterintuitive but the meaning
1542                  * of 'use_padding' is 'try to keep the overall metadata
1543                  * to its original size, adding or truncating extra
1544                  * padding if necessary' which is why we need to turn it
1545                  * off in this case.  If we don't, the extra padding block
1546                  * will just be truncated.
1547                  */
1548                 if(options->ops.operations[i].type == OP__ADD_PADDING)
1549                         use_padding = false;
1550         }
1551
1552         if(ok && needs_write) {
1553                 if(use_padding)
1554                         FLAC__metadata_chain_sort_padding(chain);
1555                 ok = FLAC__metadata_chain_write(chain, use_padding, options->preserve_modtime);
1556                 if(!ok)
1557                         fprintf(stderr, "%s: ERROR: writing FLAC file, error = %s\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1558         }
1559
1560         FLAC__metadata_chain_delete(chain);
1561
1562         return ok;
1563 }
1564
1565 FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool utf8_convert)
1566 {
1567         FLAC__bool ok = true;
1568
1569         switch(operation->type) {
1570                 case OP__SHOW_MD5SUM:
1571                 case OP__SHOW_MIN_BLOCKSIZE:
1572                 case OP__SHOW_MAX_BLOCKSIZE:
1573                 case OP__SHOW_MIN_FRAMESIZE:
1574                 case OP__SHOW_MAX_FRAMESIZE:
1575                 case OP__SHOW_SAMPLE_RATE:
1576                 case OP__SHOW_CHANNELS:
1577                 case OP__SHOW_BPS:
1578                 case OP__SHOW_TOTAL_SAMPLES:
1579                 case OP__SET_MD5SUM:
1580                 case OP__SET_MIN_BLOCKSIZE:
1581                 case OP__SET_MAX_BLOCKSIZE:
1582                 case OP__SET_MIN_FRAMESIZE:
1583                 case OP__SET_MAX_FRAMESIZE:
1584                 case OP__SET_SAMPLE_RATE:
1585                 case OP__SET_CHANNELS:
1586                 case OP__SET_BPS:
1587                 case OP__SET_TOTAL_SAMPLES:
1588                         ok = do_shorthand_operation__streaminfo(filename, chain, operation, needs_write);
1589                         break;
1590                 case OP__SHOW_VC_VENDOR:
1591                 case OP__SHOW_VC_FIELD:
1592                 case OP__REMOVE_VC_ALL:
1593                 case OP__REMOVE_VC_FIELD:
1594                 case OP__REMOVE_VC_FIRSTFIELD:
1595                 case OP__SET_VC_FIELD:
1596                 case OP__IMPORT_VC_FROM:
1597                 case OP__EXPORT_VC_TO:
1598                         ok = do_shorthand_operation__vorbis_comment(filename, chain, operation, needs_write, !utf8_convert);
1599                         break;
1600                 case OP__ADD_PADDING:
1601                         ok = do_shorthand_operation__add_padding(filename, chain, operation->argument.add_padding.length, needs_write);
1602                         break;
1603                 default:
1604                         ok = false;
1605                         FLAC__ASSERT(0);
1606                         break;
1607         };
1608
1609         return ok;
1610 }
1611
1612 FLAC__bool do_shorthand_operation__add_padding(const char *filename, FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
1613 {
1614         FLAC__StreamMetadata *padding = 0;
1615         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1616
1617         if(0 == iterator)
1618                 die("out of memory allocating iterator");
1619
1620         FLAC__metadata_iterator_init(iterator, chain);
1621
1622         while(FLAC__metadata_iterator_next(iterator))
1623                 ;
1624
1625         padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
1626         if(0 == padding)
1627                 die("out of memory allocating PADDING block");
1628
1629         padding->length = length;
1630
1631         if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) {
1632                 fprintf(stderr, "%s: ERROR: adding new PADDING block to metadata, status =\"%s\"\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1633                 FLAC__metadata_object_delete(padding);
1634                 FLAC__metadata_iterator_delete(iterator);
1635                 return false;
1636         }
1637
1638         FLAC__metadata_iterator_delete(iterator);
1639         *needs_write = true;
1640         return true;
1641 }
1642
1643 FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
1644 {
1645         unsigned i;
1646         FLAC__bool ok = true;
1647         FLAC__StreamMetadata *block;
1648         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1649
1650         if(0 == iterator)
1651                 die("out of memory allocating iterator");
1652
1653         FLAC__metadata_iterator_init(iterator, chain);
1654
1655         block = FLAC__metadata_iterator_get_block(iterator);
1656
1657         FLAC__ASSERT(0 != block);
1658         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_STREAMINFO);
1659
1660         if(0 != filename)
1661                 printf("%s:", filename);
1662
1663         switch(operation->type) {
1664                 case OP__SHOW_MD5SUM:
1665                         for(i = 0; i < 16; i++)
1666                                 printf("%02x", block->data.stream_info.md5sum[i]);
1667                         printf("\n");
1668                         break;
1669                 case OP__SHOW_MIN_BLOCKSIZE:
1670                         printf("%u\n", block->data.stream_info.min_blocksize);
1671                         break;
1672                 case OP__SHOW_MAX_BLOCKSIZE:
1673                         printf("%u\n", block->data.stream_info.max_blocksize);
1674                         break;
1675                 case OP__SHOW_MIN_FRAMESIZE:
1676                         printf("%u\n", block->data.stream_info.min_framesize);
1677                         break;
1678                 case OP__SHOW_MAX_FRAMESIZE:
1679                         printf("%u\n", block->data.stream_info.max_framesize);
1680                         break;
1681                 case OP__SHOW_SAMPLE_RATE:
1682                         printf("%u\n", block->data.stream_info.sample_rate);
1683                         break;
1684                 case OP__SHOW_CHANNELS:
1685                         printf("%u\n", block->data.stream_info.channels);
1686                         break;
1687                 case OP__SHOW_BPS:
1688                         printf("%u\n", block->data.stream_info.bits_per_sample);
1689                         break;
1690                 case OP__SHOW_TOTAL_SAMPLES:
1691                         printf("%llu\n", block->data.stream_info.total_samples);
1692                         break;
1693                 case OP__SET_MD5SUM:
1694                         memcpy(block->data.stream_info.md5sum, operation->argument.streaminfo_md5.value, 16);
1695                         *needs_write = true;
1696                         break;
1697                 case OP__SET_MIN_BLOCKSIZE:
1698                         block->data.stream_info.min_blocksize = operation->argument.streaminfo_uint32.value;
1699                         *needs_write = true;
1700                         break;
1701                 case OP__SET_MAX_BLOCKSIZE:
1702                         block->data.stream_info.max_blocksize = operation->argument.streaminfo_uint32.value;
1703                         *needs_write = true;
1704                         break;
1705                 case OP__SET_MIN_FRAMESIZE:
1706                         block->data.stream_info.min_framesize = operation->argument.streaminfo_uint32.value;
1707                         *needs_write = true;
1708                         break;
1709                 case OP__SET_MAX_FRAMESIZE:
1710                         block->data.stream_info.max_framesize = operation->argument.streaminfo_uint32.value;
1711                         *needs_write = true;
1712                         break;
1713                 case OP__SET_SAMPLE_RATE:
1714                         block->data.stream_info.sample_rate = operation->argument.streaminfo_uint32.value;
1715                         *needs_write = true;
1716                         break;
1717                 case OP__SET_CHANNELS:
1718                         block->data.stream_info.channels = operation->argument.streaminfo_uint32.value;
1719                         *needs_write = true;
1720                         break;
1721                 case OP__SET_BPS:
1722                         block->data.stream_info.bits_per_sample = operation->argument.streaminfo_uint32.value;
1723                         *needs_write = true;
1724                         break;
1725                 case OP__SET_TOTAL_SAMPLES:
1726                         block->data.stream_info.total_samples = operation->argument.streaminfo_uint64.value;
1727                         *needs_write = true;
1728                         break;
1729                 default:
1730                         ok = false;
1731                         FLAC__ASSERT(0);
1732                         break;
1733         };
1734
1735         FLAC__metadata_iterator_delete(iterator);
1736
1737         return ok;
1738 }
1739
1740 FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw)
1741 {
1742         FLAC__bool ok = true, found_vc_block = false;
1743         FLAC__StreamMetadata *block = 0;
1744         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
1745
1746         if(0 == iterator)
1747                 die("out of memory allocating iterator");
1748
1749         FLAC__metadata_iterator_init(iterator, chain);
1750
1751         do {
1752                 block = FLAC__metadata_iterator_get_block(iterator);
1753                 if(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
1754                         found_vc_block = true;
1755         } while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
1756
1757         if(!found_vc_block) {
1758                 /* create a new block if necessary */
1759                 if(operation->type == OP__SET_VC_FIELD || operation->type == OP__IMPORT_VC_FROM) {
1760                         block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
1761                         if(0 == block)
1762                                 die("out of memory allocating VORBIS_COMMENT block");
1763                         while(FLAC__metadata_iterator_next(iterator))
1764                                 ;
1765                         if(!FLAC__metadata_iterator_insert_block_after(iterator, block)) {
1766                                 fprintf(stderr, "%s: ERROR: adding new VORBIS_COMMENT block to metadata, status =\"%s\"\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
1767                                 return false;
1768                         }
1769                         /* iterator is left pointing to new block */
1770                         FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == block);
1771                 }
1772                 else {
1773                         FLAC__metadata_iterator_delete(iterator);
1774                         return ok;
1775                 }
1776         }
1777
1778         FLAC__ASSERT(0 != block);
1779         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1780
1781         switch(operation->type) {
1782                 case OP__SHOW_VC_VENDOR:
1783                         write_vc_field(filename, &block->data.vorbis_comment.vendor_string, raw, stdout);
1784                         break;
1785                 case OP__SHOW_VC_FIELD:
1786                         write_vc_fields(filename, operation->argument.vc_field_name.value, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, stdout);
1787                         break;
1788                 case OP__REMOVE_VC_ALL:
1789                         ok = remove_vc_all(filename, block, needs_write);
1790                         break;
1791                 case OP__REMOVE_VC_FIELD:
1792                         ok = remove_vc_field(block, operation->argument.vc_field_name.value, needs_write);
1793                         break;
1794                 case OP__REMOVE_VC_FIRSTFIELD:
1795                         ok = remove_vc_firstfield(filename, block, operation->argument.vc_field_name.value, needs_write);
1796                         break;
1797                 case OP__SET_VC_FIELD:
1798                         ok = set_vc_field(filename, block, &operation->argument.vc_field, needs_write, raw);
1799                         break;
1800                 case OP__IMPORT_VC_FROM:
1801                         ok = import_vc_from(filename, block, &operation->argument.vc_filename, needs_write, raw);
1802                         break;
1803                 case OP__EXPORT_VC_TO:
1804                         ok = export_vc_to(filename, block, &operation->argument.vc_filename, raw);
1805                         break;
1806                 default:
1807                         ok = false;
1808                         FLAC__ASSERT(0);
1809                         break;
1810         };
1811
1812         FLAC__metadata_iterator_delete(iterator);
1813         return ok;
1814 }
1815
1816 FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number)
1817 {
1818         unsigned i, j;
1819         FLAC__bool matches_number = false, matches_type = false;
1820         FLAC__bool has_block_number_arg = false;
1821
1822         for(i = 0; i < options->args.num_arguments; i++) {
1823                 if(options->args.arguments[i].type == ARG__BLOCK_TYPE || options->args.arguments[i].type == ARG__EXCEPT_BLOCK_TYPE) {
1824                         for(j = 0; j < options->args.arguments[i].value.block_type.num_entries; j++) {
1825                                 if(options->args.arguments[i].value.block_type.entries[j].type == block->type) {
1826                                         if(block->type != FLAC__METADATA_TYPE_APPLICATION || !options->args.arguments[i].value.block_type.entries[j].filter_application_by_id || 0 == memcmp(options->args.arguments[i].value.block_type.entries[j].application_id, block->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN/8))
1827                                                 matches_type = true;
1828                                 }
1829                         }
1830                 }
1831                 else if(options->args.arguments[i].type == ARG__BLOCK_NUMBER) {
1832                         has_block_number_arg = true;
1833                         for(j = 0; j < options->args.arguments[i].value.block_number.num_entries; j++) {
1834                                 if(options->args.arguments[i].value.block_number.entries[j] == block_number)
1835                                         matches_number = true;
1836                         }
1837                 }
1838         }
1839
1840         if(!has_block_number_arg)
1841                 matches_number = true;
1842
1843         if(options->args.checks.has_block_type) {
1844                 FLAC__ASSERT(!options->args.checks.has_except_block_type);
1845         }
1846         else if(options->args.checks.has_except_block_type)
1847                 matches_type = !matches_type;
1848         else
1849                 matches_type = true;
1850
1851         return matches_number && matches_type;
1852 }
1853
1854 void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool raw, FLAC__bool hexdump_application)
1855 {
1856         unsigned i;
1857
1858 /*@@@ yuck, should do this with a varargs function or something: */
1859 #define PPR if(filename)printf("%s:",filename);
1860         PPR; printf("METADATA block #%u\n", block_number);
1861         PPR; printf("  type: %u (%s)\n", (unsigned)block->type, block->type<=FLAC__METADATA_TYPE_VORBIS_COMMENT? FLAC__MetadataTypeString[block->type] : "UNKNOWN");
1862         PPR; printf("  is last: %s\n", block->is_last? "true":"false");
1863         PPR; printf("  length: %u\n", block->length);
1864
1865         switch(block->type) {
1866                 case FLAC__METADATA_TYPE_STREAMINFO:
1867                         PPR; printf("  minumum blocksize: %u samples\n", block->data.stream_info.min_blocksize);
1868                         PPR; printf("  maximum blocksize: %u samples\n", block->data.stream_info.max_blocksize);
1869                         PPR; printf("  minimum framesize: %u bytes\n", block->data.stream_info.min_framesize);
1870                         PPR; printf("  maximum framesize: %u bytes\n", block->data.stream_info.max_framesize);
1871                         PPR; printf("  sample_rate: %u Hz\n", block->data.stream_info.sample_rate);
1872                         PPR; printf("  channels: %u\n", block->data.stream_info.channels);
1873                         PPR; printf("  bits-per-sample: %u\n", block->data.stream_info.bits_per_sample);
1874                         PPR; printf("  total samples: %llu\n", block->data.stream_info.total_samples);
1875                         PPR; printf("  MD5 signature: ");
1876                         for(i = 0; i < 16; i++) {
1877                                 printf("%02x", (unsigned)block->data.stream_info.md5sum[i]);
1878                         }
1879                         printf("\n");
1880                         break;
1881                 case FLAC__METADATA_TYPE_PADDING:
1882                         /* nothing to print */
1883                         break;
1884                 case FLAC__METADATA_TYPE_APPLICATION:
1885                         PPR; printf("  application ID: ");
1886                         for(i = 0; i < 4; i++) {
1887                                 PPR; printf("%02x", block->data.application.id[i]);
1888                         }
1889                         PPR; printf("\n");
1890                         PPR; printf("  data contents:\n");
1891                         if(0 != block->data.application.data) {
1892                                 if(hexdump_application)
1893                                         hexdump(filename, block->data.application.data, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, "    ");
1894                                 else
1895                                         (void) fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
1896                         }
1897                         break;
1898                 case FLAC__METADATA_TYPE_SEEKTABLE:
1899                         PPR; printf("  seek points: %u\n", block->data.seek_table.num_points);
1900                         for(i = 0; i < block->data.seek_table.num_points; i++) {
1901                                 if(block->data.seek_table.points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
1902                                         PPR; printf("    point %d: sample_number=%llu, stream_offset=%llu, frame_samples=%u\n", i, block->data.seek_table.points[i].sample_number, block->data.seek_table.points[i].stream_offset, block->data.seek_table.points[i].frame_samples);
1903                                 }
1904                                 else {
1905                                         PPR; printf("    point %d: PLACEHOLDER\n", i);
1906                                 }
1907                         }
1908                         break;
1909                 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1910                         PPR; printf("  vendor string: ");
1911                         write_vc_field(0, &block->data.vorbis_comment.vendor_string, raw, stdout);
1912                         PPR; printf("  comments: %u\n", block->data.vorbis_comment.num_comments);
1913                         for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
1914                                 PPR; printf("    comment[%u]: ", i);
1915                                 write_vc_field(0, &block->data.vorbis_comment.comments[i], raw, stdout);
1916                         }
1917                         break;
1918                 default:
1919                         PPR; printf("SKIPPING block of unknown type\n");
1920                         break;
1921         }
1922 #undef PPR
1923 }
1924
1925 void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f)
1926 {
1927         if(0 != entry->entry) {
1928                 if(filename)
1929                         fprintf(f, "%s:", filename);
1930
1931                 if(!raw) {
1932                         /*
1933                          * utf8_decode() works on NULL-terminated strings, so
1934                          * we append a null to the entry.  @@@ Note, this means
1935                          * that comments that contain an embedded null will be
1936                          * truncated by utf_decode().
1937                          */
1938                         char *terminated, *converted;
1939
1940                         if(0 == (terminated = malloc(entry->length + 1)))
1941                                 die("out of memory allocating space for vorbis comment");
1942                         memcpy(terminated, entry->entry, entry->length);
1943                         terminated[entry->length] = '\0';
1944                         if(utf8_decode(terminated, &converted) >= 0) {
1945                                 (void) fwrite(converted, 1, strlen(converted), f);
1946                                 free(terminated);
1947                                 free(converted);
1948                         }
1949                         else {
1950                                 free(terminated);
1951                                 (void) fwrite(entry->entry, 1, entry->length, f);
1952                         }
1953                 }
1954                 else {
1955                         (void) fwrite(entry->entry, 1, entry->length, f);
1956                 }
1957         }
1958
1959         fprintf(f, "\n");
1960 }
1961
1962 void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f)
1963 {
1964         unsigned i;
1965         const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0;
1966
1967         for(i = 0; i < num_entries; i++) {
1968                 if(0 == field_name || field_name_matches_entry(field_name, field_name_length, entry + i))
1969                         write_vc_field(filename, entry + i, raw, f);
1970         }
1971 }
1972
1973 FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write)
1974 {
1975         FLAC__ASSERT(0 != block);
1976         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
1977         FLAC__ASSERT(0 != needs_write);
1978
1979         if(0 != block->data.vorbis_comment.comments) {
1980                 FLAC__ASSERT(block->data.vorbis_comment.num_comments > 0);
1981                 if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, 0)) {
1982                         fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
1983                         return false;
1984                 }
1985                 *needs_write = true;
1986         }
1987         else {
1988                 FLAC__ASSERT(block->data.vorbis_comment.num_comments == 0);
1989         }
1990
1991         return true;
1992 }
1993
1994 FLAC__bool remove_vc_field(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
1995 {
1996         FLAC__bool ok = true;
1997         const unsigned field_name_length = strlen(field_name);
1998         int i;
1999
2000         FLAC__ASSERT(0 != block);
2001         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
2002         FLAC__ASSERT(0 != needs_write);
2003
2004         /* must delete from end to start otherwise it will interfere with our iteration */
2005         for(i = (int)block->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
2006                 if(field_name_matches_entry(field_name, field_name_length, block->data.vorbis_comment.comments + i)) {
2007                         ok &= FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned)i);
2008                         if(ok)
2009                                 *needs_write = true;
2010                 }
2011         }
2012
2013         return ok;
2014 }
2015
2016 FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
2017 {
2018         const unsigned field_name_length = strlen(field_name);
2019         unsigned i;
2020
2021         FLAC__ASSERT(0 != block);
2022         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
2023         FLAC__ASSERT(0 != needs_write);
2024
2025         for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
2026                 if(field_name_matches_entry(field_name, field_name_length, block->data.vorbis_comment.comments + i)) {
2027                         if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned)i)) {
2028                                 fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
2029                                 return false;
2030                         }
2031                         else
2032                                 *needs_write = true;
2033                         break;
2034                 }
2035         }
2036
2037         return true;
2038 }
2039
2040 FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw)
2041 {
2042         FLAC__StreamMetadata_VorbisComment_Entry entry;
2043         char *converted;
2044         FLAC__bool needs_free = false;
2045
2046         FLAC__ASSERT(0 != block);
2047         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
2048         FLAC__ASSERT(0 != field);
2049         FLAC__ASSERT(0 != needs_write);
2050
2051         if(raw) {
2052                 entry.entry = field->field;
2053         }
2054         else if(utf8_encode(field->field, &converted) >= 0) {
2055                 entry.entry = converted;
2056                 needs_free = true;
2057         }
2058         else {
2059                 fprintf(stderr, "%s: ERROR: couldn't convert comment to UTF-8\n", filename);
2060                 return false;
2061         }
2062
2063         entry.length = strlen(entry.entry);
2064
2065         if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true)) {
2066                 if(needs_free)
2067                         free(converted);
2068                 fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
2069                 return false;
2070         }
2071         else {
2072                 *needs_write = true;
2073                 if(needs_free)
2074                         free(converted);
2075                 return true;
2076         }
2077 }
2078
2079 FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw)
2080 {
2081         FILE *f;
2082         char line[65536];
2083         FLAC__bool ret;
2084
2085         if(0 == vc_filename->value || strlen(vc_filename->value) == 0) {
2086                 fprintf(stderr, "%s: ERROR: empty import file name\n", filename);
2087                 return false;
2088         }
2089         if(0 == strcmp(vc_filename->value, "-"))
2090                 f = stdin;
2091         else
2092                 f = fopen(vc_filename->value, "r");
2093
2094         if(0 == f) {
2095                 fprintf(stderr, "%s: ERROR: can't open import file %s\n", filename, vc_filename->value);
2096                 return false;
2097         }
2098
2099         ret = true;
2100         while(ret && !feof(f)) {
2101                 fgets(line, sizeof(line), f);
2102                 if(!feof(f)) {
2103                         char *p = strchr(line, '\n');
2104                         if(0 == p) {
2105                                 fprintf(stderr, "%s: ERROR: line too long, aborting\n", vc_filename->value);
2106                                 ret = false;
2107                         }
2108                         else {
2109                                 const char *violation;
2110                                 Argument_VcField field;
2111                                 *p = '\0';
2112                                 memset(&field, 0, sizeof(Argument_VcField));
2113                                 if(!parse_vorbis_comment_field(line, &field.field, &field.field_name, &field.field_value, &field.field_value_length, &violation)) {
2114                                         FLAC__ASSERT(0 != violation);
2115                                         fprintf(stderr, "%s: ERROR: malformed vorbis comment field \"%s\",\n       %s\n", vc_filename->value, line, violation);
2116                                         ret = false;
2117                                 }
2118                                 else {
2119                                         ret = set_vc_field(filename, block, &field, needs_write, raw);
2120                                 }
2121                                 if(0 != field.field)
2122                                         free(field.field);
2123                                 if(0 != field.field_name)
2124                                         free(field.field_name);
2125                                 if(0 != field.field_value)
2126                                         free(field.field_value);
2127                         }
2128                 }
2129         };
2130
2131         if(f != stdin)
2132                 fclose(f);
2133         return ret;
2134 }
2135
2136 FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_VcFilename *vc_filename, FLAC__bool raw)
2137 {
2138         FILE *f;
2139         FLAC__bool ret;
2140
2141         if(0 == vc_filename->value || strlen(vc_filename->value) == 0) {
2142                 fprintf(stderr, "%s: ERROR: empty export file name\n", filename);
2143                 return false;
2144         }
2145         if(0 == strcmp(vc_filename->value, "-"))
2146                 f = stdout;
2147         else
2148                 f = fopen(vc_filename->value, "w");
2149
2150         if(0 == f) {
2151                 fprintf(stderr, "%s: ERROR: can't open export file %s\n", filename, vc_filename->value);
2152                 return false;
2153         }
2154
2155         ret = true;
2156
2157         write_vc_fields(0, 0, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, f);
2158
2159         if(f != stdout)
2160                 fclose(f);
2161         return ret;
2162 }
2163
2164 FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
2165 {
2166         const FLAC__byte *eq = memchr(entry->entry, '=', entry->length);
2167 #if defined _MSC_VER || defined __MINGW32__
2168 #define FLAC__STRNCASECMP strnicmp
2169 #else
2170 #define FLAC__STRNCASECMP strncasecmp
2171 #endif
2172         return (0 != eq && (unsigned)(eq-entry->entry) == field_name_length && 0 == FLAC__STRNCASECMP(field_name, entry->entry, field_name_length));
2173 #undef FLAC__STRNCASECMP
2174 }
2175
2176 void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent)
2177 {
2178         unsigned i, left = bytes;
2179         const FLAC__byte *b = buf;
2180
2181         for(i = 0; i < bytes; i += 16) {
2182                 printf("%s%s%s%08X: "
2183                         "%02X %02X %02X %02X %02X %02X %02X %02X "
2184                         "%02X %02X %02X %02X %02X %02X %02X %02X "
2185                         "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
2186                         filename? filename:"", filename? ":":"",
2187                         indent, i,
2188                         left >  0? (unsigned char)b[ 0] : 0,
2189                         left >  1? (unsigned char)b[ 1] : 0,
2190                         left >  2? (unsigned char)b[ 2] : 0,
2191                         left >  3? (unsigned char)b[ 3] : 0,
2192                         left >  4? (unsigned char)b[ 4] : 0,
2193                         left >  5? (unsigned char)b[ 5] : 0,
2194                         left >  6? (unsigned char)b[ 6] : 0,
2195                         left >  7? (unsigned char)b[ 7] : 0,
2196                         left >  8? (unsigned char)b[ 8] : 0,
2197                         left >  9? (unsigned char)b[ 9] : 0,
2198                         left > 10? (unsigned char)b[10] : 0,
2199                         left > 11? (unsigned char)b[11] : 0,
2200                         left > 12? (unsigned char)b[12] : 0,
2201                         left > 13? (unsigned char)b[13] : 0,
2202                         left > 14? (unsigned char)b[14] : 0,
2203                         left > 15? (unsigned char)b[15] : 0,
2204                         (left >  0) ? (isprint(b[ 0]) ? b[ 0] : '.') : ' ',
2205                         (left >  1) ? (isprint(b[ 1]) ? b[ 1] : '.') : ' ',
2206                         (left >  2) ? (isprint(b[ 2]) ? b[ 2] : '.') : ' ',
2207                         (left >  3) ? (isprint(b[ 3]) ? b[ 3] : '.') : ' ',
2208                         (left >  4) ? (isprint(b[ 4]) ? b[ 4] : '.') : ' ',
2209                         (left >  5) ? (isprint(b[ 5]) ? b[ 5] : '.') : ' ',
2210                         (left >  6) ? (isprint(b[ 6]) ? b[ 6] : '.') : ' ',
2211                         (left >  7) ? (isprint(b[ 7]) ? b[ 7] : '.') : ' ',
2212                         (left >  8) ? (isprint(b[ 8]) ? b[ 8] : '.') : ' ',
2213                         (left >  9) ? (isprint(b[ 9]) ? b[ 9] : '.') : ' ',
2214                         (left > 10) ? (isprint(b[10]) ? b[10] : '.') : ' ',
2215                         (left > 11) ? (isprint(b[11]) ? b[11] : '.') : ' ',
2216                         (left > 12) ? (isprint(b[12]) ? b[12] : '.') : ' ',
2217                         (left > 13) ? (isprint(b[13]) ? b[13] : '.') : ' ',
2218                         (left > 14) ? (isprint(b[14]) ? b[14] : '.') : ' ',
2219                         (left > 15) ? (isprint(b[15]) ? b[15] : '.') : ' '
2220                 );
2221                 left -= 16;
2222                 b += 16;
2223    }
2224 }
2225
2226 void undocumented_warning(const char *opt)
2227 {
2228         fprintf(stderr, "WARNING: undocmented option --%s should be used with caution,\n         only for repairing a damaged STREAMINFO block\n", opt);
2229 }