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