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