add new options: --tag-from-file for flac and --set-tag-from-file for metaflac
diff --git a/doc/html/changelog.html b/doc/html/changelog.html
index 490956e..2aabf84 100644
--- a/doc/html/changelog.html
+++ b/doc/html/changelog.html
@@ -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&atid=113478&aid=1090933&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&atid=113478&aid=1090933&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&aid=1229481&group_id=13478&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>
diff --git a/doc/html/documentation.html b/doc/html/documentation.html
index b07004d..48e725b 100644
--- a/doc/html/documentation.html
+++ b/doc/html/documentation.html
@@ -585,6 +585,15 @@
</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>
@@ -1135,11 +1144,20 @@
</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>
@@ -1148,7 +1166,7 @@
<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>
diff --git a/man/flac.sgml b/man/flac.sgml
index e959e84..44c587e 100644
--- a/man/flac.sgml
+++ b/man/flac.sgml
@@ -466,6 +466,21 @@
</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>
diff --git a/man/metaflac.sgml b/man/metaflac.sgml
index b2177e8..6a15b9a 100644
--- a/man/metaflac.sgml
+++ b/man/metaflac.sgml
@@ -244,6 +244,21 @@
</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>
diff --git a/src/flac/main.c b/src/flac/main.c
index 1e5d59e..7c94762 100644
--- a/src/flac/main.c
+++ b/src/flac/main.c
@@ -119,6 +119,7 @@
{ "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_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 @@
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 @@
}
}
else {
- const char *violation;
switch(short_option) {
case 'h':
option_values.show_help = true;
@@ -898,7 +904,7 @@
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 @@
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 @@
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");
diff --git a/src/flac/vorbiscomment.c b/src/flac/vorbiscomment.c
index 597aae9..30df758 100644
--- a/src/flac/vorbiscomment.c
+++ b/src/flac/vorbiscomment.c
@@ -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 @@
/* 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 @@
{
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 @@
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 @@
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;
diff --git a/src/flac/vorbiscomment.h b/src/flac/vorbiscomment.h
index 802558b..2f796b5 100644
--- a/src/flac/vorbiscomment.h
+++ b/src/flac/vorbiscomment.h
@@ -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
diff --git a/src/metaflac/operations_shorthand_vorbiscomment.c b/src/metaflac/operations_shorthand_vorbiscomment.c
index 843fa29..e28cbb5 100644
--- a/src/metaflac/operations_shorthand_vorbiscomment.c
+++ b/src/metaflac/operations_shorthand_vorbiscomment.c
@@ -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__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 @@
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);
diff --git a/src/metaflac/options.c b/src/metaflac/options.c
index ac6eb33..7e94742 100644
--- a/src/metaflac/options.c
+++ b/src/metaflac/options.c
@@ -63,6 +63,7 @@
{ "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 @@
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);
diff --git a/src/metaflac/options.h b/src/metaflac/options.h
index b1535d4..4fc2b8c 100644
--- a/src/metaflac/options.h
+++ b/src/metaflac/options.h
@@ -101,6 +101,7 @@
/* 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 {
diff --git a/src/metaflac/usage.c b/src/metaflac/usage.c
index 4f715ea..4ba9f58 100644
--- a/src/metaflac/usage.c
+++ b/src/metaflac/usage.c
@@ -122,6 +122,14 @@
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");