6f59b70f10dc38d3147eec2e5b9594a226aa4229
[platform/upstream/flac.git] / src / metaflac / utils.c
1 /* metaflac - Command-line FLAC metadata editor
2  * Copyright (C) 2001,2002  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 "utils.h"
20 #include "FLAC/assert.h"
21 #include "share/utf8.h"
22 #include <ctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 void die(const char *message)
28 {
29         FLAC__ASSERT(0 != message);
30         fprintf(stderr, "ERROR: %s\n", message);
31         exit(1);
32 }
33
34 #ifdef FLAC__VALGRIND_TESTING
35 size_t local_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
36 {
37         size_t ret = fwrite(ptr, size, nmemb, stream);
38         if(!ferror(stream))
39                 fflush(stream);
40         return ret;
41 }
42 #endif
43
44 char *local_strdup(const char *source)
45 {
46         char *ret;
47         FLAC__ASSERT(0 != source);
48         if(0 == (ret = strdup(source)))
49                 die("out of memory during strdup()");
50         return ret;
51 }
52
53 void local_strcat(char **dest, const char *source)
54 {
55         unsigned ndest, nsource;
56
57         FLAC__ASSERT(0 != dest);
58         FLAC__ASSERT(0 != source);
59
60         ndest = *dest? strlen(*dest) : 0;
61         nsource = strlen(source);
62
63         if(nsource == 0)
64                 return;
65
66         *dest = realloc(*dest, ndest + nsource + 1);
67         if(0 == *dest)
68                 die("out of memory growing string");
69         strcpy((*dest)+ndest, source);
70 }
71
72 void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent)
73 {
74         unsigned i, left = bytes;
75         const FLAC__byte *b = buf;
76
77         for(i = 0; i < bytes; i += 16) {
78                 printf("%s%s%s%08X: "
79                         "%02X %02X %02X %02X %02X %02X %02X %02X "
80                         "%02X %02X %02X %02X %02X %02X %02X %02X "
81                         "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
82                         filename? filename:"", filename? ":":"",
83                         indent, i,
84                         left >  0? (unsigned char)b[ 0] : 0,
85                         left >  1? (unsigned char)b[ 1] : 0,
86                         left >  2? (unsigned char)b[ 2] : 0,
87                         left >  3? (unsigned char)b[ 3] : 0,
88                         left >  4? (unsigned char)b[ 4] : 0,
89                         left >  5? (unsigned char)b[ 5] : 0,
90                         left >  6? (unsigned char)b[ 6] : 0,
91                         left >  7? (unsigned char)b[ 7] : 0,
92                         left >  8? (unsigned char)b[ 8] : 0,
93                         left >  9? (unsigned char)b[ 9] : 0,
94                         left > 10? (unsigned char)b[10] : 0,
95                         left > 11? (unsigned char)b[11] : 0,
96                         left > 12? (unsigned char)b[12] : 0,
97                         left > 13? (unsigned char)b[13] : 0,
98                         left > 14? (unsigned char)b[14] : 0,
99                         left > 15? (unsigned char)b[15] : 0,
100                         (left >  0) ? (isprint(b[ 0]) ? b[ 0] : '.') : ' ',
101                         (left >  1) ? (isprint(b[ 1]) ? b[ 1] : '.') : ' ',
102                         (left >  2) ? (isprint(b[ 2]) ? b[ 2] : '.') : ' ',
103                         (left >  3) ? (isprint(b[ 3]) ? b[ 3] : '.') : ' ',
104                         (left >  4) ? (isprint(b[ 4]) ? b[ 4] : '.') : ' ',
105                         (left >  5) ? (isprint(b[ 5]) ? b[ 5] : '.') : ' ',
106                         (left >  6) ? (isprint(b[ 6]) ? b[ 6] : '.') : ' ',
107                         (left >  7) ? (isprint(b[ 7]) ? b[ 7] : '.') : ' ',
108                         (left >  8) ? (isprint(b[ 8]) ? b[ 8] : '.') : ' ',
109                         (left >  9) ? (isprint(b[ 9]) ? b[ 9] : '.') : ' ',
110                         (left > 10) ? (isprint(b[10]) ? b[10] : '.') : ' ',
111                         (left > 11) ? (isprint(b[11]) ? b[11] : '.') : ' ',
112                         (left > 12) ? (isprint(b[12]) ? b[12] : '.') : ' ',
113                         (left > 13) ? (isprint(b[13]) ? b[13] : '.') : ' ',
114                         (left > 14) ? (isprint(b[14]) ? b[14] : '.') : ' ',
115                         (left > 15) ? (isprint(b[15]) ? b[15] : '.') : ' '
116                 );
117                 left -= 16;
118                 b += 16;
119    }
120 }
121
122 FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation)
123 {
124         static const char * const violations[] = {
125                 "field name contains invalid character",
126                 "field contains no '=' character"
127         };
128
129         char *p, *q, *s;
130
131         if(0 != field)
132                 *field = local_strdup(field_ref);
133
134         s = local_strdup(field_ref);
135
136         if(0 == (p = strchr(s, '='))) {
137                 free(s);
138                 *violation = violations[1];
139                 return false;
140         }
141         *p++ = '\0';
142
143         for(q = s; *q; q++) {
144                 if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
145                         free(s);
146                         *violation = violations[0];
147                         return false;
148                 }
149         }
150
151         *name = local_strdup(s);
152         *value = local_strdup(p);
153         *length = strlen(p);
154
155         free(s);
156         return true;
157 }
158
159 void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f)
160 {
161         if(0 != entry->entry) {
162                 if(filename)
163                         fprintf(f, "%s:", filename);
164
165                 if(!raw) {
166                         /*
167                          * utf8_decode() works on NULL-terminated strings, so
168                          * we append a null to the entry.  @@@ Note, this means
169                          * that comments that contain an embedded null will be
170                          * truncated by utf_decode().
171                          */
172                         char *terminated, *converted;
173
174                         if(0 == (terminated = malloc(entry->length + 1)))
175                                 die("out of memory allocating space for vorbis comment");
176                         memcpy(terminated, entry->entry, entry->length);
177                         terminated[entry->length] = '\0';
178                         if(utf8_decode(terminated, &converted) >= 0) {
179                                 (void) local_fwrite(converted, 1, strlen(converted), f);
180                                 free(terminated);
181                                 free(converted);
182                         }
183                         else {
184                                 free(terminated);
185                                 (void) local_fwrite(entry->entry, 1, entry->length, f);
186                         }
187                 }
188                 else {
189                         (void) local_fwrite(entry->entry, 1, entry->length, f);
190                 }
191         }
192
193         fprintf(f, "\n");
194 }
195
196 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)
197 {
198         unsigned i;
199         const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0;
200
201         for(i = 0; i < num_entries; i++) {
202                 if(0 == field_name || FLAC__metadata_object_vorbiscomment_entry_matches(entry + i, field_name, field_name_length))
203                         write_vc_field(filename, entry + i, raw, f);
204         }
205 }