115d9d0b5Syy /*
215d9d0b5Syy  * CDDL HEADER START
315d9d0b5Syy  *
415d9d0b5Syy  * The contents of this file are subject to the terms of the
515d9d0b5Syy  * Common Development and Distribution License (the "License").
615d9d0b5Syy  * You may not use this file except in compliance with the License.
715d9d0b5Syy  *
815d9d0b5Syy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
915d9d0b5Syy  * or http://www.opensolaris.org/os/licensing.
1015d9d0b5Syy  * See the License for the specific language governing permissions
1115d9d0b5Syy  * and limitations under the License.
1215d9d0b5Syy  *
1315d9d0b5Syy  * When distributing Covered Code, include this CDDL HEADER in each
1415d9d0b5Syy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1515d9d0b5Syy  * If applicable, add the following below this CDDL HEADER, with the
1615d9d0b5Syy  * fields enclosed by brackets "[]" replaced with your own identifying
1715d9d0b5Syy  * information: Portions Copyright [yyyy] [name of copyright owner]
1815d9d0b5Syy  *
1915d9d0b5Syy  * CDDL HEADER END
2015d9d0b5Syy  */
2115d9d0b5Syy 
2215d9d0b5Syy /*
2315d9d0b5Syy  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
2415d9d0b5Syy  * Use is subject to license terms.
2515d9d0b5Syy  */
2615d9d0b5Syy 
2715d9d0b5Syy #include <sys/types.h>
2815d9d0b5Syy #include <sys/param.h>
2915d9d0b5Syy #include <sys/sysmacros.h>
3015d9d0b5Syy #include <sys/systm.h>
3115d9d0b5Syy #include <sys/debug.h>
3215d9d0b5Syy #include <sys/kmem.h>
3315d9d0b5Syy #include <sys/sunddi.h>
3415d9d0b5Syy #include <sys/byteorder.h>
3515d9d0b5Syy #include <sys/errno.h>
3615d9d0b5Syy #include <sys/modctl.h>
3715d9d0b5Syy #include <sys/u8_textprep.h>
3815d9d0b5Syy #include <sys/kiconv.h>
3915d9d0b5Syy #include <sys/kiconv_cck_common.h>
4015d9d0b5Syy #include <sys/kiconv_ko.h>
4115d9d0b5Syy #include <sys/kiconv_uhc_utf8.h>
4215d9d0b5Syy #include <sys/kiconv_utf8_uhc.h>
4315d9d0b5Syy #include <sys/kiconv_euckr_utf8.h>
4415d9d0b5Syy #include <sys/kiconv_utf8_euckr.h>
4515d9d0b5Syy 
4615d9d0b5Syy static int8_t utf8_to_euckr(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
4715d9d0b5Syy 	uchar_t *ob, uchar_t *obtail, size_t *ret_val);
4815d9d0b5Syy static int8_t utf8_to_uhc(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
4915d9d0b5Syy 	uchar_t *ob, uchar_t *obtail, size_t *ret_val);
5015d9d0b5Syy static int8_t ko_to_utf8(uint32_t ko_val, uchar_t *ob, uchar_t *obtail,
5115d9d0b5Syy 	size_t *ret_val, kiconv_table_array_t *table, size_t nitems);
5215d9d0b5Syy 
5315d9d0b5Syy 
5415d9d0b5Syy #define	KICONV_KO_EUCKR		(0x01)
5515d9d0b5Syy #define	KICONV_KO_UHC		(0x02)
5615d9d0b5Syy #define	KICONV_KO_MAX_MAGIC_ID	(0x02)
5715d9d0b5Syy 
5815d9d0b5Syy static void *
open_fr_euckr()5915d9d0b5Syy open_fr_euckr()
6015d9d0b5Syy {
6115d9d0b5Syy 	return ((void *)KICONV_KO_EUCKR);
6215d9d0b5Syy }
6315d9d0b5Syy 
6415d9d0b5Syy static void *
open_fr_uhc()6515d9d0b5Syy open_fr_uhc()
6615d9d0b5Syy {
6715d9d0b5Syy 	return ((void *)KICONV_KO_UHC);
6815d9d0b5Syy }
6915d9d0b5Syy 
7015d9d0b5Syy static int
close_fr_ko(void * s)7115d9d0b5Syy close_fr_ko(void *s)
7215d9d0b5Syy {
7315d9d0b5Syy 	if ((uintptr_t)s > KICONV_KO_MAX_MAGIC_ID)
7415d9d0b5Syy 		return (EBADF);
7515d9d0b5Syy 
7615d9d0b5Syy 	return (0);
7715d9d0b5Syy }
7815d9d0b5Syy 
7915d9d0b5Syy /*
8015d9d0b5Syy  * Encoding convertor from EUC-KR to UTF-8.
8115d9d0b5Syy  */
8215d9d0b5Syy static size_t
kiconv_fr_euckr(void * kcd,char ** inbuf,size_t * inbufleft,char ** outbuf,size_t * outbufleft,int * errno)8315d9d0b5Syy kiconv_fr_euckr(void *kcd, char **inbuf, size_t *inbufleft,
84*86ef0a63SRichard Lowe     char **outbuf, size_t *outbufleft, int *errno)
8515d9d0b5Syy {
8615d9d0b5Syy 	uchar_t		*ib;
8715d9d0b5Syy 	uchar_t		*ob;
8815d9d0b5Syy 	uchar_t		*ibtail;
8915d9d0b5Syy 	uchar_t		*obtail;
9015d9d0b5Syy 	size_t		ret_val;
9115d9d0b5Syy 	int8_t		sz;
9215d9d0b5Syy 	uint32_t	euckr_val;
9315d9d0b5Syy 
9415d9d0b5Syy 	/* Check on the kiconv code conversion descriptor. */
9515d9d0b5Syy 	if (kcd == NULL || kcd == (void *)-1) {
9615d9d0b5Syy 		*errno = EBADF;
9715d9d0b5Syy 		return ((size_t)-1);
9815d9d0b5Syy 	}
9915d9d0b5Syy 
10015d9d0b5Syy 	/* If this is a state reset request, process and return. */
10115d9d0b5Syy 	if (inbuf == NULL || *inbuf == NULL) {
10215d9d0b5Syy 		return (0);
10315d9d0b5Syy 	}
10415d9d0b5Syy 
10515d9d0b5Syy 	ret_val = 0;
10615d9d0b5Syy 	ib = (uchar_t *)*inbuf;
10715d9d0b5Syy 	ob = (uchar_t *)*outbuf;
10815d9d0b5Syy 	ibtail = ib + *inbufleft;
10915d9d0b5Syy 	obtail = ob + *outbufleft;
11015d9d0b5Syy 
11115d9d0b5Syy 	while (ib < ibtail) {
11215d9d0b5Syy 		if (KICONV_IS_ASCII(*ib)) {
11315d9d0b5Syy 			if (ob >= obtail) {
11415d9d0b5Syy 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
11515d9d0b5Syy 			}
11615d9d0b5Syy 
11715d9d0b5Syy 			*ob++ = *ib++;
11815d9d0b5Syy 			continue;
11915d9d0b5Syy 		}
12015d9d0b5Syy 
12115d9d0b5Syy 		/*
12215d9d0b5Syy 		 * Issue EILSEQ error if the first byte is not a
12315d9d0b5Syy 		 * valid EUC-KR leading byte.
12415d9d0b5Syy 		 */
12515d9d0b5Syy 		if (! KICONV_KO_IS_EUCKR_BYTE(*ib)) {
12615d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
12715d9d0b5Syy 		}
12815d9d0b5Syy 
12915d9d0b5Syy 		/*
13015d9d0b5Syy 		 * Issue EINVAL error if input buffer has an incomplete
13115d9d0b5Syy 		 * character at the end of the buffer.
13215d9d0b5Syy 		 */
13315d9d0b5Syy 		if (ibtail - ib < 2) {
13415d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EINVAL);
13515d9d0b5Syy 		}
13615d9d0b5Syy 
13715d9d0b5Syy 		/*
13815d9d0b5Syy 		 * Issue EILSEQ error if the remaining byte is not
13915d9d0b5Syy 		 * a valid EUC-KR byte.
14015d9d0b5Syy 		 */
14115d9d0b5Syy 		if (! KICONV_KO_IS_EUCKR_BYTE(*(ib + 1))) {
14215d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
14315d9d0b5Syy 		}
14415d9d0b5Syy 
14515d9d0b5Syy 		euckr_val = (uint32_t)(*ib) << 8 | *(ib + 1);
14615d9d0b5Syy 		sz = ko_to_utf8(euckr_val, ob, obtail, &ret_val,
14715d9d0b5Syy 		    kiconv_euckr_utf8, KICONV_EUCKR_UTF8_MAX);
14815d9d0b5Syy 
14915d9d0b5Syy 		if (sz < 0) {
15015d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
15115d9d0b5Syy 		}
15215d9d0b5Syy 
15315d9d0b5Syy 		ib += 2;
15415d9d0b5Syy 		ob += sz;
15515d9d0b5Syy 	}
15615d9d0b5Syy 
15715d9d0b5Syy 	*inbuf = (char *)ib;
15815d9d0b5Syy 	*inbufleft = ibtail - ib;
15915d9d0b5Syy 	*outbuf = (char *)ob;
16015d9d0b5Syy 	*outbufleft = obtail - ob;
16115d9d0b5Syy 
16215d9d0b5Syy 	return (ret_val);
16315d9d0b5Syy }
16415d9d0b5Syy 
16515d9d0b5Syy /*
16615d9d0b5Syy  * String based encoding convertor from EUC-KR to UTF-8.
16715d9d0b5Syy  */
16815d9d0b5Syy static size_t
kiconvstr_fr_euckr(char * inarray,size_t * inlen,char * outarray,size_t * outlen,int flag,int * errno)16915d9d0b5Syy kiconvstr_fr_euckr(char *inarray, size_t *inlen, char *outarray,
170*86ef0a63SRichard Lowe     size_t *outlen, int flag, int *errno)
17115d9d0b5Syy {
17215d9d0b5Syy 	uchar_t		*ib;
17315d9d0b5Syy 	uchar_t		*ob;
17415d9d0b5Syy 	uchar_t		*ibtail;
17515d9d0b5Syy 	uchar_t		*obtail;
17615d9d0b5Syy 	uchar_t		*oldib;
17715d9d0b5Syy 	size_t		ret_val;
17815d9d0b5Syy 	int8_t		sz;
17915d9d0b5Syy 	uint32_t	euckr_val;
18015d9d0b5Syy 	boolean_t	do_not_ignore_null;
18115d9d0b5Syy 
18215d9d0b5Syy 	ret_val = 0;
18315d9d0b5Syy 	ib = (uchar_t *)inarray;
18415d9d0b5Syy 	ob = (uchar_t *)outarray;
18515d9d0b5Syy 	ibtail = ib + *inlen;
18615d9d0b5Syy 	obtail = ob + *outlen;
18715d9d0b5Syy 	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
18815d9d0b5Syy 
18915d9d0b5Syy 	while (ib < ibtail) {
19015d9d0b5Syy 		if (*ib == '\0' && do_not_ignore_null)
19115d9d0b5Syy 			break;
19215d9d0b5Syy 
19315d9d0b5Syy 		if (KICONV_IS_ASCII(*ib)) {
19415d9d0b5Syy 			if (ob >= obtail) {
19515d9d0b5Syy 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
19615d9d0b5Syy 			}
19715d9d0b5Syy 
19815d9d0b5Syy 			*ob++ = *ib++;
19915d9d0b5Syy 			continue;
20015d9d0b5Syy 		}
20115d9d0b5Syy 
20215d9d0b5Syy 		oldib = ib;
20315d9d0b5Syy 
20415d9d0b5Syy 		if (! KICONV_KO_IS_EUCKR_BYTE(*ib)) {
20515d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(1, EILSEQ);
20615d9d0b5Syy 		}
20715d9d0b5Syy 
20815d9d0b5Syy 		if (ibtail - ib < 2) {
20915d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(1, EINVAL);
21015d9d0b5Syy 		}
21115d9d0b5Syy 
21215d9d0b5Syy 		if (! KICONV_KO_IS_EUCKR_BYTE(*(ib + 1))) {
21315d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(2, EILSEQ);
21415d9d0b5Syy 		}
21515d9d0b5Syy 
21615d9d0b5Syy 		euckr_val = *ib++;
21715d9d0b5Syy 		euckr_val = (euckr_val << 8) | *ib++;
21815d9d0b5Syy 		sz = ko_to_utf8(euckr_val, ob, obtail, &ret_val,
21915d9d0b5Syy 		    kiconv_euckr_utf8, KICONV_EUCKR_UTF8_MAX);
22015d9d0b5Syy 
22115d9d0b5Syy 		if (sz < 0) {
22215d9d0b5Syy 			ib = oldib;
22315d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
22415d9d0b5Syy 		}
22515d9d0b5Syy 
22615d9d0b5Syy 		ob += sz;
22715d9d0b5Syy 		continue;
22815d9d0b5Syy 
22915d9d0b5Syy REPLACE_INVALID:
23015d9d0b5Syy 		if (obtail - ob < KICONV_UTF8_REPLACEMENT_CHAR_LEN) {
23115d9d0b5Syy 			ib = oldib;
23215d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
23315d9d0b5Syy 		}
23415d9d0b5Syy 
23515d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR1;
23615d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR2;
23715d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR3;
23815d9d0b5Syy 		ret_val++;
23915d9d0b5Syy 	}
24015d9d0b5Syy 
24115d9d0b5Syy 	*inlen = ibtail - ib;
24215d9d0b5Syy 	*outlen = obtail - ob;
24315d9d0b5Syy 
24415d9d0b5Syy 	return (ret_val);
24515d9d0b5Syy }
24615d9d0b5Syy 
24715d9d0b5Syy /*
24815d9d0b5Syy  * Encoding convertor from Unified Hangul Code to UTF-8.
24915d9d0b5Syy  */
25015d9d0b5Syy static size_t
kiconv_fr_uhc(void * kcd,char ** inbuf,size_t * inbufleft,char ** outbuf,size_t * outbufleft,int * errno)25115d9d0b5Syy kiconv_fr_uhc(void *kcd, char **inbuf, size_t *inbufleft,
252*86ef0a63SRichard Lowe     char **outbuf, size_t *outbufleft, int *errno)
25315d9d0b5Syy {
25415d9d0b5Syy 	uchar_t		*ib;
25515d9d0b5Syy 	uchar_t		*ob;
25615d9d0b5Syy 	uchar_t		*ibtail;
25715d9d0b5Syy 	uchar_t		*obtail;
25815d9d0b5Syy 	size_t		ret_val;
25915d9d0b5Syy 	int8_t		sz;
26015d9d0b5Syy 	uint32_t	uhc_val;
26115d9d0b5Syy 
26215d9d0b5Syy 	/* Check on the kiconv code conversion descriptor. */
26315d9d0b5Syy 	if (kcd == NULL || kcd == (void *)-1) {
26415d9d0b5Syy 		*errno = EBADF;
26515d9d0b5Syy 		return ((size_t)-1);
26615d9d0b5Syy 	}
26715d9d0b5Syy 
26815d9d0b5Syy 	/* If this is a state reset request, process and return. */
26915d9d0b5Syy 	if (inbuf == NULL || *inbuf == NULL) {
27015d9d0b5Syy 		return (0);
27115d9d0b5Syy 	}
27215d9d0b5Syy 
27315d9d0b5Syy 	ret_val = 0;
27415d9d0b5Syy 	ib = (uchar_t *)*inbuf;
27515d9d0b5Syy 	ob = (uchar_t *)*outbuf;
27615d9d0b5Syy 	ibtail = ib + *inbufleft;
27715d9d0b5Syy 	obtail = ob + *outbufleft;
27815d9d0b5Syy 
27915d9d0b5Syy 	while (ib < ibtail) {
28015d9d0b5Syy 		if (KICONV_IS_ASCII(*ib)) {
28115d9d0b5Syy 			if (ob >= obtail) {
28215d9d0b5Syy 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
28315d9d0b5Syy 			}
28415d9d0b5Syy 
28515d9d0b5Syy 			*ob++ = *ib++;
28615d9d0b5Syy 			continue;
28715d9d0b5Syy 		}
28815d9d0b5Syy 
28915d9d0b5Syy 		/*
29015d9d0b5Syy 		 * Issue EILSEQ error if the first byte is not a
29115d9d0b5Syy 		 * valid UHC leading byte.
29215d9d0b5Syy 		 */
29315d9d0b5Syy 		if (! KICONV_KO_IS_UHC_1st_BYTE(*ib)) {
29415d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
29515d9d0b5Syy 		}
29615d9d0b5Syy 
29715d9d0b5Syy 		/*
29815d9d0b5Syy 		 * Issue EINVAL error if input buffer has an incomplete
29915d9d0b5Syy 		 * character at the end of the buffer.
30015d9d0b5Syy 		 */
30115d9d0b5Syy 		if (ibtail - ib < 2) {
30215d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EINVAL);
30315d9d0b5Syy 		}
30415d9d0b5Syy 
30515d9d0b5Syy 		/*
30615d9d0b5Syy 		 * Issue EILSEQ error if the remaining byte is not
30715d9d0b5Syy 		 * a valid UHC byte.
30815d9d0b5Syy 		 */
30915d9d0b5Syy 		if (! KICONV_KO_IS_UHC_2nd_BYTE(*(ib + 1))) {
31015d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
31115d9d0b5Syy 		}
31215d9d0b5Syy 
31315d9d0b5Syy 		uhc_val = (uint32_t)(*ib) << 8 | *(ib + 1);
31415d9d0b5Syy 		sz = ko_to_utf8(uhc_val, ob, obtail, &ret_val,
31515d9d0b5Syy 		    kiconv_uhc_utf8, KICONV_UHC_UTF8_MAX);
31615d9d0b5Syy 
31715d9d0b5Syy 		if (sz < 0) {
31815d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
31915d9d0b5Syy 		}
32015d9d0b5Syy 
32115d9d0b5Syy 		ib += 2;
32215d9d0b5Syy 		ob += sz;
32315d9d0b5Syy 	}
32415d9d0b5Syy 
32515d9d0b5Syy 	*inbuf = (char *)ib;
32615d9d0b5Syy 	*inbufleft = ibtail - ib;
32715d9d0b5Syy 	*outbuf = (char *)ob;
32815d9d0b5Syy 	*outbufleft = obtail - ob;
32915d9d0b5Syy 
33015d9d0b5Syy 	return (ret_val);
33115d9d0b5Syy }
33215d9d0b5Syy 
33315d9d0b5Syy /*
33415d9d0b5Syy  * String based encoding convertor from Unified Hangul Code to UTF-8.
33515d9d0b5Syy  */
33615d9d0b5Syy static size_t
kiconvstr_fr_uhc(char * inarray,size_t * inlen,char * outarray,size_t * outlen,int flag,int * errno)33715d9d0b5Syy kiconvstr_fr_uhc(char *inarray, size_t *inlen, char *outarray,
338*86ef0a63SRichard Lowe     size_t *outlen, int flag, int *errno)
33915d9d0b5Syy {
34015d9d0b5Syy 	uchar_t		*ib;
34115d9d0b5Syy 	uchar_t		*ob;
34215d9d0b5Syy 	uchar_t		*ibtail;
34315d9d0b5Syy 	uchar_t		*obtail;
34415d9d0b5Syy 	uchar_t		*oldib;
34515d9d0b5Syy 	size_t		ret_val;
34615d9d0b5Syy 	int8_t		sz;
34715d9d0b5Syy 	uint32_t	uhc_val;
34815d9d0b5Syy 	boolean_t	do_not_ignore_null;
34915d9d0b5Syy 
35015d9d0b5Syy 	ret_val = 0;
35115d9d0b5Syy 	ib = (uchar_t *)inarray;
35215d9d0b5Syy 	ob = (uchar_t *)outarray;
35315d9d0b5Syy 	ibtail = ib + *inlen;
35415d9d0b5Syy 	obtail = ob + *outlen;
35515d9d0b5Syy 	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
35615d9d0b5Syy 
35715d9d0b5Syy 	while (ib < ibtail) {
35815d9d0b5Syy 		if (*ib == '\0' && do_not_ignore_null)
35915d9d0b5Syy 			break;
36015d9d0b5Syy 
36115d9d0b5Syy 		if (KICONV_IS_ASCII(*ib)) {
36215d9d0b5Syy 			if (ob >= obtail) {
36315d9d0b5Syy 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
36415d9d0b5Syy 			}
36515d9d0b5Syy 
36615d9d0b5Syy 			*ob++ = *ib++;
36715d9d0b5Syy 			continue;
36815d9d0b5Syy 		}
36915d9d0b5Syy 
37015d9d0b5Syy 		oldib = ib;
37115d9d0b5Syy 
37215d9d0b5Syy 		if (! KICONV_KO_IS_UHC_1st_BYTE(*ib)) {
37315d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(1, EILSEQ);
37415d9d0b5Syy 		}
37515d9d0b5Syy 
37615d9d0b5Syy 		if (ibtail - ib < 2) {
37715d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(1, EINVAL);
37815d9d0b5Syy 		}
37915d9d0b5Syy 
38015d9d0b5Syy 		if (! KICONV_KO_IS_UHC_2nd_BYTE(*(ib + 1))) {
38115d9d0b5Syy 			KICONV_SET_ERRNO_WITH_FLAG(2, EILSEQ);
38215d9d0b5Syy 		}
38315d9d0b5Syy 
38415d9d0b5Syy 		uhc_val = *ib++;
38515d9d0b5Syy 		uhc_val = (uhc_val << 8) | *ib++;
38615d9d0b5Syy 		sz = ko_to_utf8(uhc_val, ob, obtail, &ret_val,
38715d9d0b5Syy 		    kiconv_uhc_utf8, KICONV_UHC_UTF8_MAX);
38815d9d0b5Syy 
38915d9d0b5Syy 		if (sz < 0) {
39015d9d0b5Syy 			ib = oldib;
39115d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
39215d9d0b5Syy 		}
39315d9d0b5Syy 
39415d9d0b5Syy 		ob += sz;
39515d9d0b5Syy 		continue;
39615d9d0b5Syy 
39715d9d0b5Syy REPLACE_INVALID:
39815d9d0b5Syy 		if (obtail - ob < KICONV_UTF8_REPLACEMENT_CHAR_LEN) {
39915d9d0b5Syy 			ib = oldib;
40015d9d0b5Syy 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
40115d9d0b5Syy 		}
40215d9d0b5Syy 
40315d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR1;
40415d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR2;
40515d9d0b5Syy 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR3;
40615d9d0b5Syy 		ret_val++;
40715d9d0b5Syy 	}
40815d9d0b5Syy 
40915d9d0b5Syy 	*inlen = ibtail - ib;
41015d9d0b5Syy 	*outlen = obtail - ob;
41115d9d0b5Syy 
41215d9d0b5Syy 	return (ret_val);
41315d9d0b5Syy }
41415d9d0b5Syy 
41515d9d0b5Syy /*
41615d9d0b5Syy  * Encoding convertor from UTF-8 to EUC-KR.
41715d9d0b5Syy  */
41815d9d0b5Syy static size_t
kiconv_to_euckr(void * kcd,char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft,int * errno)41915d9d0b5Syy kiconv_to_euckr(void *kcd, char **inbuf, size_t *inbytesleft,
420*86ef0a63SRichard Lowe     char **outbuf, size_t *outbytesleft, int *errno)
42115d9d0b5Syy {
42215d9d0b5Syy 	return (kiconv_utf8_to_cck(kcd, inbuf, inbytesleft, outbuf,
42315d9d0b5Syy 	    outbytesleft, errno, utf8_to_euckr));
42415d9d0b5Syy }
42515d9d0b5Syy 
42615d9d0b5Syy /*
42715d9d0b5Syy  * Encoding convertor from UTF-8 to Unified Hangul Code.
42815d9d0b5Syy  */
42915d9d0b5Syy static size_t
kiconv_to_uhc(void * kcd,char ** inbuf,size_t * inbytesleft,char ** outbuf,size_t * outbytesleft,int * errno)43015d9d0b5Syy kiconv_to_uhc(void *kcd, char **inbuf, size_t *inbytesleft,
431*86ef0a63SRichard Lowe     char **outbuf, size_t *outbytesleft, int *errno)
43215d9d0b5Syy {
43315d9d0b5Syy 	return (kiconv_utf8_to_cck(kcd, inbuf, inbytesleft, outbuf,
43415d9d0b5Syy 	    outbytesleft, errno, utf8_to_uhc));
43515d9d0b5Syy }
43615d9d0b5Syy 
43715d9d0b5Syy /*
43815d9d0b5Syy  * String based encoding convertor from UTF-8 to EUC-KR.
43915d9d0b5Syy  */
44015d9d0b5Syy static size_t
kiconvstr_to_euckr(char * inarray,size_t * inlen,char * outarray,size_t * outlen,int flag,int * errno)44115d9d0b5Syy kiconvstr_to_euckr(char *inarray, size_t *inlen, char *outarray,
442*86ef0a63SRichard Lowe     size_t *outlen, int flag, int *errno)
44315d9d0b5Syy {
44415d9d0b5Syy 	return kiconvstr_utf8_to_cck((uchar_t *)inarray, inlen,
44515d9d0b5Syy 	    (uchar_t *)outarray, outlen, flag, errno, utf8_to_euckr);
44615d9d0b5Syy }
44715d9d0b5Syy 
44815d9d0b5Syy /*
44915d9d0b5Syy  * String based encoding convertor from UTF-8 to Unified Hangul Code.
45015d9d0b5Syy  */
45115d9d0b5Syy static size_t
kiconvstr_to_uhc(char * inarray,size_t * inlen,char * outarray,size_t * outlen,int flag,int * errno)45215d9d0b5Syy kiconvstr_to_uhc(char *inarray, size_t *inlen, char *outarray,
453*86ef0a63SRichard Lowe     size_t *outlen, int flag, int *errno)
45415d9d0b5Syy {
45515d9d0b5Syy 	return kiconvstr_utf8_to_cck((uchar_t *)inarray, inlen,
45615d9d0b5Syy 	    (uchar_t *)outarray, outlen, flag, errno, utf8_to_uhc);
45715d9d0b5Syy }
45815d9d0b5Syy 
45915d9d0b5Syy /*
46015d9d0b5Syy  * Convert an UTF-8 character to a character of ko encodings
46115d9d0b5Syy  * (EUC-KR or UHC).
46215d9d0b5Syy  */
46315d9d0b5Syy static int8_t
utf8_to_ko(uint32_t utf8,uchar_t * ob,uchar_t * obtail,size_t * ret_val,kiconv_table_t * table,size_t nitems)46415d9d0b5Syy utf8_to_ko(uint32_t utf8, uchar_t *ob, uchar_t *obtail, size_t *ret_val,
465*86ef0a63SRichard Lowe     kiconv_table_t *table, size_t nitems)
46615d9d0b5Syy {
46715d9d0b5Syy 	size_t	index;
46815d9d0b5Syy 	size_t	kocode;
46915d9d0b5Syy 	int8_t  kolen;
47015d9d0b5Syy 
47115d9d0b5Syy 	if (KICONV_KO_IS_UDC_IN_UTF8(utf8)) {
47215d9d0b5Syy 		/* User Definable Area handing. */
47315d9d0b5Syy 		kocode = (((utf8 & 0xF0000) >> 4) | ((utf8 & 0x3F00) >> 2) |
47415d9d0b5Syy 		    (utf8 & 0x3F)) - KICONV_KO_UDA_UCS4_START;
47515d9d0b5Syy 		if (kocode < KICONV_KO_UDA_RANGE) {
47615d9d0b5Syy 			kocode = (KICONV_KO_UDA_EUC_SEG1 << 8) |
47715d9d0b5Syy 			    (kocode + KICONV_KO_UDA_OFFSET_START);
47815d9d0b5Syy 		} else {
47915d9d0b5Syy 			/* 0x43 = 0xA1 - 0x5E */
48015d9d0b5Syy 			kocode = (KICONV_KO_UDA_EUC_SEG2 << 8) |
48115d9d0b5Syy 			    (kocode + 0x43);
48215d9d0b5Syy 		}
48315d9d0b5Syy 
48415d9d0b5Syy 		index = 1;
48515d9d0b5Syy 	} else {
48615d9d0b5Syy 		index = kiconv_binsearch(utf8, table, nitems);
48715d9d0b5Syy 		kocode = table[index].value;
48815d9d0b5Syy 	}
48915d9d0b5Syy 
49015d9d0b5Syy 	kolen = (kocode <= 0xFF) ? 1 : 2;
49115d9d0b5Syy 
49215d9d0b5Syy 	if (obtail - ob < kolen) {
49315d9d0b5Syy 		*ret_val = (size_t)-1;
49415d9d0b5Syy 		return (-1);
49515d9d0b5Syy 	}
49615d9d0b5Syy 
49715d9d0b5Syy 	if (index == 0)
49815d9d0b5Syy 		(*ret_val)++;
49915d9d0b5Syy 
50015d9d0b5Syy 	if (kolen > 1)
50115d9d0b5Syy 		*ob++ = (uchar_t)(kocode >> 8);
50215d9d0b5Syy 	*ob = (uchar_t)(kocode & 0xFF);
50315d9d0b5Syy 
50415d9d0b5Syy 	return (kolen);
50515d9d0b5Syy }
50615d9d0b5Syy 
50715d9d0b5Syy /*
50815d9d0b5Syy  * Convert an UTF-8 character to Unified Hangual Code.
50915d9d0b5Syy  */
51015d9d0b5Syy /* ARGSUSED */
51115d9d0b5Syy static int8_t
utf8_to_uhc(uint32_t utf8,uchar_t ** inbuf,uchar_t * ibtail,uchar_t * ob,uchar_t * obtail,size_t * ret_val)51215d9d0b5Syy utf8_to_uhc(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
513*86ef0a63SRichard Lowe     uchar_t *ob, uchar_t *obtail, size_t *ret_val)
51415d9d0b5Syy {
51515d9d0b5Syy 	return (utf8_to_ko(utf8, ob, obtail, ret_val, kiconv_utf8_uhc,
51615d9d0b5Syy 	    KICONV_UTF8_UHC_MAX));
51715d9d0b5Syy }
51815d9d0b5Syy 
51915d9d0b5Syy /*
52015d9d0b5Syy  * Convert an UTF-8 character to EUC-KR.
52115d9d0b5Syy  */
52215d9d0b5Syy /* ARGSUSED */
52315d9d0b5Syy static int8_t
utf8_to_euckr(uint32_t utf8,uchar_t ** inbuf,uchar_t * ibtail,uchar_t * ob,uchar_t * obtail,size_t * ret_val)52415d9d0b5Syy utf8_to_euckr(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
525*86ef0a63SRichard Lowe     uchar_t *ob, uchar_t *obtail, size_t *ret_val)
52615d9d0b5Syy {
52715d9d0b5Syy 	return (utf8_to_ko(utf8, ob, obtail, ret_val, kiconv_utf8_euckr,
52815d9d0b5Syy 	    KICONV_UTF8_EUCKR_MAX));
52915d9d0b5Syy }
53015d9d0b5Syy 
53115d9d0b5Syy /*
53215d9d0b5Syy  * Convert a single ko encoding (EUC-KR or UHC) character to UTF-8.
53315d9d0b5Syy  */
53415d9d0b5Syy static int8_t
ko_to_utf8(uint32_t ko_val,uchar_t * ob,uchar_t * obtail,size_t * ret_val,kiconv_table_array_t * table,size_t nitems)53515d9d0b5Syy ko_to_utf8(uint32_t ko_val, uchar_t *ob, uchar_t *obtail, size_t *ret_val,
536*86ef0a63SRichard Lowe     kiconv_table_array_t *table, size_t nitems)
53715d9d0b5Syy {
53815d9d0b5Syy 	size_t	index;
53915d9d0b5Syy 	int8_t	sz;
54015d9d0b5Syy 	uchar_t	udc[3];
54115d9d0b5Syy 	uchar_t	*u8;
54215d9d0b5Syy 
54315d9d0b5Syy 	if (KICONV_KO_IS_UDC_IN_EUC(ko_val)) {
54415d9d0b5Syy 		/* UDA(User Definable Area) handling. */
54515d9d0b5Syy 		uint32_t u32;
54615d9d0b5Syy 
54715d9d0b5Syy 		u32 = (ko_val & 0xFF) + (((ko_val & 0xFF00) == 0xC900) ?
54815d9d0b5Syy 		    KICONV_KO_UDA_OFFSET_1 : KICONV_KO_UDA_OFFSET_2);
54915d9d0b5Syy 		udc[0] = 0xEF;
55015d9d0b5Syy 		udc[1] = (uchar_t)(0x80 | (u32 & 0x00000FC0) >> 6);
55115d9d0b5Syy 		udc[2] = (uchar_t)(0x80 | (u32 & 0x0000003F));
55215d9d0b5Syy 		u8 = udc;
55315d9d0b5Syy 		index = 1;
55415d9d0b5Syy 	} else {
55515d9d0b5Syy 		index = kiconv_binsearch(ko_val, table, nitems);
55615d9d0b5Syy 		u8 = table[index].u8;
55715d9d0b5Syy 	}
55815d9d0b5Syy 
55915d9d0b5Syy 	sz = u8_number_of_bytes[u8[0]];
56015d9d0b5Syy 
56115d9d0b5Syy 	if (obtail - ob < sz) {
56215d9d0b5Syy 		*ret_val = (size_t)-1;
56315d9d0b5Syy 		return (-1);
56415d9d0b5Syy 	}
56515d9d0b5Syy 
56615d9d0b5Syy 	if (index == 0)
56715d9d0b5Syy 		(*ret_val)++;	/* Non-identical conversion */
56815d9d0b5Syy 
56915d9d0b5Syy 	for (index = 0; index < sz; index++)
57015d9d0b5Syy 		*ob++ = u8[index];
57115d9d0b5Syy 
57215d9d0b5Syy 	return (sz);
57315d9d0b5Syy }
57415d9d0b5Syy 
57515d9d0b5Syy static kiconv_ops_t kiconv_ko_ops_tbl[] = {
57615d9d0b5Syy 	{
57715d9d0b5Syy 		"euc-kr", "utf-8", kiconv_open_to_cck, kiconv_to_euckr,
57815d9d0b5Syy 		kiconv_close_to_cck, kiconvstr_to_euckr
57915d9d0b5Syy 	},
58015d9d0b5Syy 	{
58115d9d0b5Syy 		"utf-8", "euc-kr", open_fr_euckr, kiconv_fr_euckr,
58215d9d0b5Syy 		close_fr_ko, kiconvstr_fr_euckr
58315d9d0b5Syy 	},
58415d9d0b5Syy 	{
58515d9d0b5Syy 		"unifiedhangul", "utf-8", kiconv_open_to_cck, kiconv_to_uhc,
58615d9d0b5Syy 		kiconv_close_to_cck, kiconvstr_to_uhc
58715d9d0b5Syy 	},
58815d9d0b5Syy 	{
58915d9d0b5Syy 		"utf-8", "unifiedhangul", open_fr_uhc, kiconv_fr_uhc,
59015d9d0b5Syy 		close_fr_ko, kiconvstr_fr_uhc
59115d9d0b5Syy 	}
59215d9d0b5Syy };
59315d9d0b5Syy 
59415d9d0b5Syy static kiconv_module_info_t kiconv_ko_info = {
59515d9d0b5Syy 	"kiconv_ko",		/* module name */
59615d9d0b5Syy 	sizeof (kiconv_ko_ops_tbl) / sizeof (kiconv_ko_ops_tbl[0]),
59715d9d0b5Syy 	kiconv_ko_ops_tbl,
59815d9d0b5Syy 	0,
59915d9d0b5Syy 	NULL,
60015d9d0b5Syy 	NULL,
60115d9d0b5Syy 	0
60215d9d0b5Syy };
60315d9d0b5Syy 
60415d9d0b5Syy static struct modlkiconv modlkiconv_ko = {
60515d9d0b5Syy 	&mod_kiconvops,
60615d9d0b5Syy 	"kiconv korean module 1.0",
60715d9d0b5Syy 	&kiconv_ko_info
60815d9d0b5Syy };
60915d9d0b5Syy 
61015d9d0b5Syy static struct modlinkage modlinkage = {
61115d9d0b5Syy 	MODREV_1,
61215d9d0b5Syy 	(void *)&modlkiconv_ko,
61315d9d0b5Syy 	NULL
61415d9d0b5Syy };
61515d9d0b5Syy 
61615d9d0b5Syy int
_init(void)61715d9d0b5Syy _init(void)
61815d9d0b5Syy {
61915d9d0b5Syy 	int err;
62015d9d0b5Syy 
62115d9d0b5Syy 	err = mod_install(&modlinkage);
62215d9d0b5Syy 	if (err)
62315d9d0b5Syy 		cmn_err(CE_WARN, "kiconv_ko: failed to load kernel module");
62415d9d0b5Syy 
62515d9d0b5Syy 	return (err);
62615d9d0b5Syy }
62715d9d0b5Syy 
62815d9d0b5Syy int
_fini(void)62915d9d0b5Syy _fini(void)
63015d9d0b5Syy {
63115d9d0b5Syy 	int err;
63215d9d0b5Syy 
63315d9d0b5Syy 	/*
63415d9d0b5Syy 	 * If this module is being used, then, we cannot remove the module.
63515d9d0b5Syy 	 * The following checking will catch pretty much all usual cases.
63615d9d0b5Syy 	 *
63715d9d0b5Syy 	 * Any remaining will be catached by the kiconv_unregister_module()
63815d9d0b5Syy 	 * during mod_remove() at below.
63915d9d0b5Syy 	 */
64015d9d0b5Syy 	if (kiconv_module_ref_count(KICONV_MODULE_ID_KO))
64115d9d0b5Syy 		return (EBUSY);
64215d9d0b5Syy 
64315d9d0b5Syy 	err = mod_remove(&modlinkage);
64415d9d0b5Syy 	if (err)
64515d9d0b5Syy 		cmn_err(CE_WARN, "kiconv_ko: failed to remove kernel module");
64615d9d0b5Syy 
64715d9d0b5Syy 	return (err);
64815d9d0b5Syy }
64915d9d0b5Syy 
65015d9d0b5Syy int
_info(struct modinfo * modinfop)65115d9d0b5Syy _info(struct modinfo *modinfop)
65215d9d0b5Syy {
65315d9d0b5Syy 	return (mod_info(&modlinkage, modinfop));
65415d9d0b5Syy }
655