#include <stdio.h>
#include <iconv.h>
#include <strings.h>
#include "encode.h"

typedef unsigned short CARD16;

typedef struct _LangGroup_Info {
	int 	lang_id;
	char 	*lang_name;
	int 	*support_encodes;
	char	**support_locales;
} LangGroup_Info;

/* supported encodes for specified language */
int zh_CN_encodes[] = {
	ENCODE_GB2312,
	ENCODE_GBK,
	ENCODE_GB18030,
	ENCODE_INVALID,
};

int zh_TW_encodes[] = {
	ENCODE_BIG5,
	ENCODE_EUCTW,
	ENCODE_INVALID,
};

int zh_HK_encodes[] = {
	ENCODE_BIG5HKSCS,
	ENCODE_INVALID,
};

int th_TH_encodes[] = {
	ENCODE_EUCTH,
	ENCODE_INVALID,
};

int ko_KR_encodes[] = {
	ENCODE_EUCKR,
	ENCODE_INVALID,
};

/* supported locales for specified language */
char *zh_CN_locales[] = {
	"zh_CN",
	"zh_CN.EUC",
	"zh_CN.GBK",
	"zh_CN.GB18030",
	"zh_CN.UTF-8",
	"zh_CN.utf8",    /* for Linux */
	"zh.GBK",
	"zh.UTF-8",
	"zh",
	NULL
};

char *zh_TW_locales[] = {
	"zh_TW.eucTW",
	"zh_TW.EUC",
	"zh_TW.BIG5",
	"zh_TW.UTF-8",
	"zh_TW.utf8",    /* for Linux */
	"zh_TW",
	NULL
};

char *zh_HK_locales[] = {
	"zh_HK.big5hkscs",
	"zh_HK.BIG5HK",
	"zh_HK.UTF-8",
	"zh_HK.utf8",    /* for Linux */
	NULL
};

char *th_TH_locales[] = {
	"th_TH.TIS620",
	"th_TH.ISO8859-11",
	"th_TH.UTF-8",
	"th_TH.utf8",    /* for Linux */
	"th_TH",
	"th",
	NULL
};

char *ko_KR_locales[] = {
	"ko",
	"ko_KR.EUC",
	"ko.UTF-8",
	"ko_KR.utf8",    /* for Linux */
	NULL
};

enum {
	LANG_ZH_CN = 0,
	LANG_ZH_TW,
	LANG_ZH_HK,
	LANG_TH_TH,
	LANG_KR_KR,
	LANGS_NUM,
};

LangGroup_Info langgroup_info[LANGS_NUM] = {
	{
		LANG_ZH_CN,
		"zh_CN",
		zh_CN_encodes,
		zh_CN_locales,
	},
	{
		LANG_ZH_TW,
		"zh_TW",
		zh_TW_encodes,
		zh_TW_locales,
	},
	{
		LANG_ZH_HK,
		"zh_HK",
		zh_HK_encodes,
		zh_HK_locales,
	},
	{
		LANG_TH_TH,
		"th_TH",
		th_TH_encodes,
		th_TH_locales,
	},
	{
		LANG_KR_KR,
		"ko_KR",
		ko_KR_encodes,
		ko_KR_locales,
	},
};

typedef struct _Encode_Info {
	int 	encode_id;
	char	**called_names;
	char	**support_locales;
	char 	*iconv_codeset_name;
	iconv_t fd_iconv_to_utf8;
	iconv_t fd_iconv_from_utf8;
} Encode_Info;

/* encode names */
char *GB2312_names[] = {
	"GB2312",
	"GB",
	NULL
};

char *GBK_names[] = {
	"GBK",
	NULL
};

char *GB18030_names[] = {
	"GB18030",
	NULL
};

char *BIG5_names[] = {
	"BIG5",
	NULL
};

char *BIG5HK_names[] = {
	"BIG5HK",
	"BIG5HKSCS",
	NULL
};

char *EUC_TW_names[] = {
	"EUC_TW",
	NULL
};

char *EUC_KR_names[] = {
	"EUC_KR",
	NULL
};

char *EUC_JP_names[] = {
	"EUC_JP",
	NULL
};

char *EUC_TH_names[] = {
	"EUC_TH",
	NULL
};

char *UTF8_names[] = {
	"UTF-8",
	"UTF_8",
	"UTF8",
	NULL
};

/* supported locales for specified encode */
char *GB2312_locales[] = {
	"zh_CN.EUC",
	"zh_CN",
	"zh",
	NULL
};

