add new options: --tag-from-file for flac and --set-tag-from-file for metaflac
authorJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 18 May 2006 07:57:16 +0000 (07:57 +0000)
committerJosh Coalson <jcoalson@users.sourceforce.net>
Thu, 18 May 2006 07:57:16 +0000 (07:57 +0000)
doc/html/changelog.html
doc/html/documentation.html
man/flac.sgml
man/metaflac.sgml
src/flac/main.c
src/flac/vorbiscomment.c
src/flac/vorbiscomment.h
src/metaflac/operations_shorthand_vorbiscomment.c
src/metaflac/options.c
src/metaflac/options.h
src/metaflac/usage.c

index 490956e..2aabf84 100644 (file)
@@ -83,7 +83,8 @@
                        <li>
                                flac:
                                <ul>
-                                       <li>Add support for encoding from non-compressed AIFF-C (<a href="https://sourceforge.net/tracker/?func=detail&amp;atid=113478&amp;aid=1090933&amp;group_id=13478">SF #1090933</a>).</li>
+                                       <li>Added a new option <a href="documentation.html#flac_options_tag_from_file"><span class="argument">--tag-from-file</span></a> for setting a tag from file (e.g. for importing a cuesheet as a tag).</li>
+                                       <li>Added support for encoding from non-compressed AIFF-C (<a href="https://sourceforge.net/tracker/?func=detail&amp;atid=113478&amp;aid=1090933&amp;group_id=13478">SF #1090933</a>).</li>
                                        <li>Importing of non-CDDA-compliant cuesheets now only issues a warning, not an error (see <a href="http://www.hydrogenaudio.org/forums/index.php?showtopic=31282">here</a>).</li>
                                        <li>Fixed a bug in cuesheet parsing where it would return an error if the last line of the cuesheet did not end with a newline.</li>
                                        <li>Fixed a bug that caused a crash when <span class="argument">-a</span> and <span class="argument">-t</span> were used together (<a href="https://sourceforge.net/tracker/index.php?func=detail&amp;aid=1229481&amp;group_id=13478&amp;atid=113478">SF #1229481</a>).</li>
@@ -95,6 +96,7 @@
                        <li>
                                metaflac:
                                <ul>
+                                       <li>Added a new option <a href="documentation.html#metaflac_shorthand_set_tag_from_file"><span class="argument">--set-tag-from-file</span></a> for setting a tag from file (e.g. for importing a cuesheet as a tag).</li>
                                        <li>Added shorthand operation <a href="documentation.html#metaflac_shorthand_remove_replay_gain"><span class="argument">--remove-replay-gain</span></a> for removing ReplayGain tags.</li>
                                        <li>Importing of non-CDDA-compliant cuesheets now issues a warning.</li>
                                </ul>
index b07004d..48e725b 100644 (file)
                        </tr>
                        <tr>
                                <td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
+                                       <a name="flac_options_tag_from_file" />
+                                       <span class="argument">--tag-from-file=FIELD=FILENAME</span>
+                               </td>
+                               <td>
+                                       Like <a href="#flac_options_tag"><span class="argument">--tag</span></a>, except FILENAME is a file whose contents will be read verbatim to set the tag value.  The contents will be converted to UTF-8 from the local charset.  This can be used to store a cuesheet in a tag (e.g. <span class="argument">--tag-from-file="CUESHEET=image.cue"</span>).  Do not try to store binary data in tag fields!  Use APPLICATION blocks for that.
+                               </td>
+                       </tr>
+                       <tr>
+                               <td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
                                        <a name="flac_options_blocksize" />
                                        <span class="argument">-b #</span>, <span class="argument">--blocksize=#</span>
                                </td>
                        </tr>
                        <tr>
                                <td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
+                                       <a name="metaflac_shorthand_set_tag_from_file" />
+                                       <span class="argument">--set-tag-from-file=FIELD</span>
+                               </td>
+                               <td>
+                                       Like <a href="#metaflac_shorthand_set_tag"><span class="argument">--set-tag</span></a>, except the VALUE is a filename whose contents will be read verbatim to set the tag value.  Unless <a href="#metaflac_options_no_utf8_convert"><span class="argument">--no-utf8-convert</span></a> is specified, the contents will be converted to UTF-8 from the local charset.  This can be used to store a cuesheet in a tag (e.g. <span class="argument">--set-tag-from-file="CUESHEET=image.cue"</span>).  Do not try to store binary data in tag fields!  Use APPLICATION blocks for that.
+                               </td>
+                       </tr>
+                       <tr>
+                               <td nowrap="nowrap" align="right" valign="top" bgcolor="#F4F4CC">
                                        <a name="metaflac_shorthand_import_tags_from" />
                                        <span class="argument">--import-tags-from=FILE</span>
                                </td>
                                <td>
-                                       Import tags from a file.  Use '-' for stdin.  Each line should be of the form <span class="argument">NAME=VALUE</span>.  Multi-line comments are currently not supported.  Specify <span class="argument">--remove-all-tags</span> and/or <span class="argument">--no-utf8-convert</span> before <span class="argument">--import-tags-from</span> if necessary.
+                                       Import tags from a file.  Use '-' for stdin.  Each line should be of the form <span class="argument">NAME=VALUE</span>.  Multi-line comments are currently not supported.  Specify <span class="argument">--remove-all-tags</span> and/or <a href="#metaflac_options_no_utf8_convert"><span class="argument">--no-utf8-convert</span></a> before <span class="argument">--import-tags-from</span> if necessary.
                                </td>
                        </tr>
                        <tr>
                                        <span class="argument">--export-tags-to=FILE</span>
                                </td>
                                <td>
-                                       Export tags to a file.  Use '-' for stdin.  Each line will be of the form <span class="argument">NAME=VALUE</span>.  Specify <span class="argument">--no-utf8-convert</span> if necessary.
+                                       Export tags to a file.  Use '-' for stdin.  Each line will be of the form <span class="argument">NAME=VALUE</span>.  Specify <a href="#metaflac_options_no_utf8_convert"><span class="argument">--no-utf8-convert</span></a> if necessary.
                                </td>
                        </tr>
                        <tr>
index e959e84..44c587e 100644 (file)
              </varlistentry>
 
              <varlistentry>
+               <term><option>--tag-from-file</option>=<replaceable>FIELD=FILENAME</replaceable></term>
+
+               <listitem>
+                 <para>Like --tag, except FILENAME is a file whose
+                   contents will be read verbatim to set the tag
+                   value.  The contents will be converted to UTF-8
+                   from the local charset.  This can be used to
+                   store a cuesheet in a tag (e.g.
+                   --tag-from-file="CUESHEET=image.cue").  Do not
+                   try to store binary data in tag fields!  Use
+                   APPLICATION blocks for that.</para>
+               </listitem>
+             </varlistentry>
+
+             <varlistentry>
                <term><option>-b</option> <replaceable>#</replaceable>, <option>--blocksize</option>=<replaceable>#</replaceable></term>
 
                <listitem>
index b2177e8..6a15b9a 100644 (file)
@@ -244,6 +244,21 @@ manpage.1: manpage.sgml
         </listitem>
       </varlistentry>
       <varlistentry>
+        <term><option>--set-tag-from-file=field</option></term>
+        <listitem>
+          <para>
+           Like --set-tag, except the VALUE is a filename whose
+           contents will be read verbatim to set the tag value.
+           Unless --no-utf8-convert is specified, the contents will be
+           converted to UTF-8 from the local charset.  This can be used
+           to store a cuesheet in a tag (e.g.
+           --set-tag-from-file="CUESHEET=image.cue").  Do not try to
+           store binary data in tag fields!  Use APPLICATION blocks for
+           that.
+         </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
        <term><option>--import-tags-from=file</option></term>
         <listitem>
          <para>
index 1e5d59e..7c94762 100644 (file)
@@ -119,6 +119,7 @@ static struct share__option long_options_[] = {
        { "cuesheet"                  , share__required_argument, 0, 0 },
        { "no-cued-seekpoints"        , share__no_argument, 0, 0 },
        { "tag"                       , share__required_argument, 0, 'T' },
+       { "tag-from-file"             , share__required_argument, 0, 0 },
        { "compression-level-0"       , share__no_argument, 0, '0' },
        { "compression-level-1"       , share__no_argument, 0, '1' },
        { "compression-level-2"       , share__no_argument, 0, '2' },
@@ -656,6 +657,7 @@ int parse_options(int argc, char *argv[])
 
 int parse_option(int short_option, const char *long_option, const char *option_argument)
 {
+       const char *violation;
        char *p;
 
        if(short_option == 0) {
@@ -715,6 +717,11 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                        FLAC__ASSERT(0 != option_argument);
                        option_values.cuesheet_filename = option_argument;
                }
+               else if(0 == strcmp(long_option, "tag-from-file")) {
+                       FLAC__ASSERT(0 != option_argument);
+                       if(!flac__vorbiscomment_add(option_values.vorbis_comment, option_argument, /*value_from_file=*/true, &violation))
+                               return usage_error("ERROR: (--tag-from-file) %s\n", violation);
+               }
                else if(0 == strcmp(long_option, "no-cued-seekpoints")) {
                        option_values.cued_seekpoints = false;
                }
@@ -858,7 +865,6 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                }
        }
        else {
-               const char *violation;
                switch(short_option) {
                        case 'h':
                                option_values.show_help = true;
@@ -898,7 +904,7 @@ int parse_option(int short_option, const char *long_option, const char *option_a
                                break;
                        case 'T':
                                FLAC__ASSERT(0 != option_argument);
-                               if(!flac__vorbiscomment_add(option_values.vorbis_comment, option_argument, &violation))
+                               if(!flac__vorbiscomment_add(option_values.vorbis_comment, option_argument, /*value_from_file=*/false, &violation))
                                        return usage_error("ERROR: (-T/--tag) %s\n", violation);
                                break;
                        case '0':
@@ -1208,6 +1214,7 @@ void show_help()
        printf("      --replay-gain            Calculate ReplayGain & store in FLAC tags\n");
        printf("      --cuesheet=FILENAME      Import cuesheet and store in CUESHEET block\n");
        printf("  -T, --tag=FIELD=VALUE        Add a FLAC tag; may appear multiple times\n");
+       printf("      --tag-from-file=FIELD=FILENAME   Like --tag but gets value from file\n");
        printf("  -S, --seekpoint={#|X|#x|#s}  Add seek point(s)\n");
        printf("  -P, --padding=#              Write a PADDING block of length #\n");
        printf("  -0, --compression-level-0, --fast  Synonymous with -l 0 -b 1152 -r 2,2\n");
@@ -1392,6 +1399,14 @@ void show_explain()
        printf("                               comment if necessary.  This option may appear\n");
        printf("                               more than once to add several comments.  NOTE:\n");
        printf("                               all tags will be added to all encoded files.\n");
+       printf("      --tag-from-file=FIELD=FILENAME   Like --tag, except FILENAME is a file\n");
+       printf("                               whose contents will be read verbatim to set the\n");
+       printf("                               tag value.  The contents will be converted to\n");
+       printf("                               UTF-8 from the local charset.  This can be used\n");
+       printf("                               to store a cuesheet in a tag (e.g.\n");
+       printf("                               --tag-from-file=\"CUESHEET=image.cue\").  Do not\n");
+       printf("                               try to store binary data in tag fields!  Use\n");
+       printf("                               APPLICATION blocks for that.\n");
        printf("  -S, --seekpoint={#|X|#x|#s}  Include a point or points in a SEEKTABLE\n");
        printf("       #  : a specific sample number for a seek point\n");
        printf("       X  : a placeholder point (always goes at the end of the SEEKTABLE)\n");
index 597aae9..30df758 100644 (file)
@@ -23,6 +23,7 @@
 #include "vorbiscomment.h"
 #include "FLAC/assert.h"
 #include "FLAC/metadata.h"
+#include "share/grabbag.h" /* for grabbag__file_get_filesize() */
 #include "share/utf8.h"
 #include <ctype.h>
 #include <stdio.h>
@@ -32,7 +33,7 @@
 
 /*
  * This struct and the following 4 static functions are copied from
- * ../metaflac/main.c.  Maybe someday there will be a convenience
+ * ../metaflac/.  Maybe someday there will be a convenience
  * library for Vorbis comment parsing.
  */
 typedef struct {
@@ -41,6 +42,7 @@ typedef struct {
        /* according to the vorbis spec, field values can contain \0 so simple C strings are not enough here */
        unsigned field_value_length;
        char *field_value;
+       FLAC__bool field_value_from_file; /* true if field_value holds a filename for the value, false for plain value */
 } Argument_VcField;
 
 static void die(const char *message)
@@ -101,34 +103,102 @@ static FLAC__bool set_vc_field(FLAC__StreamMetadata *block, const Argument_VcFie
 {
        FLAC__StreamMetadata_VorbisComment_Entry entry;
        char *converted;
-       FLAC__bool needs_free = false;
 
        FLAC__ASSERT(0 != block);
        FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
        FLAC__ASSERT(0 != field);
        FLAC__ASSERT(0 != needs_write);
 
-       if(raw) {
-               entry.entry = (FLAC__byte *)field->field;
-       }
-       else if(utf8_encode(field->field, &converted) >= 0) {
-               entry.entry = (FLAC__byte *)converted;
-               needs_free = true;
-       }
-       else {
-               *violation = "couldn't convert comment to UTF-8";
-               return false;
-       }
+       if(field->field_value_from_file) {
+               /* read the file into 'data' */
+               FILE *f = 0;
+               char *data = 0;
+               const off_t size = grabbag__file_get_filesize(field->field_value);
+               if(size < 0) {
+                       *violation = "can't open file for tag value";
+                       return false;
+               }
+               if(size >= 0x100000) { /* magic arbitrary limit, actual format limit is near 16MB */
+                       *violation = "file for tag value is too large";
+                       return false;
+               }
+               if(0 == (data = malloc(size+1)))
+                       die("out of memory allocating tag value");
+               data[size] = '\0';
+               if(0 == (f = fopen(field->field_value, "rb")) || fread(data, 1, size, f) != (size_t)size) {
+                       free(data);
+                       if(f)
+                               fclose(f);
+                       *violation = "error while reading file for tag value";
+                       return false;
+               }
+               fclose(f);
+               if(strlen(data) != (size_t)size) {
+                       free(data);
+                       *violation = "file for tag value has embedded NULs";
+                       return false;
+               }
 
-       entry.length = strlen((const char *)entry.entry);
+               /* move 'data' into 'converted', converting to UTF-8 if necessary */
+               if(raw) {
+                       converted = data;
+               }
+               else if(utf8_encode(data, &converted) >= 0) {
+                       free(data);
+               }
+               else {
+                       free(data);
+                       *violation = "error converting file contents to UTF-8 for tag value";
+                       return false;
+               }
 
-       if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) {
-               if(needs_free)
+               /* create and entry and append it */
+               if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, field->field_name, converted)) {
                        free(converted);
-               *violation = "memory allocation failure";
-               return false;
+                       *violation = "file for tag value is not valid UTF-8";
+                       return false;
+               }
+               free(converted);
+               if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) {
+                       *violation = "memory allocation failure";
+                       return false;
+               }
+
+               *needs_write = true;
+               return true;
        }
        else {
+               FLAC__bool needs_free = false;
+               if(raw) {
+                       entry.entry = (FLAC__byte *)field->field;
+               }
+               else if(utf8_encode(field->field, &converted) >= 0) {
+                       entry.entry = (FLAC__byte *)converted;
+                       needs_free = true;
+               }
+               else {
+                       *violation = "error converting comment to UTF-8";
+                       return false;
+               }
+               entry.length = strlen((const char *)entry.entry);
+               if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) {
+                       if(needs_free)
+                               free(converted);
+                       /*
+                        * our previous parsing has already established that the field
+                        * name is OK, so it must be the field value
+                        */
+                       *violation = "tag value for is not valid UTF-8";
+                       return false;
+               }
+
+               if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) {
+                       if(needs_free)
+                               free(converted);
+                       *violation = "memory allocation failure";
+                       return false;
+               }
+
                *needs_write = true;
                if(needs_free)
                        free(converted);
@@ -150,7 +220,7 @@ static void free_field(Argument_VcField *obj)
                free(obj->field_value);
 }
 
-FLAC__bool flac__vorbiscomment_add(FLAC__StreamMetadata *block, const char *comment, const char **violation)
+FLAC__bool flac__vorbiscomment_add(FLAC__StreamMetadata *block, const char *comment, FLAC__bool value_from_file, const char **violation)
 {
        Argument_VcField parsed;
        FLAC__bool dummy;
@@ -161,6 +231,7 @@ FLAC__bool flac__vorbiscomment_add(FLAC__StreamMetadata *block, const char *comm
 
        memset(&parsed, 0, sizeof(parsed));
 
+       parsed.field_value_from_file = value_from_file;
        if(!parse_vorbis_comment_field(comment, &(parsed.field), &(parsed.field_name), &(parsed.field_value), &(parsed.field_value_length), violation)) {
                free_field(&parsed);
                return false;
index 802558b..2f796b5 100644 (file)
@@ -21,6 +21,6 @@
 
 #include "FLAC/metadata.h"
 
-FLAC__bool flac__vorbiscomment_add(FLAC__StreamMetadata *block, const char *comment, const char **violation);
+FLAC__bool flac__vorbiscomment_add(FLAC__StreamMetadata *block, const char *comment, FLAC__bool value_from_file, const char **violation);
 
 #endif
index 843fa29..e28cbb5 100644 (file)
@@ -19,6 +19,7 @@
 #include "options.h"
 #include "utils.h"
 #include "FLAC/assert.h"
+#include "share/grabbag.h" /* for grabbag__file_get_filesize() */
 #include "share/utf8.h"
 #include <stdlib.h>
 #include <string.h>
@@ -171,34 +172,102 @@ FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const
 {
        FLAC__StreamMetadata_VorbisComment_Entry entry;
        char *converted;
-       FLAC__bool needs_free = false;
 
        FLAC__ASSERT(0 != block);
        FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
        FLAC__ASSERT(0 != field);
        FLAC__ASSERT(0 != needs_write);
 
-       if(raw) {
-               entry.entry = (FLAC__byte *)field->field;
-       }
-       else if(utf8_encode(field->field, &converted) >= 0) {
-               entry.entry = (FLAC__byte *)converted;
-               needs_free = true;
-       }
-       else {
-               fprintf(stderr, "%s: ERROR: couldn't convert comment to UTF-8\n", filename);
-               return false;
-       }
+       if(field->field_value_from_file) {
+               /* read the file into 'data' */
+               FILE *f = 0;
+               char *data = 0;
+               const off_t size = grabbag__file_get_filesize(field->field_value);
+               if(size < 0) {
+                       fprintf(stderr, "%s: ERROR: can't open file '%s' for '%s' tag value\n", filename, field->field_value, field->field_name);
+                       return false;
+               }
+               if(size >= 0x100000) { /* magic arbitrary limit, actual format limit is near 16MB */
+                       fprintf(stderr, "%s: ERROR: file '%s' for '%s' tag value is too large\n", filename, field->field_value, field->field_name);
+                       return false;
+               }
+               if(0 == (data = malloc(size+1)))
+                       die("out of memory allocating tag value");
+               data[size] = '\0';
+               if(0 == (f = fopen(field->field_value, "rb")) || fread(data, 1, size, f) != (size_t)size) {
+                       free(data);
+                       if(f)
+                               fclose(f);
+                       fprintf(stderr, "%s: ERROR: while reading file '%s' for '%s' tag value\n", filename, field->field_value, field->field_name);
+                       return false;
+               }
+               fclose(f);
+               if(strlen(data) != (size_t)size) {
+                       free(data);
+                       fprintf(stderr, "%s: ERROR: file '%s' for '%s' tag value has embedded NULs\n", filename, field->field_value, field->field_name);
+                       return false;
+               }
 
-       entry.length = strlen((const char *)entry.entry);
+               /* move 'data' into 'converted', converting to UTF-8 if necessary */
+               if(raw) {
+                       converted = data;
+               }
+               else if(utf8_encode(data, &converted) >= 0) {
+                       free(data);
+               }
+               else {
+                       free(data);
+                       fprintf(stderr, "%s: ERROR: converting file '%s' contents to UTF-8 for tag value\n", filename, field->field_value);
+                       return false;
+               }
 
-       if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) {
-               if(needs_free)
+               /* create and entry and append it */
+               if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&entry, field->field_name, converted)) {
                        free(converted);
-               fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
-               return false;
+                       fprintf(stderr, "%s: ERROR: file '%s' for '%s' tag value is not valid UTF-8\n", filename, field->field_value, field->field_name);
+                       return false;
+               }
+               free(converted);
+               if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/false)) {
+                       fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
+                       return false;
+               }
+
+               *needs_write = true;
+               return true;
        }
        else {
+               FLAC__bool needs_free = false;
+               if(raw) {
+                       entry.entry = (FLAC__byte *)field->field;
+               }
+               else if(utf8_encode(field->field, &converted) >= 0) {
+                       entry.entry = (FLAC__byte *)converted;
+                       needs_free = true;
+               }
+               else {
+                       fprintf(stderr, "%s: ERROR: converting comment '%s' to UTF-8\n", filename, field->field);
+                       return false;
+               }
+               entry.length = strlen((const char *)entry.entry);
+               if(!FLAC__format_vorbiscomment_entry_is_legal(entry.entry, entry.length)) {
+                       if(needs_free)
+                               free(converted);
+                       /*
+                        * our previous parsing has already established that the field
+                        * name is OK, so it must be the field value
+                        */
+                       fprintf(stderr, "%s: ERROR: tag value for '%s' is not valid UTF-8\n", filename, field->field_name);
+                       return false;
+               }
+
+               if(!FLAC__metadata_object_vorbiscomment_append_comment(block, entry, /*copy=*/true)) {
+                       if(needs_free)
+                               free(converted);
+                       fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
+                       return false;
+               }
+
                *needs_write = true;
                if(needs_free)
                        free(converted);
@@ -240,6 +309,7 @@ FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, con
                                Argument_VcField field;
                                *p = '\0';
                                memset(&field, 0, sizeof(Argument_VcField));
+                               field.field_value_from_file = false;
                                if(!parse_vorbis_comment_field(line, &field.field, &field.field_name, &field.field_value, &field.field_value_length, &violation)) {
                                        FLAC__ASSERT(0 != violation);
                                        fprintf(stderr, "%s: ERROR: malformed vorbis comment field \"%s\",\n       %s\n", vc_filename->value, line, violation);
index ac6eb33..7e94742 100644 (file)
@@ -63,6 +63,7 @@ struct share__option long_options_[] = {
        { "remove-tag", 1, 0, 0 }, 
        { "remove-first-tag", 1, 0, 0 }, 
        { "set-tag", 1, 0, 0 }, 
+       { "set-tag-from-file", 1, 0, 0 }, 
        { "import-tags-from", 1, 0, 0 }, 
        { "export-tags-to", 1, 0, 0 }, 
        { "show-vc-vendor", 0, 0, 0 }, /* deprecated */
@@ -503,6 +504,18 @@ FLAC__bool parse_option(int option_index, const char *option_argument, CommandLi
                        fprintf(stderr, "WARNING: --%s is deprecated, the new name is --set-tag\n", opt);
                op = append_shorthand_operation(options, OP__SET_VC_FIELD);
                FLAC__ASSERT(0 != option_argument);
+               op->argument.vc_field.field_value_from_file = false;
+               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)) {
+                       FLAC__ASSERT(0 != violation);
+                       fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n       %s\n", opt, option_argument, violation);
+                       ok = false;
+               }
+       }
+       else if(0 == strcmp(opt, "set-tag-from-file")) {
+               const char *violation;
+               op = append_shorthand_operation(options, OP__SET_VC_FIELD);
+               FLAC__ASSERT(0 != option_argument);
+               op->argument.vc_field.field_value_from_file = true;
                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)) {
                        FLAC__ASSERT(0 != violation);
                        fprintf(stderr, "ERROR (--%s): malformed vorbis comment field \"%s\",\n       %s\n", opt, option_argument, violation);
index b1535d4..4fc2b8c 100644 (file)
@@ -101,6 +101,7 @@ typedef struct {
        /* according to the vorbis spec, field values can contain \0 so simple C strings are not enough here */
        unsigned field_value_length;
        char *field_value;
+       FLAC__bool field_value_from_file; /* true if field_value holds a filename for the value, false for plain value */
 } Argument_VcField;
 
 typedef struct {
index 4f715ea..4ba9f58 100644 (file)
@@ -122,6 +122,14 @@ int long_usage(const char *message, ...)
        fprintf(out, "--set-tag=FIELD       Add a tag.  The FIELD must comply with the Vorbis comment\n");
        fprintf(out, "                      spec, of the form \"NAME=VALUE\".  If there is currently\n");
        fprintf(out, "                      no tag block, one will be created.\n");
+       fprintf(out, "--set-tag-from-file=FIELD   Like --set-tag, except the VALUE is a filename\n");
+       fprintf(out, "                      whose contents will be read verbatim to set the tag value.\n");
+       fprintf(out, "                      Unless --no-utf8-convert is specified, the contents will\n");
+       fprintf(out, "                      be converted to UTF-8 from the local charset.  This can\n");
+       fprintf(out, "                      be used to store a cuesheet in a tag (e.g.\n");
+       fprintf(out, "                      --set-tag-from-file=\"CUESHEET=image.cue\").  Do not try to\n");
+       fprintf(out, "                      store binary data in tag fields!  Use APPLICATION blocks\n");
+       fprintf(out, "                      for that.\n");
        fprintf(out, "--import-tags-from=FILE Import tags from a file.  Use '-' for stdin.  Each line\n");
        fprintf(out, "                      should be of the form NAME=VALUE.  Multi-line comments\n");
        fprintf(out, "                      are currently not supported.  Specify --remove-all-tags\n");