1219019Sgabor/* $FreeBSD$ */
2219019Sgabor/* $NetBSD: citrus_hz.c,v 1.2 2008/06/14 16:01:07 tnozaki Exp $ */
3219019Sgabor
4219019Sgabor/*-
5219019Sgabor * Copyright (c)2004, 2006 Citrus Project,
6219019Sgabor * All rights reserved.
7219019Sgabor *
8219019Sgabor * Redistribution and use in source and binary forms, with or without
9219019Sgabor * modification, are permitted provided that the following conditions
10219019Sgabor * are met:
11219019Sgabor * 1. Redistributions of source code must retain the above copyright
12219019Sgabor *    notice, this list of conditions and the following disclaimer.
13219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
14219019Sgabor *    notice, this list of conditions and the following disclaimer in the
15219019Sgabor *    documentation and/or other materials provided with the distribution.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor *
29219019Sgabor */
30219019Sgabor
31219019Sgabor#include <sys/cdefs.h>
32219019Sgabor#include <sys/queue.h>
33219019Sgabor#include <sys/types.h>
34219019Sgabor
35219019Sgabor#include <assert.h>
36219019Sgabor#include <errno.h>
37219019Sgabor#include <limits.h>
38219019Sgabor#include <stddef.h>
39219019Sgabor#include <stdint.h>
40219019Sgabor#include <stdlib.h>
41219019Sgabor#include <string.h>
42219019Sgabor#include <wchar.h>
43219019Sgabor
44219019Sgabor#include "citrus_namespace.h"
45219019Sgabor#include "citrus_types.h"
46219019Sgabor#include "citrus_bcs.h"
47219019Sgabor#include "citrus_module.h"
48219019Sgabor#include "citrus_stdenc.h"
49219019Sgabor
50219019Sgabor#include "citrus_hz.h"
51219019Sgabor#include "citrus_prop.h"
52219019Sgabor
53219019Sgabor/*
54219019Sgabor * wchar_t mapping:
55219019Sgabor *
56219019Sgabor * CTRL/ASCII	00000000 00000000 00000000 gxxxxxxx
57219019Sgabor * GB2312	00000000 00000000 0xxxxxxx gxxxxxxx
58219019Sgabor * 94/96*n (~M)	0mmmmmmm 0xxxxxxx 0xxxxxxx gxxxxxxx
59219019Sgabor */
60219019Sgabor
61219019Sgabor#define ESCAPE_CHAR	'~'
62219019Sgabor
63219019Sgabortypedef enum {
64219019Sgabor	CTRL = 0, ASCII = 1, GB2312 = 2, CS94 = 3, CS96 = 4
65219019Sgabor} charset_t;
66219019Sgabor
67219019Sgabortypedef struct {
68267829Sdelphij	int	 start;
69219019Sgabor	int	 end;
70219019Sgabor	int	 width;
71219019Sgabor} range_t;
72219019Sgabor
73219019Sgaborstatic const range_t ranges[] = {
74219019Sgabor#define RANGE(start, end) { start, end, (end - start) + 1 }
75219019Sgabor/* CTRL   */ RANGE(0x00, 0x1F),
76219019Sgabor/* ASCII  */ RANGE(0x20, 0x7F),
77219019Sgabor/* GB2312 */ RANGE(0x21, 0x7E),
78219019Sgabor/* CS94   */ RANGE(0x21, 0x7E),
79219019Sgabor/* CS96   */ RANGE(0x20, 0x7F),
80219019Sgabor#undef RANGE
81219019Sgabor};
82219019Sgabor
83219019Sgabortypedef struct escape_t escape_t;
84219019Sgabortypedef struct {
85219019Sgabor	charset_t	 charset;
86219019Sgabor	escape_t	*escape;
87219019Sgabor	ssize_t		 length;
88219019Sgabor#define ROWCOL_MAX	3
89219019Sgabor} graphic_t;
90219019Sgabor
91219019Sgabortypedef TAILQ_HEAD(escape_list, escape_t) escape_list;
92219019Sgaborstruct escape_t {
93219019Sgabor	TAILQ_ENTRY(escape_t)	 entry;
94219019Sgabor	escape_list		*set;
95219019Sgabor	graphic_t		*left;
96219019Sgabor	graphic_t		*right;
97219019Sgabor	int			 ch;
98219019Sgabor};
99219019Sgabor
100219019Sgabor#define GL(escape)	((escape)->left)
101219019Sgabor#define GR(escape)	((escape)->right)
102219019Sgabor#define SET(escape)	((escape)->set)
103219019Sgabor#define ESC(escape)	((escape)->ch)
104219019Sgabor#define INIT(escape)	(TAILQ_FIRST(SET(escape)))
105219019Sgabor
106219019Sgaborstatic __inline escape_t *
107219019Sgaborfind_escape(escape_list *set, int ch)
108219019Sgabor{
109219019Sgabor	escape_t *escape;
110219019Sgabor
111219019Sgabor	TAILQ_FOREACH(escape, set, entry) {
112219019Sgabor		if (ESC(escape) == ch)
113219019Sgabor			break;
114219019Sgabor	}
115219019Sgabor
116219019Sgabor	return (escape);
117219019Sgabor}
118219019Sgabor
119219019Sgabortypedef struct {
120219019Sgabor	escape_list	 e0;
121219019Sgabor	escape_list	 e1;
122219019Sgabor	graphic_t	*ascii;
123219019Sgabor	graphic_t	*gb2312;
124219019Sgabor} _HZEncodingInfo;
125219019Sgabor
126219019Sgabor#define E0SET(ei)	(&(ei)->e0)
127219019Sgabor#define E1SET(ei)	(&(ei)->e1)
128219019Sgabor#define INIT0(ei)	(TAILQ_FIRST(E0SET(ei)))
129219019Sgabor#define INIT1(ei)	(TAILQ_FIRST(E1SET(ei)))
130219019Sgabor
131219019Sgabortypedef struct {
132219019Sgabor	escape_t	*inuse;
133219019Sgabor	int		 chlen;
134219019Sgabor	char		 ch[ROWCOL_MAX];
135219019Sgabor} _HZState;
136219019Sgabor
137219019Sgabor#define _CEI_TO_EI(_cei_)		(&(_cei_)->ei)
138219019Sgabor#define _CEI_TO_STATE(_cei_, _func_)	(_cei_)->states.s_##_func_
139219019Sgabor
140219019Sgabor#define _FUNCNAME(m)			_citrus_HZ_##m
141219019Sgabor#define _ENCODING_INFO			_HZEncodingInfo
142219019Sgabor#define _ENCODING_STATE			_HZState
143219019Sgabor#define _ENCODING_MB_CUR_MAX(_ei_)	MB_LEN_MAX
144219019Sgabor#define _ENCODING_IS_STATE_DEPENDENT		1
145219019Sgabor#define _STATE_NEEDS_EXPLICIT_INIT(_ps_)	((_ps_)->inuse == NULL)
146219019Sgabor
147219019Sgaborstatic __inline void
148219019Sgabor_citrus_HZ_init_state(_HZEncodingInfo * __restrict ei,
149219019Sgabor    _HZState * __restrict psenc)
150219019Sgabor{
151219019Sgabor
152219019Sgabor	psenc->chlen = 0;
153219019Sgabor	psenc->inuse = INIT0(ei);
154219019Sgabor}
155219019Sgabor
156219019Sgaborstatic __inline void
157219019Sgabor/*ARGSUSED*/
158219019Sgabor_citrus_HZ_pack_state(_HZEncodingInfo * __restrict ei __unused,
159219019Sgabor    void *__restrict pspriv, const _HZState * __restrict psenc)
160219019Sgabor{
161219019Sgabor
162219019Sgabor	memcpy(pspriv, (const void *)psenc, sizeof(*psenc));
163219019Sgabor}
164219019Sgabor
165219019Sgaborstatic __inline void
166219019Sgabor/*ARGSUSED*/
167219019Sgabor_citrus_HZ_unpack_state(_HZEncodingInfo * __restrict ei __unused,
168219019Sgabor    _HZState * __restrict psenc, const void * __restrict pspriv)
169219019Sgabor{
170219019Sgabor
171219019Sgabor	memcpy((void *)psenc, pspriv, sizeof(*psenc));
172219019Sgabor}
173219019Sgabor
174219019Sgaborstatic int
175219019Sgabor_citrus_HZ_mbrtowc_priv(_HZEncodingInfo * __restrict ei,
176252583Speter    wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
177219019Sgabor    _HZState * __restrict psenc, size_t * __restrict nresult)
178219019Sgabor{
179219019Sgabor	escape_t *candidate, *init;
180219019Sgabor	graphic_t *graphic;
181219019Sgabor	const range_t *range;
182252583Speter	const char *s0;
183219019Sgabor	wchar_t wc;
184219019Sgabor	int bit, ch, head, len, tail;
185219019Sgabor
186219019Sgabor	if (*s == NULL) {
187219019Sgabor		_citrus_HZ_init_state(ei, psenc);
188219019Sgabor		*nresult = 1;
189219019Sgabor		return (0);
190219019Sgabor	}
191219019Sgabor	s0 = *s;
192219019Sgabor	if (psenc->chlen < 0 || psenc->inuse == NULL)
193219019Sgabor		return (EINVAL);
194219019Sgabor
195219019Sgabor	wc = (wchar_t)0;
196219019Sgabor	bit = head = tail = 0;
197219019Sgabor	graphic = NULL;
198219019Sgabor	for (len = 0; len <= MB_LEN_MAX;) {
199219019Sgabor		if (psenc->chlen == tail) {
200219019Sgabor			if (n-- < 1) {
201219019Sgabor				*s = s0;
202219019Sgabor				*nresult = (size_t)-2;
203219019Sgabor				return (0);
204219019Sgabor			}
205219019Sgabor			psenc->ch[psenc->chlen++] = *s0++;
206219019Sgabor			++len;
207219019Sgabor		}
208219019Sgabor		ch = (unsigned char)psenc->ch[tail++];
209219019Sgabor		if (tail == 1) {
210219019Sgabor			if ((ch & ~0x80) <= 0x1F) {
211219019Sgabor				if (psenc->inuse != INIT0(ei))
212219019Sgabor					break;
213219019Sgabor				wc = (wchar_t)ch;
214219019Sgabor				goto done;
215219019Sgabor			}
216219019Sgabor			if (ch & 0x80) {
217219019Sgabor				graphic = GR(psenc->inuse);
218219019Sgabor				bit = 0x80;
219219019Sgabor				ch &= ~0x80;
220219019Sgabor			} else {
221219019Sgabor				graphic = GL(psenc->inuse);
222219019Sgabor				if (ch == ESCAPE_CHAR)
223219019Sgabor					continue;
224219019Sgabor				bit = 0x0;
225219019Sgabor			}
226219019Sgabor			if (graphic == NULL)
227219019Sgabor				break;
228219019Sgabor		} else if (tail == 2 && psenc->ch[0] == ESCAPE_CHAR) {
229219019Sgabor			if (tail < psenc->chlen)
230219019Sgabor				return (EINVAL);
231219019Sgabor			if (ch == ESCAPE_CHAR) {
232219019Sgabor				++head;
233219019Sgabor			} else if (ch == '\n') {
234219019Sgabor				if (psenc->inuse != INIT0(ei))
235219019Sgabor					break;
236219019Sgabor				tail = psenc->chlen = 0;
237219019Sgabor				continue;
238219019Sgabor			} else {
239219019Sgabor				candidate = NULL;
240219019Sgabor				init = INIT0(ei);
241219019Sgabor				if (psenc->inuse == init) {
242219019Sgabor					init = INIT1(ei);
243219019Sgabor				} else if (INIT(psenc->inuse) == init) {
244219019Sgabor					if (ESC(init) != ch)
245219019Sgabor						break;
246219019Sgabor					candidate = init;
247219019Sgabor				}
248219019Sgabor				if (candidate == NULL) {
249219019Sgabor					candidate = find_escape(
250219019Sgabor					    SET(psenc->inuse), ch);
251219019Sgabor					if (candidate == NULL) {
252219019Sgabor						if (init == NULL ||
253219019Sgabor						    ESC(init) != ch)
254219019Sgabor							break;
255219019Sgabor						candidate = init;
256219019Sgabor					}
257219019Sgabor				}
258219019Sgabor				psenc->inuse = candidate;
259219019Sgabor				tail = psenc->chlen = 0;
260219019Sgabor				continue;
261219019Sgabor			}
262219019Sgabor		} else if (ch & 0x80) {
263219019Sgabor			if (graphic != GR(psenc->inuse))
264219019Sgabor				break;
265219019Sgabor			ch &= ~0x80;
266219019Sgabor		} else {
267219019Sgabor			if (graphic != GL(psenc->inuse))
268219019Sgabor				break;
269219019Sgabor		}
270219019Sgabor		range = &ranges[(size_t)graphic->charset];
271219019Sgabor		if (range->start > ch || range->end < ch)
272219019Sgabor			break;
273219019Sgabor		wc <<= 8;
274219019Sgabor		wc |= ch;
275219019Sgabor		if (graphic->length == (tail - head)) {
276219019Sgabor			if (graphic->charset > GB2312)
277219019Sgabor				bit |= ESC(psenc->inuse) << 24;
278219019Sgabor			wc |= bit;
279219019Sgabor			goto done;
280219019Sgabor		}
281219019Sgabor	}
282219019Sgabor	*nresult = (size_t)-1;
283219019Sgabor	return (EILSEQ);
284219019Sgabordone:
285219019Sgabor	if (tail < psenc->chlen)
286219019Sgabor		return (EINVAL);
287219019Sgabor	*s = s0;
288219019Sgabor	if (pwc != NULL)
289219019Sgabor		*pwc = wc;
290219019Sgabor	psenc->chlen = 0;
291219019Sgabor	*nresult = (wc == 0) ? 0 : len;
292219019Sgabor
293219019Sgabor	return (0);
294219019Sgabor}
295219019Sgabor
296219019Sgaborstatic int
297219019Sgabor_citrus_HZ_wcrtomb_priv(_HZEncodingInfo * __restrict ei,
298219019Sgabor    char * __restrict s, size_t n, wchar_t wc,
299219019Sgabor    _HZState * __restrict psenc, size_t * __restrict nresult)
300219019Sgabor{
301219019Sgabor	escape_t *candidate, *init;
302219019Sgabor	graphic_t *graphic;
303219019Sgabor	const range_t *range;
304219019Sgabor	size_t len;
305219019Sgabor	int bit, ch;
306219019Sgabor
307219019Sgabor	if (psenc->chlen != 0 || psenc->inuse == NULL)
308219019Sgabor		return (EINVAL);
309219019Sgabor	if (wc & 0x80) {
310219019Sgabor		bit = 0x80;
311219019Sgabor		wc &= ~0x80;
312219019Sgabor	} else {
313219019Sgabor		bit = 0x0;
314219019Sgabor	}
315219019Sgabor	if ((uint32_t)wc <= 0x1F) {
316219019Sgabor		candidate = INIT0(ei);
317219019Sgabor		graphic = (bit == 0) ? candidate->left : candidate->right;
318219019Sgabor		if (graphic == NULL)
319219019Sgabor			goto ilseq;
320219019Sgabor		range = &ranges[(size_t)CTRL];
321219019Sgabor		len = 1;
322219019Sgabor	} else if ((uint32_t)wc <= 0x7F) {
323219019Sgabor		graphic = ei->ascii;
324219019Sgabor		if (graphic == NULL)
325219019Sgabor			goto ilseq;
326219019Sgabor		candidate = graphic->escape;
327219019Sgabor		range = &ranges[(size_t)graphic->charset];
328219019Sgabor		len = graphic->length;
329219019Sgabor	} else if ((uint32_t)wc <= 0x7F7F) {
330219019Sgabor		graphic = ei->gb2312;
331219019Sgabor		if (graphic == NULL)
332219019Sgabor			goto ilseq;
333219019Sgabor		candidate = graphic->escape;
334219019Sgabor		range = &ranges[(size_t)graphic->charset];
335219019Sgabor		len = graphic->length;
336219019Sgabor	} else {
337219019Sgabor		ch = (wc >> 24) & 0xFF;
338219019Sgabor		candidate = find_escape(E0SET(ei), ch);
339219019Sgabor		if (candidate == NULL) {
340219019Sgabor			candidate = find_escape(E1SET(ei), ch);
341219019Sgabor			if (candidate == NULL)
342219019Sgabor				goto ilseq;
343219019Sgabor		}
344219019Sgabor		wc &= ~0xFF000000;
345219019Sgabor		graphic = (bit == 0) ? candidate->left : candidate->right;
346219019Sgabor		if (graphic == NULL)
347219019Sgabor			goto ilseq;
348219019Sgabor		range = &ranges[(size_t)graphic->charset];
349219019Sgabor		len = graphic->length;
350219019Sgabor	}
351219019Sgabor	if (psenc->inuse != candidate) {
352219019Sgabor		init = INIT0(ei);
353219019Sgabor		if (SET(psenc->inuse) == SET(candidate)) {
354219019Sgabor			if (INIT(psenc->inuse) != init ||
355219019Sgabor			    psenc->inuse == init || candidate == init)
356219019Sgabor				init = NULL;
357219019Sgabor		} else if (candidate == (init = INIT(candidate))) {
358219019Sgabor			init = NULL;
359219019Sgabor		}
360219019Sgabor		if (init != NULL) {
361219019Sgabor			if (n < 2)
362219019Sgabor				return (E2BIG);
363219019Sgabor			n -= 2;
364219019Sgabor			psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
365219019Sgabor			psenc->ch[psenc->chlen++] = ESC(init);
366219019Sgabor		}
367219019Sgabor		if (n < 2)
368219019Sgabor			return (E2BIG);
369219019Sgabor		n -= 2;
370219019Sgabor		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
371219019Sgabor		psenc->ch[psenc->chlen++] = ESC(candidate);
372219019Sgabor		psenc->inuse = candidate;
373219019Sgabor	}
374219019Sgabor	if (n < len)
375219019Sgabor		return (E2BIG);
376219019Sgabor	while (len-- > 0) {
377219019Sgabor		ch = (wc >> (len * 8)) & 0xFF;
378219019Sgabor		if (range->start > ch || range->end < ch)
379219019Sgabor			goto ilseq;
380219019Sgabor		psenc->ch[psenc->chlen++] = ch | bit;
381219019Sgabor	}
382219019Sgabor	memcpy(s, psenc->ch, psenc->chlen);
383219019Sgabor	*nresult = psenc->chlen;
384219019Sgabor	psenc->chlen = 0;
385219019Sgabor
386219019Sgabor	return (0);
387219019Sgabor
388219019Sgaborilseq:
389219019Sgabor	*nresult = (size_t)-1;
390219019Sgabor	return (EILSEQ);
391219019Sgabor}
392219019Sgabor
393219019Sgaborstatic __inline int
394219019Sgabor_citrus_HZ_put_state_reset(_HZEncodingInfo * __restrict ei,
395219019Sgabor    char * __restrict s, size_t n, _HZState * __restrict psenc,
396219019Sgabor    size_t * __restrict nresult)
397219019Sgabor{
398219019Sgabor	escape_t *candidate;
399219019Sgabor
400219019Sgabor	if (psenc->chlen != 0 || psenc->inuse == NULL)
401219019Sgabor		return (EINVAL);
402219019Sgabor	candidate = INIT0(ei);
403219019Sgabor	if (psenc->inuse != candidate) {
404219019Sgabor		if (n < 2)
405219019Sgabor			return (E2BIG);
406219019Sgabor		n -= 2;
407219019Sgabor		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
408219019Sgabor		psenc->ch[psenc->chlen++] = ESC(candidate);
409219019Sgabor	}
410219019Sgabor	if (n < 1)
411219019Sgabor		return (E2BIG);
412219019Sgabor	if (psenc->chlen > 0)
413219019Sgabor		memcpy(s, psenc->ch, psenc->chlen);
414219019Sgabor	*nresult = psenc->chlen;
415219019Sgabor	_citrus_HZ_init_state(ei, psenc);
416219019Sgabor
417219019Sgabor	return (0);
418219019Sgabor}
419219019Sgabor
420219019Sgaborstatic __inline int
421219019Sgabor_citrus_HZ_stdenc_get_state_desc_generic(_HZEncodingInfo * __restrict ei,
422219019Sgabor    _HZState * __restrict psenc, int * __restrict rstate)
423219019Sgabor{
424219019Sgabor
425219019Sgabor	if (psenc->chlen < 0 || psenc->inuse == NULL)
426219019Sgabor		return (EINVAL);
427219019Sgabor	*rstate = (psenc->chlen == 0)
428219019Sgabor	    ? ((psenc->inuse == INIT0(ei))
429219019Sgabor	        ? _STDENC_SDGEN_INITIAL
430219019Sgabor	        : _STDENC_SDGEN_STABLE)
431219019Sgabor	    : ((psenc->ch[0] == ESCAPE_CHAR)
432219019Sgabor	        ? _STDENC_SDGEN_INCOMPLETE_SHIFT
433219019Sgabor	        : _STDENC_SDGEN_INCOMPLETE_CHAR);
434219019Sgabor
435219019Sgabor	return (0);
436219019Sgabor}
437219019Sgabor
438219019Sgaborstatic __inline int
439219019Sgabor/*ARGSUSED*/
440219019Sgabor_citrus_HZ_stdenc_wctocs(_HZEncodingInfo * __restrict ei __unused,
441219019Sgabor    _csid_t * __restrict csid, _index_t * __restrict idx, wchar_t wc)
442219019Sgabor{
443219019Sgabor	int bit;
444219019Sgabor
445219019Sgabor	if (wc & 0x80) {
446219019Sgabor		bit = 0x80;
447219019Sgabor		wc &= ~0x80;
448219019Sgabor	} else
449219019Sgabor		bit = 0x0;
450219019Sgabor	if ((uint32_t)wc <= 0x7F) {
451219019Sgabor		*csid = (_csid_t)bit;
452219019Sgabor		*idx = (_index_t)wc;
453219019Sgabor	} else if ((uint32_t)wc <= 0x7F7F) {
454219019Sgabor		*csid = (_csid_t)(bit | 0x8000);
455219019Sgabor		*idx = (_index_t)wc;
456219019Sgabor	} else {
457219019Sgabor		*csid = (_index_t)(wc & ~0x00FFFF7F);
458219019Sgabor		*idx = (_csid_t)(wc & 0x00FFFF7F);
459219019Sgabor	}
460219019Sgabor
461219019Sgabor	return (0);
462219019Sgabor}
463219019Sgabor
464219019Sgaborstatic __inline int
465219019Sgabor/*ARGSUSED*/
466219019Sgabor_citrus_HZ_stdenc_cstowc(_HZEncodingInfo * __restrict ei __unused,
467219019Sgabor    wchar_t * __restrict wc, _csid_t csid, _index_t idx)
468219019Sgabor{
469219019Sgabor
470219019Sgabor	*wc = (wchar_t)idx;
471219019Sgabor	switch (csid) {
472219019Sgabor	case 0x80:
473219019Sgabor	case 0x8080:
474219019Sgabor		*wc |= (wchar_t)0x80;
475219019Sgabor		/*FALLTHROUGH*/
476219019Sgabor	case 0x0:
477219019Sgabor	case 0x8000:
478219019Sgabor		break;
479219019Sgabor	default:
480219019Sgabor		*wc |= (wchar_t)csid;
481219019Sgabor	}
482219019Sgabor
483219019Sgabor	return (0);
484219019Sgabor}
485219019Sgabor
486219019Sgaborstatic void
487219019Sgabor_citrus_HZ_encoding_module_uninit(_HZEncodingInfo *ei)
488219019Sgabor{
489219019Sgabor	escape_t *escape;
490219019Sgabor
491219019Sgabor	while ((escape = TAILQ_FIRST(E0SET(ei))) != NULL) {
492219019Sgabor		TAILQ_REMOVE(E0SET(ei), escape, entry);
493219019Sgabor		free(GL(escape));
494219019Sgabor		free(GR(escape));
495219019Sgabor		free(escape);
496219019Sgabor	}
497219019Sgabor	while ((escape = TAILQ_FIRST(E1SET(ei))) != NULL) {
498219019Sgabor		TAILQ_REMOVE(E1SET(ei), escape, entry);
499219019Sgabor		free(GL(escape));
500219019Sgabor		free(GR(escape));
501219019Sgabor		free(escape);
502219019Sgabor	}
503219019Sgabor}
504219019Sgabor
505219019Sgaborstatic int
506267829Sdelphij_citrus_HZ_parse_char(void *context, const char *name __unused, const char *s)
507219019Sgabor{
508219019Sgabor	escape_t *escape;
509219019Sgabor	void **p;
510219019Sgabor
511267829Sdelphij	p = (void **)context;
512219019Sgabor	escape = (escape_t *)p[0];
513219019Sgabor	if (escape->ch != '\0')
514219019Sgabor		return (EINVAL);
515219019Sgabor	escape->ch = *s++;
516219019Sgabor	if (escape->ch == ESCAPE_CHAR || *s != '\0')
517219019Sgabor		return (EINVAL);
518219019Sgabor
519219019Sgabor	return (0);
520219019Sgabor}
521219019Sgabor
522219019Sgaborstatic int
523267829Sdelphij_citrus_HZ_parse_graphic(void *context, const char *name, const char *s)
524219019Sgabor{
525219019Sgabor	_HZEncodingInfo *ei;
526219019Sgabor	escape_t *escape;
527219019Sgabor	graphic_t *graphic;
528219019Sgabor	void **p;
529219019Sgabor
530267829Sdelphij	p = (void **)context;
531219019Sgabor	escape = (escape_t *)p[0];
532219019Sgabor	ei = (_HZEncodingInfo *)p[1];
533219019Sgabor	graphic = malloc(sizeof(*graphic));
534219019Sgabor	if (graphic == NULL)
535219019Sgabor		return (ENOMEM);
536219019Sgabor	memset(graphic, 0, sizeof(*graphic));
537219019Sgabor	if (strcmp("GL", name) == 0) {
538219019Sgabor		if (GL(escape) != NULL)
539219019Sgabor			goto release;
540219019Sgabor		GL(escape) = graphic;
541219019Sgabor	} else if (strcmp("GR", name) == 0) {
542219019Sgabor		if (GR(escape) != NULL)
543219019Sgabor			goto release;
544219019Sgabor		GR(escape) = graphic;
545219019Sgabor	} else {
546219019Sgaborrelease:
547219019Sgabor		free(graphic);
548219019Sgabor		return (EINVAL);
549219019Sgabor	}
550219019Sgabor	graphic->escape = escape;
551219019Sgabor	if (_bcs_strncasecmp("ASCII", s, 5) == 0) {
552219019Sgabor		if (s[5] != '\0')
553219019Sgabor			return (EINVAL);
554219019Sgabor		graphic->charset = ASCII;
555219019Sgabor		graphic->length = 1;
556219019Sgabor		ei->ascii = graphic;
557219019Sgabor		return (0);
558219019Sgabor	} else if (_bcs_strncasecmp("GB2312", s, 6) == 0) {
559219019Sgabor		if (s[6] != '\0')
560219019Sgabor			return (EINVAL);
561219019Sgabor		graphic->charset = GB2312;
562219019Sgabor		graphic->length = 2;
563219019Sgabor		ei->gb2312 = graphic;
564219019Sgabor		return (0);
565219019Sgabor	} else if (strncmp("94*", s, 3) == 0)
566219019Sgabor		graphic->charset = CS94;
567219019Sgabor	else if (strncmp("96*", s, 3) == 0)
568219019Sgabor		graphic->charset = CS96;
569219019Sgabor	else
570219019Sgabor		return (EINVAL);
571219019Sgabor	s += 3;
572219019Sgabor	switch(*s) {
573219019Sgabor	case '1': case '2': case '3':
574219019Sgabor		graphic->length = (size_t)(*s - '0');
575219019Sgabor		if (*++s == '\0')
576219019Sgabor			break;
577219019Sgabor	/*FALLTHROUGH*/
578219019Sgabor	default:
579219019Sgabor		return (EINVAL);
580219019Sgabor	}
581219019Sgabor	return (0);
582219019Sgabor}
583219019Sgabor
584219019Sgaborstatic const _citrus_prop_hint_t escape_hints[] = {
585219019Sgabor_CITRUS_PROP_HINT_STR("CH", &_citrus_HZ_parse_char),
586219019Sgabor_CITRUS_PROP_HINT_STR("GL", &_citrus_HZ_parse_graphic),
587219019Sgabor_CITRUS_PROP_HINT_STR("GR", &_citrus_HZ_parse_graphic),
588219019Sgabor_CITRUS_PROP_HINT_END
589219019Sgabor};
590219019Sgabor
591219019Sgaborstatic int
592267829Sdelphij_citrus_HZ_parse_escape(void *context, const char *name, const char *s)
593219019Sgabor{
594219019Sgabor	_HZEncodingInfo *ei;
595219019Sgabor	escape_t *escape;
596219019Sgabor	void *p[2];
597219019Sgabor
598267829Sdelphij	ei = (_HZEncodingInfo *)context;
599219019Sgabor	escape = malloc(sizeof(*escape));
600219019Sgabor	if (escape == NULL)
601219019Sgabor		return (EINVAL);
602219019Sgabor	memset(escape, 0, sizeof(*escape));
603219019Sgabor	if (strcmp("0", name) == 0) {
604219019Sgabor		escape->set = E0SET(ei);
605219019Sgabor		TAILQ_INSERT_TAIL(E0SET(ei), escape, entry);
606219019Sgabor	} else if (strcmp("1", name) == 0) {
607219019Sgabor		escape->set = E1SET(ei);
608219019Sgabor		TAILQ_INSERT_TAIL(E1SET(ei), escape, entry);
609219019Sgabor	} else {
610219019Sgabor		free(escape);
611219019Sgabor		return (EINVAL);
612219019Sgabor	}
613219019Sgabor	p[0] = (void *)escape;
614219019Sgabor	p[1] = (void *)ei;
615219019Sgabor	return (_citrus_prop_parse_variable(
616219019Sgabor	    escape_hints, (void *)&p[0], s, strlen(s)));
617219019Sgabor}
618219019Sgabor
619219019Sgaborstatic const _citrus_prop_hint_t root_hints[] = {
620219019Sgabor_CITRUS_PROP_HINT_STR("0", &_citrus_HZ_parse_escape),
621219019Sgabor_CITRUS_PROP_HINT_STR("1", &_citrus_HZ_parse_escape),
622219019Sgabor_CITRUS_PROP_HINT_END
623219019Sgabor};
624219019Sgabor
625219019Sgaborstatic int
626219019Sgabor_citrus_HZ_encoding_module_init(_HZEncodingInfo * __restrict ei,
627219019Sgabor    const void * __restrict var, size_t lenvar)
628219019Sgabor{
629219019Sgabor	int errnum;
630219019Sgabor
631219019Sgabor	memset(ei, 0, sizeof(*ei));
632219019Sgabor	TAILQ_INIT(E0SET(ei));
633219019Sgabor	TAILQ_INIT(E1SET(ei));
634219019Sgabor	errnum = _citrus_prop_parse_variable(
635219019Sgabor	    root_hints, (void *)ei, var, lenvar);
636219019Sgabor	if (errnum != 0)
637219019Sgabor		_citrus_HZ_encoding_module_uninit(ei);
638219019Sgabor	return (errnum);
639219019Sgabor}
640219019Sgabor
641219019Sgabor/* ----------------------------------------------------------------------
642219019Sgabor * public interface for stdenc
643219019Sgabor */
644219019Sgabor
645219019Sgabor_CITRUS_STDENC_DECLS(HZ);
646219019Sgabor_CITRUS_STDENC_DEF_OPS(HZ);
647219019Sgabor
648219019Sgabor#include "citrus_stdenc_template.h"
649