1122145Sdavidxu/*-
2128004Stjr * Copyright (c) 2004 Tim J. Robbins. All rights reserved.
3122145Sdavidxu * Copyright (c) 2003 David Xu <davidxu@freebsd.org>
4122145Sdavidxu * All rights reserved.
5122145Sdavidxu *
6235785Stheraven * Copyright (c) 2011 The FreeBSD Foundation
7235785Stheraven * All rights reserved.
8235785Stheraven * Portions of this software were developed by David Chisnall
9235785Stheraven * under sponsorship from the FreeBSD Foundation.
10235785Stheraven *
11122145Sdavidxu * Redistribution and use in source and binary forms, with or without
12122145Sdavidxu * modification, are permitted provided that the following conditions
13122145Sdavidxu * are met:
14122145Sdavidxu * 1. Redistributions of source code must retain the above copyright
15122145Sdavidxu *    notice, this list of conditions and the following disclaimer.
16122145Sdavidxu * 2. Redistributions in binary form must reproduce the above copyright
17122145Sdavidxu *    notice, this list of conditions and the following disclaimer in the
18122145Sdavidxu *    documentation and/or other materials provided with the distribution.
19122145Sdavidxu *
20122145Sdavidxu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21122145Sdavidxu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22122145Sdavidxu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23122145Sdavidxu * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24122145Sdavidxu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25122145Sdavidxu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26122145Sdavidxu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27122145Sdavidxu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28122145Sdavidxu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29122145Sdavidxu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30122145Sdavidxu * SUCH DAMAGE.
31122145Sdavidxu */
32122145Sdavidxu
33128004Stjr#include <sys/param.h>
34122145Sdavidxu__FBSDID("$FreeBSD$");
35122145Sdavidxu
36128155Stjr#include <errno.h>
37122145Sdavidxu#include <runetype.h>
38122145Sdavidxu#include <stdlib.h>
39128004Stjr#include <string.h>
40122145Sdavidxu#include <wchar.h>
41129153Stjr#include "mblocal.h"
42122145Sdavidxu
43142654Sphantomstatic size_t	_GB2312_mbrtowc(wchar_t * __restrict, const char * __restrict,
44142654Sphantom		    size_t, mbstate_t * __restrict);
45142654Sphantomstatic int	_GB2312_mbsinit(const mbstate_t *);
46142654Sphantomstatic size_t	_GB2312_wcrtomb(char * __restrict, wchar_t,
47142654Sphantom		    mbstate_t * __restrict);
48122145Sdavidxu
49128004Stjrtypedef struct {
50128004Stjr	int	count;
51128004Stjr	u_char	bytes[2];
52128004Stjr} _GB2312State;
53128004Stjr
54122145Sdavidxuint
55235785Stheraven_GB2312_init(struct xlocale_ctype *l, _RuneLocale *rl)
56122145Sdavidxu{
57122145Sdavidxu
58235785Stheraven	l->runes = rl;
59235785Stheraven	l->__mbrtowc = _GB2312_mbrtowc;
60235785Stheraven	l->__wcrtomb = _GB2312_wcrtomb;
61235785Stheraven	l->__mbsinit = _GB2312_mbsinit;
62235785Stheraven	l->__mb_cur_max = 2;
63235785Stheraven	l->__mb_sb_limit = 128;
64122145Sdavidxu	return (0);
65122145Sdavidxu}
66122145Sdavidxu
67142654Sphantomstatic int
68128004Stjr_GB2312_mbsinit(const mbstate_t *ps)
69128004Stjr{
70128004Stjr
71128081Stjr	return (ps == NULL || ((const _GB2312State *)ps)->count == 0);
72128004Stjr}
73128004Stjr
74122282Stjrstatic __inline int
75122145Sdavidxu_GB2312_check(const char *str, size_t n)
76122145Sdavidxu{
77122145Sdavidxu	const u_char *s = (const u_char *)str;
78122145Sdavidxu
79122145Sdavidxu	if (n == 0)
80122145Sdavidxu		/* Incomplete multibyte sequence */
81122145Sdavidxu		return (-2);
82122145Sdavidxu	if (s[0] >= 0xa1 && s[0] <= 0xfe) {
83122145Sdavidxu		if (n < 2)
84122145Sdavidxu			/* Incomplete multibyte sequence */
85122145Sdavidxu			return (-2);
86122145Sdavidxu		if (s[1] < 0xa1 || s[1] > 0xfe)
87122145Sdavidxu			/* Invalid multibyte sequence */
88122145Sdavidxu			return (-1);
89122145Sdavidxu		return (2);
90122145Sdavidxu	} else if (s[0] & 0x80) {
91122145Sdavidxu		/* Invalid multibyte sequence */
92122145Sdavidxu		return (-1);
93122145Sdavidxu	}
94122145Sdavidxu	return (1);
95122145Sdavidxu}
96122145Sdavidxu
97142654Sphantomstatic size_t
98122145Sdavidxu_GB2312_mbrtowc(wchar_t * __restrict pwc, const char * __restrict s, size_t n,
99128004Stjr    mbstate_t * __restrict ps)
100122145Sdavidxu{
101128004Stjr	_GB2312State *gs;
102122145Sdavidxu	wchar_t wc;
103128004Stjr	int i, len, ocount;
104128004Stjr	size_t ncopy;
105122145Sdavidxu
106128089Sdavidxu	gs = (_GB2312State *)ps;
107128004Stjr
108128155Stjr	if (gs->count < 0 || gs->count > sizeof(gs->bytes)) {
109128155Stjr		errno = EINVAL;
110128155Stjr		return ((size_t)-1);
111128155Stjr	}
112128155Stjr
113128004Stjr	if (s == NULL) {
114128004Stjr		s = "";
115128004Stjr		n = 1;
116128004Stjr		pwc = NULL;
117128004Stjr	}
118128004Stjr
119128004Stjr	ncopy = MIN(MIN(n, MB_CUR_MAX), sizeof(gs->bytes) - gs->count);
120128004Stjr	memcpy(gs->bytes + gs->count, s, ncopy);
121128004Stjr	ocount = gs->count;
122128004Stjr	gs->count += ncopy;
123128004Stjr	s = (char *)gs->bytes;
124128004Stjr	n = gs->count;
125128004Stjr
126122145Sdavidxu	if ((len = _GB2312_check(s, n)) < 0)
127122145Sdavidxu		return ((size_t)len);
128122145Sdavidxu	wc = 0;
129122145Sdavidxu	i = len;
130122145Sdavidxu	while (i-- > 0)
131122145Sdavidxu		wc = (wc << 8) | (unsigned char)*s++;
132122145Sdavidxu	if (pwc != NULL)
133122145Sdavidxu		*pwc = wc;
134128004Stjr	gs->count = 0;
135128004Stjr	return (wc == L'\0' ? 0 : len - ocount);
136122145Sdavidxu}
137122145Sdavidxu
138142654Sphantomstatic size_t
139128155Stjr_GB2312_wcrtomb(char * __restrict s, wchar_t wc, mbstate_t * __restrict ps)
140122145Sdavidxu{
141128155Stjr	_GB2312State *gs;
142122145Sdavidxu
143128155Stjr	gs = (_GB2312State *)ps;
144128155Stjr
145128155Stjr	if (gs->count != 0) {
146128155Stjr		errno = EINVAL;
147128155Stjr		return ((size_t)-1);
148128155Stjr	}
149128155Stjr
150122145Sdavidxu	if (s == NULL)
151122145Sdavidxu		/* Reset to initial shift state (no-op) */
152122145Sdavidxu		return (1);
153122145Sdavidxu	if (wc & 0x8000) {
154122145Sdavidxu		*s++ = (wc >> 8) & 0xff;
155122145Sdavidxu		*s = wc & 0xff;
156122145Sdavidxu		return (2);
157122145Sdavidxu	}
158122145Sdavidxu	*s = wc & 0xff;
159122145Sdavidxu	return (1);
160122145Sdavidxu}
161