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