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