1//========================================================================= 2// FILENAME : tagutils-misc.c 3// DESCRIPTION : Misc routines for supporting tagutils 4//========================================================================= 5// Copyright (c) 2008- NETGEAR, Inc. All Rights Reserved. 6//========================================================================= 7 8/* This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 */ 22 23/************************************************************************** 24* Language 25**************************************************************************/ 26 27#define MAX_ICONV_BUF 1024 28 29typedef enum { 30 ICONV_OK, 31 ICONV_TRYNEXT, 32 ICONV_FATAL 33} iconv_result; 34 35static iconv_result 36do_iconv(const char* to_ces, const char* from_ces, 37 char *inbuf, size_t inbytesleft, 38 char *outbuf_orig, size_t outbytesleft_orig) 39{ 40#ifdef HAVE_ICONV_H 41 size_t rc; 42 iconv_result ret = ICONV_OK; 43 44 size_t outbytesleft = outbytesleft_orig - 1; 45 char* outbuf = outbuf_orig; 46 47 iconv_t cd = iconv_open(to_ces, from_ces); 48 49 if(cd == (iconv_t)-1) 50 { 51 return ICONV_FATAL; 52 } 53 rc = iconv(cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft); 54 if(rc == (size_t)-1) 55 { 56 if(errno == E2BIG) 57 { 58 ret = ICONV_FATAL; 59 } 60 else 61 { 62 ret = ICONV_TRYNEXT; 63 memset(outbuf_orig, '\0', outbytesleft_orig); 64 } 65 } 66 iconv_close(cd); 67 68 return ret; 69#else // HAVE_ICONV_H 70 return ICONV_FATAL; 71#endif // HAVE_ICONV_H 72} 73 74#define N_LANG_ALT 8 75struct { 76 char *lang; 77 char *cpnames[N_LANG_ALT]; 78} iconv_map[] = { 79 { "ja_JP", { "CP932", "CP950", "CP936", "ISO-8859-1", 0 } }, 80 { "zh_CN", { "CP936", "CP950", "CP932", "ISO-8859-1", 0 } }, 81 { "zh_TW", { "CP950", "CP936", "CP932", "ISO-8859-1", 0 } }, 82 { "ko_KR", { "CP949", "ISO-8859-1", 0 } }, 83 { 0, { 0 } } 84}; 85static int lang_index = -1; 86 87static int 88_lang2cp(char *lang) 89{ 90 int cp; 91 92 if(!lang || lang[0] == '\0') 93 return -1; 94 for(cp = 0; iconv_map[cp].lang; cp++) 95 { 96 if(!strcasecmp(iconv_map[cp].lang, lang)) 97 return cp; 98 } 99 return -2; 100} 101 102static unsigned char* 103_get_utf8_text(const id3_ucs4_t* native_text) 104{ 105 unsigned char *utf8_text = NULL; 106 char *in, *in8, *iconv_buf; 107 iconv_result rc; 108 int i, n; 109 110 in = (char*)id3_ucs4_latin1duplicate(native_text); 111 if(!in) 112 { 113 goto out; 114 } 115 116 in8 = (char*)id3_ucs4_utf8duplicate(native_text); 117 if(!in8) 118 { 119 free(in); 120 goto out; 121 } 122 123 iconv_buf = (char*)calloc(MAX_ICONV_BUF, sizeof(char)); 124 if(!iconv_buf) 125 { 126 free(in); free(in8); 127 goto out; 128 } 129 130 i = lang_index; 131 // (1) try utf8 -> default 132 rc = do_iconv(iconv_map[i].cpnames[0], "UTF-8", in8, strlen(in8), iconv_buf, MAX_ICONV_BUF); 133 if(rc == ICONV_OK) 134 { 135 utf8_text = (unsigned char*)in8; 136 free(iconv_buf); 137 } 138 else if(rc == ICONV_TRYNEXT) 139 { 140 // (2) try default -> utf8 141 rc = do_iconv("UTF-8", iconv_map[i].cpnames[0], in, strlen(in), iconv_buf, MAX_ICONV_BUF); 142 if(rc == ICONV_OK) 143 { 144 utf8_text = (unsigned char*)iconv_buf; 145 } 146 else if(rc == ICONV_TRYNEXT) 147 { 148 // (3) try other encodes 149 for(n = 1; n < N_LANG_ALT && iconv_map[i].cpnames[n]; n++) 150 { 151 rc = do_iconv("UTF-8", iconv_map[i].cpnames[n], in, strlen(in), iconv_buf, MAX_ICONV_BUF); 152 if(rc == ICONV_OK) 153 { 154 utf8_text = (unsigned char*)iconv_buf; 155 break; 156 } 157 } 158 if(!utf8_text) 159 { 160 // cannot iconv 161 utf8_text = (unsigned char*)id3_ucs4_utf8duplicate(native_text); 162 free(iconv_buf); 163 } 164 } 165 free(in8); 166 } 167 free(in); 168 169 out: 170 if(!utf8_text) 171 { 172 utf8_text = (unsigned char*)strdup("UNKNOWN"); 173 } 174 175 return utf8_text; 176} 177 178 179static void 180vc_scan(struct song_metadata *psong, const char *comment, const size_t length) 181{ 182 char strbuf[1024]; 183 184 if(length > (sizeof(strbuf) - 1)) 185 { 186 if( strncasecmp(comment, "LYRICS=", 7) != 0 ) 187 { 188 DPRINTF(E_WARN, L_SCANNER, "Vorbis %.*s too long [%s]\n", (index(comment, '=')-comment), comment, psong->path); 189 } 190 return; 191 } 192 strncpy(strbuf, comment, length); 193 strbuf[length] = '\0'; 194 195 // ALBUM, ARTIST, PUBLISHER, COPYRIGHT, DISCNUMBER, ISRC, EAN/UPN, LABEL, LABELNO, 196 // LICENSE, OPUS, SOURCEMEDIA, TITLE, TRACKNUMBER, VERSION, ENCODED-BY, ENCODING, 197 // -- foollowing tags are muliples 198 // COMPOSER, ARRANGER, LYRICIST, AUTHOR, CONDUCTOR, PERFORMER, ENSEMBLE, PART 199 // PARTNUMBER, GENRE, DATE, LOCATION, COMMENT 200 if(!strncasecmp(strbuf, "ALBUM=", 6)) 201 { 202 if( *(strbuf+6) ) 203 psong->album = strdup(strbuf + 6); 204 } 205 else if(!strncasecmp(strbuf, "ARTIST=", 7)) 206 { 207 if( *(strbuf+7) ) 208 psong->contributor[ROLE_ARTIST] = strdup(strbuf + 7); 209 } 210 else if(!strncasecmp(strbuf, "ARTISTSORT=", 11)) 211 { 212 psong->contributor_sort[ROLE_ARTIST] = strdup(strbuf + 11); 213 } 214 else if(!strncasecmp(strbuf, "TITLE=", 6)) 215 { 216 if( *(strbuf+6) ) 217 psong->title = strdup(strbuf + 6); 218 } 219 else if(!strncasecmp(strbuf, "TRACKNUMBER=", 12)) 220 { 221 psong->track = atoi(strbuf + 12); 222 } 223 else if(!strncasecmp(strbuf, "DISCNUMBER=", 11)) 224 { 225 psong->disc = atoi(strbuf + 11); 226 } 227 else if(!strncasecmp(strbuf, "GENRE=", 6)) 228 { 229 if( *(strbuf+6) ) 230 psong->genre = strdup(strbuf + 6); 231 } 232 else if(!strncasecmp(strbuf, "DATE=", 5)) 233 { 234 if(length >= (5 + 10) && 235 isdigit(strbuf[5 + 0]) && isdigit(strbuf[5 + 1]) && ispunct(strbuf[5 + 2]) && 236 isdigit(strbuf[5 + 3]) && isdigit(strbuf[5 + 4]) && ispunct(strbuf[5 + 5]) && 237 isdigit(strbuf[5 + 6]) && isdigit(strbuf[5 + 7]) && isdigit(strbuf[5 + 8]) && isdigit(strbuf[5 + 9])) 238 { 239 // nn-nn-yyyy 240 strbuf[5 + 10] = '\0'; 241 psong->year = atoi(strbuf + 5 + 6); 242 } 243 else 244 { 245 // year first. year is at most 4 digit. 246 strbuf[5 + 4] = '\0'; 247 psong->year = atoi(strbuf + 5); 248 } 249 } 250 else if(!strncasecmp(strbuf, "COMMENT=", 8)) 251 { 252 if( *(strbuf+8) ) 253 psong->comment = strdup(strbuf + 8); 254 } 255 else if(!strncasecmp(strbuf, "MUSICBRAINZ_ALBUMID=", 20)) 256 { 257 psong->musicbrainz_albumid = strdup(strbuf + 20); 258 } 259 else if(!strncasecmp(strbuf, "MUSICBRAINZ_TRACKID=", 20)) 260 { 261 psong->musicbrainz_trackid = strdup(strbuf + 20); 262 } 263 else if(!strncasecmp(strbuf, "MUSICBRAINZ_TRACKID=", 20)) 264 { 265 psong->musicbrainz_trackid = strdup(strbuf + 20); 266 } 267 else if(!strncasecmp(strbuf, "MUSICBRAINZ_ARTISTID=", 21)) 268 { 269 psong->musicbrainz_artistid = strdup(strbuf + 21); 270 } 271 else if(!strncasecmp(strbuf, "MUSICBRAINZ_ALBUMARTISTID=", 26)) 272 { 273 psong->musicbrainz_albumartistid = strdup(strbuf + 26); 274 } 275} 276