Decode the UCS4 to the current locale instead of latin1.
Kurt Roeckx <[email protected]> Sat, 17 Jun 2006 19:55:42 +0200
| Newsgroups | gmane.comp.audio.mad.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi, madplay currently always converts the id3 tag information to latin1. The attached patch converts it to the currently codeset based on nl_langinfo(CODESET). It seems the configure script already checks for nl_langinfo and iconv with the automake macro's, so I didn't have to patch configure for this. Maybe those function belong in libid3tag instead, it would be more logical. It would also not require us to use non-exported functions. The reason I just put it in madplay is that this only require me to change one source package. There is one problem with this patch that I know of, and that is that it doesn't properly deal with (argualby broken) iconv() implementations saying the input sequence is invalid when it can't map a valid input character to the output encoding, and I'm not sure how to deal with it, see the comment. Kurt
madplay-0.15.2b+codeset.diff
(text/plain, 3.7 KB)
--- 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 <sys/mman.h>
# endif
+# if defined(HAVE_ICONV)
+# include <iconv.h>
+# endif
+
+# if defined(HAVE_LANGINFO_CODESET)
+# include <langinfo.h>
+# 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;