fix bug in MD5 sum printing
[flac.git] / src / metaflac / main.c
index 1183fe1..4f3b56a 100644 (file)
@@ -33,17 +33,19 @@ more powerful operations yet to add:
 #include <stdlib.h>
 #include <string.h>
 
-#if HAVE_GETOPT_LONG
+#if 0
+/*[JEC] was:#if HAVE_GETOPT_LONG*/
+/*[JEC] see flac/include/share/getopt.h as to why the change */
 #  include <getopt.h>
 #else
 #  include "share/getopt.h"
 #endif
 
 /*
-   getopt format struct; note we don't use short options so we just
+   FLAC__share__getopt format struct; note we don't use short options so we just
    set the 'val' field to 0 everywhere to indicate a valid option.
 */
-static struct option long_options_[] = {
+static struct FLAC__share__option long_options_[] = {
        /* global options */
     { "preserve-modtime", 0, 0, 0 },
     { "with-filename", 0, 0, 0 },
@@ -65,6 +67,7 @@ static struct option long_options_[] = {
     { "remove-vc-field", 1, 0, 0 },
     { "remove-vc-firstfield", 1, 0, 0 },
     { "set-vc-field", 1, 0, 0 },
+    { "add-padding", 1, 0, 0 },
        /* major operations */
     { "help", 0, 0, 0 },
     { "list", 0, 0, 0 },
@@ -99,6 +102,7 @@ typedef enum {
        OP__REMOVE_VC_FIELD,
        OP__REMOVE_VC_FIRSTFIELD,
        OP__SET_VC_FIELD,
+       OP__ADD_PADDING,
        OP__LIST,
        OP__APPEND,
        OP__REMOVE,
@@ -120,6 +124,7 @@ typedef struct {
 } Argument_VcFieldName;
 
 typedef struct {
+       char *field; /* the whole field as passed on the command line, i.e. "NAME=VALUE" */
        char *field_name;
        /* according to the vorbis spec, field values can contain \0 so simple C strings are not enough here */
        unsigned field_value_length;
@@ -132,7 +137,7 @@ typedef struct {
 } Argument_BlockNumber;
 
 typedef struct {
-       FLAC__MetaDataType type;
+       FLAC__MetadataType type;
        char application_id[4]; /* only relevant if type == FLAC__STREAM_METADATA_TYPE_APPLICATION */
        FLAC__bool filter_application_by_id;
 } Argument_BlockTypeEntry;
@@ -151,12 +156,17 @@ typedef struct {
 } Argument_FromFile;
 
 typedef struct {
+       unsigned length;
+} Argument_AddPadding;
+
+typedef struct {
        OperationType type;
        union {
                Argument_VcFieldName show_vc_field;
                Argument_VcFieldName remove_vc_field;
                Argument_VcFieldName remove_vc_firstfield;
                Argument_VcField set_vc_field;
+               Argument_AddPadding add_padding;
        } argument;
 } Operation;
 
@@ -193,7 +203,7 @@ typedef struct {
                unsigned capacity;
        } args;
        unsigned num_files;
-       const char **filenames;
+       char **filenames;
 } CommandLineOptions;
 
 static void die(const char *message);
@@ -209,7 +219,8 @@ static Argument *append_argument(CommandLineOptions *options, ArgumentType type)
 static int short_usage(const char *message, ...);
 static int long_usage(const char *message, ...);
 static char *local_strdup(const char *source);
-static FLAC__bool parse_vorbis_comment_field(const char *field, char **name, char **value, unsigned *length);
+static FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length);
+static FLAC__bool parse_add_padding(const char *in, unsigned *out);
 static FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out);
 static FLAC__bool parse_block_type(const char *in, Argument_BlockType *out);
 static FLAC__bool parse_data_format(const char *in, Argument_DataFormat *out);
@@ -217,17 +228,25 @@ static FLAC__bool parse_application_data_format(const char *in, FLAC__bool *out)
 static FLAC__bool do_operations(const CommandLineOptions *options);
 static FLAC__bool do_major_operation(const CommandLineOptions *options);
 static FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options);
-static FLAC__bool do_major_operation__list(const char *filename, FLAC__MetaData_Chain *chain, const CommandLineOptions *options);
-static FLAC__bool do_major_operation__append(FLAC__MetaData_Chain *chain, const CommandLineOptions *options);
-static FLAC__bool do_major_operation__remove(FLAC__MetaData_Chain *chain, const CommandLineOptions *options);
-static FLAC__bool do_major_operation__remove_all(FLAC__MetaData_Chain *chain, const CommandLineOptions *options);
+static FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
+static FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
+static FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
+static FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options);
 static FLAC__bool do_shorthand_operations(const CommandLineOptions *options);
 static FLAC__bool do_shorthand_operations_on_file(const char *fielname, const CommandLineOptions *options);
