intlconv: add intlconv_to_utf8() and use it in dvb_support.c also fixes #2319

This commit is contained in:
Jaroslav Kysela 2014-10-02 13:36:31 +02:00
parent ba8d90ff48
commit 4faa0efc87
3 changed files with 98 additions and 30 deletions

View file

@ -25,12 +25,12 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <iconv.h>
#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;
}

View file

@ -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;
}
/*
*
*/

View file

@ -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_ */