cannam@86
|
1 /* metaflac - Command-line FLAC metadata editor
|
cannam@86
|
2 * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson
|
cannam@86
|
3 *
|
cannam@86
|
4 * This program is free software; you can redistribute it and/or
|
cannam@86
|
5 * modify it under the terms of the GNU General Public License
|
cannam@86
|
6 * as published by the Free Software Foundation; either version 2
|
cannam@86
|
7 * of the License, or (at your option) any later version.
|
cannam@86
|
8 *
|
cannam@86
|
9 * This program is distributed in the hope that it will be useful,
|
cannam@86
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
cannam@86
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
cannam@86
|
12 * GNU General Public License for more details.
|
cannam@86
|
13 *
|
cannam@86
|
14 * You should have received a copy of the GNU General Public License
|
cannam@86
|
15 * along with this program; if not, write to the Free Software
|
cannam@86
|
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
cannam@86
|
17 */
|
cannam@86
|
18
|
cannam@86
|
19 #if HAVE_CONFIG_H
|
cannam@86
|
20 # include <config.h>
|
cannam@86
|
21 #endif
|
cannam@86
|
22
|
cannam@86
|
23 #include "utils.h"
|
cannam@86
|
24 #include "FLAC/assert.h"
|
cannam@86
|
25 #include "share/alloc.h"
|
cannam@86
|
26 #include "share/utf8.h"
|
cannam@86
|
27 #include <ctype.h>
|
cannam@86
|
28 #include <stdarg.h>
|
cannam@86
|
29 #include <stdio.h>
|
cannam@86
|
30 #include <stdlib.h>
|
cannam@86
|
31 #include <string.h>
|
cannam@86
|
32
|
cannam@86
|
33 void die(const char *message)
|
cannam@86
|
34 {
|
cannam@86
|
35 FLAC__ASSERT(0 != message);
|
cannam@86
|
36 fprintf(stderr, "ERROR: %s\n", message);
|
cannam@86
|
37 exit(1);
|
cannam@86
|
38 }
|
cannam@86
|
39
|
cannam@86
|
40 #ifdef FLAC__VALGRIND_TESTING
|
cannam@86
|
41 size_t local_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
|
cannam@86
|
42 {
|
cannam@86
|
43 size_t ret = fwrite(ptr, size, nmemb, stream);
|
cannam@86
|
44 if(!ferror(stream))
|
cannam@86
|
45 fflush(stream);
|
cannam@86
|
46 return ret;
|
cannam@86
|
47 }
|
cannam@86
|
48 #endif
|
cannam@86
|
49
|
cannam@86
|
50 char *local_strdup(const char *source)
|
cannam@86
|
51 {
|
cannam@86
|
52 char *ret;
|
cannam@86
|
53 FLAC__ASSERT(0 != source);
|
cannam@86
|
54 if(0 == (ret = strdup(source)))
|
cannam@86
|
55 die("out of memory during strdup()");
|
cannam@86
|
56 return ret;
|
cannam@86
|
57 }
|
cannam@86
|
58
|
cannam@86
|
59 void local_strcat(char **dest, const char *source)
|
cannam@86
|
60 {
|
cannam@86
|
61 size_t ndest, nsource;
|
cannam@86
|
62
|
cannam@86
|
63 FLAC__ASSERT(0 != dest);
|
cannam@86
|
64 FLAC__ASSERT(0 != source);
|
cannam@86
|
65
|
cannam@86
|
66 ndest = *dest? strlen(*dest) : 0;
|
cannam@86
|
67 nsource = strlen(source);
|
cannam@86
|
68
|
cannam@86
|
69 if(nsource == 0)
|
cannam@86
|
70 return;
|
cannam@86
|
71
|
cannam@86
|
72 *dest = (char*)safe_realloc_add_3op_(*dest, ndest, /*+*/nsource, /*+*/1);
|
cannam@86
|
73 if(0 == *dest)
|
cannam@86
|
74 die("out of memory growing string");
|
cannam@86
|
75 strcpy((*dest)+ndest, source);
|
cannam@86
|
76 }
|
cannam@86
|
77
|
cannam@86
|
78 void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent)
|
cannam@86
|
79 {
|
cannam@86
|
80 unsigned i, left = bytes;
|
cannam@86
|
81 const FLAC__byte *b = buf;
|
cannam@86
|
82
|
cannam@86
|
83 for(i = 0; i < bytes; i += 16) {
|
cannam@86
|
84 printf("%s%s%s%08X: "
|
cannam@86
|
85 "%02X %02X %02X %02X %02X %02X %02X %02X "
|
cannam@86
|
86 "%02X %02X %02X %02X %02X %02X %02X %02X "
|
cannam@86
|
87 "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
|
cannam@86
|
88 filename? filename:"", filename? ":":"",
|
cannam@86
|
89 indent, i,
|
cannam@86
|
90 left > 0? (unsigned char)b[ 0] : 0,
|
cannam@86
|
91 left > 1? (unsigned char)b[ 1] : 0,
|
cannam@86
|
92 left > 2? (unsigned char)b[ 2] : 0,
|
cannam@86
|
93 left > 3? (unsigned char)b[ 3] : 0,
|
cannam@86
|
94 left > 4? (unsigned char)b[ 4] : 0,
|
cannam@86
|
95 left > 5? (unsigned char)b[ 5] : 0,
|
cannam@86
|
96 left > 6? (unsigned char)b[ 6] : 0,
|
cannam@86
|
97 left > 7? (unsigned char)b[ 7] : 0,
|
cannam@86
|
98 left > 8? (unsigned char)b[ 8] : 0,
|
cannam@86
|
99 left > 9? (unsigned char)b[ 9] : 0,
|
cannam@86
|
100 left > 10? (unsigned char)b[10] : 0,
|
cannam@86
|
101 left > 11? (unsigned char)b[11] : 0,
|
cannam@86
|
102 left > 12? (unsigned char)b[12] : 0,
|
cannam@86
|
103 left > 13? (unsigned char)b[13] : 0,
|
cannam@86
|
104 left > 14? (unsigned char)b[14] : 0,
|
cannam@86
|
105 left > 15? (unsigned char)b[15] : 0,
|
cannam@86
|
106 (left > 0) ? (isprint(b[ 0]) ? b[ 0] : '.') : ' ',
|
cannam@86
|
107 (left > 1) ? (isprint(b[ 1]) ? b[ 1] : '.') : ' ',
|
cannam@86
|
108 (left > 2) ? (isprint(b[ 2]) ? b[ 2] : '.') : ' ',
|
cannam@86
|
109 (left > 3) ? (isprint(b[ 3]) ? b[ 3] : '.') : ' ',
|
cannam@86
|
110 (left > 4) ? (isprint(b[ 4]) ? b[ 4] : '.') : ' ',
|
cannam@86
|
111 (left > 5) ? (isprint(b[ 5]) ? b[ 5] : '.') : ' ',
|
cannam@86
|
112 (left > 6) ? (isprint(b[ 6]) ? b[ 6] : '.') : ' ',
|
cannam@86
|
113 (left > 7) ? (isprint(b[ 7]) ? b[ 7] : '.') : ' ',
|
cannam@86
|
114 (left > 8) ? (isprint(b[ 8]) ? b[ 8] : '.') : ' ',
|
cannam@86
|
115 (left > 9) ? (isprint(b[ 9]) ? b[ 9] : '.') : ' ',
|
cannam@86
|
116 (left > 10) ? (isprint(b[10]) ? b[10] : '.') : ' ',
|
cannam@86
|
117 (left > 11) ? (isprint(b[11]) ? b[11] : '.') : ' ',
|
cannam@86
|
118 (left > 12) ? (isprint(b[12]) ? b[12] : '.') : ' ',
|
cannam@86
|
119 (left > 13) ? (isprint(b[13]) ? b[13] : '.') : ' ',
|
cannam@86
|
120 (left > 14) ? (isprint(b[14]) ? b[14] : '.') : ' ',
|
cannam@86
|
121 (left > 15) ? (isprint(b[15]) ? b[15] : '.') : ' '
|
cannam@86
|
122 );
|
cannam@86
|
123 left -= 16;
|
cannam@86
|
124 b += 16;
|
cannam@86
|
125 }
|
cannam@86
|
126 }
|
cannam@86
|
127
|
cannam@86
|
128 void print_error_with_chain_status(FLAC__Metadata_Chain *chain, const char *format, ...)
|
cannam@86
|
129 {
|
cannam@86
|
130 const FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status(chain);
|
cannam@86
|
131 va_list args;
|
cannam@86
|
132
|
cannam@86
|
133 FLAC__ASSERT(0 != format);
|
cannam@86
|
134
|
cannam@86
|
135 va_start(args, format);
|
cannam@86
|
136
|
cannam@86
|
137 (void) vfprintf(stderr, format, args);
|
cannam@86
|
138
|
cannam@86
|
139 va_end(args);
|
cannam@86
|
140
|
cannam@86
|
141 fprintf(stderr, ", status = \"%s\"\n", FLAC__Metadata_ChainStatusString[status]);
|
cannam@86
|
142
|
cannam@86
|
143 if(status == FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE) {
|
cannam@86
|
144 fprintf(stderr, "\n"
|
cannam@86
|
145 "The FLAC file could not be opened. Most likely the file does not exist\n"
|
cannam@86
|
146 "or is not readable.\n"
|
cannam@86
|
147 );
|
cannam@86
|
148 }
|
cannam@86
|
149 else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE) {
|
cannam@86
|
150 fprintf(stderr, "\n"
|
cannam@86
|
151 "The file does not appear to be a FLAC file.\n"
|
cannam@86
|
152 );
|
cannam@86
|
153 }
|
cannam@86
|
154 else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE) {
|
cannam@86
|
155 fprintf(stderr, "\n"
|
cannam@86
|
156 "The FLAC file does not have write permissions.\n"
|
cannam@86
|
157 );
|
cannam@86
|
158 }
|
cannam@86
|
159 else if(status == FLAC__METADATA_CHAIN_STATUS_BAD_METADATA) {
|
cannam@86
|
160 fprintf(stderr, "\n"
|
cannam@86
|
161 "The metadata to be writted does not conform to the FLAC metadata\n"
|
cannam@86
|
162 "specifications.\n"
|
cannam@86
|
163 );
|
cannam@86
|
164 }
|
cannam@86
|
165 else if(status == FLAC__METADATA_CHAIN_STATUS_READ_ERROR) {
|
cannam@86
|
166 fprintf(stderr, "\n"
|
cannam@86
|
167 "There was an error while reading the FLAC file.\n"
|
cannam@86
|
168 );
|
cannam@86
|
169 }
|
cannam@86
|
170 else if(status == FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR) {
|
cannam@86
|
171 fprintf(stderr, "\n"
|
cannam@86
|
172 "There was an error while writing FLAC file; most probably the disk is\n"
|
cannam@86
|
173 "full.\n"
|
cannam@86
|
174 );
|
cannam@86
|
175 }
|
cannam@86
|
176 else if(status == FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR) {
|
cannam@86
|
177 fprintf(stderr, "\n"
|
cannam@86
|
178 "There was an error removing the temporary FLAC file.\n"
|
cannam@86
|
179 );
|
cannam@86
|
180 }
|
cannam@86
|
181 }
|
cannam@86
|
182
|
cannam@86
|
183 FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation)
|
cannam@86
|
184 {
|
cannam@86
|
185 static const char * const violations[] = {
|
cannam@86
|
186 "field name contains invalid character",
|
cannam@86
|
187 "field contains no '=' character"
|
cannam@86
|
188 };
|
cannam@86
|
189
|
cannam@86
|
190 char *p, *q, *s;
|
cannam@86
|
191
|
cannam@86
|
192 if(0 != field)
|
cannam@86
|
193 *field = local_strdup(field_ref);
|
cannam@86
|
194
|
cannam@86
|
195 s = local_strdup(field_ref);
|
cannam@86
|
196
|
cannam@86
|
197 if(0 == (p = strchr(s, '='))) {
|
cannam@86
|
198 free(s);
|
cannam@86
|
199 *violation = violations[1];
|
cannam@86
|
200 return false;
|
cannam@86
|
201 }
|
cannam@86
|
202 *p++ = '\0';
|
cannam@86
|
203
|
cannam@86
|
204 for(q = s; *q; q++) {
|
cannam@86
|
205 if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
|
cannam@86
|
206 free(s);
|
cannam@86
|
207 *violation = violations[0];
|
cannam@86
|
208 return false;
|
cannam@86
|
209 }
|
cannam@86
|
210 }
|
cannam@86
|
211
|
cannam@86
|
212 *name = local_strdup(s);
|
cannam@86
|
213 *value = local_strdup(p);
|
cannam@86
|
214 *length = strlen(p);
|
cannam@86
|
215
|
cannam@86
|
216 free(s);
|
cannam@86
|
217 return true;
|
cannam@86
|
218 }
|
cannam@86
|
219
|
cannam@86
|
220 void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f)
|
cannam@86
|
221 {
|
cannam@86
|
222 if(0 != entry->entry) {
|
cannam@86
|
223 if(filename)
|
cannam@86
|
224 fprintf(f, "%s:", filename);
|
cannam@86
|
225
|
cannam@86
|
226 if(!raw) {
|
cannam@86
|
227 /*
|
cannam@86
|
228 * WATCHOUT: comments that contain an embedded null will
|
cannam@86
|
229 * be truncated by utf_decode().
|
cannam@86
|
230 */
|
cannam@86
|
231 char *converted;
|
cannam@86
|
232
|
cannam@86
|
233 if(utf8_decode((const char *)entry->entry, &converted) >= 0) {
|
cannam@86
|
234 (void) local_fwrite(converted, 1, strlen(converted), f);
|
cannam@86
|
235 free(converted);
|
cannam@86
|
236 }
|
cannam@86
|
237 else {
|
cannam@86
|
238 (void) local_fwrite(entry->entry, 1, entry->length, f);
|
cannam@86
|
239 }
|
cannam@86
|
240 }
|
cannam@86
|
241 else {
|
cannam@86
|
242 (void) local_fwrite(entry->entry, 1, entry->length, f);
|
cannam@86
|
243 }
|
cannam@86
|
244 }
|
cannam@86
|
245
|
cannam@86
|
246 putc('\n', f);
|
cannam@86
|
247 }
|
cannam@86
|
248
|
cannam@86
|
249 void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f)
|
cannam@86
|
250 {
|
cannam@86
|
251 unsigned i;
|
cannam@86
|
252 const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0;
|
cannam@86
|
253
|
cannam@86
|
254 for(i = 0; i < num_entries; i++) {
|
cannam@86
|
255 if(0 == field_name || FLAC__metadata_object_vorbiscomment_entry_matches(entry[i], field_name, field_name_length))
|
cannam@86
|
256 write_vc_field(filename, entry + i, raw, f);
|
cannam@86
|
257 }
|
cannam@86
|
258 }
|