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