#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/modctl.h>
#include <sys/byteorder.h>
#include <sys/errno.h>
#include <sys/kiconv.h>
#include <sys/kiconv_latin1.h>
#define KICONV_TBLID_1252 (0x00)
#define KICONV_TBLID_8859_1 (0x01)
#define KICONV_TBLID_8859_15 (0x02)
#define KICONV_TBLID_850 (0x03)
#define KICONV_MAX_MAPPING_TBLID (0x03)
extern const int8_t u8_number_of_bytes[];
extern const uint8_t u8_valid_min_2nd_byte[];
extern const uint8_t u8_valid_max_2nd_byte[];
static void *
open_to_1252()
{
kiconv_state_t s;
s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
s->id = KICONV_TBLID_1252;
s->bom_processed = 0;
return ((void *)s);
}
static void *
open_to_88591()
{
kiconv_state_t s;
s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
s->id = KICONV_TBLID_8859_1;
s->bom_processed = 0;
return ((void *)s);
}
static void *
open_to_885915()
{
kiconv_state_t s;
s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
s->id = KICONV_TBLID_8859_15;
s->bom_processed = 0;
return ((void *)s);
}
static void *
open_to_850()
{
kiconv_state_t s;
s = (kiconv_state_t)kmem_alloc(sizeof (kiconv_state_data_t), KM_SLEEP);
s->id = KICONV_TBLID_850;
s->bom_processed = 0;
return ((void *)s);
}
static void *
open_fr_1252()
{
return ((void *)KICONV_TBLID_1252);
}
static void *
open_fr_88591()
{
return ((void *)KICONV_TBLID_8859_1);
}
static void *
open_fr_885915()
{
return ((void *)KICONV_TBLID_8859_15);
}
static void *
open_fr_850()
{
return ((void *)KICONV_TBLID_850);
}
static int
close_to_sb(void *s)
{
if (! s || s == (void *)-1)
return (EBADF);
kmem_free(s, sizeof (kiconv_state_data_t));
return (0);
}
static int
close_fr_sb(void *s)
{
if ((ulong_t)s > KICONV_MAX_MAPPING_TBLID)
return (EBADF);
return (0);
}
static size_t
kiconv_to_sb(void *kcd, char **inbuf, size_t *inbytesleft, char **outbuf,
size_t *outbytesleft, int *errno)
{
size_t id;
size_t ret_val;
uchar_t *ib;
uchar_t *oldib;
uchar_t *ob;
uchar_t *ibtail;
uchar_t *obtail;
uint32_t u8;
size_t i;
size_t l;
size_t h;
size_t init_h;
int8_t sz;
boolean_t second;
if (! kcd || kcd == (void *)-1) {
*errno = EBADF;
return ((size_t)-1);
}
id = ((kiconv_state_t)kcd)->id;
if (id > KICONV_MAX_MAPPING_TBLID) {
*errno = EBADF;
return ((size_t)-1);
}
if (! inbuf || ! (*inbuf)) {
((kiconv_state_t)kcd)->bom_processed = 0;
return ((size_t)0);
}
ret_val = 0;
ib = (uchar_t *)*inbuf;
ob = (uchar_t *)*outbuf;
ibtail = ib + *inbytesleft;
obtail = ob + *outbytesleft;
init_h = sizeof (to_sb_tbl[id]) / sizeof (kiconv_to_sb_tbl_comp_t) - 1;
if (((kiconv_state_t)kcd)->bom_processed == 0 && (ibtail - ib) >= 3 &&
*ib == 0xef && *(ib + 1) == 0xbb && *(ib + 2) == 0xbf)
ib += 3;
((kiconv_state_t)kcd)->bom_processed = 1;
while (ib < ibtail) {
sz = u8_number_of_bytes[*ib];
if (sz <= 0) {
*errno = EILSEQ;
ret_val = (size_t)-1;
break;
}
if (ob >= obtail) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
if (sz == 1) {
*ob++ = *ib++;
continue;
}
if ((ibtail - ib) < sz) {
*errno = EINVAL;
ret_val = (size_t)-1;
break;
}
oldib = ib;
u8 = *ib++;
second = B_TRUE;
for (i = 1; i < sz; i++) {
if (second) {
if (*ib < u8_valid_min_2nd_byte[u8] ||
*ib > u8_valid_max_2nd_byte[u8]) {
*errno = EILSEQ;
ret_val = (size_t)-1;
ib = oldib;
goto TO_SB_ILLEGAL_CHAR_ERR;
}
second = B_FALSE;
} else if (*ib < 0x80 || *ib > 0xbf) {
*errno = EILSEQ;
ret_val = (size_t)-1;
ib = oldib;
goto TO_SB_ILLEGAL_CHAR_ERR;
}
u8 = (u8 << 8) | ((uint32_t)*ib);
ib++;
}
i = l = 0;
h = init_h;
while (l <= h) {
i = (l + h) / 2;
if (to_sb_tbl[id][i].u8 == u8)
break;
else if (to_sb_tbl[id][i].u8 < u8)
l = i + 1;
else
h = i - 1;
}
if (to_sb_tbl[id][i].u8 == u8) {
*ob++ = to_sb_tbl[id][i].sb;
} else {
*ob++ = KICONV_ASCII_REPLACEMENT_CHAR;
ret_val++;
}
}
TO_SB_ILLEGAL_CHAR_ERR:
*inbuf = (char *)ib;
*inbytesleft = ibtail - ib;
*outbuf = (char *)ob;
*outbytesleft = obtail - ob;
return (ret_val);
}
static size_t
kiconv_fr_sb(void *kcd, char **inbuf, size_t *inbytesleft, char **outbuf,
size_t *outbytesleft, int *errno)
{
size_t ret_val;
uchar_t *ib;
uchar_t *ob;
uchar_t *ibtail;
uchar_t *obtail;
size_t i;
size_t k;
int8_t sz;
if ((ulong_t)kcd > KICONV_MAX_MAPPING_TBLID) {
*errno = EBADF;
return ((size_t)-1);
}
if (! inbuf || ! (*inbuf))
return ((size_t)0);
ret_val = 0;
ib = (uchar_t *)*inbuf;
ob = (uchar_t *)*outbuf;
ibtail = ib + *inbytesleft;
obtail = ob + *outbytesleft;
while (ib < ibtail) {
if (*ib < 0x80) {
if (ob >= obtail) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
*ob++ = *ib++;
continue;
}
k = *ib - 0x80;
sz = u8_number_of_bytes[to_u8_tbl[(ulong_t)kcd][k].u8[0]];
if (sz <= 0) {
*errno = EILSEQ;
ret_val = (size_t)-1;
break;
}
if ((obtail - ob) < sz) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
for (i = 0; i < sz; i++)
*ob++ = to_u8_tbl[(ulong_t)kcd][k].u8[i];
ib++;
}
*inbuf = (char *)ib;
*inbytesleft = ibtail - ib;
*outbuf = (char *)ob;
*outbytesleft = obtail - ob;
return (ret_val);
}
static size_t
kiconvstr_to_sb(size_t id, uchar_t *ib, size_t *inlen, uchar_t *ob,
size_t *outlen, int flag, int *errno)
{
size_t ret_val;
uchar_t *oldib;
uchar_t *ibtail;
uchar_t *obtail;
uint32_t u8;
size_t i;
size_t l;
size_t h;
size_t init_h;
int8_t sz;
boolean_t second;
boolean_t do_not_ignore_null;
if (id > KICONV_MAX_MAPPING_TBLID) {
*errno = EBADF;
return ((size_t)-1);
}
ret_val = 0;
ibtail = ib + *inlen;
obtail = ob + *outlen;
do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
init_h = sizeof (to_sb_tbl[id]) / sizeof (kiconv_to_sb_tbl_comp_t) - 1;
if ((ibtail - ib) >= 3 && *ib == 0xef && *(ib + 1) == 0xbb &&
*(ib + 2) == 0xbf)
ib += 3;
while (ib < ibtail) {
sz = u8_number_of_bytes[*ib];
if (sz <= 0) {
if (flag & KICONV_REPLACE_INVALID) {
if (ob >= obtail) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
ib++;
goto STR_TO_SB_REPLACE_INVALID;
}
*errno = EILSEQ;
ret_val = (size_t)-1;
break;
}
if (*ib == '\0' && do_not_ignore_null)
break;
if (ob >= obtail) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
if (sz == 1) {
*ob++ = *ib++;
continue;
}
if ((ibtail - ib) < sz) {
if (flag & KICONV_REPLACE_INVALID) {
ib = ibtail;
goto STR_TO_SB_REPLACE_INVALID;
}
*errno = EINVAL;
ret_val = (size_t)-1;
break;
}
oldib = ib;
u8 = *ib++;
second = B_TRUE;
for (i = 1; i < sz; i++) {
if (second) {
if (*ib < u8_valid_min_2nd_byte[u8] ||
*ib > u8_valid_max_2nd_byte[u8]) {
if (flag & KICONV_REPLACE_INVALID) {
ib = oldib + sz;
goto STR_TO_SB_REPLACE_INVALID;
}
*errno = EILSEQ;
ret_val = (size_t)-1;
ib = oldib;
goto STR_TO_SB_ILLEGAL_CHAR_ERR;
}
second = B_FALSE;
} else if (*ib < 0x80 || *ib > 0xbf) {
if (flag & KICONV_REPLACE_INVALID) {
ib = oldib + sz;
goto STR_TO_SB_REPLACE_INVALID;
}
*errno = EILSEQ;
ret_val = (size_t)-1;
ib = oldib;
goto STR_TO_SB_ILLEGAL_CHAR_ERR;
}
u8 = (u8 << 8) | ((uint32_t)*ib);
ib++;
}
i = l = 0;
h = init_h;
while (l <= h) {
i = (l + h) / 2;
if (to_sb_tbl[id][i].u8 == u8)
break;
else if (to_sb_tbl[id][i].u8 < u8)
l = i + 1;
else
h = i - 1;
}
if (to_sb_tbl[id][i].u8 == u8) {
*ob++ = to_sb_tbl[id][i].sb;
} else {
STR_TO_SB_REPLACE_INVALID:
*ob++ = KICONV_ASCII_REPLACEMENT_CHAR;
ret_val++;
}
}
STR_TO_SB_ILLEGAL_CHAR_ERR:
*inlen = ibtail - ib;
*outlen = obtail - ob;
return (ret_val);
}
static size_t
kiconvstr_to_1252(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_to_sb(KICONV_TBLID_1252, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_to_1(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_to_sb(KICONV_TBLID_8859_1, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_to_15(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_to_sb(KICONV_TBLID_8859_15, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_to_850(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_to_sb(KICONV_TBLID_850, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_fr_sb(size_t id, uchar_t *ib, size_t *inlen, uchar_t *ob,
size_t *outlen, int flag, int *errno)
{
size_t ret_val;
uchar_t *ibtail;
uchar_t *obtail;
size_t i;
size_t k;
int8_t sz;
boolean_t do_not_ignore_null;
ret_val = 0;
ibtail = ib + *inlen;
obtail = ob + *outlen;
do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
while (ib < ibtail) {
if (*ib == '\0' && do_not_ignore_null)
break;
if (*ib < 0x80) {
if (ob >= obtail) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
*ob++ = *ib++;
continue;
}
k = *ib - 0x80;
sz = u8_number_of_bytes[to_u8_tbl[id][k].u8[0]];
if (sz <= 0) {
if (flag & KICONV_REPLACE_INVALID) {
if ((obtail - ob) < 3) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
*ob++ = 0xef;
*ob++ = 0xbf;
*ob++ = 0xbd;
ret_val++;
ib++;
continue;
}
*errno = EILSEQ;
ret_val = (size_t)-1;
break;
}
if ((obtail - ob) < sz) {
*errno = E2BIG;
ret_val = (size_t)-1;
break;
}
for (i = 0; i < sz; i++)
*ob++ = to_u8_tbl[id][k].u8[i];
ib++;
}
*inlen = ibtail - ib;
*outlen = obtail - ob;
return (ret_val);
}
static size_t
kiconvstr_fr_1252(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_fr_sb(KICONV_TBLID_1252, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_fr_1(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_fr_sb(KICONV_TBLID_8859_1, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_fr_15(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_fr_sb(KICONV_TBLID_8859_15, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
static size_t
kiconvstr_fr_850(char *inarray, size_t *inlen, char *outarray,
size_t *outlen, int flag, int *errno)
{
return (kiconvstr_fr_sb(KICONV_TBLID_850, (uchar_t *)inarray,
inlen, (uchar_t *)outarray, outlen, flag, errno));
}
#define KICONV_MAX_CODEID_ENTRY 68
#define KICONV_MAX_CODEID 42
static kiconv_code_list_t code_list[KICONV_MAX_CODEID_ENTRY] = {
{ "utf8", 0 },
{ "cp1252", 1 },
{ "1252", 1 },
{ "iso88591", 2 },
{ "iso885915", 3 },
{ "cp850", 4 },
{ "850", 4 },
{ "eucjp", 5 },
{ "eucjpms", 6 },
{ "cp932", 7 },
{ "932", 7 },
{ "shiftjis", 8 },
{ "pck", 8 },
{ "sjis", 8 },
{ "gb18030", 9 },
{ "gbk", 10 },
{ "cp936", 10 },
{ "936", 10 },
{ "euccn", 11 },
{ "euckr", 12 },
{ "unifiedhangul", 13 },
{ "cp949", 13 },
{ "949", 13 },
{ "big5", 14 },
{ "cp950", 14 },
{ "950", 14 },
{ "big5hkscs", 15 },
{ "euctw", 16 },
{ "cp950hkscs", 17 },
{ "cp1250", 18 },
{ "1250", 18 },
{ "iso88592", 19 },
{ "cp852", 20 },
{ "852", 20 },
{ "cp1251", 21 },
{ "1251", 21 },
{ "iso88595", 22 },
{ "koi8r", 23 },
{ "cp866", 24 },
{ "866", 24 },
{ "cp1253", 25 },
{ "1253", 25 },
{ "iso88597", 26 },
{ "cp737", 27 },
{ "737", 27 },
{ "cp1254", 28 },
{ "1254", 28 },
{ "iso88599", 29 },
{ "cp857", 30 },
{ "857", 30 },
{ "cp1256", 31 },
{ "1256", 31 },
{ "iso88596", 32 },
{ "cp720", 33 },
{ "720", 33 },
{ "cp1255", 34 },
{ "1255", 34 },
{ "iso88598", 35 },
{ "cp862", 36 },
{ "862", 36 },
{ "cp1257", 37 },
{ "1257", 37 },
{ "iso885913", 38 },
{ "iso885910", 39 },
{ "iso885911", 40 },
{ "tis620", 40 },
{ "iso88593", 41 },
{ "iso88594", 42 },
};
#define KICONV_MAX_CONVERSIONS 84
static kiconv_conv_list_t conv_list[KICONV_MAX_CONVERSIONS] = {
{
1, 0, KICONV_EMBEDDED,
open_to_1252, kiconv_to_sb, close_to_sb, kiconvstr_to_1252
},
{
0, 1, KICONV_EMBEDDED,
open_fr_1252, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1252
},
{
2, 0, KICONV_EMBEDDED,
open_to_88591, kiconv_to_sb, close_to_sb, kiconvstr_to_1
},
{
0, 2, KICONV_EMBEDDED,
open_fr_88591, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_1
},
{
3, 0, KICONV_EMBEDDED,
open_to_885915, kiconv_to_sb, close_to_sb, kiconvstr_to_15
},
{
0, 3, KICONV_EMBEDDED,
open_fr_885915, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_15
},
{
4, 0, KICONV_EMBEDDED,
open_to_850, kiconv_to_sb, close_to_sb, kiconvstr_to_850
},
{
0, 4, KICONV_EMBEDDED,
open_fr_850, kiconv_fr_sb, close_fr_sb, kiconvstr_fr_850
},
{ 0, 5, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 5, 0, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 0, 6, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 6, 0, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 0, 7, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 7, 0, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 0, 8, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 8, 0, KICONV_MODULE_ID_JA, NULL, NULL, NULL, NULL },
{ 0, 9, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 9, 0, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 0, 10, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 10, 0, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 0, 11, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 11, 0, KICONV_MODULE_ID_SC, NULL, NULL, NULL, NULL },
{ 0, 12, KICONV_MODULE_ID_KO, NULL, NULL, NULL, NULL },
{ 12, 0, KICONV_MODULE_ID_KO, NULL, NULL, NULL, NULL },
{ 0, 13, KICONV_MODULE_ID_KO, NULL, NULL, NULL, NULL },
{ 13, 0, KICONV_MODULE_ID_KO, NULL, NULL, NULL, NULL },
{ 0, 14, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 14, 0, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 0, 15, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 15, 0, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 0, 16, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 16, 0, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 0, 17, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 17, 0, KICONV_MODULE_ID_TC, NULL, NULL, NULL, NULL },
{ 0, 18, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 18, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 19, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 19, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 20, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 20, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 21, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 21, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 22, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 22, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 23, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 23, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 24, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 24, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 25, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 25, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 26, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 26, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 27, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 27, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 28, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 28, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 29, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 29, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 30, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 30, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 31, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 31, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 32, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 32, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 33, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 33, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 34, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 34, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 35, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 35, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 36, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 36, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 37, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 37, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 38, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 38, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 39, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 39, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 40, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 40, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 41, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 41, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 0, 42, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
{ 42, 0, KICONV_MODULE_ID_EMEA, NULL, NULL, NULL, NULL },
};
static kiconv_mod_list_t module_list[KICONV_MAX_MODULE_ID + 1] = {
"kiconv_embedded", 0,
"kiconv_ja", 0,
"kiconv_sc", 0,
"kiconv_ko", 0,
"kiconv_tc", 0,
"kiconv_emea", 0,
};
static kmutex_t conv_list_lock;
void
kiconv_init()
{
mutex_init(&conv_list_lock, NULL, MUTEX_DEFAULT, NULL);
}
size_t
kiconv_module_ref_count(size_t mid)
{
int count;
if (mid <= 0 || mid > KICONV_MAX_MODULE_ID)
return (0);
mutex_enter(&conv_list_lock);
count = module_list[mid].refcount;
mutex_exit(&conv_list_lock);
return (count);
}
static size_t
normalize_codename(const char *n)
{
char s[KICONV_MAX_CODENAME_LEN + 1];
size_t i;
if (n == NULL)
return ((size_t)-1);
for (i = 0; *n; n++) {
if (KICONV_SKIPPABLE_CHAR(*n))
continue;
if (i >= KICONV_MAX_CODENAME_LEN)
return ((size_t)-1);
s[i++] = (*n >= 'A' && *n <= 'Z') ? *n - 'A' + 'a' : *n;
}
s[i] = '\0';
for (i = 0; i < KICONV_MAX_CODEID_ENTRY; i++)
if (strcmp(s, code_list[i].name) == 0)
return (code_list[i].id);
return ((size_t)-1);
}
int
kiconv_register_module(kiconv_module_info_t *info)
{
size_t mid;
size_t fid;
size_t tid;
size_t i;
size_t j;
kiconv_ops_t *op;
if (info == NULL || info->module_name == NULL ||
info->kiconv_num_convs == 0 || info->kiconv_ops_tbl == NULL)
return (EINVAL);
for (mid = 1; mid <= KICONV_MAX_MODULE_ID; mid++)
if (strcmp(module_list[mid].name, info->module_name) == 0)
break;
if (mid > KICONV_MAX_MODULE_ID)
return (EINVAL);
mutex_enter(&conv_list_lock);
if (module_list[mid].refcount > 0) {
mutex_exit(&conv_list_lock);
return (EAGAIN);
}
for (i = 0; i < info->kiconv_num_convs; i++) {
op = &(info->kiconv_ops_tbl[i]);
fid = normalize_codename(op->fromcode);
tid = normalize_codename(op->tocode);
if (op->kiconv_open == NULL || op->kiconv == NULL ||
op->kiconv_close == NULL || op->kiconvstr == NULL)
continue;
for (j = 0; j < KICONV_MAX_CONVERSIONS; j++) {
if (conv_list[j].mid == mid &&
conv_list[j].fid == fid &&
conv_list[j].tid == tid) {
if (conv_list[j].open == NULL) {
conv_list[j].open = op->kiconv_open;
conv_list[j].kiconv = op->kiconv;
conv_list[j].close = op->kiconv_close;
conv_list[j].kiconvstr = op->kiconvstr;
}
break;
}
}
}
mutex_exit(&conv_list_lock);
return (0);
}
int
kiconv_unregister_module(kiconv_module_info_t *info)
{
size_t mid;
size_t i;
if (info == NULL || info->module_name == NULL ||
info->kiconv_num_convs == 0 || info->kiconv_ops_tbl == NULL)
return (EINVAL);
for (mid = 1; mid <= KICONV_MAX_MODULE_ID; mid++)
if (strcmp(module_list[mid].name, info->module_name) == 0)
break;
if (mid > KICONV_MAX_MODULE_ID)
return (EINVAL);
mutex_enter(&conv_list_lock);
if (module_list[mid].refcount > 0) {
mutex_exit(&conv_list_lock);
return (EBUSY);
}
for (i = 0; i < KICONV_MAX_CONVERSIONS; i++) {
if (conv_list[i].mid == mid) {
conv_list[i].open = NULL;
conv_list[i].kiconv = NULL;
conv_list[i].close = NULL;
conv_list[i].kiconvstr = NULL;
}
}
mutex_exit(&conv_list_lock);
return (0);
}
static kiconv_t
check_and_load_conversions(const char *tocode, const char *fromcode)
{
kiconv_t kcd;
size_t tid;
size_t fid;
size_t mid;
size_t i;
tid = normalize_codename(tocode);
if (tid == (size_t)-1)
return ((kiconv_t)-1);
fid = normalize_codename(fromcode);
if (fid == (size_t)-1)
return ((kiconv_t)-1);
mutex_enter(&conv_list_lock);
for (i = 0; i < KICONV_MAX_CONVERSIONS; i++)
if (conv_list[i].tid == tid && conv_list[i].fid == fid)
break;
if (i >= KICONV_MAX_CONVERSIONS) {
mutex_exit(&conv_list_lock);
return ((kiconv_t)-1);
}
mid = conv_list[i].mid;
if (conv_list[i].open == NULL) {
mutex_exit(&conv_list_lock);
if (modload("kiconv", module_list[mid].name) < 0)
return ((kiconv_t)-1);
mutex_enter(&conv_list_lock);
if (conv_list[i].open == NULL) {
mutex_exit(&conv_list_lock);
return ((kiconv_t)-1);
}
}
if (module_list[mid].refcount < UINT_MAX)
module_list[mid].refcount++;
mutex_exit(&conv_list_lock);
kcd = (kiconv_t)kmem_alloc(sizeof (kiconv_data_t), KM_SLEEP);
kcd->handle = (void *)-1;
kcd->id = i;
return (kcd);
}
kiconv_t
kiconv_open(const char *tocode, const char *fromcode)
{
kiconv_t kcd;
size_t mid;
kcd = check_and_load_conversions(tocode, fromcode);
if (kcd == (kiconv_t)-1)
return ((kiconv_t)-1);
kcd->handle = (conv_list[kcd->id].open)();
if (kcd->handle == (void *)-1) {
mid = conv_list[kcd->id].mid;
mutex_enter(&conv_list_lock);
if (module_list[mid].refcount > 0)
module_list[mid].refcount--;
mutex_exit(&conv_list_lock);
kmem_free((void *)kcd, sizeof (kiconv_data_t));
return ((kiconv_t)-1);
}
return (kcd);
}
size_t
kiconv(kiconv_t kcd, char **inbuf, size_t *inbytesleft,
char **outbuf, size_t *outbytesleft, int *errno)
{
if (! kcd || kcd == (kiconv_t)-1 || conv_list[kcd->id].kiconv == NULL) {
*errno = EBADF;
return ((size_t)-1);
}
return ((conv_list[kcd->id].kiconv)(kcd->handle, inbuf, inbytesleft,
outbuf, outbytesleft, errno));
}
int
kiconv_close(kiconv_t kcd)
{
int ret;
size_t mid;
if (! kcd || kcd == (kiconv_t)-1 || conv_list[kcd->id].close == NULL)
return (EBADF);
mid = conv_list[kcd->id].mid;
ret = (conv_list[kcd->id].close)(kcd->handle);
kmem_free((void *)kcd, sizeof (kiconv_data_t));
mutex_enter(&conv_list_lock);
if (module_list[mid].refcount > 0)
module_list[mid].refcount--;
mutex_exit(&conv_list_lock);
return (ret);
}
size_t
kiconvstr(const char *tocode, const char *fromcode, char *inarray,
size_t *inlen, char *outarray, size_t *outlen, int flag, int *errno)
{
kiconv_t kcd;
size_t ret;
size_t mid;
kcd = check_and_load_conversions(tocode, fromcode);
if (kcd == (kiconv_t)-1 || conv_list[kcd->id].kiconvstr == NULL) {
*errno = EBADF;
return ((size_t)-1);
}
mid = conv_list[kcd->id].mid;
ret = (conv_list[kcd->id].kiconvstr)(inarray, inlen, outarray, outlen,
flag, errno);
kmem_free((void *)kcd, sizeof (kiconv_data_t));
mutex_enter(&conv_list_lock);
if (module_list[mid].refcount > 0)
module_list[mid].refcount--;
mutex_exit(&conv_list_lock);
return (ret);
}