diff --git a/src/input/mpegts/dvb_support.c b/src/input/mpegts/dvb_support.c index c8c6bcff..36dce677 100644 --- a/src/input/mpegts/dvb_support.c +++ b/src/input/mpegts/dvb_support.c @@ -25,12 +25,12 @@ #include #include #include -#include #include "tvheadend.h" #include "dvb.h" #include "dvb_charset_tables.h" #include "input.h" +#include "intlconv.h" static int convert_iso_8859[16] = { -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 11, 12, 13 @@ -40,32 +40,14 @@ static int convert_iso_8859[16] = { #define convert_ucs2 16 #define convert_gb 17 -static inline int code_convert(const char *from_charset,const char *to_charset,char *inbuf,size_t inlen,char *outbuf,size_t *outlen) -{ - iconv_t cd; - - char **pin = &inbuf; - char **pout = &outbuf; - - cd = iconv_open(to_charset,from_charset); - if (cd==0) return -1; - memset(outbuf,0,*outlen); - if (iconv(cd,pin,&inlen,pout,outlen)==-1) return -1; - iconv_close(cd); - return 0; -} - -static inline int gb2u(char *inbuf,int inlen,char *outbuf,size_t *outlen) -{ - return code_convert("gb2312","utf-8",inbuf,inlen,outbuf,outlen); -} - static inline size_t conv_gb(const uint8_t *src, size_t srclen, char *dst, size_t *dstlen) { - size_t len=*dstlen; - gb2u((char *)src,srclen,dst,dstlen); - dst+=len-*dstlen; + ssize_t len; + len = intlconv_to_utf8(dst, *dstlen, "gb2312", (char *)src, srclen); + if (len < 0 || len > *dstlen) + return -1; + *dstlen -= len; return 0; } diff --git a/src/intlconv.c b/src/intlconv.c index 985b77f3..6e198a38 100644 --- a/src/intlconv.c +++ b/src/intlconv.c @@ -12,10 +12,27 @@ static RB_HEAD(,intlconv_cache) intlconv_all; static intlconv_cache_t *intlconv_last_ic; pthread_mutex_t intlconv_lock; +static RB_HEAD(,intlconv_cache) intlconv_src_all; +static intlconv_cache_t *intlconv_last_src_ic; +pthread_mutex_t intlconv_lock_src; + +static inline size_t +tvh_iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft) +{ +#ifdef PLATFORM_FREEBSD + return iconv(cd, (const char **)inbuf, inbytesleft, + (const char **)outbuf, outbytesleft); +#else + return iconv(cd, inbuf, inbytesleft, outbuf, outbytesleft); +#endif +} + void intlconv_init( void ) { pthread_mutex_init(&intlconv_lock, NULL); + pthread_mutex_init(&intlconv_lock_src, NULL); } void @@ -31,6 +48,13 @@ intlconv_done( void ) RB_REMOVE(&intlconv_all, ic, ic_link); free(ic); } + intlconv_last_src_ic = NULL; + while ((ic = RB_FIRST(&intlconv_src_all)) != NULL) { + iconv_close(ic->ic_handle); + free(ic->ic_charset_id); + RB_REMOVE(&intlconv_src_all, ic, ic_link); + free(ic); + } pthread_mutex_unlock(&intlconv_lock); } @@ -121,18 +145,16 @@ intlconv_utf8( char *dst, size_t dst_size, ic->ic_handle = c; RB_INSERT_SORTED(&intlconv_all, ic, ic_link, intlconv_cmp); } + intlconv_last_ic = ic; found: + pthread_mutex_unlock(&intlconv_lock); inbuf = (char **)&src_utf8; inbuf_left = strlen(src_utf8); outbuf = &dst; outbuf_left = dst_size; - res = iconv(ic->ic_handle, inbuf, &inbuf_left, outbuf, &outbuf_left); - if (res == -1) { + res = tvh_iconv(ic->ic_handle, inbuf, &inbuf_left, outbuf, &outbuf_left); + if (res == -1) res = -errno; - } else { - intlconv_last_ic = ic; - } - pthread_mutex_unlock(&intlconv_lock); if (res >= 0) res = dst_size - outbuf_left; return res; @@ -161,6 +183,65 @@ intlconv_utf8safestr( const char *dst_charset_id, return res; } +ssize_t +intlconv_to_utf8( char *dst, size_t dst_size, + const char *src_charset_id, + const char *src, size_t src_size ) +{ + intlconv_cache_t templ, *ic; + char **inbuf, **outbuf; + size_t inbuf_left, outbuf_left; + ssize_t res; + + if (src_charset_id == NULL) { + strncpy(dst, src, dst_size); + dst[dst_size - 1] = '\0'; + return strlen(dst); + } + templ.ic_charset_id = (char *)src_charset_id; + pthread_mutex_lock(&intlconv_lock_src); + if (intlconv_last_src_ic && + strcmp(intlconv_last_src_ic->ic_charset_id, src_charset_id) == 0) { + ic = intlconv_last_src_ic; + goto found; + } + ic = RB_FIND(&intlconv_src_all, &templ, ic_link, intlconv_cmp); + if (!ic) { + iconv_t c = iconv_open("UTF-8", src_charset_id); + if ((iconv_t)-1 == c) { + pthread_mutex_unlock(&intlconv_lock_src); + return -EIO; + } + ic = malloc(sizeof(*ic)); + if (ic == NULL) { + pthread_mutex_unlock(&intlconv_lock_src); + return -ENOMEM; + } + ic->ic_charset_id = strdup(src_charset_id); + if (ic->ic_charset_id == NULL) { + pthread_mutex_unlock(&intlconv_lock_src); + free(ic); + iconv_close(c); + return -ENOMEM; + } + ic->ic_handle = c; + RB_INSERT_SORTED(&intlconv_src_all, ic, ic_link, intlconv_cmp); + } + intlconv_last_src_ic = ic; +found: + pthread_mutex_unlock(&intlconv_lock_src); + inbuf = (char **)&src; + inbuf_left = src_size; + outbuf = &dst; + outbuf_left = dst_size; + res = tvh_iconv(ic->ic_handle, inbuf, &inbuf_left, outbuf, &outbuf_left); + if (res == -1) + res = -errno; + if (res >= 0) + res = dst_size - outbuf_left; + return res; +} + /* * */ diff --git a/src/intlconv.h b/src/intlconv.h index 1b57f6e0..53decb71 100644 --- a/src/intlconv.h +++ b/src/intlconv.h @@ -38,4 +38,9 @@ intlconv_utf8safestr( const char *dst_charset_id, const char *src_utf8, size_t max_size ); +ssize_t +intlconv_to_utf8( char *dst, size_t dst_size, + const char *src_charset_id, + const char *src, size_t src_size ); + #endif /* INTLCONV_H_ */