--- madplay-0.15.2b/player.c 2004-02-23 21:34:53.000000000 +0000 +++ madplay-0.15.2b+codeset/player.c 2006-06-17 17:38:33.000000000 +0000 @@ -71,6 +71,14 @@ # include # endif +# if defined(HAVE_ICONV) +# include +# endif + +# if defined(HAVE_LANGINFO_CODESET) +# include +# endif + # if !defined(O_BINARY) # define O_BINARY 0 # endif @@ -598,6 +606,103 @@ return 0; } +/* Note: id3_ucs4_length and id3_utf8_size are part of libid3tag, + * but they're currently not exported it in a public header. + */ +id3_length_t id3_ucs4_length(id3_ucs4_t const *ucs4); +id3_length_t id3_utf8_length(id3_utf8_t const *); + +/* + * NAME: ucs4_to_locale_strdup() + * DESCRIPTION: convert ucs4 string to locale encoding + */ +static +char *ucs4_to_locale_strdup(const id3_ucs4_t *ucs4) +#if defined(HAVE_ICONV) && defined(HAVE_LANGINFO_CODESET) +{ + size_t len; + id3_utf8_t *utf8 = NULL; + char *outbuf = NULL; + size_t inlen, outlen, retlen; + char *in_p, *out_p; + iconv_t cd; + + /* + * First convert from ucs4 to utf8, so we have a charset + * that isn't endian dependant, and then can + * covert it using iconv() to the current codeset. + */ + len = id3_ucs4_length(ucs4) + 1; + utf8 = id3_ucs4_utf8duplicate(ucs4); + if (!utf8) + { + error("ucs4_to_locale_strdup", + _("id3_ucs4_utf8duplicate() failed.")); + goto fail; + } + + cd = iconv_open(nl_langinfo(CODESET), "UTF-8"); + if (cd == (iconv_t)(-1)) + { + error("ucs4_to_locale_strdup", + _("Can't convert characters to current codeset")); + free(utf8); + goto fail; + } + + /* + * UTF8 is at most 5 bytes per character and should be the worst + * case encoding, so we use this here. + */ + outbuf = malloc(len*5); + if (!outbuf) + { + error("ucs4_to_locale_strdup", + _("not enough memory to allocate buffer")); + free(utf8); + goto fail; + } + memset(outbuf, 0, len*5); + + in_p = (char *)utf8; + out_p = outbuf; + inlen = id3_utf8_length(utf8); + outlen = len*5; + + /* Some implementations of iconv() return -1 and + * set errno to EILSEQ on valid input for which + * the output encoding doesn't have a correspondending + * character. The standard says they should do an + * implementation defined conversion in this case. + * + * Currently we print an error and just return the first + * few chars it could convert, if any. To be the same + * as other code, it should probably skip the input + * character and set the output character to + * ID3_UCS4_REPLACEMENTCHAR. It's just a little hard + * to do so in case you don't know the output encoding. + * + * Is there a better solution to this? + */ + retlen = iconv(cd, &in_p, &inlen, &out_p, &outlen); + if (retlen == (size_t)(-1) || inlen != 0) + { + error("ucs4_to_locale_strdup", + _("Conversion failed")); + } + free(utf8); + iconv_close(cd); + return outbuf; +fail: + /* This is probably going to break something if this ever happens. */ + return NULL; +} +#else +{ + return id3_ucs4_latin1duplicate(ucs4); +} +#endif + /* * NAME: show_id3() * DESCRIPTION: display ID3 tag information @@ -652,7 +757,7 @@ if (strcmp(info[i].id, ID3_FRAME_GENRE) == 0) ucs4 = id3_genre_name(ucs4); - latin1 = id3_ucs4_latin1duplicate(ucs4); + latin1 = ucs4_to_locale_strdup(ucs4); if (latin1 == 0) goto fail; @@ -685,7 +790,7 @@ ucs4 = id3_field_getfullstring(id3_frame_field(frame, 3)); assert(ucs4); - latin1 = id3_ucs4_latin1duplicate(ucs4); + latin1 = ucs4_to_locale_strdup(ucs4); if (latin1 == 0) goto fail; @@ -1211,7 +1316,7 @@ if (nstrings > 0) { id3_latin1_t *latin1; - latin1 = id3_ucs4_latin1duplicate(id3_field_getstrings(field, 0)); + latin1 = ucs4_to_locale_strdup(id3_field_getstrings(field, 0)); if (latin1) { signed long ms;