Home | History | Annotate | Line # | Download | only in modules
citrus_hz.c revision 1.4
      1 /* $NetBSD: citrus_hz.c,v 1.4 2014/06/24 22:24:18 spz Exp $ */
      2 
      3 /*-
      4  * Copyright (c)2004, 2006 Citrus Project,
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  *
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 #if defined(LIBC_SCCS) && !defined(lint)
     32 __RCSID("$NetBSD: citrus_hz.c,v 1.4 2014/06/24 22:24:18 spz Exp $");
     33 #endif /* LIBC_SCCS and not lint */
     34 
     35 #include <sys/queue.h>
     36 #include <sys/types.h>
     37 #include <assert.h>
     38 #include <errno.h>
     39 #include <string.h>
     40 #include <stdint.h>
     41 #include <stdlib.h>
     42 #include <stddef.h>
     43 #include <limits.h>
     44 #include <wchar.h>
     45 
     46 #include "citrus_namespace.h"
     47 #include "citrus_types.h"
     48 #include "citrus_bcs.h"
     49 #include "citrus_module.h"
     50 #include "citrus_ctype.h"
     51 #include "citrus_stdenc.h"
     52 
     53 #include "citrus_hz.h"
     54 #include "citrus_prop.h"
     55 
     56 /*
     57  * wchar_t mapping:
     58  *
     59  * CTRL/ASCII	00000000 00000000 00000000 gxxxxxxx
     60  * GB2312	00000000 00000000 0xxxxxxx gxxxxxxx
     61  * 94/96*n (~M)	0mmmmmmm 0xxxxxxx 0xxxxxxx gxxxxxxx
     62  */
     63 
     64 #define ESCAPE_CHAR	'~'
     65 
     66 typedef enum {
     67 	CTRL = 0, ASCII = 1, GB2312 = 2, CS94 = 3, CS96 = 4
     68 } charset_t;
     69 
     70 typedef struct {
     71 	int start, end, width;
     72 } range_t;
     73 
     74 static const range_t ranges[] = {
     75 #define RANGE(start, end) { start, end, (end - start) + 1 }
     76 /* CTRL   */ RANGE(0x00, 0x1F),
     77 /* ASCII  */ RANGE(0x20, 0x7F),
     78 /* GB2312 */ RANGE(0x21, 0x7E),
     79 /* CS94   */ RANGE(0x21, 0x7E),
     80 /* CS96   */ RANGE(0x20, 0x7F),
     81 #undef RANGE
     82 };
     83 
     84 typedef struct escape_t escape_t;
     85 typedef struct {
     86 	charset_t charset;
     87 	size_t length;
     88 #define ROWCOL_MAX	3
     89 	escape_t *escape;
     90 } graphic_t;
     91 
     92 typedef TAILQ_HEAD(escape_list, escape_t) escape_list;
     93 struct escape_t {
     94 	TAILQ_ENTRY(escape_t) entry;
     95 	int ch;
     96 	graphic_t *left, *right;
     97 	escape_list *set;
     98 };
     99 
    100 #define GL(escape)	((escape)->left)
    101 #define GR(escape)	((escape)->right)
    102 #define SET(escape)	((escape)->set)
    103 #define ESC(escape)	((escape)->ch)
    104 #define INIT(escape)	(TAILQ_FIRST(SET(escape)))
    105 
    106 static __inline escape_t *
    107 find_escape(escape_list *set, int ch)
    108 {
    109 	escape_t *escape;
    110 
    111 	_DIAGASSERT(set != NULL);
    112 
    113 	TAILQ_FOREACH(escape, set, entry) {
    114 		if (ESC(escape) == ch)
    115 			break;
    116 	}
    117 
    118 	return escape;
    119 }
    120 
    121 typedef struct {
    122 	escape_list e0, e1;
    123 	graphic_t *ascii, *gb2312;
    124 } _HZEncodingInfo;
    125 
    126 #define E0SET(ei)	(&(ei)->e0)
    127 #define E1SET(ei)	(&(ei)->e1)
    128 #define INIT0(ei)	(TAILQ_FIRST(E0SET(ei)))
    129 #define INIT1(ei)	(TAILQ_FIRST(E1SET(ei)))
    130 
    131 typedef struct {
    132 	int chlen;
    133 	char ch[ROWCOL_MAX];
    134 	escape_t *inuse;
    135 } _HZState;
    136 
    137 typedef struct {
    138 	_HZEncodingInfo		ei;
    139 	struct {
    140 		/* for future multi-locale facility */
    141 		_HZState	s_mblen;
    142 		_HZState	s_mbrlen;
    143 		_HZState	s_mbrtowc;
    144 		_HZState	s_mbtowc;
    145 		_HZState	s_mbsrtowcs;
    146 		_HZState	s_mbsnrtowcs;
    147 		_HZState	s_wcrtomb;
    148 		_HZState	s_wcsrtombs;
    149 		_HZState	s_wcsnrtombs;
    150 		_HZState	s_wctomb;
    151 	} states;
    152 } _HZCTypeInfo;
    153 
    154 #define _CEI_TO_EI(_cei_)		(&(_cei_)->ei)
    155 #define _CEI_TO_STATE(_cei_, _func_)	(_cei_)->states.s_##_func_
    156 
    157 #define _FUNCNAME(m)			_citrus_HZ_##m
    158 #define _ENCODING_INFO			_HZEncodingInfo
    159 #define _CTYPE_INFO			_HZCTypeInfo
    160 #define _ENCODING_STATE			_HZState
    161 #define _ENCODING_MB_CUR_MAX(_ei_)	MB_LEN_MAX
    162 #define _ENCODING_IS_STATE_DEPENDENT		1
    163 #define _STATE_NEEDS_EXPLICIT_INIT(_ps_)	((_ps_)->inuse == NULL)
    164 
    165 static __inline void
    166 _citrus_HZ_init_state(_HZEncodingInfo * __restrict ei,
    167 	_HZState * __restrict psenc)
    168 {
    169 	_DIAGASSERT(ei != NULL);
    170 	_DIAGASSERT(psenc != NULL);
    171 
    172 	psenc->chlen = 0;
    173 	psenc->inuse = INIT0(ei);
    174 }
    175 
    176 static __inline void
    177 /*ARGSUSED*/
    178 _citrus_HZ_pack_state(_HZEncodingInfo * __restrict ei,
    179 	void *__restrict pspriv, const _HZState * __restrict psenc)
    180 {
    181 	/* ei may be unused */
    182 	_DIAGASSERT(pspriv != NULL);
    183 	_DIAGASSERT(psenc != NULL);
    184 
    185 	memcpy(pspriv, (const void *)psenc, sizeof(*psenc));
    186 }
    187 
    188 static __inline void
    189 /*ARGSUSED*/
    190 _citrus_HZ_unpack_state(_HZEncodingInfo * __restrict ei,
    191 	_HZState * __restrict psenc, const void * __restrict pspriv)
    192 {
    193 	/* ei may be unused */
    194 	_DIAGASSERT(psenc != NULL);
    195 	_DIAGASSERT(pspriv != NULL);
    196 
    197 	memcpy((void *)psenc, pspriv, sizeof(*psenc));
    198 }
    199 
    200 static int
    201 _citrus_HZ_mbrtowc_priv(_HZEncodingInfo * __restrict ei,
    202 	wchar_t * __restrict pwc, const char ** __restrict s, size_t n,
    203 	_HZState * __restrict psenc, size_t * __restrict nresult)
    204 {
    205 	const char *s0;
    206 	wchar_t wc;
    207 	int bit, head, tail, len, ch;
    208 	graphic_t *graphic;
    209 	escape_t *candidate, *init;
    210 	const range_t *range;
    211 
    212 	_DIAGASSERT(ei != NULL);
    213 	/* pwc may be null */
    214 	_DIAGASSERT(s != NULL);
    215 	_DIAGASSERT(psenc != NULL);
    216 	_DIAGASSERT(nresult != NULL);
    217 
    218 	if (*s == NULL) {
    219 		_citrus_HZ_init_state(ei, psenc);
    220 		*nresult = 1;
    221 		return 0;
    222 	}
    223 	s0 = *s;
    224 	if (psenc->chlen < 0 || psenc->inuse == NULL)
    225 		return EINVAL;
    226 
    227 	wc = (wchar_t)0;
    228 	bit = head = tail = 0;
    229 	graphic = NULL;
    230 	for (len = 0; len <= MB_LEN_MAX; /**/) {
    231 		if (psenc->chlen == tail) {
    232 			if (n-- < 1) {
    233 				*s = s0;
    234 				*nresult = (size_t)-2;
    235 				return 0;
    236 			}
    237 			psenc->ch[psenc->chlen++] = *s0++;
    238 			++len;
    239 		}
    240 		ch = (unsigned char)psenc->ch[tail++];
    241 		if (tail == 1) {
    242 			if ((ch & ~0x80) <= 0x1F) {
    243 				if (psenc->inuse != INIT0(ei))
    244 					break;
    245 				wc = (wchar_t)ch;
    246 				goto done;
    247 			}
    248 			if (ch & 0x80) {
    249 				graphic = GR(psenc->inuse);
    250 				bit = 0x80;
    251 				ch &= ~0x80;
    252 			} else {
    253 				graphic = GL(psenc->inuse);
    254 				if (ch == ESCAPE_CHAR)
    255 					continue;
    256 				bit = 0x0;
    257 			}
    258 			if (graphic == NULL)
    259 				break;
    260 		} else if (tail == 2 && psenc->ch[0] == ESCAPE_CHAR) {
    261 			if (tail < psenc->chlen)
    262 				return EINVAL;
    263 			if (ch == ESCAPE_CHAR) {
    264 				++head;
    265 			} else if (ch == '\n') {
    266 				if (psenc->inuse != INIT0(ei))
    267 					break;
    268 				tail = psenc->chlen = 0;
    269 				continue;
    270 			} else {
    271 				candidate = NULL;
    272 				init = INIT0(ei);
    273 				_DIAGASSERT(init != NULL);
    274 				if (psenc->inuse == init) {
    275 					init = INIT1(ei);
    276 				} else if (INIT(psenc->inuse) == init) {
    277 					if (ESC(init) != ch)
    278 						break;
    279 					candidate = init;
    280 				}
    281 				if (candidate == NULL) {
    282 					candidate = find_escape(
    283 					    SET(psenc->inuse), ch);
    284 					if (candidate == NULL) {
    285 						if (init == NULL ||
    286 						    ESC(init) != ch)
    287 							break;
    288 						candidate = init;
    289 					}
    290 				}
    291 				psenc->inuse = candidate;
    292 				tail = psenc->chlen = 0;
    293 				continue;
    294 			}
    295 		} else if (ch & 0x80) {
    296 			if (graphic != GR(psenc->inuse))
    297 				break;
    298 			ch &= ~0x80;
    299 		} else {
    300 			if (graphic != GL(psenc->inuse))
    301 				break;
    302 		}
    303 		_DIAGASSERT(graphic != NULL);
    304 		range = &ranges[(size_t)graphic->charset];
    305 		if (range->start > ch || range->end < ch)
    306 			break;
    307 		wc <<= 8;
    308 		wc |= ch;
    309 		if (graphic->length == (tail - head)) {
    310 			if (graphic->charset > GB2312)
    311 				bit |= ESC(psenc->inuse) << 24;
    312 			wc |= bit;
    313 			goto done;
    314 		}
    315 	}
    316 	*nresult = (size_t)-1;
    317 	return EILSEQ;
    318 done:
    319 	if (tail < psenc->chlen)
    320 		return EINVAL;
    321 	*s = s0;
    322 	if (pwc != NULL)
    323 		*pwc = wc;
    324 	psenc->chlen = 0;
    325 	*nresult = (wc == 0) ? 0 : len;
    326 
    327 	return 0;
    328 }
    329 
    330 static int
    331 _citrus_HZ_wcrtomb_priv(_HZEncodingInfo * __restrict ei,
    332 	char * __restrict s, size_t n, wchar_t wc,
    333 	_HZState * __restrict psenc, size_t * __restrict nresult)
    334 {
    335 	int bit, ch;
    336 	escape_t *candidate, *init;
    337 	graphic_t *graphic;
    338 	size_t len;
    339 	const range_t *range;
    340 
    341 	_DIAGASSERT(ei != NULL);
    342 	_DIAGASSERT(s != NULL);
    343 	_DIAGASSERT(psenc != NULL);
    344 	_DIAGASSERT(nresult != NULL);
    345 
    346 	if (psenc->chlen != 0 || psenc->inuse == NULL)
    347 		return EINVAL;
    348 	if (wc & 0x80) {
    349 		bit = 0x80;
    350 		wc &= ~0x80;
    351 	} else {
    352 		bit = 0x0;
    353 	}
    354 	if ((uint32_t)wc <= 0x1F) {
    355 		candidate = INIT0(ei);
    356 		graphic = (bit == 0)
    357 		    ? candidate->left : candidate->right;
    358 		if (graphic == NULL)
    359 			goto ilseq;
    360 		range = &ranges[(size_t)CTRL];
    361 		len = 1;
    362 	} else if ((uint32_t)wc <= 0x7F) {
    363 		graphic = ei->ascii;
    364 		if (graphic == NULL)
    365 			goto ilseq;
    366 		candidate = graphic->escape;
    367 		range = &ranges[(size_t)graphic->charset];
    368 		len = graphic->length;
    369 	} else if ((uint32_t)wc <= 0x7F7F) {
    370 		graphic = ei->gb2312;
    371 		if (graphic == NULL)
    372 			goto ilseq;
    373 		candidate = graphic->escape;
    374 		range = &ranges[(size_t)graphic->charset];
    375 		len = graphic->length;
    376 	} else {
    377 		ch = (wc >> 24) & 0xFF;
    378 		candidate = find_escape(E0SET(ei), ch);
    379 		if (candidate == NULL) {
    380 			candidate = find_escape(E1SET(ei), ch);
    381 			if (candidate == NULL)
    382 				goto ilseq;
    383 		}
    384 		wc &= ~0xFF000000;
    385 		graphic = (bit == 0)
    386 		    ? candidate->left : candidate->right;
    387 		if (graphic == NULL)
    388 			goto ilseq;
    389 		range = &ranges[(size_t)graphic->charset];
    390 		len = graphic->length;
    391 	}
    392 	if (psenc->inuse != candidate) {
    393 		init = INIT0(ei);
    394 		if (SET(psenc->inuse) == SET(candidate)) {
    395 			if (INIT(psenc->inuse) != init ||
    396 			    psenc->inuse == init || candidate == init)
    397 				init = NULL;
    398 		} else if (candidate == (init = INIT(candidate))) {
    399 			init = NULL;
    400 		}
    401 		if (init != NULL) {
    402 			if (n < 2)
    403 				return E2BIG;
    404 			n -= 2;
    405 			psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
    406 			psenc->ch[psenc->chlen++] = ESC(init);
    407 		}
    408 		if (n < 2)
    409 			return E2BIG;
    410 		n -= 2;
    411 		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
    412 		psenc->ch[psenc->chlen++] = ESC(candidate);
    413 		psenc->inuse = candidate;
    414 	}
    415 	if (n < len)
    416 		return E2BIG;
    417 	while (len-- > 0) {
    418 		ch = (wc >> (len * 8)) & 0xFF;
    419 		if (range->start > ch || range->end < ch)
    420 			goto ilseq;
    421 		psenc->ch[psenc->chlen++] = ch | bit;
    422 	}
    423 	memcpy(s, psenc->ch, psenc->chlen);
    424 	*nresult = psenc->chlen;
    425 	psenc->chlen = 0;
    426 
    427 	return 0;
    428 
    429 ilseq:
    430 	*nresult = (size_t)-1;
    431 	return EILSEQ;
    432 }
    433 
    434 static __inline int
    435 _citrus_HZ_put_state_reset(_HZEncodingInfo * __restrict ei,
    436 	char * __restrict s, size_t n, _HZState * __restrict psenc,
    437 	size_t * __restrict nresult)
    438 {
    439 	escape_t *candidate;
    440 
    441 	_DIAGASSERT(ei != NULL);
    442 	_DIAGASSERT(s != NULL);
    443 	_DIAGASSERT(psenc != NULL);
    444 	_DIAGASSERT(nresult != NULL);
    445 
    446 	if (psenc->chlen != 0 || psenc->inuse == NULL)
    447 		return EINVAL;
    448 	candidate = INIT0(ei);
    449 	if (psenc->inuse != candidate) {
    450 		if (n < 2)
    451 			return E2BIG;
    452 		n -= 2;
    453 		psenc->ch[psenc->chlen++] = ESCAPE_CHAR;
    454 		psenc->ch[psenc->chlen++] = ESC(candidate);
    455 	}
    456 	if (n < 1)
    457 		return E2BIG;
    458 	if (psenc->chlen > 0)
    459 		memcpy(s, psenc->ch, psenc->chlen);
    460 	*nresult = psenc->chlen;
    461 	_citrus_HZ_init_state(ei, psenc);
    462 
    463 	return 0;
    464 }
    465 
    466 static __inline int
    467 _citrus_HZ_stdenc_get_state_desc_generic(_HZEncodingInfo * __restrict ei,
    468 	_HZState * __restrict psenc, int * __restrict rstate)
    469 {
    470 	_DIAGASSERT(ei != NULL);
    471 	_DIAGASSERT(psenc != NULL);
    472 	_DIAGASSERT(rstate != NULL);
    473 
    474 	if (psenc->chlen < 0 || psenc->inuse == NULL)
    475 		return EINVAL;
    476 	*rstate = (psenc->chlen == 0)
    477 	    ? ((psenc->inuse == INIT0(ei))
    478 	        ? _STDENC_SDGEN_INITIAL
    479 	        : _STDENC_SDGEN_STABLE)
    480 	    : ((psenc->ch[0] == ESCAPE_CHAR)
    481 	        ? _STDENC_SDGEN_INCOMPLETE_SHIFT
    482 	        : _STDENC_SDGEN_INCOMPLETE_CHAR);
    483 
    484 	return 0;
    485 }
    486 
    487 static __inline int
    488 /*ARGSUSED*/
    489 _citrus_HZ_stdenc_wctocs(_HZEncodingInfo * __restrict ei,
    490 	_csid_t * __restrict csid, _index_t * __restrict idx, wchar_t wc)
    491 {
    492 	int bit;
    493 
    494 	_DIAGASSERT(csid != NULL);
    495 	_DIAGASSERT(idx != NULL);
    496 
    497 	if (wc & 0x80) {
    498 		bit = 0x80;
    499 		wc &= ~0x80;
    500 	} else {
    501 		bit = 0x0;
    502 	}
    503 	if ((uint32_t)wc <= 0x7F) {
    504 		*csid = (_csid_t)bit;
    505 		*idx = (_index_t)wc;
    506 	} else if ((uint32_t)wc <= 0x7F7F) {
    507 		*csid = (_csid_t)(bit | 0x8000);
    508 		*idx = (_index_t)wc;
    509 	} else {
    510 		*csid = (_index_t)(wc & ~0x00FFFF7F);
    511 		*idx = (_csid_t)(wc & 0x00FFFF7F);
    512 	}
    513 
    514 	return 0;
    515 }
    516 
    517 static __inline int
    518 /*ARGSUSED*/
    519 _citrus_HZ_stdenc_cstowc(_HZEncodingInfo * __restrict ei,
    520 	wchar_t * __restrict wc, _csid_t csid, _index_t idx)
    521 {
    522 	_DIAGASSERT(ei != NULL);
    523 	_DIAGASSERT(wc != NULL);
    524 
    525 	*wc = (wchar_t)idx;
    526 	switch (csid) {
    527 	case 0x80:
    528 	case 0x8080:
    529 		*wc |= (wchar_t)0x80;
    530 	/*FALLTHROUGH*/
    531 	case 0x0:
    532 	case 0x8000:
    533 		break;
    534 	default:
    535 		*wc |= (wchar_t)csid;
    536 	}
    537 
    538 	return 0;
    539 }
    540 
    541 static void
    542 _citrus_HZ_encoding_module_uninit(_HZEncodingInfo *ei)
    543 {
    544 	escape_t *escape;
    545 
    546 	_DIAGASSERT(ei != NULL);
    547 	while ((escape = TAILQ_FIRST(E0SET(ei))) != NULL) {
    548 		TAILQ_REMOVE(E0SET(ei), escape, entry);
    549 		free(GL(escape));
    550 		free(GR(escape));
    551 		free(escape);
    552 	}
    553 	while ((escape = TAILQ_FIRST(E1SET(ei))) != NULL) {
    554 		TAILQ_REMOVE(E1SET(ei), escape, entry);
    555 		free(GL(escape));
    556 		free(GR(escape));
    557 		free(escape);
    558 	}
    559 }
    560 
    561 static int
    562 _citrus_HZ_parse_char(void *context, const char *name, const char *s)
    563 {
    564 	void **p;
    565 	escape_t *escape;
    566 
    567 	_DIAGASSERT(context != NULL && *context != NULL);
    568 	_DIAGASSERT(name != NULL);
    569 	_DIAGASSERT(s != NULL);
    570 
    571 	p = (void **)context;
    572 	escape = (escape_t *)p[0];
    573 	if (escape->ch != '\0')
    574 		return EINVAL;
    575 	escape->ch = *s++;
    576 	if (escape->ch == ESCAPE_CHAR || *s != '\0')
    577 		return EINVAL;
    578 
    579 	return 0;
    580 }
    581 
    582 static int
    583 _citrus_HZ_parse_graphic(void *context, const char *name, const char *s)
    584 {
    585 	void **p;
    586 	_HZEncodingInfo *ei;
    587 	escape_t *escape;
    588 	graphic_t *graphic;
    589 
    590 	_DIAGASSERT(context != NULL && *context != NULL);
    591 	_DIAGASSERT(name != NULL);
    592 	_DIAGASSERT(s != NULL);
    593 
    594 	p = (void **)context;
    595 	escape = (escape_t *)p[0];
    596 	ei = (_HZEncodingInfo *)p[1];
    597 	graphic = malloc(sizeof(*graphic));
    598 	if (graphic == NULL)
    599 		return ENOMEM;
    600 	memset(graphic, 0, sizeof(*graphic));
    601 	if (strcmp("GL", name) == 0) {
    602 		if (GL(escape) != NULL)
    603 			goto release;
    604 		GL(escape) = graphic;
    605 	} else if (strcmp("GR", name) == 0) {
    606 		if (GR(escape) != NULL)
    607 			goto release;
    608 		GR(escape) = graphic;
    609 	} else {
    610 release:
    611 		free(graphic);
    612 		return EINVAL;
    613 	}
    614 	graphic->escape = escape;
    615 	if (_bcs_strncasecmp("ASCII", s, 5) == 0) {
    616 		if (s[5] != '\0')
    617 			return EINVAL;
    618 		graphic->charset = ASCII;
    619 		graphic->length = 1;
    620 		ei->ascii = graphic;
    621 		return 0;
    622 	} else if (_bcs_strncasecmp("GB2312", s, 6) == 0) {
    623 		if (s[6] != '\0')
    624 			return EINVAL;
    625 		graphic->charset = GB2312;
    626 		graphic->length = 2;
    627 		ei->gb2312 = graphic;
    628 		return 0;
    629 	} else if (strncmp("94*", s, 3) == 0) {
    630 		graphic->charset = CS94;
    631 	} else if (strncmp("96*", s, 3) == 0) {
    632 		graphic->charset = CS96;
    633 	} else {
    634 		return EINVAL;
    635 	}
    636 	s += 3;
    637 	switch(*s) {
    638 	case '1': case '2': case '3':
    639 		graphic->length = (size_t)(*s - '0');
    640 		if (*++s == '\0')
    641 			break;
    642 	/*FALLTHROUGH*/
    643 	default:
    644 		return EINVAL;
    645 	}
    646 	return 0;
    647 }
    648 
    649 static const _citrus_prop_hint_t escape_hints[] = {
    650 _CITRUS_PROP_HINT_STR("CH", &_citrus_HZ_parse_char),
    651 _CITRUS_PROP_HINT_STR("GL", &_citrus_HZ_parse_graphic),
    652 _CITRUS_PROP_HINT_STR("GR", &_citrus_HZ_parse_graphic),
    653 _CITRUS_PROP_HINT_END
    654 };
    655 
    656 static int
    657 _citrus_HZ_parse_escape(void *context, const char *name, const char *s)
    658 {
    659 	_HZEncodingInfo *ei;
    660 	escape_t *escape;
    661 	void *p[2];
    662 
    663 	_DIAGASSERT(context != NULL);
    664 	_DIAGASSERT(name != NULL);
    665 	_DIAGASSERT(s != NULL);
    666 
    667 	ei = (_HZEncodingInfo *)context;
    668 	escape = malloc(sizeof(*escape));
    669 	if (escape == NULL)
    670 		return EINVAL;
    671 	memset(escape, 0, sizeof(*escape));
    672 	if (strcmp("0", name) == 0) {
    673 		escape->set = E0SET(ei);
    674 		TAILQ_INSERT_TAIL(E0SET(ei), escape, entry);
    675 	} else if (strcmp("1", name) == 0) {
    676 		escape->set = E1SET(ei);
    677 		TAILQ_INSERT_TAIL(E1SET(ei), escape, entry);
    678 	} else {
    679 		free(escape);
    680 		return EINVAL;
    681 	}
    682 	p[0] = (void *)escape;
    683 	p[1] = (void *)ei;
    684 	return _citrus_prop_parse_variable(
    685 	    escape_hints, (void *)&p[0], s, strlen(s));
    686 }
    687 
    688 static const _citrus_prop_hint_t root_hints[] = {
    689 _CITRUS_PROP_HINT_STR("0", &_citrus_HZ_parse_escape),
    690 _CITRUS_PROP_HINT_STR("1", &_citrus_HZ_parse_escape),
    691 _CITRUS_PROP_HINT_END
    692 };
    693 
    694 static int
    695 _citrus_HZ_encoding_module_init(_HZEncodingInfo * __restrict ei,
    696 	const void * __restrict var, size_t lenvar)
    697 {
    698 	int errnum;
    699 
    700 	_DIAGASSERT(ei != NULL);
    701 
    702 	memset(ei, 0, sizeof(*ei));
    703 	TAILQ_INIT(E0SET(ei));
    704 	TAILQ_INIT(E1SET(ei));
    705 	errnum = _citrus_prop_parse_variable(
    706 	    root_hints, (void *)ei, var, lenvar);
    707 	if (errnum != 0)
    708 		_citrus_HZ_encoding_module_uninit(ei);
    709 	return errnum;
    710 }
    711 
    712 /* ----------------------------------------------------------------------
    713  * public interface for ctype
    714  */
    715 
    716 _CITRUS_CTYPE_DECLS(HZ);
    717 _CITRUS_CTYPE_DEF_OPS(HZ);
    718 
    719 #include "citrus_ctype_template.h"
    720 
    721 /* ----------------------------------------------------------------------
    722  * public interface for stdenc
    723  */
    724 
    725 _CITRUS_STDENC_DECLS(HZ);
    726 _CITRUS_STDENC_DEF_OPS(HZ);
    727 
    728 #include "citrus_stdenc_template.h"
    729