-static FLAC__bool do_shorthand_operation(const char *filename, FLAC__MetaData_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
-static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__MetaData_Chain *chain, OperationType op);
-static FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__MetaData_Chain *chain, OperationType op, FLAC__bool *needs_write);
-static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetaData *block, unsigned block_number);
-static void write_metadata(const char *filename, FLAC__StreamMetaData *block, unsigned block_number, FLAC__bool hexdump_application);
+static FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
+static FLAC__bool do_shorthand_operation__add_padding(FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write);
+static FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, OperationType op);
+static FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write);
+static FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number);
+static void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool hexdump_application);
+static void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry);
+static void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries);
+static FLAC__bool remove_vc_all(FLAC__StreamMetadata *block, FLAC__bool *needs_write);
+static FLAC__bool remove_vc_field(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
+static FLAC__bool remove_vc_firstfield(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
+static FLAC__bool set_vc_field(FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write);
+static FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry);
 static void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent);
 
 int main(int argc, char *argv[])
@@ -287,10 +306,10 @@ FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options)
     int option_index = 1;
        FLAC__bool had_error = false;
 
-    while ((ret = getopt_long(argc, argv, "", long_options_, &option_index)) != -1) {
+    while ((ret = FLAC__share__getopt_long(argc, argv, "", long_options_, &option_index)) != -1) {
         switch (ret) {
             case 0:
-                               had_error |= !parse_option(option_index, optarg, options);
+                               had_error |= !parse_option(option_index, FLAC__share__optarg, options);
                 break;
                        case '?':
                        case ':':
@@ -303,22 +322,22 @@ FLAC__bool parse_options(int argc, char *argv[], CommandLineOptions *options)
     }
 
        if(options->prefix_with_filename == 2)
-               options->prefix_with_filename = (argc - optind > 1);
+               options->prefix_with_filename = (argc - FLAC__share__optind > 1);
 
-       if(optind >= argc && !options->show_long_help) {
+       if(FLAC__share__optind >= argc && !options->show_long_help) {
                fprintf(stderr,"ERROR: you must specify at least one FLAC file;\n");
                fprintf(stderr,"       metaflac cannot be used as a pipe\n");
                had_error = true;
        }
 
-       options->num_files = argc - optind;
+       options->num_files = argc - FLAC__share__optind;
 
        if(options->num_files > 0) {
                unsigned i = 0;
                if(0 == (options->filenames = malloc(sizeof(char *) * options->num_files)))
                        die("out of memory allocating space for file names list");
-               while(optind < argc)
-                       options->filenames[i++] = local_strdup(argv[optind++]);
+               while(FLAC__share__optind < argc)
+                       options->filenames[i++] = local_strdup(argv[FLAC__share__optind++]);
        }
 
        if(options->args.checks.num_major_ops > 0) {
@@ -413,11 +432,19 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi
     else if(0 == strcmp(opt, "set-vc-field")) {
                op = append_shorthand_operation(options, OP__SET_VC_FIELD);
                FLAC__ASSERT(0 != option_argument);
-               if(!parse_vorbis_comment_field(option_argument, &(op->argument.set_vc_field.field_name), &(op->argument.set_vc_field.field_value), &(op->argument.set_vc_field.field_value_length))) {
+               if(!parse_vorbis_comment_field(option_argument, &(op->argument.set_vc_field.field), &(op->argument.set_vc_field.field_name), &(op->argument.set_vc_field.field_value), &(op->argument.set_vc_field.field_value_length))) {
                        fprintf(stderr, "ERROR: malformed vorbis comment field \"%s\"\n", option_argument);
                        ok = false;
                }
        }
+    else if(0 == strcmp(opt, "add-padding")) {
+               op = append_shorthand_operation(options, OP__ADD_PADDING);
+               FLAC__ASSERT(0 != option_argument);
+               if(!parse_add_padding(option_argument, &(op->argument.add_padding.length))) {
+                       fprintf(stderr, "ERROR: illegal length \"%s\", length must be >= 0 and < 2^%u\n", option_argument, FLAC__STREAM_METADATA_LENGTH_LEN);
+                       ok = false;
+               }
+       }
     else if(0 == strcmp(opt, "help")) {
                options->show_long_help = true;
        }
@@ -510,6 +537,8 @@ void free_options(CommandLineOptions *options)
                                        free(op->argument.show_vc_field.field_name);
                                break;
                        case OP__SET_VC_FIELD:
+                               if(0 != op->argument.set_vc_field.field)
+                                       free(op->argument.set_vc_field.field);
                                if(0 != op->argument.set_vc_field.field_name)
                                        free(op->argument.set_vc_field.field_name);
                                if(0 != op->argument.set_vc_field.field_value)
@@ -723,6 +752,9 @@ int long_usage(const char *message, ...)
     fprintf(out, "                      the Vorbis comment spec, of the form \"NAME=VALUE\".  If\n");
     fprintf(out, "                      there is currently no VORBIS_COMMENT block, one will be\n");
     fprintf(out, "                      created.\n");
+    fprintf(out, "--add-padding=length  Add a padding block of the given length (in bytes).\n");
+    fprintf(out, "                      The overall length of the new block will be 4 + length;\n");
+    fprintf(out, "                      the extra 4 bytes is for the metadata block header.\n");
     fprintf(out, "\n");
     fprintf(out, "Major operations:\n");
     fprintf(out, "--list\n");
@@ -822,9 +854,14 @@ char *local_strdup(const char *source)
        return ret;
 }
 
-FLAC__bool parse_vorbis_comment_field(const char *field, char **name, char **value, unsigned *length)
+FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length)
 {
-       char *p, *s = local_strdup(field);
+       char *p, *s;
+
+       if(0 != field)
+               *field = local_strdup(field_ref);
+
+       s = local_strdup(field_ref);
 
        if(0 == (p = strchr(s, '='))) {
                free(s);
@@ -839,6 +876,12 @@ FLAC__bool parse_vorbis_comment_field(const char *field, char **name, char **val
        return true;
 }
 
+FLAC__bool parse_add_padding(const char *in, unsigned *out)
+{
+       *out = (unsigned)strtoul(in, 0, 10);
+       return *out < (1u << FLAC__STREAM_METADATA_LENGTH_LEN);
+}
+
 FLAC__bool parse_block_number(const char *in, Argument_BlockNumber *out)
 {
        char *p, *q, *s, *end;
@@ -1015,13 +1058,13 @@ FLAC__bool do_major_operation(const CommandLineOptions *options)
 FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOptions *options)
 {
        FLAC__bool ok = true, needs_write = false;
-       FLAC__MetaData_Chain *chain = FLAC__metadata_chain_new();
+       FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
 
        if(0 == chain)
                die("out of memory allocating chain");
 
        if(!FLAC__metadata_chain_read(chain, filename)) {
-               fprintf(stderr, "ERROR: reading metadata, status = \"%s\"\n", FLAC__MetaData_ChainStatusString[FLAC__metadata_chain_status(chain)]);
+               fprintf(stderr, "ERROR: reading metadata, status = \"%s\"\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
                return false;
        }
 
@@ -1059,7 +1102,7 @@ FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOpt
                        FLAC__metadata_chain_sort_padding(chain);
                ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
                if(!ok)
-                       fprintf(stderr, "ERROR: writing FLAC file %s, error = %s\n", filename, FLAC__MetaData_ChainStatusString[FLAC__metadata_chain_status(chain)]);
+                       fprintf(stderr, "ERROR: writing FLAC file %s, error = %s\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
        }
 
        FLAC__metadata_chain_delete(chain);
@@ -1067,10 +1110,10 @@ FLAC__bool do_major_operation_on_file(const char *filename, const CommandLineOpt
        return ok;
 }
 
-FLAC__bool do_major_operation__list(const char *filename, FLAC__MetaData_Chain *chain, const CommandLineOptions *options)
+FLAC__bool do_major_operation__list(const char *filename, FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
 {
-       FLAC__MetaData_Iterator *iterator = FLAC__metadata_iterator_new();
-       FLAC__StreamMetaData *block;
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
+       FLAC__StreamMetadata *block;
        FLAC__bool ok = true;
        unsigned block_number;
 
@@ -1095,15 +1138,16 @@ FLAC__bool do_major_operation__list(const char *filename, FLAC__MetaData_Chain *
        return ok;
 }
 
-FLAC__bool do_major_operation__append(FLAC__MetaData_Chain *chain, const CommandLineOptions *options)
+FLAC__bool do_major_operation__append(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
 {
+       (void) chain, (void) options;
        fprintf(stderr, "ERROR: --append not implemented yet\n"); /*@@@*/
        return false;
 }
 
-FLAC__bool do_major_operation__remove(FLAC__MetaData_Chain *chain, const CommandLineOptions *options)
+FLAC__bool do_major_operation__remove(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
 {
-       FLAC__MetaData_Iterator *iterator = FLAC__metadata_iterator_new();
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
        FLAC__bool ok = true;
        unsigned block_number;
 
@@ -1115,8 +1159,11 @@ FLAC__bool do_major_operation__remove(FLAC__MetaData_Chain *chain, const Command
        block_number = 0;
        while(ok && FLAC__metadata_iterator_next(iterator)) {
                block_number++;
-               if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number))
+               if(passes_filter(options, FLAC__metadata_iterator_get_block(iterator), block_number)) {
                        ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
+                       if(options->use_padding)
+                               ok &= FLAC__metadata_iterator_next(iterator);
+               }
        }
 
        FLAC__metadata_iterator_delete(iterator);
@@ -1124,9 +1171,9 @@ FLAC__bool do_major_operation__remove(FLAC__MetaData_Chain *chain, const Command
        return ok;
 }
 
-FLAC__bool do_major_operation__remove_all(FLAC__MetaData_Chain *chain, const CommandLineOptions *options)
+FLAC__bool do_major_operation__remove_all(FLAC__Metadata_Chain *chain, const CommandLineOptions *options)
 {
-       FLAC__MetaData_Iterator *iterator = FLAC__metadata_iterator_new();
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
        FLAC__bool ok = true;
 
        if(0 == iterator)
@@ -1134,8 +1181,11 @@ FLAC__bool do_major_operation__remove_all(FLAC__MetaData_Chain *chain, const Com
 
        FLAC__metadata_iterator_init(iterator, chain);
 
-       while(ok && FLAC__metadata_iterator_next(iterator))
+       while(ok && FLAC__metadata_iterator_next(iterator)) {
                ok &= FLAC__metadata_iterator_delete_block(iterator, options->use_padding);
+               if(options->use_padding)
+                       ok &= FLAC__metadata_iterator_next(iterator);
+       }
 
        FLAC__metadata_iterator_delete(iterator);
 
@@ -1158,13 +1208,13 @@ FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLi
 {
        unsigned i;
        FLAC__bool ok = true, needs_write = false;
-       FLAC__MetaData_Chain *chain = FLAC__metadata_chain_new();
+       FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();
 
        if(0 == chain)
                die("out of memory allocating chain");
 
        if(!FLAC__metadata_chain_read(chain, filename)) {
-               fprintf(stderr, "ERROR: reading metadata, status = \"%s\"\n", FLAC__MetaData_ChainStatusString[FLAC__metadata_chain_status(chain)]);
+               fprintf(stderr, "ERROR: reading metadata, status = \"%s\"\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
                return false;
        }
 
@@ -1176,7 +1226,7 @@ FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLi
                        FLAC__metadata_chain_sort_padding(chain);
                ok = FLAC__metadata_chain_write(chain, options->use_padding, options->preserve_modtime);
                if(!ok)
-                       fprintf(stderr, "ERROR: writing FLAC file %s, error = %s\n", filename, FLAC__MetaData_ChainStatusString[FLAC__metadata_chain_status(chain)]);
+                       fprintf(stderr, "ERROR: writing FLAC file %s, error = %s\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
        }
 
        FLAC__metadata_chain_delete(chain);
@@ -1184,9 +1234,8 @@ FLAC__bool do_shorthand_operations_on_file(const char *filename, const CommandLi
        return ok;
 }
 
-FLAC__bool do_shorthand_operation(const char *filename, FLAC__MetaData_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
+FLAC__bool do_shorthand_operation(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
 {
-       unsigned i;
        FLAC__bool ok = true;
 
        switch(operation->type) {
@@ -1207,7 +1256,10 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__MetaData_Chain *ch
                case OP__REMOVE_VC_FIELD:
                case OP__REMOVE_VC_FIRSTFIELD:
                case OP__SET_VC_FIELD:
-                       ok = do_shorthand_operation__vorbis_comment(filename, chain, operation->type, needs_write);
+                       ok = do_shorthand_operation__vorbis_comment(filename, chain, operation, needs_write);
+                       break;
+               case OP__ADD_PADDING:
+                       ok = do_shorthand_operation__add_padding(chain, operation->argument.add_padding.length, needs_write);
                        break;
                default:
                        ok = false;
@@ -1218,12 +1270,41 @@ FLAC__bool do_shorthand_operation(const char *filename, FLAC__MetaData_Chain *ch
        return ok;
 }
 
-FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__MetaData_Chain *chain, OperationType op)
+FLAC__bool do_shorthand_operation__add_padding(FLAC__Metadata_Chain *chain, unsigned length, FLAC__bool *needs_write)
+{
+       FLAC__StreamMetadata *padding = 0;
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
+
+       if(0 == iterator)
+               die("out of memory allocating iterator");
+
+       FLAC__metadata_iterator_init(iterator, chain);
+
+       while(FLAC__metadata_iterator_next(iterator))
+               ;
+
+       padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
+       if(0 == padding)
+               die("out of memory allocating PADDING block");
+
+       padding->length = length;
+
+       if(!FLAC__metadata_iterator_insert_block_after(iterator, padding)) {
+               fprintf(stderr, "ERROR: adding new PADDING block to metadata, status =\"%s\"\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
+               FLAC__metadata_object_delete(padding);
+               return false;
+       }
+
+       *needs_write = true;
+       return true;
+}
+
+FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__Metadata_Chain *chain, OperationType op)
 {
        unsigned i;
        FLAC__bool ok = true;
-       FLAC__StreamMetaData *block;
-       FLAC__MetaData_Iterator *iterator = FLAC__metadata_iterator_new();
+       FLAC__StreamMetadata *block;
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
 
        if(0 == iterator)
                die("out of memory allocating iterator");
@@ -1279,11 +1360,11 @@ FLAC__bool do_shorthand_operation__streaminfo(const char *filename, FLAC__MetaDa
        return ok;
 }
 
-FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__MetaData_Chain *chain, OperationType op, FLAC__bool *needs_write)
+FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write)
 {
        FLAC__bool ok = true, found_vc_block = false;
-       FLAC__StreamMetaData *block = 0;
-       FLAC__MetaData_Iterator *iterator = FLAC__metadata_iterator_new();
+       FLAC__StreamMetadata *block = 0;
+       FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
 
        if(0 == iterator)
                die("out of memory allocating iterator");
@@ -1297,14 +1378,14 @@ FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Me
        } while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
 
        /* create a new block if necessary */
-       if(!found_vc_block && op == OP__SET_VC_FIELD) {
+       if(!found_vc_block && operation->type == OP__SET_VC_FIELD) {
                block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
                if(0 == block)
                        die("out of memory allocating VORBIS_COMMENT block");
                while(FLAC__metadata_iterator_next(iterator))
                        ;
                if(!FLAC__metadata_iterator_insert_block_after(iterator, block)) {
-                       fprintf(stderr, "ERROR: adding new VORBIS_COMMENT block to metadata\n");
+                       fprintf(stderr, "ERROR: adding new VORBIS_COMMENT block to metadata, status =\"%s\"\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
                        return false;
                }
                /* iterator is left pointing to new block */
@@ -1314,14 +1395,25 @@ FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Me
        FLAC__ASSERT(0 != block);
        FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
 
-       /*@@@@ set needs_write = true where necessary */
-       switch(op) {
+       switch(operation->type) {
                case OP__SHOW_VC_VENDOR:
+                       write_vc_field(filename, &block->data.vorbis_comment.vendor_string);
+                       break;
                case OP__SHOW_VC_FIELD:
+                       write_vc_fields(filename, operation->argument.show_vc_field.field_name, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments);
+                       break;
                case OP__REMOVE_VC_ALL:
+                       ok = remove_vc_all(block, needs_write);
+                       break;
                case OP__REMOVE_VC_FIELD:
+                       ok = remove_vc_field(block, operation->argument.remove_vc_field.field_name, needs_write);
+                       break;
                case OP__REMOVE_VC_FIRSTFIELD:
+                       ok = remove_vc_firstfield(block, operation->argument.remove_vc_firstfield.field_name, needs_write);
+                       break;
                case OP__SET_VC_FIELD:
+                       ok = set_vc_field(block, &operation->argument.set_vc_field, needs_write);
+                       break;
                default:
                        ok = false;
                        FLAC__ASSERT(0);
@@ -1331,7 +1423,7 @@ FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__Me
        return ok;
 }
 
-FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetaData *block, unsigned block_number)
+FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMetadata *block, unsigned block_number)
 {
        unsigned i, j;
        FLAC__bool matches_number = false, matches_type = false;
@@ -1369,13 +1461,14 @@ FLAC__bool passes_filter(const CommandLineOptions *options, const FLAC__StreamMe
        return matches_number && matches_type;
 }
 
-void write_metadata(const char *filename, FLAC__StreamMetaData *block, unsigned block_number, FLAC__bool hexdump_application)
+void write_metadata(const char *filename, FLAC__StreamMetadata *block, unsigned block_number, FLAC__bool hexdump_application)
 {
        unsigned i;
 
+/*@@@ yuck, should do this with a varargs function or something: */
 #define PPR if(filename)printf("%s:",filename);
        PPR; printf("METADATA block #%u\n", block_number);
-       PPR; printf("  type: %u (%s)\n", (unsigned)block->type, block->type<=FLAC__METADATA_TYPE_VORBIS_COMMENT? FLAC__MetaDataTypeString[block->type] : "UNKNOWN");
+       PPR; printf("  type: %u (%s)\n", (unsigned)block->type, block->type<=FLAC__METADATA_TYPE_VORBIS_COMMENT? FLAC__MetadataTypeString[block->type] : "UNKNOWN");
        PPR; printf("  is last: %s\n", block->is_last? "true":"false");
        PPR; printf("  length: %u\n", block->length);
 
@@ -1391,9 +1484,9 @@ void write_metadata(const char *filename, FLAC__StreamMetaData *block, unsigned
                        PPR; printf("  total samples: %llu\n", block->data.stream_info.total_samples);
                        PPR; printf("  MD5 signature: ");
                        for(i = 0; i < 16; i++) {
-                               PPR; printf("%02x", block->data.stream_info.md5sum[i]);
+                               printf("%02x", (unsigned)block->data.stream_info.md5sum[i]);
                        }
-                       PPR; printf("\n");
+                       printf("\n");
                        break;
                case FLAC__METADATA_TYPE_PADDING:
                        /* nothing to print */
@@ -1409,16 +1502,30 @@ void write_metadata(const char *filename, FLAC__StreamMetaData *block, unsigned
                                if(hexdump_application)
                                        hexdump(filename, block->data.application.data, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, "    ");
                                else
-                                       fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
+                                       (void) fwrite(block->data.application.data, 1, block->length - FLAC__STREAM_METADATA_HEADER_LENGTH, stdout);
                        }
                        break;
                case FLAC__METADATA_TYPE_SEEKTABLE:
                        PPR; printf("  seek points: %u\n", block->data.seek_table.num_points);
                        for(i = 0; i < block->data.seek_table.num_points; i++) {
-                               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);
+                               if(block->data.seek_table.points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) {
+                                       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);
+                               }
+                               else {
+                                       PPR; printf("    point %d: PLACEHOLDER\n", i);
+                               }
                        }
                        break;
                case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+                       PPR; printf("  vendor string: ");
+                       fwrite(block->data.vorbis_comment.vendor_string.entry, 1, block->data.vorbis_comment.vendor_string.length, stdout);
+                       printf("\n");
+                       PPR; printf("  comments: %u\n", block->data.vorbis_comment.num_comments);
+                       for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
+                               PPR; printf("    comment[%u]: ", i);
+                               fwrite(block->data.vorbis_comment.comments[i].entry, 1, block->data.vorbis_comment.comments[i].length, stdout);
+                               printf("\n");
+                       }
                        break;
                default:
                        PPR; printf("SKIPPING block of unknown type\n");
@@ -1427,6 +1534,113 @@ void write_metadata(const char *filename, FLAC__StreamMetaData *block, unsigned
 #undef PPR
 }
 
+void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
+{
+       if(filename)
+               printf("%s:", filename);
+       (void) fwrite(entry->entry, 1, entry->length, stdout);
+       printf("\n");
+}
+
+void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries)
+{
+       unsigned i;
+       const unsigned field_name_length = strlen(field_name);
+
+       for(i = 0; i < num_entries; i++) {
+               if(field_name_matches_entry(field_name, field_name_length, entry + i))
+                       write_vc_field(filename, entry + i);
+       }
+}
+
+FLAC__bool remove_vc_all(FLAC__StreamMetadata *block, FLAC__bool *needs_write)
+{
+       FLAC__ASSERT(0 != block);
+       FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(0 != needs_write);
+
+       if(0 != block->data.vorbis_comment.comments) {
+               FLAC__ASSERT(block->data.vorbis_comment.num_comments > 0);
+               if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, 0))
+                       return false;
+               *needs_write = true;
+       }
+       else {
+               FLAC__ASSERT(block->data.vorbis_comment.num_comments == 0);
+       }
+
+       return true;
+}
+
+FLAC__bool remove_vc_field(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
+{
+       FLAC__bool ok = true;
+       const unsigned field_name_length = strlen(field_name);
+       int i;
+
+       FLAC__ASSERT(0 != block);
+       FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(0 != needs_write);
+
+       /* must delete from end to start otherwise it will interfere with our iteration */
+       for(i = (int)block->data.vorbis_comment.num_comments - 1; ok && i >= 0; i--) {
+               if(field_name_matches_entry(field_name, field_name_length, block->data.vorbis_comment.comments + i)) {
+                       ok &= FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned)i);
+                       if(ok)
+                               *needs_write = true;
+               }
+       }
+
+       return ok;
+}
+
+FLAC__bool remove_vc_firstfield(FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
+{
+       const unsigned field_name_length = strlen(field_name);
+       unsigned i;
+
+       FLAC__ASSERT(0 != block);
+       FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(0 != needs_write);
+
+       for(i = 0; i < block->data.vorbis_comment.num_comments; i++) {
+               if(field_name_matches_entry(field_name, field_name_length, block->data.vorbis_comment.comments + i)) {
+                       if(!FLAC__metadata_object_vorbiscomment_delete_comment(block, (unsigned)i))
+                               return false;
+                       else
+                               *needs_write = true;
+                       break;
+               }
+       }
+
+       return true;
+}
+
+FLAC__bool set_vc_field(FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write)
+{
+       FLAC__StreamMetadata_VorbisComment_Entry entry;
+       FLAC__ASSERT(0 != block);
+       FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
+       FLAC__ASSERT(0 != field);
+       FLAC__ASSERT(0 != needs_write);
+
+       entry.length = strlen(field->field);
+       entry.entry = field->field;
+
+       if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true)) {
+               return false;
+       }
+       else {
+               *needs_write = true;
+               return true;
+       }
+}
+
+FLAC__bool field_name_matches_entry(const char *field_name, unsigned field_name_length, const FLAC__StreamMetadata_VorbisComment_Entry *entry)
+{
+       return (0 != memchr(entry->entry, '=', entry->length) && 0 == strncmp(field_name, entry->entry, field_name_length));
+}
+
 void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent)
 {
        unsigned i, left = bytes;