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 {
68219019Sgabor	int	 end;
69219019Sgabor	int	 start;
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
156260264Sdim#if 0
157219019Sgaborstatic __inline void
158219019Sgabor/*ARGSUSED*/
159219019Sgabor_citrus_HZ_pack_state(_HZEncodingInfo * __restrict ei __unused,
160219019Sgabor    void *__restrict pspriv, const _HZState * __restrict psenc)
161219019Sgabor{
162219019Sgabor
163219019Sgabor	memcpy(pspriv, (const void *)psenc, sizeof(*psenc));
164219019Sgabor}
165219019Sgabor
166219019Sgaborstatic __inline void
167219019Sgabor/*ARGSUSED*/
168219019Sgabor_citrus_HZ_unpack_state(_HZEncodingInfo * __restrict ei __unused,
169219019Sgabor    _HZState * __restrict psenc, const void * __restrict pspriv)
170219019Sgabor{
171219019Sgabor
172219019Sgabor	memcpy((void *)psenc, pspriv, sizeof(*psenc));
173219019Sgabor}
174260264Sdim#endif
175219019Sgabor
176219019Sgaborstatic int
177219019Sgabor_citrus_HZ_mbrtowc_priv(_HZEncodingInfo * __restrict ei,
178219019Sgabor    wchar_t * __restrict pwc, char ** __restrict s, size_t n,
179219019Sgabor    _HZState * __restrict psenc, size_t * __restrict nresult)
180219019Sgabor{
181219019Sgabor	escape_t *candidate, *init;
182219019Sgabor	graphic_t *graphic;
183219019Sgabor	const range_t *range;
184219019Sgabor	char *s0;
185219019Sgabor	wchar_t wc;
186219019Sgabor	int bit, ch, head, len, tail;
187219019Sgabor
188219019Sgabor	if (*s == NULL) {
189219019Sgabor		_citrus_HZ_init_state(ei, psenc);
190219019Sgabor		*nresult = 1;
191219019Sgabor		return (0);
192219019Sgabor	}
193219019Sgabor	s0 = *s;
194219019Sgabor	if (psenc->chlen < 0 || psenc->inuse == NULL)
195219019Sgabor		return (EINVAL);
196219019Sgabor
197219019Sgabor	wc = (wchar_t)0;
198219019Sgabor	bit = head = tail = 0;
199219019Sgabor	graphic = NULL;
200219019Sgabor	for (len = 0; len <= MB_LEN_MAX;) {
201219019Sgabor		if (psenc->chlen == tail) {
202219019Sgabor			if (n-- < 1) {
203219019Sgabor				*s = s0;
204219019Sgabor				*nresult = (size_t)-2;
205219019Sgabor				return (0);
206219019Sgabor			}
207219019Sgabor			psenc->ch[psenc->chlen++] = *s0++;
208219019Sgabor			++len;
209219019Sgabor		}
210219019Sgabor		ch = (unsigned char)psenc->ch[tail++];
211219019Sgabor		if (tail == 1) {
212219019Sgabor			if ((ch & ~0x80) <= 0x1F) {
213219019Sgabor				if (psenc->inuse != INIT0(ei))
214219019Sgabor					break;
215219019Sgabor				wc = (wchar_t)ch;
216219019Sgabor				goto done;
217219019Sgabor			}
218219019Sgabor			if (ch & 0x80) {
219219019Sgabor				graphic = GR(psenc->inuse);
220219019Sgabor				bit = 0x80;
221219019Sgabor				ch &= ~0x80;
222219019Sgabor			} else {
223219019Sgabor				graphic = GL(psenc->inuse);
224219019Sgabor				if (ch == ESCAPE_CHAR)
225219019Sgabor					continue;
226219019Sgabor				bit = 0x0;
227219019Sgabor			}
228219019Sgabor			if (graphic == NULL)
229219019Sgabor				break;
230219019Sgabor		} else if (tail == 2 && psenc->ch[0] == ESCAPE_CHAR) {
231219019Sgabor			if (tail < psenc->chlen)
232219019Sgabor				return (EINVAL);
233219019Sgabor			if (ch == ESCAPE_CHAR) {
234219019Sgabor				++head;
235219019Sgabor			} else if (ch == '\n') {
236219019Sgabor				if (psenc->inuse != INIT0(ei))
237219019Sgabor					break;
238219019Sgabor				tail = psenc->chlen = 0;
239219019Sgabor				continue;
240219019Sgabor			} else {
241219019Sgabor				candidate = NULL;
242219019Sgabor				init = INIT0(ei);
243219019Sgabor				if (psenc->inuse == init) {
244219019Sgabor					init = INIT1(ei);
245219019Sgabor				} else if (INIT(psenc->inuse) == init) {
246219019Sgabor					if (ESC(init) != ch)
247219019Sgabor						break;
248219019Sgabor					candidate = init;
249219019Sgabor				}
250219019Sgabor				if (candidate == NULL) {
251219019Sgabor					candidate = find_escape(
252219019Sgabor					    SET(psenc->inuse), ch);
253219019Sgabor					if (candidate == NULL) {
254219019Sgabor						if (init == NULL ||
255219019Sgabor						    ESC(init) != ch)
256219019Sgabor							break;
257219019Sgabor						candidate = init;
258219019Sgabor					}
259219019Sgabor				}
260219019Sgabor				psenc->inuse = candidate;
261219019Sgabor				tail = psenc->chlen = 0;
262219019Sgabor				continue;
263219019Sgabor			}
264219019Sgabor		} else if (ch & 0x80) {
265219019Sgabor			if (graphic != GR(psenc->inuse))
266219019Sgabor				break;
267219019Sgabor			ch &= ~0x80;
268219019Sgabor		} else {
269219019Sgabor			if (graphic != GL(psenc->inuse))
270219019Sgabor				break;
271219019Sgabor		}
272219019Sgabor		range = &ranges[(size_t)graphic->charset];
273219019Sgabor		if (range->start > ch || range->end < ch)
274219019Sgabor			break;
275219019Sgabor		wc <<= 8;
276219019Sgabor		wc |= ch;
277219019Sgabor		if (graphic->length == (tail - head)) {
278219019Sgabor			if (graphic->charset > GB2312)
279219019Sgabor				bit |= ESC(psenc->inuse) << 24;
280219019Sgabor			wc |= bit;
281219019Sgabor			goto done;
282219019Sgabor		}
283219019Sgabor	}
284219019Sgabor	*nresult = (size_t)-1;
285219019Sgabor	return (EILSEQ);
286219019Sgabordone:
287219019Sgabor	if (tail < psenc->chlen)
288219019Sgabor		return (EINVAL);
289219019Sgabor	*s = s0;
290219019Sgabor	if (pwc != NULL)
291219019Sgabor		*pwc = wc;
292219019Sgabor	psenc->chlen = 0;
293219019Sgabor	*nresult = (wc == 0) ? 0 : len;
294219019Sgabor
295219019Sgabor	return (0);
296219019Sgabor}
297219019Sgabor
298219019Sgaborstatic int
299219019Sgabor_citrus_HZ_wcrtomb_priv(_HZEncodingInfo * __restrict ei,
300219019Sgabor    char * __restrict s, size_t n, wchar_t wc,
301219019Sgabor    _HZState * __restrict psenc, size_t * __restrict nresult)
302219019Sgabor{
303219019Sgabor	escape_t *candidate, *init;
304219019Sgabor	graphic_t *graphic;
305219019Sgabor	const range_t *range;
306219019Sgabor	size_t len;
307219019Sgabor	int bit, ch;
308219019Sgabor
309219019Sgabor	if (psenc->chlen != 0 || psenc->inuse == NULL)
310219019Sgabor		return (EINVAL);
311219019Sgabor	if (wc & 0x80) {
312219019Sgabor		bit = 0x80;
313219019Sgabor		wc &= ~0x80;
314219019Sgabor	} else {
315219019Sgabor		bit = 0x0;
316219019Sgabor	}
317219019Sgabor	if ((uint32_t)wc <= 0x1F) {
318219019Sgabor		candidate = INIT0(ei);
319219019Sgabor		graphic = (bit == 0) ? candidate->left : candidate->right;
320219019Sgabor		if (graphic == NULL)
321219019Sgabor			goto ilseq;
322219019Sgabor		range = &ranges[(size_t)CTRL];
323219019Sgabor		len = 1;
324219019Sgabor	} else if ((uint32_t)wc <= 0x7F) {
325219019Sgabor		graphic = ei->ascii;
326219019Sgabor		if (graphic == NULL)
327219019Sgabor			goto ilseq;
328219019Sgabor		candidate = graphic->escape;
329219019Sgabor		range = &ranges[(size_t)graphic->charset];
330219019Sgabor		len = graphic->length;
331219019Sgabor	} else if ((uint32_t)wc <= 0x7F7F) {
332219019Sgabor		graphic = ei->gb2312;
333219019Sgabor		if (graphic == NULL)
334219019Sgabor			goto ilseq;
335219019Sgabor		candidate = graphic->escape;
336219019Sgabor		range = &ranges[(size_t)graphic->charset];
337219019Sgabor		len = graphic->length;
338219019Sgabor	} else {
339219019Sgabor		ch = (wc >> 24) & 0xFF;
340219019Sgabor		candidate = find_escape(E0SET(ei), ch);
341219019Sgabor		if (candidate == NULL) {
342219019Sgabor			candidate = find_escape(E1SET(ei), ch);
343219019Sgabor			if (candidate == NULL)
344219019Sgabor				goto ilseq;
345219019Sgabor		}
346219019Sgabor		wc &= ~0xFF000000;
347219019Sgabor		graphic = (bit == 0) ? candidate->left : candidate->right;
348219019Sgabor		if (graphic == NULL)
349219019Sgabor			goto ilseq;
350219019Sgabor		range = &ranges[(size_t)graphic->charset];
351219019Sgabor		len = graphic->length;
352219019Sgabor	}
353219019Sgabor	if (psenc->inuse != candidate) {
354219019Sgabor		init = INIT0(ei);
355219019Sgabor		if (SET(psenc->inuse) == SET(candidate)) {
356219019Sgabor			if (INIT(psenc->inuse) != init ||
357219019Sgabor			    psenc->inuse == init || candidate == init)
358219019Sgabor				init = NULL;
359219019Sgabor		} else if (candidate == (init = INIT(candidate))) {
360219019Sgabor			init = NULL;
361219019Sgabor		}
362219019Sgabor		if (init != NULL) {
363219019Sgabor			if (n < 2)
364219019Sgabor				return (E2BIG);
365219019Sgabor			n -= 2;
366219019Sgabor			psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
367219019Sgabor			psenc->ch[psenc->chlen++] = ESC(init);
368219019Sgabor		}
369219019Sgabor		if (n < 2)
370219019Sgabor			return (E2BIG);
371219019Sgabor		n -= 2;
372219019Sgabor		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
373219019Sgabor		psenc->ch[psenc->chlen++] = ESC(candidate);
374219019Sgabor		psenc->inuse = candidate;
375219019Sgabor	}
376219019Sgabor	if (n < len)
377219019Sgabor		return (E2BIG);
378219019Sgabor	while (len-- > 0) {
379219019Sgabor		ch = (wc >> (len * 8)) & 0xFF;
380219019Sgabor		if (range->start > ch || range->end < ch)
381219019Sgabor			goto ilseq;
382219019Sgabor		psenc->ch[psenc->chlen++] = ch | bit;
383219019Sgabor	}
384219019Sgabor	memcpy(s, psenc->ch, psenc->chlen);
385219019Sgabor	*nresult = psenc->chlen;
386219019Sgabor	psenc->chlen = 0;
387219019Sgabor
388219019Sgabor	return (0);
389219019Sgabor
390219019Sgaborilseq:
391219019Sgabor	*nresult = (size_t)-1;
392219019Sgabor	return (EILSEQ);
393219019Sgabor}
394219019Sgabor
395219019Sgaborstatic __inline int
396219019Sgabor_citrus_HZ_put_state_reset(_HZEncodingInfo * __restrict ei,
397219019Sgabor    char * __restrict s, size_t n, _HZState * __restrict psenc,
398219019Sgabor    size_t * __restrict nresult)
399219019Sgabor{
400219019Sgabor	escape_t *candidate;
401219019Sgabor
402219019Sgabor	if (psenc->chlen != 0 || psenc->inuse == NULL)
403219019Sgabor		return (EINVAL);
404219019Sgabor	candidate = INIT0(ei);
405219019Sgabor	if (psenc->inuse != candidate) {
406219019Sgabor		if (n < 2)
407219019Sgabor			return (E2BIG);
408219019Sgabor		n -= 2;
409219019Sgabor		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
410219019Sgabor		psenc->ch[psenc->chlen++] = ESC(candidate);
411219019Sgabor	}
412219019Sgabor	if (n < 1)
413219019Sgabor		return (E2BIG);
414219019Sgabor	if (psenc->chlen > 0)
415219019Sgabor		memcpy(s, psenc->ch, psenc->chlen);
416219019Sgabor	*nresult = psenc->chlen;
417219019Sgabor	_citrus_HZ_init_state(ei, psenc);
418219019Sgabor
419219019Sgabor	return (0);
420219019Sgabor}
421219019Sgabor
422219019Sgaborstatic __inline int
423219019Sgabor_citrus_HZ_stdenc_get_state_desc_generic(_HZEncodingInfo * __restrict ei,
424219019Sgabor    _HZState * __restrict psenc, int * __restrict rstate)
425219019Sgabor{
426219019Sgabor
427219019Sgabor	if (psenc->chlen < 0 || psenc->inuse == NULL)
428219019Sgabor		return (EINVAL);
429219019Sgabor	*rstate = (psenc->chlen == 0)
430219019Sgabor	    ? ((psenc->inuse == INIT0(ei))
431219019Sgabor	        ? _STDENC_SDGEN_INITIAL
432219019Sgabor	        : _STDENC_SDGEN_STABLE)
433219019Sgabor	    : ((psenc->ch[0] == ESCAPE_CHAR)
434219019Sgabor	        ? _STDENC_SDGEN_INCOMPLETE_SHIFT
435219019Sgabor	        : _STDENC_SDGEN_INCOMPLETE_CHAR);
436219019Sgabor
437219019Sgabor	return (0);
438219019Sgabor}
439219019Sgabor
440219019Sgaborstatic __inline int
441219019Sgabor/*ARGSUSED*/
442219019Sgabor_citrus_HZ_stdenc_wctocs(_HZEncodingInfo * __restrict ei __unused,
443219019Sgabor    _csid_t * __restrict csid, _index_t * __restrict idx, wchar_t wc)
444219019Sgabor{
445219019Sgabor	int bit;
446219019Sgabor
447219019Sgabor	if (wc & 0x80) {
448219019Sgabor		bit = 0x80;
449219019Sgabor		wc &= ~0x80;
450219019Sgabor	} else
451219019Sgabor		bit = 0x0;
452219019Sgabor	if ((uint32_t)wc <= 0x7F) {
453219019Sgabor		*csid = (_csid_t)bit;
454219019Sgabor		*idx = (_index_t)wc;
455219019Sgabor	} else if ((uint32_t)wc <= 0x7F7F) {
456219019Sgabor		*csid = (_csid_t)(bit | 0x8000);
457219019Sgabor		*idx = (_index_t)wc;
458219019Sgabor	} else {
459219019Sgabor		*csid = (_index_t)(wc & ~0x00FFFF7F);
460219019Sgabor		*idx = (_csid_t)(wc & 0x00FFFF7F);
461219019Sgabor	}
462219019Sgabor
463219019Sgabor	return (0);
464219019Sgabor}
465219019Sgabor
466219019Sgaborstatic __inline int
467219019Sgabor/*ARGSUSED*/
468219019Sgabor_citrus_HZ_stdenc_cstowc(_HZEncodingInfo * __restrict ei __unused,
469219019Sgabor    wchar_t * __restrict wc, _csid_t csid, _index_t idx)
470219019Sgabor{
471219019Sgabor
472219019Sgabor	*wc = (wchar_t)idx;
473219019Sgabor	switch (csid) {
474219019Sgabor	case 0x80:
475219019Sgabor	case 0x8080:
476219019Sgabor		*wc |= (wchar_t)0x80;
477219019Sgabor		/*FALLTHROUGH*/
478219019Sgabor	case 0x0:
479219019Sgabor	case 0x8000:
480219019Sgabor		break;
481219019Sgabor	default:
482219019Sgabor		*wc |= (wchar_t)csid;
483219019Sgabor	}
484219019Sgabor
485219019Sgabor	return (0);
486219019Sgabor}
487219019Sgabor
488219019Sgaborstatic void
489219019Sgabor_citrus_HZ_encoding_module_uninit(_HZEncodingInfo *ei)
490219019Sgabor{
491219019Sgabor	escape_t *escape;
492219019Sgabor
493219019Sgabor	while ((escape = TAILQ_FIRST(E0SET(ei))) != NULL) {
494219019Sgabor		TAILQ_REMOVE(E0SET(ei), escape, entry);
495219019Sgabor		free(GL(escape));
496219019Sgabor		free(GR(escape));
497219019Sgabor		free(escape);
498219019Sgabor	}
499219019Sgabor	while ((escape = TAILQ_FIRST(E1SET(ei))) != NULL) {
500219019Sgabor		TAILQ_REMOVE(E1SET(ei), escape, entry);
501219019Sgabor		free(GL(escape));
502219019Sgabor		free(GR(escape));
503219019Sgabor		free(escape);
504219019Sgabor	}
505219019Sgabor}
506219019Sgabor
507219019Sgaborstatic int
508219019Sgabor_citrus_HZ_parse_char(void **context, const char *name __unused, const char *s)
509219019Sgabor{
510219019Sgabor	escape_t *escape;
511219019Sgabor	void **p;
512219019Sgabor
513219019Sgabor	p = (void **)*context;
514219019Sgabor	escape = (escape_t *)p[0];
515219019Sgabor	if (escape->ch != '\0')
516219019Sgabor		return (EINVAL);
517219019Sgabor	escape->ch = *s++;
518219019Sgabor	if (escape->ch == ESCAPE_CHAR || *s != '\0')
519219019Sgabor		return (EINVAL);
520219019Sgabor
521219019Sgabor	return (0);
522219019Sgabor}
523219019Sgabor
524219019Sgaborstatic int
525219019Sgabor_citrus_HZ_parse_graphic(void **context, const char *name, const char *s)
526219019Sgabor{
527219019Sgabor	_HZEncodingInfo *ei;
528219019Sgabor	escape_t *escape;
529219019Sgabor	graphic_t *graphic;
530219019Sgabor	void **p;
531219019Sgabor
532219019Sgabor	p = (void **)*context;
533219019Sgabor	escape = (escape_t *)p[0];
534219019Sgabor	ei = (_HZEncodingInfo *)p[1];
535219019Sgabor	graphic = malloc(sizeof(*graphic));
536219019Sgabor	if (graphic == NULL)
537219019Sgabor		return (ENOMEM);
538219019Sgabor	memset(graphic, 0, sizeof(*graphic));
539219019Sgabor	if (strcmp("GL", name) == 0) {
540219019Sgabor		if (GL(escape) != NULL)
541219019Sgabor			goto release;
542219019Sgabor		GL(escape) = graphic;
543219019Sgabor	} else if (strcmp("GR", name) == 0) {
544219019Sgabor		if (GR(escape) != NULL)
545219019Sgabor			goto release;
546219019Sgabor		GR(escape) = graphic;
547219019Sgabor	} else {
548219019Sgaborrelease:
549219019Sgabor		free(graphic);
550219019Sgabor		return (EINVAL);
551219019Sgabor	}
552219019Sgabor	graphic->escape = escape;
553219019Sgabor	if (_bcs_strncasecmp("ASCII", s, 5) == 0) {
554219019Sgabor		if (s[5] != '\0')
555219019Sgabor			return (EINVAL);
556219019Sgabor		graphic->charset = ASCII;
557219019Sgabor		graphic->length = 1;
558219019Sgabor		ei->ascii = graphic;
559219019Sgabor		return (0);
560219019Sgabor	} else if (_bcs_strncasecmp("GB2312", s, 6) == 0) {
561219019Sgabor		if (s[6] != '\0')
562219019Sgabor			return (EINVAL);
563219019Sgabor		graphic->charset = GB2312;
564219019Sgabor		graphic->length = 2;
565219019Sgabor		ei->gb2312 = graphic;
566219019Sgabor		return (0);
567219019Sgabor	} else if (strncmp("94*", s, 3) == 0)
568219019Sgabor		graphic->charset = CS94;
569219019Sgabor	else if (strncmp("96*", s, 3) == 0)
570219019Sgabor		graphic->charset = CS96;
571219019Sgabor	else
572219019Sgabor		return (EINVAL);
573219019Sgabor	s += 3;
574219019Sgabor	switch(*s) {
575219019Sgabor	case '1': case '2': case '3':
576219019Sgabor		graphic->length = (size_t)(*s - '0');
577219019Sgabor		if (*++s == '\0')
578219019Sgabor			break;
579219019Sgabor	/*FALLTHROUGH*/
580219019Sgabor	default:
581219019Sgabor		return (EINVAL);
582219019Sgabor	}
583219019Sgabor	return (0);
584219019Sgabor}
585219019Sgabor
586219019Sgaborstatic const _citrus_prop_hint_t escape_hints[] = {
587219019Sgabor_CITRUS_PROP_HINT_STR("CH", &_citrus_HZ_parse_char),
588219019Sgabor_CITRUS_PROP_HINT_STR("GL", &_citrus_HZ_parse_graphic),
589219019Sgabor_CITRUS_PROP_HINT_STR("GR", &_citrus_HZ_parse_graphic),
590219019Sgabor_CITRUS_PROP_HINT_END
591219019Sgabor};
592219019Sgabor
593219019Sgaborstatic int
594219019Sgabor_citrus_HZ_parse_escape(void **context, const char *name, const char *s)
595219019Sgabor{
596219019Sgabor	_HZEncodingInfo *ei;
597219019Sgabor	escape_t *escape;
598219019Sgabor	void *p[2];
599219019Sgabor
600219019Sgabor	ei = (_HZEncodingInfo *)*context;
601219019Sgabor	escape = malloc(sizeof(*escape));
602219019Sgabor	if (escape == NULL)
603219019Sgabor		return (EINVAL);
604219019Sgabor	memset(escape, 0, sizeof(*escape));
605219019Sgabor	if (strcmp("0", name) == 0) {
606219019Sgabor		escape->set = E0SET(ei);
607219019Sgabor		TAILQ_INSERT_TAIL(E0SET(ei), escape, entry);
608219019Sgabor	} else if (strcmp("1", name) == 0) {
609219019Sgabor		escape->set = E1SET(ei);
610219019Sgabor		TAILQ_INSERT_TAIL(E1SET(ei), escape, entry);
611219019Sgabor	} else {
612219019Sgabor		free(escape);
613219019Sgabor		return (EINVAL);
614219019Sgabor	}
615219019Sgabor	p[0] = (void *)escape;
616219019Sgabor	p[1] = (void *)ei;
617219019Sgabor	return (_citrus_prop_parse_variable(
618219019Sgabor	    escape_hints, (void *)&p[0], s, strlen(s)));
619219019Sgabor}
620219019Sgabor
621219019Sgaborstatic const _citrus_prop_hint_t root_hints[] = {
622219019Sgabor_CITRUS_PROP_HINT_STR("0", &_citrus_HZ_parse_escape),
623219019Sgabor_CITRUS_PROP_HINT_STR("1", &_citrus_HZ_parse_escape),
624219019Sgabor_CITRUS_PROP_HINT_END
625219019Sgabor};
626219019Sgabor
627219019Sgaborstatic int
628219019Sgabor_citrus_HZ_encoding_module_init(_HZEncodingInfo * __restrict ei,
629219019Sgabor    const void * __restrict var, size_t lenvar)
630219019Sgabor{
631219019Sgabor	int errnum;
632219019Sgabor
633219019Sgabor	memset(ei, 0, sizeof(*ei));
634219019Sgabor	TAILQ_INIT(E0SET(ei));
635219019Sgabor	TAILQ_INIT(E1SET(ei));
636219019Sgabor	errnum = _citrus_prop_parse_variable(
637219019Sgabor	    root_hints, (void *)ei, var, lenvar);
638219019Sgabor	if (errnum != 0)
639219019Sgabor		_citrus_HZ_encoding_module_uninit(ei);
640219019Sgabor	return (errnum);
641219019Sgabor}
642219019Sgabor
643219019Sgabor/* ----------------------------------------------------------------------
644219019Sgabor * public interface for stdenc
645219019Sgabor */
646219019Sgabor
647219019Sgabor_CITRUS_STDENC_DECLS(HZ);
648219019Sgabor_CITRUS_STDENC_DEF_OPS(HZ);
649219019Sgabor
650219019Sgabor#include "citrus_stdenc_template.h"
651