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