char *GBK_locales[] = {
	"zh_CN.GBK",
	"zh.GBK",
	NULL
};

char *GB18030_locales[] = {
	"zh_CN.GB18030",
	"zh_CN.UTF-8",
	"zh_CN.utf8",    /* for Linux */
	NULL
};

char *BIG5_locales[] = {
	"zh_TW.BIG5",
	"zh_TW.UTF-8",
	"zh_TW.utf8",    /* for Linux */
	"zh_TW",
	NULL
};

char *BIG5HK_locales[] = {
	"zh_HK.big5hkscs",
	"zh_HK.BIG5HK",
	"zh_HK.UTF-8",
	"zh_HK.utf8",    /* for Linux */
	NULL
};

char *EUC_TW_locales[] = {
	"zh_TW.eucTW",
	"zh_TW.EUC",
	"zh_TW",
	NULL
};

char *EUC_KR_locales[] = {
	"ko_KR.EUC",
	"ko_KR.UTF-8",
	"ko_KR.utf8",    /* for Linux */
	NULL
};

char *EUC_JP_locales[] = {
	"ja_JP",
	"ja_JP.UTF-8",
	"ja_JP.eucjp",
	"ja_JP.utf8",    /* for Linux */
	NULL
};

char *EUC_TH_locales[] = {
	"th_TH",
	"th_TH.TIS620",
	"th_TH.UTF-8",
	"th_TH.utf8",    /* for Linux */
	NULL
};

char *UTF8_locales[] = {
	"zh_TW.UTF-8",
	"zh_TW.utf8",    /* for Linux */
	"zh_HK.UTF-8",
	"zh_HK.utf8",    /* for Linux */
	NULL
};

/* iconv codeset name for specified encode */
#define GB2312_CODESET_NAME  	"GB2312"
#define GBK_CODESET_NAME	"GBK"
#define GB18030_CODESET_NAME	"GB18030"
#define BIG5_CODESET_NAME 	"BIG5"
#define BIG5HK_CODESET_NAME	"BIG5-HKSCS"
#define EUC_TW_CODESET_NAME	"EUC-TW"
#define EUC_KR_CODESET_NAME	"EUC-KR"
#define EUC_JP_CODESET_NAME	"EUC-JP"
#define EUC_TH_CODESET_NAME	"TIS620"
#define UTF8_CODESET_NAME	"UTF-8"

/* unknown encode name */
#define UNKNOWN_ENCODE		"UNKNOWN"

Encode_Info  encode_info[ENCODES_NUM + 1] = {
	{ 
		ENCODE_GB2312,
		GB2312_names,
		GB2312_locales,
		GB2312_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_GBK,
		GBK_names,
		GBK_locales,
		GBK_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_GB18030,
		GB18030_names,
		GB18030_locales,
		GB18030_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_BIG5,
		BIG5_names,
		BIG5_locales,
		BIG5_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_BIG5HKSCS,
		BIG5HK_names,
		BIG5HK_locales,
		BIG5HK_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_EUCTW,
		EUC_TW_names,
		EUC_TW_locales,
		EUC_TW_CODESET_NAME,
		NULL,
		NULL,
	},
	{
		ENCODE_EUCKR,
		EUC_KR_names,
		EUC_KR_locales,
		EUC_KR_CODESET_NAME,
		NULL,
		NULL,
	},
	{
		ENCODE_EUCJP,
		EUC_JP_names,
		EUC_JP_locales,
		EUC_JP_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_EUCTH,
		EUC_TH_names,
		EUC_TH_locales,
		EUC_TH_CODESET_NAME,
		NULL,
		NULL,
	},
	{ 
		ENCODE_UTF8,
		UTF8_names,
		UTF8_locales,
		UTF8_CODESET_NAME,
		NULL,
		NULL,
	}
};

iconv_t  fd_iconv_UTF8_to_UTF16 = NULL;
iconv_t  fd_iconv_UTF16_to_UTF8 = NULL;

char *get_langname_from_langid(int langid)
{
	int ret = langid;
	
	if (langid < 0 || langid >= LANGS_NUM) 
		ret = 0;

	return(langgroup_info[ret].lang_name);
}

