1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/sysmacros.h>
31 #include <sys/systm.h>
32 #include <sys/debug.h>
33 #include <sys/kmem.h>
34 #include <sys/sunddi.h>
35 #include <sys/byteorder.h>
36 #include <sys/errno.h>
37 #include <sys/modctl.h>
38 #include <sys/kiconv.h>
39 #include <sys/u8_textprep.h>
40 #include <sys/kiconv_cck_common.h>
41 #include <sys/kiconv_sc.h>
42 #include <sys/kiconv_gb18030_utf8.h>
43 #include <sys/kiconv_gb2312_utf8.h>
44 #include <sys/kiconv_utf8_gb18030.h>
45 #include <sys/kiconv_utf8_gb2312.h>
46 
47 static int8_t gb2312_to_utf8(uchar_t byte1, uchar_t byte2, uchar_t *ob,
48 	uchar_t *obtail, size_t *ret_val);
49 static int8_t gbk_to_utf8(uint32_t gbk_val, uchar_t *ob, uchar_t *obtail,
50 	size_t *ret_val, boolean_t isgbk4);
51 static int8_t utf8_to_gb2312(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
52 	uchar_t *ob, uchar_t *obtail, size_t *ret);
53 static int8_t utf8_to_gbk(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
54 	uchar_t *ob, uchar_t *obtail, size_t *ret);
55 static int8_t utf8_to_gb18030(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
56 	uchar_t *ob, uchar_t *obtail, size_t *ret);
57 
58 #define	KICONV_SC_GB18030		(0x01)
59 #define	KICONV_SC_GBK			(0x02)
60 #define	KICONV_SC_EUCCN			(0x03)
61 #define	KICONV_SC_MAX_MAGIC_ID		(0x03)
62 
63 static void *
64 open_fr_gb18030()
65 {
66 	return ((void *)KICONV_SC_GB18030);
67 }
68 
69 static void *
70 open_fr_gbk()
71 {
72 	return ((void *)KICONV_SC_GBK);
73 }
74 
75 static void *
76 open_fr_euccn()
77 {
78 	return ((void *)KICONV_SC_EUCCN);
79 }
80 
81 static int
82 close_fr_sc(void *s)
83 {
84 	if ((uintptr_t)s > KICONV_SC_MAX_MAGIC_ID)
85 		return (EBADF);
86 
87 	return (0);
88 }
89 
90 /*
91  * Encoding convertor from UTF-8 to GB18030.
92  */
93 size_t
94 kiconv_to_gb18030(void *kcd, char **inbuf, size_t *inbytesleft,
95 	char **outbuf, size_t *outbytesleft, int *errno)
96 {
97 
98 	return kiconv_utf8_to_cck(kcd, inbuf, inbytesleft, outbuf,
99 	    outbytesleft, errno, utf8_to_gb18030);
100 }
101 
102 /*
103  * String based encoding convertor from UTF-8 to GB18030.
104  */
105 size_t
106 kiconvstr_to_gb18030(char *inarray, size_t *inlen, char *outarray,
107 	size_t *outlen, int flag, int *errno)
108 {
109 	return kiconvstr_utf8_to_cck((uchar_t *)inarray, inlen,
110 	    (uchar_t *)outarray, outlen, flag, errno, utf8_to_gb18030);
111 }
112 
113 /*
114  * Encoding convertor from GB18030 to UTF-8.
115  */
116 size_t
117 kiconv_fr_gb18030(void *kcd, char **inbuf, size_t *inbytesleft,
118 	char **outbuf, size_t *outbytesleft, int *errno)
119 {
120 	uchar_t		*ib;
121 	uchar_t		*ob;
122 	uchar_t		*ibtail;
123 	uchar_t		*obtail;
124 	size_t		ret_val;
125 	int8_t		sz;
126 	uint32_t	gb_val;
127 	boolean_t	isgbk4;
128 
129 	/* Check on the kiconv code conversion descriptor. */
130 	if (kcd == NULL || kcd == (void *)-1) {
131 		*errno = EBADF;
132 		return ((size_t)-1);
133 	}
134 
135 	/* If this is a state reset request, process and return. */
136 	if (inbuf == NULL || *inbuf == NULL) {
137 		return (0);
138 	}
139 
140 	ret_val = 0;
141 	ib = (uchar_t *)*inbuf;
142 	ob = (uchar_t *)*outbuf;
143 	ibtail = ib + *inbytesleft;
144 	obtail = ob + *outbytesleft;
145 
146 	while (ib < ibtail) {
147 		if (KICONV_IS_ASCII(*ib)) {
148 			if (ob >= obtail) {
149 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
150 			}
151 
152 			*ob++ = *ib++;
153 			continue;
154 		}
155 
156 		/*
157 		 * Issue EILSEQ error if the first byte is not a
158 		 * valid GB18030 leading byte.
159 		 */
160 		if (! KICONV_SC_IS_GBK_1st_BYTE(*ib)) {
161 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
162 		}
163 
164 		isgbk4 = (ibtail - ib < 2) ? B_FALSE :
165 		    KICONV_SC_IS_GB18030_2nd_BYTE(*(ib + 1));
166 
167 		if (isgbk4) {
168 			if (ibtail - ib < 4) {
169 				KICONV_SET_ERRNO_AND_BREAK(EINVAL);
170 			}
171 
172 			if (! (KICONV_SC_IS_GB18030_2nd_BYTE(*(ib + 1)) &&
173 			    KICONV_SC_IS_GB18030_3rd_BYTE(*(ib + 2)) &&
174 			    KICONV_SC_IS_GB18030_4th_BYTE(*(ib + 3)))) {
175 				KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
176 			}
177 
178 			gb_val = (uint32_t)(*ib) << 24 |
179 			    (uint32_t)(*(ib + 1)) << 16 |
180 			    (uint32_t)(*(ib + 2)) << 8 | *(ib + 3);
181 		} else {
182 			if (ibtail - ib < 2) {
183 				KICONV_SET_ERRNO_AND_BREAK(EINVAL);
184 			}
185 
186 			if (! KICONV_SC_IS_GBK_2nd_BYTE(*(ib + 1))) {
187 				KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
188 			}
189 
190 			gb_val = (uint32_t)(*ib) << 8 | *(ib + 1);
191 		}
192 
193 		sz = gbk_to_utf8(gb_val, ob, obtail, &ret_val, isgbk4);
194 		if (sz < 0) {
195 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
196 		}
197 
198 		ib += isgbk4 ? 4 : 2;
199 		ob += sz;
200 	}
201 
202 	*inbuf = (char *)ib;
203 	*inbytesleft = ibtail - ib;
204 	*outbuf = (char *)ob;
205 	*outbytesleft = obtail - ob;
206 
207 	return (ret_val);
208 }
209 
210 /*
211  * String based encoding convertor from GB18030 to UTF-8.
212  */
213 size_t
214 kiconvstr_fr_gb18030(char *inarray, size_t *inlen, char *outarray,
215 	size_t *outlen, int flag, int *errno)
216 {
217 	uchar_t		*ib;
218 	uchar_t		*ob;
219 	uchar_t		*ibtail;
220 	uchar_t		*obtail;
221 	uchar_t		*oldib;
222 	size_t		ret_val;
223 	int8_t		sz;
224 	uint32_t	gb_val;
225 	boolean_t	isgbk4;
226 	boolean_t	do_not_ignore_null;
227 
228 	ret_val = 0;
229 	ib = (uchar_t *)inarray;
230 	ob = (uchar_t *)outarray;
231 	ibtail = ib + *inlen;
232 	obtail = ob + *outlen;
233 	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
234 
235 	while (ib < ibtail) {
236 		if (*ib == '\0' && do_not_ignore_null)
237 			break;
238 
239 		if (KICONV_IS_ASCII(*ib)) {
240 			if (ob >= obtail) {
241 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
242 			}
243 
244 			*ob++ = *ib++;
245 			continue;
246 		}
247 
248 		oldib = ib;
249 
250 		if (! KICONV_SC_IS_GBK_1st_BYTE(*ib)) {
251 			KICONV_SET_ERRNO_WITH_FLAG(1, EILSEQ);
252 		}
253 
254 		isgbk4 = (ibtail - ib < 2) ? B_FALSE :
255 		    KICONV_SC_IS_GB18030_2nd_BYTE(*(ib + 1));
256 
257 		if (isgbk4) {
258 			if (ibtail - ib < 4) {
259 				if (flag & KICONV_REPLACE_INVALID) {
260 					ib = ibtail;
261 					goto REPLACE_INVALID;
262 				}
263 
264 				KICONV_SET_ERRNO_AND_BREAK(EINVAL);
265 			}
266 
267 			if (! (KICONV_SC_IS_GB18030_2nd_BYTE(*(ib + 1)) &&
268 			    KICONV_SC_IS_GB18030_3rd_BYTE(*(ib + 2)) &&
269 			    KICONV_SC_IS_GB18030_4th_BYTE(*(ib + 3)))) {
270 				KICONV_SET_ERRNO_WITH_FLAG(4, EILSEQ);
271 			}
272 
273 			gb_val = (uint32_t)(*ib) << 24 |
274 			    (uint32_t)(*(ib + 1)) << 16 |
275 			    (uint32_t)(*(ib + 2)) << 8 | *(ib + 3);
276 		} else {
277 			if (ibtail - ib < 2) {
278 				if (flag & KICONV_REPLACE_INVALID) {
279 					ib = ibtail;
280 					goto REPLACE_INVALID;
281 				}
282 
283 				KICONV_SET_ERRNO_AND_BREAK(EINVAL);
284 			}
285 
286 			if (! KICONV_SC_IS_GBK_2nd_BYTE(*(ib + 1))) {
287 				KICONV_SET_ERRNO_WITH_FLAG(2, EILSEQ);
288 			}
289 
290 			gb_val = (uint32_t)(*ib) << 8 | *(ib + 1);
291 		}
292 
293 		sz = gbk_to_utf8(gb_val, ob, obtail, &ret_val, isgbk4);
294 		if (sz < 0) {
295 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
296 		}
297 
298 		ib += isgbk4 ? 4 : 2;
299 		ob += sz;
300 		continue;
301 
302 REPLACE_INVALID:
303 		if (obtail - ob < KICONV_UTF8_REPLACEMENT_CHAR_LEN) {
304 			ib = oldib;
305 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
306 		}
307 
308 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR1;
309 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR2;
310 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR3;
311 		ret_val++;
312 	}
313 
314 	*inlen = ibtail - ib;
315 	*outlen = obtail - ob;
316 
317 	return (ret_val);
318 }
319 
320 /*
321  * Encoding convertor from UTF-8 to GBK.
322  */
323 size_t
324 kiconv_to_gbk(void *kcd, char **inbuf, size_t *inbytesleft,
325 	char **outbuf, size_t *outbytesleft, int *errno)
326 {
327 
328 	return kiconv_utf8_to_cck(kcd, inbuf, inbytesleft, outbuf,
329 	    outbytesleft, errno, utf8_to_gbk);
330 }
331 
332 /*
333  * String based encoding convertor from UTF-8 to GBK.
334  */
335 size_t
336 kiconvstr_to_gbk(char *inarray, size_t *inlen, char *outarray,
337 	size_t *outlen, int flag, int *errno)
338 {
339 	return kiconvstr_utf8_to_cck((uchar_t *)inarray, inlen,
340 	    (uchar_t *)outarray, outlen, flag, errno, utf8_to_gbk);
341 }
342 
343 /*
344  * Encoding convertor from GBK to UTF-8.
345  */
346 size_t
347 kiconv_fr_gbk(void *kcd, char **inbuf, size_t *inbytesleft,
348 	char **outbuf, size_t *outbytesleft, int *errno)
349 {
350 	uchar_t		*ib;
351 	uchar_t		*ob;
352 	uchar_t		*ibtail;
353 	uchar_t		*obtail;
354 	size_t		ret_val;
355 	int8_t		sz;
356 	uint32_t	gb_val;
357 
358 	/* Check on the kiconv code conversion descriptor. */
359 	if (kcd == NULL || kcd == (void *)-1) {
360 		*errno = EBADF;
361 		return ((size_t)-1);
362 	}
363 
364 	/* If this is a state reset request, process and return. */
365 	if (inbuf == NULL || *inbuf == NULL) {
366 		return (0);
367 	}
368 
369 	ret_val = 0;
370 	ib = (uchar_t *)*inbuf;
371 	ob = (uchar_t *)*outbuf;
372 	ibtail = ib + *inbytesleft;
373 	obtail = ob + *outbytesleft;
374 
375 	while (ib < ibtail) {
376 		if (KICONV_IS_ASCII(*ib)) {
377 			if (ob >= obtail) {
378 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
379 			}
380 
381 			*ob++ = *ib++;
382 			continue;
383 		}
384 
385 		/*
386 		 * Issue EILSEQ error if the first byte is not a
387 		 * valid GBK leading byte.
388 		 */
389 		if (! KICONV_SC_IS_GBK_1st_BYTE(*ib)) {
390 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
391 		}
392 
393 		/*
394 		 * Issue EINVAL error if input buffer has an incomplete
395 		 * character at the end of the buffer.
396 		 */
397 		if (ibtail - ib < 2) {
398 			KICONV_SET_ERRNO_AND_BREAK(EINVAL);
399 		}
400 
401 		/*
402 		 * Issue EILSEQ error if the remaining byte is not
403 		 * a valid GBK byte.
404 		 */
405 		if (! KICONV_SC_IS_GBK_2nd_BYTE(*(ib + 1))) {
406 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
407 		}
408 
409 		/* Now we have a valid GBK character. */
410 		gb_val = (uint32_t)(*ib) << 8 | *(ib + 1);
411 		sz = gbk_to_utf8(gb_val, ob, obtail, &ret_val, B_FALSE);
412 
413 		if (sz < 0) {
414 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
415 		}
416 
417 		ib += 2;
418 		ob += sz;
419 	}
420 
421 	*inbuf = (char *)ib;
422 	*inbytesleft = ibtail - ib;
423 	*outbuf = (char *)ob;
424 	*outbytesleft = obtail - ob;
425 
426 	return (ret_val);
427 }
428 
429 /*
430  * String based encoding convertor from GBK to UTF-8.
431  */
432 size_t
433 kiconvstr_fr_gbk(char *inarray, size_t *inlen, char *outarray,
434 	size_t *outlen, int flag, int *errno)
435 {
436 	uchar_t		*ib;
437 	uchar_t		*ob;
438 	uchar_t		*ibtail;
439 	uchar_t		*obtail;
440 	uchar_t		*oldib;
441 	size_t		ret_val;
442 	int8_t		sz;
443 	uint32_t	gb_val;
444 	boolean_t	do_not_ignore_null;
445 
446 	ret_val = 0;
447 	ib = (uchar_t *)inarray;
448 	ob = (uchar_t *)outarray;
449 	ibtail = ib + *inlen;
450 	obtail = ob + *outlen;
451 	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
452 
453 	while (ib < ibtail) {
454 		if (*ib == '\0' && do_not_ignore_null)
455 			break;
456 
457 		if (KICONV_IS_ASCII(*ib)) {
458 			if (ob >= obtail) {
459 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
460 			}
461 
462 			*ob++ = *ib++;
463 			continue;
464 		}
465 
466 		oldib = ib;
467 
468 		if (! KICONV_SC_IS_GBK_1st_BYTE(*ib)) {
469 			KICONV_SET_ERRNO_WITH_FLAG(1, EILSEQ);
470 		}
471 
472 		if (ibtail - ib < 2) {
473 			KICONV_SET_ERRNO_WITH_FLAG(1, EINVAL);
474 		}
475 
476 		if (! KICONV_SC_IS_GBK_2nd_BYTE(*(ib + 1))) {
477 			KICONV_SET_ERRNO_WITH_FLAG(2, EILSEQ);
478 		}
479 
480 		gb_val = (uint32_t)(*ib << 8) | *(ib + 1);
481 		sz = gbk_to_utf8(gb_val, ob, obtail, &ret_val, B_FALSE);
482 
483 		if (sz < 0) {
484 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
485 		}
486 
487 		ib += 2;
488 		ob += sz;
489 		continue;
490 
491 REPLACE_INVALID:
492 		if (obtail - ob < KICONV_UTF8_REPLACEMENT_CHAR_LEN) {
493 			ib = oldib;
494 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
495 		}
496 
497 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR1;
498 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR2;
499 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR3;
500 		ret_val++;
501 	}
502 
503 	*inlen = ibtail - ib;
504 	*outlen = obtail - ob;
505 
506 	return (ret_val);
507 }
508 
509 /*
510  * Encoding convertor from UTF-8 to EUC-CN.
511  */
512 size_t
513 kiconv_to_euccn(void *kcd, char **inbuf, size_t *inbytesleft,
514 	char **outbuf, size_t *outbytesleft, int *errno)
515 {
516 	return kiconv_utf8_to_cck(kcd, inbuf, inbytesleft, outbuf,
517 	    outbytesleft, errno, utf8_to_gb2312);
518 }
519 
520 /*
521  * String based encoding convertor from UTF-8 to EUC-CN.
522  */
523 size_t
524 kiconvstr_to_euccn(char *inarray, size_t *inlen, char *outarray,
525 	size_t *outlen, int flag, int *errno)
526 {
527 	return kiconvstr_utf8_to_cck((uchar_t *)inarray, inlen,
528 	    (uchar_t *)outarray, outlen, flag, errno, utf8_to_gb2312);
529 }
530 
531 /*
532  * Encoding converto from EUC-CN to UTF-8 code.
533  */
534 size_t
535 kiconv_fr_euccn(void *kcd, char **inbuf, size_t *inbytesleft,
536 	char **outbuf, size_t *outbytesleft, int *errno)
537 {
538 	uchar_t		*ib;
539 	uchar_t		*ob;
540 	uchar_t		*ibtail;
541 	uchar_t		*obtail;
542 	size_t		ret_val;
543 	int8_t		sz;
544 
545 	/* Check on the kiconv code conversion descriptor. */
546 	if (kcd == NULL || kcd == (void *)-1) {
547 		*errno = EBADF;
548 		return ((size_t)-1);
549 	}
550 
551 	/* If this is a state reset request, process and return. */
552 	if (inbuf == NULL || *inbuf == NULL) {
553 		return (0);
554 	}
555 
556 	ret_val = 0;
557 	ib = (uchar_t *)*inbuf;
558 	ob = (uchar_t *)*outbuf;
559 	ibtail = ib + *inbytesleft;
560 	obtail = ob + *outbytesleft;
561 
562 	while (ib < ibtail) {
563 		if (KICONV_IS_ASCII(*ib)) {
564 			if (ob >= obtail) {
565 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
566 			}
567 
568 			*ob++ = *ib++;
569 			continue;
570 		}
571 
572 		/*
573 		 * Issue EILSEQ error if the first byte is not a
574 		 * valid GB2312 leading byte.
575 		 */
576 		if (! KICONV_SC_IS_GB2312_BYTE(*ib)) {
577 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
578 		}
579 
580 		/*
581 		 * Issue EINVAL error if input buffer has an incomplete
582 		 * character at the end of the buffer.
583 		 */
584 		if (ibtail - ib < 2) {
585 			KICONV_SET_ERRNO_AND_BREAK(EINVAL);
586 		}
587 
588 		/*
589 		 * Issue EILSEQ error if the remaining byte is not
590 		 * a valid GB2312 byte.
591 		 */
592 		if (! KICONV_SC_IS_GB2312_BYTE(*(ib + 1))) {
593 			KICONV_SET_ERRNO_AND_BREAK(EILSEQ);
594 		}
595 
596 		/* Now we have a valid GB2312 character */
597 		sz = gb2312_to_utf8(*ib, *(ib + 1), ob, obtail, &ret_val);
598 		if (sz < 0) {
599 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
600 		}
601 
602 		ib += 2;
603 		ob += sz;
604 	}
605 
606 	*inbuf = (char *)ib;
607 	*inbytesleft = ibtail - ib;
608 	*outbuf = (char *)ob;
609 	*outbytesleft = obtail - ob;
610 
611 	return (ret_val);
612 }
613 
614 /*
615  * String based encoding convertor from EUC-CN to UTF-8.
616  */
617 size_t
618 kiconvstr_fr_euccn(char *inarray, size_t *inlen, char *outarray,
619     size_t *outlen, int flag, int *errno)
620 {
621 	uchar_t		*ib;
622 	uchar_t		*ob;
623 	uchar_t		*ibtail;
624 	uchar_t		*obtail;
625 	uchar_t		*oldib;
626 	size_t		ret_val;
627 	int8_t		sz;
628 	boolean_t	do_not_ignore_null;
629 
630 	ret_val = 0;
631 	ib = (uchar_t *)inarray;
632 	ob = (uchar_t *)outarray;
633 	ibtail = ib + *inlen;
634 	obtail = ob + *outlen;
635 	do_not_ignore_null = ((flag & KICONV_IGNORE_NULL) == 0);
636 
637 	while (ib < ibtail) {
638 		if (*ib == '\0' && do_not_ignore_null)
639 			break;
640 
641 		if (KICONV_IS_ASCII(*ib)) {
642 			if (ob >= obtail) {
643 				KICONV_SET_ERRNO_AND_BREAK(E2BIG);
644 			}
645 
646 			*ob++ = *ib++;
647 			continue;
648 		}
649 
650 		oldib = ib;
651 
652 		if (! KICONV_SC_IS_GB2312_BYTE(*ib)) {
653 			KICONV_SET_ERRNO_WITH_FLAG(1, EILSEQ);
654 		}
655 
656 		if (ibtail - ib < 2) {
657 			KICONV_SET_ERRNO_WITH_FLAG(1, EINVAL);
658 		}
659 
660 		if (! KICONV_SC_IS_GB2312_BYTE(*(ib + 1))) {
661 			KICONV_SET_ERRNO_WITH_FLAG(2, EILSEQ);
662 		}
663 
664 		sz = gb2312_to_utf8(*ib, *(ib + 1), ob, obtail, &ret_val);
665 		if (sz < 0) {
666 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
667 		}
668 
669 		ib += 2;
670 		ob += sz;
671 		continue;
672 
673 REPLACE_INVALID:
674 		if (obtail - ob < KICONV_UTF8_REPLACEMENT_CHAR_LEN) {
675 			ib = oldib;
676 			KICONV_SET_ERRNO_AND_BREAK(E2BIG);
677 		}
678 
679 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR1;
680 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR2;
681 		*ob++ = KICONV_UTF8_REPLACEMENT_CHAR3;
682 		ret_val++;
683 	}
684 
685 	*inlen = ibtail - ib;
686 	*outlen = obtail - ob;
687 
688 	return (ret_val);
689 }
690 
691 /*
692  * Convert single GB2312 character to UTF-8.
693  * Return: > 0  - Converted successfully
694  *         = -1 - E2BIG
695  */
696 static int8_t
697 gb2312_to_utf8(uchar_t b1, uchar_t b2, uchar_t *ob, uchar_t *obtail,
698 	size_t *ret_val)
699 {
700 	size_t	index;
701 	int8_t	sz;
702 	uchar_t	*u8;
703 
704 	/* index = (b1 - KICONV_EUC_START) * 94 + b2 - KICONV_EUC_START; */
705 	index = b1 * 94 + b2 - 0x3BBF;
706 
707 	if (index >= KICONV_GB2312_UTF8_MAX)
708 		index = KICONV_GB2312_UTF8_MAX - 1;	/* Map to 0xEFBFBD */
709 
710 	u8 = kiconv_gb2312_utf8[index];
711 	sz = u8_number_of_bytes[u8[0]];
712 
713 	if (obtail - ob < sz) {
714 		*ret_val = (size_t)-1;
715 		return (-1);
716 	}
717 
718 	for (index = 0; index < sz; index++)
719 		*ob++ = u8[index];
720 
721 	/*
722 	 * As kiconv_gb2312_utf8 contain muliple KICONV_UTF8_REPLACEMENT_CHAR
723 	 * elements, so need to ckeck more.
724 	 */
725 	if (sz == KICONV_UTF8_REPLACEMENT_CHAR_LEN &&
726 	    u8[0] == KICONV_UTF8_REPLACEMENT_CHAR1 &&
727 	    u8[1] == KICONV_UTF8_REPLACEMENT_CHAR2 &&
728 	    u8[2] == KICONV_UTF8_REPLACEMENT_CHAR3)
729 		(*ret_val)++;
730 
731 	return (sz);
732 }
733 
734 /*
735  * Convert single GB18030 or GBK character to UTF-8.
736  * Return: > 0  - Converted successfully
737  *         = -1 - E2BIG
738  */
739 static int8_t
740 gbk_to_utf8(uint32_t gbk_val, uchar_t *ob, uchar_t *obtail, size_t *ret_val,
741 	boolean_t isgbk4)
742 {
743 	size_t	index;
744 	int8_t	sz;
745 	uchar_t	u8array[4];
746 	uchar_t	*u8;
747 
748 	if (isgbk4) {
749 		if (gbk_val >= KICONV_SC_PLANE1_GB18030_START) {
750 			uint32_t	u32;
751 
752 			/*
753 			 * u32 = ((gbk_val >> 24) - 0x90) * 12600 +
754 			 *   (((gbk_val & 0xFF0000) >> 16) - 0x30) * 1260 +
755 			 *   (((gbk_val & 0xFF00) >> 8) - 0x81) * 10 +
756 			 *   (gbk_val & 0xFF - 0x30)+
757 			 *   KICONV_SC_PLANE1_UCS4_START;
758 			 */
759 			u32 = (gbk_val >> 24) * 12600 +
760 			    ((gbk_val & 0xFF0000) >> 16) * 1260 +
761 			    ((gbk_val & 0xFF00) >> 8) * 10 +
762 			    (gbk_val & 0xFF) - 0x1BA0FA;
763 			u8array[0] = (uchar_t)(0xF0 | ((u32 & 0x1C0000) >> 18));
764 			u8array[1] = (uchar_t)(0x80 | ((u32 & 0x03F000) >> 12));
765 			u8array[2] = (uchar_t)(0x80 | ((u32 & 0x000FC0) >> 6));
766 			u8array[3] = (uchar_t)(0x80 | (u32 & 0x00003F));
767 			u8 = u8array;
768 			index = 1;
769 		} else {
770 			index = kiconv_binsearch(gbk_val,
771 			    kiconv_gbk4_utf8, KICONV_GBK4_UTF8_MAX);
772 			u8 = kiconv_gbk4_utf8[index].u8;
773 		}
774 	} else {
775 		index = kiconv_binsearch(gbk_val,
776 		    kiconv_gbk_utf8, KICONV_GBK_UTF8_MAX);
777 		u8 = kiconv_gbk_utf8[index].u8;
778 	}
779 
780 	sz = u8_number_of_bytes[u8[0]];
781 	if (obtail - ob < sz) {
782 		*ret_val = (size_t)-1;
783 		return (-1);
784 	}
785 
786 	if (index == 0)
787 		(*ret_val)++;	/* Non-identical conversion */
788 
789 	for (index = 0; index < sz; index++)
790 		*ob++ = u8[index];
791 
792 	return (sz);
793 }
794 
795 /*
796  * Convert single UTF-8 character to GB18030.
797  * Return: > 0  - Converted successfully
798  *         = -1 - E2BIG
799  */
800 /* ARGSUSED */
801 static int8_t
802 utf8_to_gb18030(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
803 	uchar_t *ob, uchar_t *obtail, size_t *ret)
804 {
805 	size_t 		index;
806 	int8_t		gbklen;
807 	uint32_t	gbkcode;
808 
809 	if (utf8 >= KICONV_SC_PLANE1_UTF8_START) {
810 		/* Four bytes GB18030 [0x90308130, 0xe339fe39] handling. */
811 		uint32_t	u32;
812 
813 		u32 = (((utf8 & 0x07000000) >> 6) | ((utf8 & 0x3F0000) >> 4) |
814 		    ((utf8 & 0x3F00) >> 2) | (utf8 & 0x3F)) -
815 		    KICONV_SC_PLANE1_UCS4_START;
816 		gbkcode = ((u32 / 12600 + 0x90) << 24) |
817 		    (((u32 % 12600) / 1260 + 0x30) << 16) |
818 		    (((u32 % 1260) / 10 + 0x81) << 8) | (u32 % 10 + 0x30);
819 		gbklen = 4;
820 		index = 1;
821 	} else {
822 		index = kiconv_binsearch(utf8, kiconv_utf8_gb18030,
823 		    KICONV_UTF8_GB18030_MAX);
824 		gbkcode = kiconv_utf8_gb18030[index].value;
825 		KICONV_SC_GET_GB_LEN(gbkcode, gbklen);
826 	}
827 
828 	if (obtail - ob < gbklen) {
829 		*ret = (size_t)-1;
830 		return (-1);
831 	}
832 
833 	if (index == 0)
834 		(*ret)++;		/* Non-identical conversion */
835 
836 	if (gbklen == 2) {
837 		*ob++ = (uchar_t)(gbkcode >> 8);
838 	} else if (gbklen == 4) {
839 		*ob++ = (uchar_t)(gbkcode >> 24);
840 		*ob++ = (uchar_t)(gbkcode >> 16);
841 		*ob++ = (uchar_t)(gbkcode >> 8);
842 	}
843 	*ob = (uchar_t)(gbkcode & 0xFF);
844 
845 	return (gbklen);
846 }
847 
848 /*
849  * Convert single UTF-8 character to GBK.
850  * Return: > 0  - Converted successfully
851  *         = -1 - E2BIG
852  */
853 /* ARGSUSED */
854 static int8_t
855 utf8_to_gbk(uint32_t utf8, uchar_t **inbuf, uchar_t *ibtail,
856 	uchar_t *ob, uchar_t *obtail, size_t *ret)
857 {
858 	size_t 		index;
859 	int8_t		gbklen;
860 	uint32_t	gbkcode;
861 
862 	index = kiconv_binsearch(utf8, kiconv_utf8_gb18030,
863 	    KICONV_UTF8_GB18030_MAX);
864 	gbkcode = kiconv_utf8_gb18030[index].value;
865 	KICONV_SC_GET_GB_LEN(gbkcode, gbklen);
866 
867 	/* GBK and GB18030 share the same table, so check the length. */
868 	if (gbklen == 4) {
869 		index = 0;
870 		gbkcode = kiconv_utf8_gb18030[index].value;
871 		gbklen = 1;
872 	}
873 
874 	if (obtail - ob < gbklen) {
875 		*ret = (size_t)-1;
876 		return (-1);
877 	}
878 
879 	if (index == 0)
880 		(*ret)++;		/* Non-identical conversion */
881 
882 	if (gbklen > 1)
883 		*ob++ = (uchar_t)(gbkcode >> 8);
884 	*ob = (uchar_t)(gbkcode & 0xFF);
885 
886 	return (gbklen);
887 }
888 
889 /*
890  * Convert single UTF-8 character to GB2312.
891  * Return: > 0  - Converted successfully
892  *         = -1 - E2BIG
893  */
894 /* ARGSUSED */
895 static int8_t
896 utf8_to_gb2312(uint32_t utf8, uchar_t **inbuf, uchar_t *intail,
897 	uchar_t *ob, uchar_t *obtail, size_t *ret)
898 {
899 	size_t		index;
900 	int8_t		gblen;
901 	uint32_t	gbcode;
902 
903 	index = kiconv_binsearch(utf8, kiconv_utf8_gb2312,
904 	    KICONV_UTF8_GB2312_MAX);
905 	gbcode = kiconv_utf8_gb2312[index].value;
906 	gblen = (gbcode <= 0xFF) ? 1 : 2;
907 
908 	if (obtail - ob < gblen) {
909 		*ret = (size_t)-1;
910 		return (-1);
911 	}
912 
913 	if (index == 0)
914 		(*ret)++;
915 
916 	if (gblen > 1)
917 		*ob++ = (uchar_t)(gbcode >> 8);
918 	*ob = (uchar_t)(gbcode & 0xFF);
919 
920 	return (gblen);
921 }
922 
923 static kiconv_ops_t kiconv_sc_ops_tbl[] = {
924 	{
925 		"gb18030", "utf-8", kiconv_open_to_cck, kiconv_to_gb18030,
926 		kiconv_close_to_cck, kiconvstr_to_gb18030
927 	},
928 	{
929 		"utf-8", "gb18030", open_fr_gb18030, kiconv_fr_gb18030,
930 		close_fr_sc, kiconvstr_fr_gb18030
931 	},
932 	{
933 		"gbk", "utf-8", kiconv_open_to_cck, kiconv_to_gbk,
934 		kiconv_close_to_cck, kiconvstr_to_gbk
935 	},
936 	{
937 		"utf-8", "gbk", open_fr_gbk, kiconv_fr_gbk,
938 		close_fr_sc, kiconvstr_fr_gbk
939 	},
940 	{
941 		"euccn", "utf-8", kiconv_open_to_cck, kiconv_to_euccn,
942 		kiconv_close_to_cck, kiconvstr_to_euccn
943 	},
944 	{
945 		"utf-8", "euccn", open_fr_euccn, kiconv_fr_euccn,
946 		close_fr_sc, kiconvstr_fr_euccn
947 	},
948 };
949 
950 static kiconv_module_info_t kiconv_sc_info = {
951 	"kiconv_sc",		/* module name */
952 	sizeof (kiconv_sc_ops_tbl) / sizeof (kiconv_sc_ops_tbl[0]),
953 	kiconv_sc_ops_tbl,
954 	0,
955 	NULL,
956 	NULL,
957 	0
958 };
959 
960 static struct modlkiconv modlkiconv_sc = {
961 	&mod_kiconvops,
962 	"kiconv Simplified Chinese module 1.0",
963 	&kiconv_sc_info
964 };
965 
966 static struct modlinkage modlinkage = {
967 	MODREV_1,
968 	(void *)&modlkiconv_sc,
969 	NULL
970 };
971 
972 int
973 _init(void)
974 {
975 	int err;
976 
977 	err = mod_install(&modlinkage);
978 	if (err)
979 		cmn_err(CE_WARN, "kiconv_sc: failed to load kernel module");
980 
981 	return (err);
982 }
983 
984 int
985 _fini(void)
986 {
987 	int err;
988 
989 	/*
990 	 * If this module is being used, then, we cannot remove the module.
991 	 * The following checking will catch pretty much all usual cases.
992 	 *
993 	 * Any remaining will be catached by the kiconv_unregister_module()
994 	 * during mod_remove() at below.
995 	 */
996 	if (kiconv_module_ref_count(KICONV_MODULE_ID_SC))
997 		return (EBUSY);
998 
999 	err = mod_remove(&modlinkage);
1000 	if (err)
1001 		cmn_err(CE_WARN, "kiconv_sc: failed to remove kernel module");
1002 
1003 	return (err);
1004 }
1005 
1006 int
1007 _info(struct modinfo *modinfop)
1008 {
1009 	return (mod_info(&modlinkage, modinfop));
1010 }
1011