diff --git a/src/lib/lightmediascanner_charset_conv.c b/src/lib/lightmediascanner_charset_conv.c index 77b82aa..cfe3148 100644 --- a/src/lib/lightmediascanner_charset_conv.c +++ b/src/lib/lightmediascanner_charset_conv.c @@ -26,7 +26,6 @@ #include struct lms_charset_conv { - iconv_t check; iconv_t fallback; unsigned int size; iconv_t *convs; @@ -53,16 +52,10 @@ lms_charset_conv_new(void) return NULL; } - lcc->check = iconv_open("UTF-8", "UTF-8"); - if (lcc->check == (iconv_t)-1) { - perror("ERROR: could not create conversion checker"); - goto error_check; - } - lcc->fallback = iconv_open("UTF-8//IGNORE", "UTF-8"); if (lcc->fallback == (iconv_t)-1) { perror("ERROR: could not create conversion fallback"); - goto error_fallback; + goto error; } lcc->size = 0; @@ -70,9 +63,7 @@ lms_charset_conv_new(void) lcc->names = NULL; return lcc; - error_fallback: - iconv_close(lcc->check); - error_check: + error: free(lcc); return NULL; @@ -91,7 +82,6 @@ lms_charset_conv_free(lms_charset_conv_t *lcc) if (!lcc) return; - iconv_close(lcc->check); iconv_close(lcc->fallback); for (i = 0; i < lcc->size; i++) { @@ -224,51 +214,58 @@ lms_charset_conv_del(lms_charset_conv_t *lcc, const char *charset) } static int -_check(lms_charset_conv_t *lcc, const char *istr, unsigned int ilen, char *ostr, unsigned int olen) -{ - char *inbuf, *outbuf; - size_t r, inlen, outlen; - - inbuf = (char *)istr; - inlen = ilen; - outbuf = ostr; - outlen = olen; - - iconv(lcc->check, NULL, NULL, NULL, NULL); - r = iconv(lcc->check, &inbuf, &inlen, &outbuf, &outlen); - if (r == (size_t)-1) - return -1; - else - return 0; -} - -static int -_conv(iconv_t cd, char **p_str, unsigned int *p_len, char *ostr, unsigned int olen) +_conv(iconv_t cd, char **in, unsigned int *inlen, char **out, unsigned int *outlen) { - char *inbuf, *outbuf; - size_t r, inlen, outlen; - - inbuf = *p_str; - inlen = *p_len; - outbuf = ostr; - outlen = olen; + char *inp, *outp; + size_t inremain, outremain, outsize, r, used; + int done = 0, have_error = 0; + + inp = *in; + inremain = *inlen; + outp = *out; + outremain = *outlen; + outsize = *outlen; + + while (!done && !have_error) { + r = iconv(cd, &inp, &inremain, &outp, &outremain); + if (r == (size_t)-1) { + switch (errno) { + case EINVAL: + done = 1; + break; + case E2BIG: + used = outp - *out; + outsize *= 2; + out = realloc(*out, outsize); + if (!out) { + perror("realloc"); + return -1; + } + outp = *out + used; + outremain = outsize - used - 1; + break; + case EILSEQ: + default: + have_error = 1; + break; + } + return -1; + } + else { + break; + } + } - iconv(cd, NULL, NULL, NULL, NULL); - r = iconv(cd, &inbuf, &inlen, &outbuf, &outlen); - if (r == (size_t)-1) + if (have_error) return -1; - *p_len = olen - outlen; - free(*p_str); - *p_str = ostr; + *outp = '\0'; + *outlen = outp - *out; - outbuf = realloc(*p_str, *p_len + 1); - if (!outbuf) - perror("realloc"); - else - *p_str = outbuf; + free(*in); - (*p_str)[*p_len] = '\0'; + *inlen = inp - *in; + *in = *out; return 0; } @@ -277,81 +274,44 @@ _conv(iconv_t cd, char **p_str, unsigned int *p_len, char *ostr, unsigned int ol * If required, do charset conversion to UTF-8. * * @param lcc existing Light Media Scanner charset conversion. - * @param p_str string to be converted. - * @param p_len string size. + * @param in string to be converted. + * @param inlen string size. * * @return On success 0 is returned. */ int -lms_charset_conv(lms_charset_conv_t *lcc, char **p_str, unsigned int *p_len) +lms_charset_conv(lms_charset_conv_t *lcc, char **in, unsigned int *inlen) { - char *outstr; + char *out; int i, outlen; if (!lcc) return -1; - if (!p_str) + if (!in) return -2; - if (!p_len) + if (!inlen) return -3; - if (!*p_str || !*p_len) + if (!*in || !*inlen) return 0; - outlen = 2 * *p_len; - outstr = malloc(outlen + 1); - if (!outstr) { + outlen = 2 * *inlen; + out = malloc(outlen + 1); + if (!out) { perror("malloc"); return -4; } - if (_check(lcc, *p_str, *p_len, outstr, outlen) == 0) { - free(outstr); - return 0; - } - for (i = 0; i < lcc->size; i++) - if (_conv(lcc->convs[i], p_str, p_len, outstr, outlen) == 0) + if (_conv(lcc->convs[i], in, inlen, &out, &outlen) == 0) return 0; fprintf(stderr, "WARNING: could not convert '%*s' to any charset, use fallback\n", - *p_len, *p_str); - i = _conv(lcc->fallback, p_str, p_len, outstr, outlen); + *inlen, *in); + i = _conv(lcc->fallback, in, inlen, &out, &outlen); if (i < 0) { - memset(*p_str, '?', *p_len); - free(outstr); + memset(*in, '?', *inlen); + free(out); } return i; } - -/** - * Check if strings is not UTF-8 and conversion is required. - * - * @param lcc existing Light Media Scanner charset conversion. - * @param str string to be analysed. - * @param len string size. - * - * @return 0 if string is already UTF-8. - */ -int -lms_charset_conv_check(lms_charset_conv_t *lcc, const char *str, unsigned int len) -{ - char *outstr; - int r, outlen; - - if (!lcc) - return -1; - if (!str || !len) - return 0; - - outlen = 2 * len; - outstr = malloc(outlen); - if (!outstr) { - perror("malloc"); - return -2; - } - - r = _check(lcc, str, len, outstr, outlen); - free(outstr); - return r; -}