xref: /minix/lib/libc/citrus/modules/citrus_hz.c (revision 0a6a1f1d)
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 *
find_escape(escape_list * set,int ch)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
_citrus_HZ_init_state(_HZEncodingInfo * __restrict ei,_HZState * __restrict psenc)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*/
_citrus_HZ_pack_state(_HZEncodingInfo * __restrict ei,void * __restrict pspriv,const _HZState * __restrict psenc)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*/
_citrus_HZ_unpack_state(_HZEncodingInfo * __restrict ei,_HZState * __restrict psenc,const void * __restrict pspriv)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
_citrus_HZ_mbrtowc_priv(_HZEncodingInfo * __restrict ei,wchar_t * __restrict pwc,const char ** __restrict s,size_t n,_HZState * __restrict psenc,size_t * __restrict nresult)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
_citrus_HZ_wcrtomb_priv(_HZEncodingInfo * __restrict ei,char * __restrict s,size_t n,wchar_t wc,_HZState * __restrict psenc,size_t * __restrict nresult)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
_citrus_HZ_put_state_reset(_HZEncodingInfo * __restrict ei,char * __restrict s,size_t n,_HZState * __restrict psenc,size_t * __restrict nresult)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
_citrus_HZ_stdenc_get_state_desc_generic(_HZEncodingInfo * __restrict ei,_HZState * __restrict psenc,int * __restrict rstate)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*/
_citrus_HZ_stdenc_wctocs(_HZEncodingInfo * __restrict ei,_csid_t * __restrict csid,_index_t * __restrict idx,wchar_t wc)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*/
_citrus_HZ_stdenc_cstowc(_HZEncodingInfo * __restrict ei,wchar_t * __restrict wc,_csid_t csid,_index_t idx)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
_citrus_HZ_encoding_module_uninit(_HZEncodingInfo * ei)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
_citrus_HZ_parse_char(void * context,const char * name,const char * s)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
_citrus_HZ_parse_graphic(void * context,const char * name,const char * s)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
_citrus_HZ_parse_escape(void * context,const char * name,const char * s)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
_citrus_HZ_encoding_module_init(_HZEncodingInfo * __restrict ei,const void * __restrict var,size_t lenvar)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