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