iconv_xlat16.c revision 256281
126159Sse/*-
226159Sse * Copyright (c) 2003, 2005 Ryuichiro Imura
326159Sse * All rights reserved.
426159Sse *
526159Sse * Redistribution and use in source and binary forms, with or without
626159Sse * modification, are permitted provided that the following conditions
726159Sse * are met:
826159Sse * 1. Redistributions of source code must retain the above copyright
926159Sse *    notice, this list of conditions and the following disclaimer.
1026159Sse * 2. Redistributions in binary form must reproduce the above copyright
1126159Sse *    notice, this list of conditions and the following disclaimer in the
1226159Sse *    documentation and/or other materials provided with the distribution.
1326159Sse *
1426159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1526159Sse * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1626159Sse * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1726159Sse * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1826159Sse * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1926159Sse * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2026159Sse * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2126159Sse * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2226159Sse * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2326159Sse * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2426159Sse * SUCH DAMAGE.
2526159Sse */
2650477Speter
2726159Sse#include <sys/cdefs.h>
2826159Sse__FBSDID("$FreeBSD: stable/10/sys/libkern/iconv_xlat16.c 194638 2009-06-22 17:09:46Z delphij $");
296100Sse
3039231Sgibbs#include <sys/param.h>
3139231Sgibbs#include <sys/kernel.h>
3239231Sgibbs#include <sys/systm.h>
3339231Sgibbs#include <sys/malloc.h>
3439231Sgibbs#include <sys/iconv.h>
3526159Sse
366767Sse#include "iconv_converter_if.h"
3726159Sse
3826159Sse/*
3926159Sse * "XLAT16" converter
4026159Sse */
416100Sse
4226159Sse#ifdef MODULE_DEPEND
4326159SseMODULE_DEPEND(iconv_xlat16, libiconv, 2, 2, 2);
4426159Sse#endif
456100Sse
4626159Sse#define C2I1(c)	((c) & 0x8000 ? ((c) & 0xff) | 0x100 : (c) & 0xff)
476100Sse#define C2I2(c)	((c) & 0x8000 ? ((c) >> 8) & 0x7f : ((c) >> 8) & 0xff)
4826159Sse
4926159Sse/*
5026159Sse * XLAT16 converter instance
5126159Sse */
5226159Ssestruct iconv_xlat16 {
536100Sse	KOBJ_FIELDS;
5426159Sse	uint32_t *		d_table[0x200];
556100Sse	void *			f_ctp;
5626159Sse	void *			t_ctp;
5745720Speter	struct iconv_cspair *	d_csp;
5826159Sse};
596100Sse
6026159Ssestatic int
6126159Sseiconv_xlat16_open(struct iconv_converter_class *dcp,
6226159Sse	struct iconv_cspair *csp, struct iconv_cspair *cspf, void **dpp)
6326159Sse{
646100Sse	struct iconv_xlat16 *dp;
6526159Sse	uint32_t *headp, **idxp;
6626159Sse	int i;
677233Sse
6838304Sgibbs	dp = (struct iconv_xlat16 *)kobj_create((struct kobj_class*)dcp, M_ICONV, M_WAITOK);
6926159Sse	headp = (uint32_t *)((caddr_t)csp->cp_data + sizeof(dp->d_table));
7026159Sse	idxp = (uint32_t **)csp->cp_data;
7126159Sse	for (i = 0 ; i < 0x200 ; i++) {
726100Sse		if (*idxp) {
7326159Sse			dp->d_table[i] = headp;
7426159Sse			headp += 0x80;
7526159Sse		} else {
7626159Sse			dp->d_table[i] = NULL;
776100Sse		}
7826159Sse		idxp++;
7926159Sse	}
8026159Sse
816100Sse	if (strcmp(csp->cp_to, KICONV_WCTYPE_NAME) != 0) {
8226159Sse		if (iconv_open(KICONV_WCTYPE_NAME, csp->cp_from, &dp->f_ctp) != 0)
8326159Sse			dp->f_ctp = NULL;
847233Sse		if (iconv_open(KICONV_WCTYPE_NAME, csp->cp_to, &dp->t_ctp) != 0)
8547339Sgallatin			dp->t_ctp = NULL;
8626159Sse	} else {
8726159Sse		dp->f_ctp = dp->t_ctp = dp;
8826159Sse	}
897233Sse
9026159Sse	dp->d_csp = csp;
9126159Sse	csp->cp_refcount++;
9226159Sse	*dpp = (void*)dp;
936100Sse	return (0);
9426159Sse}
956100Sse
9626159Ssestatic int
9726159Sseiconv_xlat16_close(void *data)
9826159Sse{
9926159Sse	struct iconv_xlat16 *dp = data;
10026159Sse
10126159Sse	if (dp->f_ctp && dp->f_ctp != data)
10226159Sse		iconv_close(dp->f_ctp);
1036100Sse	if (dp->t_ctp && dp->t_ctp != data)
10426159Sse		iconv_close(dp->t_ctp);
10526159Sse	dp->d_csp->cp_refcount--;
1067233Sse	kobj_delete((struct kobj*)data, M_ICONV);
10726159Sse	return (0);
10826159Sse}
10926159Sse
11026159Ssestatic int
11126159Sseiconv_xlat16_conv(void *d2p, const char **inbuf,
11226159Sse	size_t *inbytesleft, char **outbuf, size_t *outbytesleft,
11326159Sse	int convchar, int casetype)
11426159Sse{
11526159Sse	struct iconv_xlat16 *dp = (struct iconv_xlat16*)d2p;
11626159Sse	const char *src;
11726159Sse	char *dst;
1186100Sse	int nullin, ret = 0;
11926159Sse	size_t in, on, ir, or, inlen;
12026159Sse	uint32_t code;
12126159Sse	u_char u, l;
12226159Sse	uint16_t c1, c2, ctmp;
12326159Sse
12426159Sse	if (inbuf == NULL || *inbuf == NULL || outbuf == NULL || *outbuf == NULL)
12526159Sse		return (0);
12626159Sse	ir = in = *inbytesleft;
12726159Sse	or = on = *outbytesleft;
12826159Sse	src = *inbuf;
12926159Sse	dst = *outbuf;
13026159Sse
13126159Sse	while(ir > 0 && or > 0) {
13226159Sse
13326159Sse		inlen = 0;
13426159Sse		code = 0;
13526159Sse
13639231Sgibbs		c1 = ir > 1 ? *(src+1) & 0xff : 0;
13739231Sgibbs		c2 = *src & 0xff;
13861047Speter		ctmp = 0;
13961047Speter
14061047Speter		c1 = c2 & 0x80 ? c1 | 0x100 : c1;
14161047Speter		c2 = c2 & 0x80 ? c2 & 0x7f : c2;
14261047Speter
14361047Speter		if (ir > 1 && dp->d_table[c1] && dp->d_table[c1][c2]) {
14461047Speter			/*
14561047Speter			 * inbuf char is a double byte char
14661047Speter			 */
14739231Sgibbs			inlen = 2;
14826159Sse
14926159Sse			/* toupper,tolower */
15054412Speter			if (casetype == KICONV_FROM_LOWER && dp->f_ctp)
15154412Speter				ctmp = towlower(((u_char)*src << 8) | (u_char)*(src + 1),
15257332Sdfr				    dp->f_ctp);
15326159Sse			else if (casetype == KICONV_FROM_UPPER && dp->f_ctp)
15426159Sse				ctmp = towupper(((u_char)*src << 8) | (u_char)*(src + 1),
15526159Sse				    dp->f_ctp);
15626159Sse			if (ctmp) {
15726159Sse				c1 = C2I1(ctmp);
15840004Sdfr				c2 = C2I2(ctmp);
15940004Sdfr			}
16040004Sdfr		}
16140004Sdfr
16245720Speter		if (inlen == 0) {
16347646Sroger			c1 &= 0xff00;
16447646Sroger			if (!dp->d_table[c1]) {
16547646Sroger				ret = -1;
16645720Speter				break;
16745720Speter			}
16845720Speter			/*
16945720Speter			 * inbuf char is a single byte char
17052243Sdfr			 */
17152243Sdfr			inlen = 1;
17252243Sdfr
17352243Sdfr			if (casetype & (KICONV_FROM_LOWER|KICONV_FROM_UPPER))
17452243Sdfr				code = dp->d_table[c1][c2];
17552243Sdfr
17652243Sdfr			if (casetype == KICONV_FROM_LOWER) {
17745720Speter				if (dp->f_ctp)
17845720Speter					ctmp = towlower((u_char)*src, dp->f_ctp);
17945720Speter				else if (code & XLAT16_HAS_FROM_LOWER_CASE)
18045720Speter					ctmp = (u_char)(code >> 16);
18145720Speter			} else if (casetype == KICONV_FROM_UPPER) {
18245720Speter				if (dp->f_ctp)
18345720Speter					ctmp = towupper((u_char)*src, dp->f_ctp);
18445720Speter				else if (code & XLAT16_HAS_FROM_UPPER_CASE)
18545720Speter					ctmp = (u_char)(code >> 16);
18645720Speter			}
18745720Speter			if (ctmp) {
18845720Speter				c1 = C2I1(ctmp << 8);
18945720Speter				c2 = C2I2(ctmp << 8);
19045720Speter			}
19145720Speter		}
19245720Speter
19345720Speter		code = dp->d_table[c1][c2];
19447339Sgallatin		if (!code) {
19545720Speter			ret = -1;
19645720Speter			break;
19745720Speter		}
19845720Speter
19945720Speter		nullin = (code & XLAT16_ACCEPT_NULL_IN) ? 1 : 0;
20045720Speter		if (inlen == 1 && nullin) {
20145720Speter			/*
20245720Speter			 * XLAT16_ACCEPT_NULL_IN requires inbuf has 2byte
20345720Speter			 */
20445720Speter			ret = -1;
20545720Speter			break;
20645720Speter		}
20745720Speter
20845720Speter		/*
20945720Speter		 * now start translation
21045720Speter		 */
21145720Speter		u = (u_char)(code >> 8);
21245720Speter		l = (u_char)code;
21345720Speter
21445720Speter#ifdef XLAT16_ACCEPT_3BYTE_CHR
21545720Speter		if (code & XLAT16_IS_3BYTE_CHR) {
21645720Speter			if (or < 3) {
21745720Speter				ret = -1;
21845720Speter				break;
21945720Speter			}
22045720Speter			*dst++ = u;
22145720Speter			*dst++ = l;
22245720Speter			*dst++ = (u_char)(code >> 16);
22345720Speter			or -= 3;
22445720Speter		} else
22545720Speter#endif
22645720Speter		if (u || code & XLAT16_ACCEPT_NULL_OUT) {
22745720Speter			if (or < 2) {
22845720Speter				ret = -1;
22945720Speter				break;
23045720Speter			}
23147339Sgallatin
23245720Speter			/* toupper,tolower */
23345720Speter			if (casetype == KICONV_LOWER && dp->t_ctp) {
23445720Speter				code = towlower((uint16_t)code, dp->t_ctp);
23545720Speter				u = (u_char)(code >> 8);
23645720Speter				l = (u_char)code;
23745720Speter			}
23845720Speter			if (casetype == KICONV_UPPER && dp->t_ctp) {
23945720Speter				code = towupper((uint16_t)code, dp->t_ctp);
24045720Speter				u = (u_char)(code >> 8);
24145720Speter				l = (u_char)code;
24245720Speter			}
24345720Speter
24445720Speter			*dst++ = u;
24547339Sgallatin			*dst++ = l;
24647339Sgallatin			or -= 2;
24747339Sgallatin		} else {
24847339Sgallatin			/* toupper,tolower */
24947339Sgallatin			if (casetype == KICONV_LOWER) {
25047339Sgallatin				if (dp->t_ctp)
25147339Sgallatin					l = (u_char)towlower(l, dp->t_ctp);
25247339Sgallatin				else if (code & XLAT16_HAS_LOWER_CASE)
25347339Sgallatin					l = (u_char)(code >> 16);
25447339Sgallatin			}
25547339Sgallatin			if (casetype == KICONV_UPPER) {
25647339Sgallatin				if (dp->t_ctp)
25747339Sgallatin					l = (u_char)towupper(l, dp->t_ctp);
25847339Sgallatin				else if (code & XLAT16_HAS_UPPER_CASE)
25947339Sgallatin					l = (u_char)(code >> 16);
26047339Sgallatin			}
26147339Sgallatin
26247339Sgallatin			*dst++ = l;
26347339Sgallatin			or--;
26447339Sgallatin		}
26547339Sgallatin
26647339Sgallatin		if (inlen == 2) {
26747339Sgallatin			/*
26847339Sgallatin			 * there is a case that inbuf char is a single
26947339Sgallatin			 * byte char while inlen == 2
27047339Sgallatin			 */
27145720Speter			if ((u_char)*(src+1) == 0 && !nullin ) {
27245720Speter				src++;
27361047Speter				ir--;
27426159Sse			} else {
27558320Speter				src += 2;
27658287Speter				ir -= 2;
27758287Speter			}
27858287Speter		} else {
27961047Speter			src++;
28061047Speter			ir--;
28158287Speter		}
28258287Speter
28326159Sse		if (convchar == 1)
28426159Sse			break;
28513597Sse	}
28612453Sbde
28726159Sse	*inbuf += in - ir;
28826159Sse	*outbuf += on - or;
28926159Sse	*inbytesleft -= in - ir;
29026159Sse	*outbytesleft -= on - or;
29126159Sse	return (ret);
29226159Sse}
29341766Sdillon
29426159Ssestatic const char *
29526159Sseiconv_xlat16_name(struct iconv_converter_class *dcp)
29626159Sse{
2977233Sse	return ("xlat16");
2987233Sse}
29937841Sdfr
30037841Sdfrstatic int
30137841Sdfriconv_xlat16_tolower(void *d2p, register int c)
30237841Sdfr{
30337841Sdfr        struct iconv_xlat16 *dp = (struct iconv_xlat16*)d2p;
30437841Sdfr	register int c1, c2, out;
30526159Sse
30626159Sse	if (c < 0x100) {
30737841Sdfr		c1 = C2I1(c << 8);
30826159Sse		c2 = C2I2(c << 8);
30942614Sbde	} else if (c < 0x10000) {
31042614Sbde                c1 = C2I1(c);
31142614Sbde                c2 = C2I2(c);
31242614Sbde	} else
3137233Sse		return (c);
3147233Sse
31547646Sroger	if (dp->d_table[c1] && dp->d_table[c1][c2] & XLAT16_HAS_LOWER_CASE) {
31647646Sroger		/*return (int)(dp->d_table[c1][c2] & 0xffff);*/
31747646Sroger		out = dp->d_table[c1][c2] & 0xffff;
31846591Speter		if ((out & 0xff) == 0)
31946591Speter			out = (out >> 8) & 0xff;
32046023Speter		return (out);
32146023Speter	} else
32246023Speter		return (c);
32346023Speter}
32446023Speter
32546023Speterstatic int
32646023Spetericonv_xlat16_toupper(void *d2p, register int c)
32761047Speter{
32846023Speter        struct iconv_xlat16 *dp = (struct iconv_xlat16*)d2p;
32939231Sgibbs	register int c1, c2, out;
330
331	if (c < 0x100) {
332		c1 = C2I1(c << 8);
333		c2 = C2I2(c << 8);
334	} else if (c < 0x10000) {
335                c1 = C2I1(c);
336                c2 = C2I2(c);
337	} else
338		return (c);
339
340	if (dp->d_table[c1] && dp->d_table[c1][c2] & XLAT16_HAS_UPPER_CASE) {
341		out = dp->d_table[c1][c2] & 0xffff;
342		if ((out & 0xff) == 0)
343			out = (out >> 8) & 0xff;
344		return (out);
345	} else
346		return (c);
347}
348
349static kobj_method_t iconv_xlat16_methods[] = {
350	KOBJMETHOD(iconv_converter_open,	iconv_xlat16_open),
351	KOBJMETHOD(iconv_converter_close,	iconv_xlat16_close),
352	KOBJMETHOD(iconv_converter_conv,	iconv_xlat16_conv),
353#if 0
354	KOBJMETHOD(iconv_converter_init,	iconv_xlat16_init),
355	KOBJMETHOD(iconv_converter_done,	iconv_xlat16_done),
356#endif
357	KOBJMETHOD(iconv_converter_name,	iconv_xlat16_name),
358	KOBJMETHOD(iconv_converter_tolower,	iconv_xlat16_tolower),
359	KOBJMETHOD(iconv_converter_toupper,	iconv_xlat16_toupper),
360	{0, 0}
361};
362
363KICONV_CONVERTER(xlat16, sizeof(struct iconv_xlat16));
364