Add 2003 to copyright notice
[flac.git] / src / metaflac / operations_shorthand_vorbiscomment.c
1 /* metaflac - Command-line FLAC metadata editor
2  * Copyright (C) 2001,2002,2003  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 #include "options.h"
20 #include "utils.h"
21 #include "FLAC/assert.h"
22 #include "share/utf8.h"
23 #include <stdlib.h>
24 #include <string.h>
25
26 static FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write);
27 static FLAC__bool remove_vc_field(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
28 static FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write);
29 static FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw);
30 static FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_Filename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw);
31 static FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_Filename *vc_filename, FLAC__bool raw);
32
33 FLAC__bool do_shorthand_operation__vorbis_comment(const char *filename, FLAC__bool prefix_with_filename, FLAC__Metadata_Chain *chain, const Operation *operation, FLAC__bool *needs_write, FLAC__bool raw)
34 {
35         FLAC__bool ok = true, found_vc_block = false;
36         FLAC__StreamMetadata *block = 0;
37         FLAC__Metadata_Iterator *iterator = FLAC__metadata_iterator_new();
38
39         if(0 == iterator)
40                 die("out of memory allocating iterator");
41
42         FLAC__metadata_iterator_init(iterator, chain);
43
44         do {
45                 block = FLAC__metadata_iterator_get_block(iterator);
46                 if(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
47                         found_vc_block = true;
48         } while(!found_vc_block && FLAC__metadata_iterator_next(iterator));
49
50         if(!found_vc_block) {
51                 /* create a new block if necessary */
52                 if(operation->type == OP__SET_VC_FIELD || operation->type == OP__IMPORT_VC_FROM) {
53                         block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
54                         if(0 == block)
55                                 die("out of memory allocating VORBIS_COMMENT block");
56                         while(FLAC__metadata_iterator_next(iterator))
57                                 ;
58                         if(!FLAC__metadata_iterator_insert_block_after(iterator, block)) {
59                                 fprintf(stderr, "%s: ERROR: adding new VORBIS_COMMENT block to metadata, status =\"%s\"\n", filename, FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(chain)]);
60                                 return false;
61                         }
62                         /* iterator is left pointing to new block */
63                         FLAC__ASSERT(FLAC__metadata_iterator_get_block(iterator) == block);
64                 }
65                 else {
66                         FLAC__metadata_iterator_delete(iterator);
67                         return ok;
68                 }
69         }
70
71         FLAC__ASSERT(0 != block);
72         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
73
74         switch(operation->type) {
75                 case OP__SHOW_VC_VENDOR:
76                         write_vc_field(prefix_with_filename? filename : 0, &block->data.vorbis_comment.vendor_string, raw, stdout);
77                         break;
78                 case OP__SHOW_VC_FIELD:
79                         write_vc_fields(prefix_with_filename? filename : 0, operation->argument.vc_field_name.value, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, stdout);
80                         break;
81                 case OP__REMOVE_VC_ALL:
82                         ok = remove_vc_all(filename, block, needs_write);
83                         break;
84                 case OP__REMOVE_VC_FIELD:
85                         ok = remove_vc_field(filename, block, operation->argument.vc_field_name.value, needs_write);
86                         break;
87                 case OP__REMOVE_VC_FIRSTFIELD:
88                         ok = remove_vc_firstfield(filename, block, operation->argument.vc_field_name.value, needs_write);
89                         break;
90                 case OP__SET_VC_FIELD:
91                         ok = set_vc_field(filename, block, &operation->argument.vc_field, needs_write, raw);
92                         break;
93                 case OP__IMPORT_VC_FROM:
94                         ok = import_vc_from(filename, block, &operation->argument.filename, needs_write, raw);
95                         break;
96                 case OP__EXPORT_VC_TO:
97                         ok = export_vc_to(filename, block, &operation->argument.filename, raw);
98                         break;
99                 default:
100                         ok = false;
101                         FLAC__ASSERT(0);
102                         break;
103         };
104
105         FLAC__metadata_iterator_delete(iterator);
106         return ok;
107 }
108
109 /*
110  * local routines
111  */
112
113 FLAC__bool remove_vc_all(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write)
114 {
115         FLAC__ASSERT(0 != block);
116         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
117         FLAC__ASSERT(0 != needs_write);
118
119         if(0 != block->data.vorbis_comment.comments) {
120                 FLAC__ASSERT(block->data.vorbis_comment.num_comments > 0);
121                 if(!FLAC__metadata_object_vorbiscomment_resize_comments(block, 0)) {
122                         fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
123                         return false;
124                 }
125                 *needs_write = true;
126         }
127         else {
128                 FLAC__ASSERT(block->data.vorbis_comment.num_comments == 0);
129         }
130
131         return true;
132 }
133
134 FLAC__bool remove_vc_field(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
135 {
136         int n;
137
138         FLAC__ASSERT(0 != needs_write);
139
140         n = FLAC__metadata_object_vorbiscomment_remove_entries_matching(block, field_name);
141
142         if(n < 0) {
143                 fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
144                 return false;
145         }
146         else if(n > 0)
147                 *needs_write = true;
148
149         return true;
150 }
151
152 FLAC__bool remove_vc_firstfield(const char *filename, FLAC__StreamMetadata *block, const char *field_name, FLAC__bool *needs_write)
153 {
154         int n;
155
156         FLAC__ASSERT(0 != needs_write);
157
158         n = FLAC__metadata_object_vorbiscomment_remove_entry_matching(block, field_name);
159
160         if(n < 0) {
161                 fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
162                 return false;
163         }
164         else if(n > 0)
165                 *needs_write = true;
166
167         return true;
168 }
169
170 FLAC__bool set_vc_field(const char *filename, FLAC__StreamMetadata *block, const Argument_VcField *field, FLAC__bool *needs_write, FLAC__bool raw)
171 {
172         FLAC__StreamMetadata_VorbisComment_Entry entry;
173         char *converted;
174         FLAC__bool needs_free = false;
175
176         FLAC__ASSERT(0 != block);
177         FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
178         FLAC__ASSERT(0 != field);
179         FLAC__ASSERT(0 != needs_write);
180
181         if(raw) {
182                 entry.entry = field->field;
183         }
184         else if(utf8_encode(field->field, &converted) >= 0) {
185                 entry.entry = converted;
186                 needs_free = true;
187         }
188         else {
189                 fprintf(stderr, "%s: ERROR: couldn't convert comment to UTF-8\n", filename);
190                 return false;
191         }
192
193         entry.length = strlen(entry.entry);
194
195         if(!FLAC__metadata_object_vorbiscomment_insert_comment(block, block->data.vorbis_comment.num_comments, entry, /*copy=*/true)) {
196                 if(needs_free)
197                         free(converted);
198                 fprintf(stderr, "%s: ERROR: memory allocation failure\n", filename);
199                 return false;
200         }
201         else {
202                 *needs_write = true;
203                 if(needs_free)
204                         free(converted);
205                 return true;
206         }
207 }
208
209 FLAC__bool import_vc_from(const char *filename, FLAC__StreamMetadata *block, const Argument_Filename *vc_filename, FLAC__bool *needs_write, FLAC__bool raw)
210 {
211         FILE *f;
212         char line[65536];
213         FLAC__bool ret;
214
215         if(0 == vc_filename->value || strlen(vc_filename->value) == 0) {
216                 fprintf(stderr, "%s: ERROR: empty import file name\n", filename);
217                 return false;
218         }
219         if(0 == strcmp(vc_filename->value, "-"))
220                 f = stdin;
221         else
222                 f = fopen(vc_filename->value, "r");
223
224         if(0 == f) {
225                 fprintf(stderr, "%s: ERROR: can't open import file %s\n", filename, vc_filename->value);
226                 return false;
227         }
228
229         ret = true;
230         while(ret && !feof(f)) {
231                 fgets(line, sizeof(line), f);
232                 if(!feof(f)) {
233                         char *p = strchr(line, '\n');
234                         if(0 == p) {
235                                 fprintf(stderr, "%s: ERROR: line too long, aborting\n", vc_filename->value);
236                                 ret = false;
237                         }
238                         else {
239                                 const char *violation;
240                                 Argument_VcField field;
241                                 *p = '\0';
242                                 memset(&field, 0, sizeof(Argument_VcField));
243                                 if(!parse_vorbis_comment_field(line, &field.field, &field.field_name, &field.field_value, &field.field_value_length, &violation)) {
244                                         FLAC__ASSERT(0 != violation);
245                                         fprintf(stderr, "%s: ERROR: malformed vorbis comment field \"%s\",\n       %s\n", vc_filename->value, line, violation);
246                                         ret = false;
247                                 }
248                                 else {
249                                         ret = set_vc_field(filename, block, &field, needs_write, raw);
250                                 }
251                                 if(0 != field.field)
252                                         free(field.field);
253                                 if(0 != field.field_name)
254                                         free(field.field_name);
255                                 if(0 != field.field_value)
256                                         free(field.field_value);
257                         }
258                 }
259         };
260
261         if(f != stdin)
262                 fclose(f);
263         return ret;
264 }
265
266 FLAC__bool export_vc_to(const char *filename, FLAC__StreamMetadata *block, const Argument_Filename *vc_filename, FLAC__bool raw)
267 {
268         FILE *f;
269         FLAC__bool ret;
270
271         if(0 == vc_filename->value || strlen(vc_filename->value) == 0) {
272                 fprintf(stderr, "%s: ERROR: empty export file name\n", filename);
273                 return false;
274         }
275         if(0 == strcmp(vc_filename->value, "-"))
276                 f = stdout;
277         else
278                 f = fopen(vc_filename->value, "w");
279
280         if(0 == f) {
281                 fprintf(stderr, "%s: ERROR: can't open export file %s\n", filename, vc_filename->value);
282                 return false;
283         }
284
285         ret = true;
286
287         write_vc_fields(0, 0, block->data.vorbis_comment.comments, block->data.vorbis_comment.num_comments, raw, f);
288
289         if(f != stdout)
290                 fclose(f);
291         return ret;
292 }