char *get_langname_from_locale(char *locale)
{
	int lang_id, i, ret;
	char *s;

	ret = -1;
	for (lang_id = 0; lang_id < LANGS_NUM; lang_id++) {
		i = 0;
		while (1) {
			s = langgroup_info[lang_id].support_locales[i];
			if (!s || !*s) break;
			if (!strcasecmp(s, locale)) {
				ret = lang_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}

	if (ret == -1) 
		ret = 0;

	return(langgroup_info[ret].lang_name);
}

char *get_langname_from_encodeid(int encodeid)
{
	int lang_id, i, ret;
	int support_encode;

	ret = -1;
	for (lang_id = 0; lang_id < LANGS_NUM; lang_id++) {
		i = 0;
		while (1) {
			support_encode = langgroup_info[lang_id].support_encodes[i];
			if (support_encode == ENCODE_INVALID) break;
			if (encodeid == support_encode) {
				ret = lang_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}

	if (ret == -1) 
		ret = 0;

	return(langgroup_info[ret].lang_name);
}

int get_langid_from_locale(char *locale)
{
	int lang_id, i, ret;
	char *s;

	ret = -1;
	for (lang_id = 0; lang_id < LANGS_NUM; lang_id++) {
		i = 0;
		while (1) {
			s = langgroup_info[lang_id].support_locales[i];
			if (!s || !*s) break;
			if (!strcasecmp(s, locale)) {
				ret = lang_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}

	if (ret == -1) 
		ret = 0;

	return(ret);
}
int get_langid_from_localeid(int localeid)
{
	int lang_id, i, ret;
	int support_encode;

	ret = -1;
	for (lang_id = 0; lang_id < LANGS_NUM; lang_id++) {
		i = 0;
		while (1) {
			support_encode = langgroup_info[lang_id].support_encodes[i];
			if (support_encode == ENCODE_INVALID) break;
			if (localeid == support_encode) {
				ret = lang_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}

	if (ret == -1) 
		ret = 0;

	return(ret);
}

int get_encodeid_from_name(char *name)
{
	int encode_id, i, ret;
	char *s;
	
	ret = -1;
	for (encode_id = 0; encode_id < ENCODES_NUM; encode_id++) {
		i = 0;
		while (1) {
			s = encode_info[encode_id].called_names[i];
			if (!s || !*s) break;
			if (!strcasecmp(s, name)) {
				ret = encode_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}
	if (ret == -1) ret = ENCODE_GB2312; /* return default encode */
	return(ret);	
}

int get_encodeid_from_locale(char *locale)
{
	int encode_id, i, ret;
	char *s;
	
	ret = -1;
	for (encode_id = 0; encode_id < ENCODES_NUM; encode_id++) {
		i = 0;
		while (1) {
			s = encode_info[encode_id].support_locales[i];
			if (!s || !*s) break;
			if (!strcasecmp(s, locale)) {
				ret = encode_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}

	if (ret == -1) ret = ENCODE_INVALID; /* return default encode */

	return(ret);	
}

char *get_name_from_encodeid(int encode_id)
{
	if (encode_id >= 0 && encode_id <= ENCODES_NUM)
		return(encode_info[encode_id].called_names[0]);
	else
		return(NULL);
}

char *get_default_locale_from_locale(char *locale)
{
	int encode_id, i, ret;
	char *s;

	ret = -1;
	for (encode_id = 0; encode_id < ENCODES_NUM; encode_id++) {
		i = 0;
		while (1) {
			s = encode_info[encode_id].support_locales[i];
			if (!s || !*s) break;
			if (!strcasecmp(s, locale)) {
				ret = encode_id;
				break;
			}
			i++;
		}
		if (ret != -1) break;
	}
	
	if (ret == -1) return(NULL);

	return(encode_info[ret].support_locales[0]);
}

int  get_char_len_by_encodeid(int encode_id, unsigned char *ch_ptr)
{
	int ret = 2;  /* default character length */
	unsigned char code0, code1;

	code0 = ch_ptr[0];
	if (code0 < 0x80) return(1);

	if (encode_id == ENCODE_UTF8) {
		if (code0 > 0xe0)		/* 3 bytes */
			ret = 3;
	} else if (encode_id == ENCODE_GB18030) {
		code1 = ch_ptr[1];
		if (code0 >=0x81 && code0 <= 0xFE) {
			if (code1 >= 0x30 && code1 <= 0x39)
				ret = 4;
		} 
	} else if (encode_id == ENCODE_EUCTW) {
		if (code0 == 0x8e)		/* 4 bytes */
			ret = 4;
	} else if (encode_id == ENCODE_EUCTH) {
		ret = 1;
	}

	return(ret);
}

int get_char_count_by_encodeid(int encode_id, unsigned char *ch_ptr, int *char_count)
{
	int str_len = strlen((char *)ch_ptr);
	int str_pos = 0;
	int char_len;
	unsigned char *pStr;

	*char_count = 0;
	while (str_pos < str_len) {
		pStr = ch_ptr + str_pos;
		char_len = get_char_len_by_encodeid(encode_id, pStr);
		if (str_pos + char_len <= str_len) {
			(*char_count)++;
			str_pos += char_len;
		} else {
			return(-1);
		}
	}

	return(0);
}

int is_valid_code(int encode_id, unsigned char *int_code, int code_len)
{
	unsigned char code0, code1, code2, code3;
	
	code0 = int_code[0];
	code1 = int_code[1];

	switch (encode_id) {
		case ENCODE_GB2312:
			if (code0 < 0xA1 || code0 > 0xFE)
				return (-1);
			if (code1 < 0xA1 || code1 > 0xFE)
				return (-1);
			break;
			
		case ENCODE_GBK:
			if (code0 < 0x81 || code0 > 0xFE)
				return (-1);
			if (code1 < 0x40 || code1 > 0xFE || code1 == 0x7F)
				return (-1);
			break;

		case ENCODE_GB18030:
			if (code_len == 2) {
				if (code0 < 0x81 || code0 > 0xFE)
					return (-1);
				if (code1 < 0x40 || code1 > 0xFE || code1 == 0x7F)
					return (-1);
			} else if (code_len == 4) {
				code2 = int_code[2];
				code3 = int_code[3];
				if (code0 < 0x81 || code0 > 0xFE)
					return (-1);
				if (code1 < 0x30 || code1 > 0x39)
					return (-1);
				if (code2 < 0x81 || code2 > 0xFE)
					return (-1);
				if (code3 < 0x30 || code3 > 0x39)
					return (-1);
			}
			break;

		case ENCODE_BIG5:
#if 0
			/* define in lcbig5.c */
			if ((code0 >= 0xA1) && code0 <= 0xC5) || (code0 >= 0xC9 && code0 <= 0xF9)) {
				if (code1 < 0x40 || code1 == 0xFF || (code1 >= 0x7F && code1 <= 0xA0))
					return (-1);
				else
					return (0);
			} else {
				if (code0 == 0xC6 && (code1 >= 0x40 && code1 <= 0x7E))
					return (0);
				else
					return (-1);
			}
#endif
					
			if (code0 < 0xA1 || code0 > 0xFE)
				return (-1);
			if (code1 < 0x40 || code1 > 0xFE)
				return (-1);
			if (code1 > 0x7E && code1 < 0xA1)
				return (-1);
			break;

		case ENCODE_EUCTW:
			if (code_len == 2) {
				if (code0 < 0x80 || code1 <0x80)
					return (-1);
			} if (code_len == 4) {
				code2 = int_code[2];
				code3 = int_code[3];
				if (code0 != 0x8E)
					return(-1);
				if (code1 < 0x80 || code2 < 0x80 || code3 < 0x80)
					return(-1);
			}
			break;

		case ENCODE_BIG5HKSCS:
			if (code0 < 0x81 || code0 > 0xFE)
				return (-1);
			if (code1 < 0x40 || code1 > 0xFE)
				return (-1);
			break;

		case ENCODE_EUCKR:
			if (code0 < 0xA1 || code0 > 0xFE)
				return (-1);
			if (code1 < 0xA1 || code1 > 0xFE)
				return (-1);
			break;

		case ENCODE_UTF8:
			break;
	}
	return(0);
}

int is_valid_encode_string(int encode_id, unsigned char *hzstr, int hzlen)
{
	int i, char_len, ret;
	unsigned char *ptr;

	i = 0;
	while (i < hzlen) {
		ptr = hzstr + i;
		if (*ptr < 0x80) {
			if (*ptr == 0x3f && i < hzlen-1) {
				if (*(ptr+1) == 0x3f)
					return(-1);
			}

			i++;
		} else {
			char_len = get_char_len_by_encodeid(encode_id, ptr);
			ret = is_valid_code(encode_id, ptr, char_len);
			if (ret == -1)
				return(-1);
			i += char_len;
		}
	}

	return (0);
}

/* iconv functions */
int Convert_Native_To_UTF8(int encode_id, char *from_buf, size_t from_left,
			   char **to_buf, size_t * to_left)
{
	char	*ip;
	char		*op;
	size_t		ileft, oleft;
	iconv_t		fd_iconv;
	char 		*codeset;
	size_t		ret = 0;
	
	if (encode_id < 0 || encode_id >= ENCODES_NUM)
		return(-1);

	if ( (from_left < 0) || (*to_left < 0) )
		return(-1);

	ip = (char *) from_buf;
	ileft = from_left;

	op = *((char **) to_buf);
	oleft = *to_left;

	if (encode_id == ENCODE_UTF8) {
		if (ileft > oleft)
			return(-1);
		memcpy(op, ip, ileft);
		*to_left = oleft - ileft;
		return(0);
	}

	fd_iconv = encode_info[encode_id].fd_iconv_to_utf8;
	if (fd_iconv == (iconv_t)-1) return(-1);

	if (fd_iconv == NULL) {
		codeset = encode_info[encode_id].iconv_codeset_name;
		fd_iconv = iconv_open("UTF-8", codeset);
		encode_info[encode_id].fd_iconv_to_utf8 = fd_iconv;
		if ( fd_iconv == (iconv_t) -1 )
			return(-1);
	}

	ret = iconv(fd_iconv, &ip, &ileft, &op, &oleft);
	if (ret == -1) {
		return(-1);
	}
	*to_left = oleft;
	return(0);

}

int Convert_UTF8_To_Native(int encode_id, char *from_buf, size_t from_left,
			   char **to_buf, size_t * to_left)
{
	char	*ip;
	char		*op;
	size_t		ileft, oleft;
	iconv_t		fd_iconv;
	char 		*codeset;
	size_t		ret = 0;
	
	if (encode_id < 0 || encode_id >= ENCODES_NUM)
		return(-1);

	if ( (from_left < 0) || (*to_left < 0) )
		return(-1);

	ip = (char *) from_buf;
	ileft = from_left;

	op = *((char **) to_buf);
	oleft = *to_left;

	if (encode_id == ENCODE_UTF8) {
		if (ileft > oleft)
			return(-1);
		memcpy(op, ip, ileft);
		*to_left = oleft - ileft;
		return(0);
	}

	fd_iconv = encode_info[encode_id].fd_iconv_from_utf8;
	if (fd_iconv == (iconv_t)-1) return(-1);

	if (fd_iconv == NULL) {
		codeset = encode_info[encode_id].iconv_codeset_name;
		fd_iconv = iconv_open(codeset, "UTF-8");
		encode_info[encode_id].fd_iconv_from_utf8 = fd_iconv;
		if ( fd_iconv == (iconv_t) -1 )
			return(-1);
	}

	ret = iconv(fd_iconv, &ip, &ileft, &op, &oleft);
	if (ret == -1) {
		return(-1);
	}
	*to_left = oleft;
	return(0);
}

#define UTF16_STRLEN    1024
	
int Convert_Native_To_UTF16(int encode_id, char *from_buf, size_t from_left,
			   char **to_buf, size_t *to_left)
{
	char	*ip;
	char		*op;
	size_t		ileft, oleft;

	char 		*codeset;
	iconv_t		fd_iconv_native_to_utf8;

	size_t		ret = 0;
	int 		skip_native_to_utf8_iconv = 0;

	if (encode_id < 0 || encode_id >= ENCODES_NUM)
		return(-1);

	if ( (from_left < 0) || (*to_left < 0) )
		return(-1);

	/* Initialize the iconv of utf8_to_ucs2 */
	if (fd_iconv_UTF8_to_UTF16 == (iconv_t)-1 )
		 return(-1);

	if (fd_iconv_UTF8_to_UTF16 == NULL) {
		fd_iconv_UTF8_to_UTF16 = iconv_open("UTF-16", "UTF-8");
		if (fd_iconv_UTF8_to_UTF16 == (iconv_t)-1 )
			return(-1);
	}

	if (encode_id == ENCODE_UTF8)
		skip_native_to_utf8_iconv = 1;

	ip = (char *) from_buf;
	ileft = from_left;

	op = *((char **) to_buf);
	oleft = *to_left;

	if (!skip_native_to_utf8_iconv) {
		char		buffer[UTF16_STRLEN];   /* Fix me! */
		const size_t	buf_len = UTF16_STRLEN;
		char		*src, *dst;
		size_t		src_len, dst_len;

		/* Initialize the iconv of native_to_utf8 */
		fd_iconv_native_to_utf8 = encode_info[encode_id].fd_iconv_to_utf8;
		if (fd_iconv_native_to_utf8 == (iconv_t)-1) return(-1);

		if (fd_iconv_native_to_utf8 == NULL) {
			codeset = encode_info[encode_id].iconv_codeset_name;
			fd_iconv_native_to_utf8 = iconv_open("UTF-8", codeset);
			encode_info[encode_id].fd_iconv_to_utf8 = fd_iconv_native_to_utf8;
			if ( fd_iconv_native_to_utf8 == (iconv_t) -1 )
				return(-1);
		}

		while ((ileft > 0) && (oleft > 0)) {
			dst = buffer;
			dst_len = buf_len;
			ret = iconv(fd_iconv_native_to_utf8, &ip, &ileft, (char **) &dst, &dst_len);
			if (ret == -1) {
				return(-1);
			}
			src = buffer;
			src_len = buf_len - dst_len;
			ret = iconv(fd_iconv_UTF8_to_UTF16, (char **) &src, &src_len, &op, &oleft);
			if (ret == -1) {
				return(-1);
			}
		}

	} else {
		ret = iconv(fd_iconv_UTF8_to_UTF16, &ip, &ileft, &op, &oleft);
		if (ret == -1) {
			 return(-1);
		}
	}

	if (0xFEFF == **((CARD16 **) to_buf)) {
		memmove(*to_buf, *to_buf + 2, *to_left - oleft - 2);
		*to_left = (oleft + 2);
	} else {
		*to_left = oleft;
	}

	return(0);
}

int Convert_UTF16_To_Native(int encode_id, char *from_buf, size_t from_left,
			   char **to_buf, size_t * to_left)
{
	char	*ip;
	char		*op;
	size_t		ileft, oleft;
	char 		*codeset;
	iconv_t		fd_iconv_utf8_to_native;

	size_t		ret = 0;
	int 		skip_utf8_to_native_iconv = 0;

	if (encode_id < 0 || encode_id >= ENCODES_NUM)
		return(-1);

	if ( (from_left < 0) || (*to_left < 0) )
		return(-1);

	/* Initialize the iconv of utf8_to_ucs2 */
	if (fd_iconv_UTF16_to_UTF8 == (iconv_t)-1 )
		 return(-1);

	if (fd_iconv_UTF16_to_UTF8 == NULL) {
		fd_iconv_UTF16_to_UTF8 = iconv_open("UTF-8", "UTF-16");
		if (fd_iconv_UTF16_to_UTF8 == (iconv_t)-1 )
			return(-1);
	}

	if (encode_id == ENCODE_UTF8)
		skip_utf8_to_native_iconv = 1;

	ip = (char *) from_buf;
	ileft = from_left;

	op = *((char **) to_buf);
	oleft = *to_left;

	if (!skip_utf8_to_native_iconv) {
		char		buffer[UTF16_STRLEN];   /* Fix me! */
		const size_t	buf_len = UTF16_STRLEN;
		char		*src, *dst;
		size_t		src_len, dst_len;

		/* Initialize the iconv of native_to_utf8 */
		fd_iconv_utf8_to_native = encode_info[encode_id].fd_iconv_from_utf8;
		if (fd_iconv_utf8_to_native == (iconv_t)-1) return(-1);

		if (fd_iconv_utf8_to_native == NULL) {
			codeset = encode_info[encode_id].iconv_codeset_name;
			fd_iconv_utf8_to_native = iconv_open(codeset, "UTF-8");
			encode_info[encode_id].fd_iconv_from_utf8 = fd_iconv_utf8_to_native;
			if ( fd_iconv_utf8_to_native == (iconv_t) -1 )
				return(-1);
		}

		while ((ileft > 0) && (oleft > 0)) {
			dst = buffer;
			dst_len = buf_len;
			ret = iconv(fd_iconv_UTF16_to_UTF8, &ip, &ileft, (char **) &dst, &dst_len);
			if (ret == -1) {
				return(-1);
			}
			src = buffer;
			src_len = buf_len - dst_len;
			ret = iconv(fd_iconv_utf8_to_native, (char **) &src, &src_len, &op, &oleft);
			if (ret == -1) {
				return(-1);
			}
		}

	} else {
		ret = iconv(fd_iconv_UTF16_to_UTF8, &ip, &ileft, &op, &oleft);
		if (ret == -1) {
			 return(-1);
		}
	}

	*to_left = oleft;

	return(0);
}
