keylogin(1): Fix a warning and raise WARNS to 6.
[dragonfly.git] / lib / libc / citrus / modules / citrus_big5.c
1 /* $NetBSD: citrus_big5.c,v 1.11 2006/11/22 23:38:27 tnozaki Exp $ */
2
3 /*-
4  * Copyright (c)2002, 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  * Copyright (c) 1993
31  *      The Regents of the University of California.  All rights reserved.
32  *
33  * This code is derived from software contributed to Berkeley by
34  * Paul Borman at Krystal Technologies.
35  *
36  * Redistribution and use in source and binary forms, with or without
37  * modification, are permitted provided that the following conditions
38  * are met:
39  * 1. Redistributions of source code must retain the above copyright
40  *    notice, this list of conditions and the following disclaimer.
41  * 2. Redistributions in binary form must reproduce the above copyright
42  *    notice, this list of conditions and the following disclaimer in the
43  *    documentation and/or other materials provided with the distribution.
44  * 3. Neither the name of the University nor the names of its contributors
45  *    may be used to endorse or promote products derived from this software
46  *    without specific prior written permission.
47  *
48  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58  * SUCH DAMAGE.
59  */
60
61 #include <sys/queue.h>
62 #include <sys/types.h>
63 #include <assert.h>
64 #include <errno.h>
65 #include <limits.h>
66 #include <locale.h>
67 #include <stddef.h>
68 #include <stdint.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <wchar.h>
73
74 #include "citrus_namespace.h"
75 #include "citrus_types.h"
76 #include "citrus_bcs.h"
77 #include "citrus_module.h"
78 #include "citrus_ctype.h"
79 #include "citrus_stdenc.h"
80 #include "citrus_big5.h"
81
82 #include "citrus_prop.h"
83
84 /* ----------------------------------------------------------------------
85  * private stuffs used by templates
86  */
87
88 typedef struct {
89         char ch[2];
90         int chlen;
91 } _BIG5State;
92
93 typedef struct _BIG5Exclude {
94         TAILQ_ENTRY(_BIG5Exclude) entry;
95         wint_t start, end;
96 } _BIG5Exclude;
97
98 typedef TAILQ_HEAD(_BIG5ExcludeList, _BIG5Exclude) _BIG5ExcludeList;
99
100 typedef struct {
101         int cell[0x100];
102         _BIG5ExcludeList excludes;
103 } _BIG5EncodingInfo;
104
105 typedef struct {
106         _BIG5EncodingInfo       ei;
107         struct {
108                 /* for future multi-locale facility */
109                 _BIG5State      s_mblen;
110                 _BIG5State      s_mbrlen;
111                 _BIG5State      s_mbrtowc;
112                 _BIG5State      s_mbtowc;
113                 _BIG5State      s_mbsrtowcs;
114                 _BIG5State      s_wcrtomb;
115                 _BIG5State      s_wcsrtombs;
116                 _BIG5State      s_wctomb;
117         } states;
118 } _BIG5CTypeInfo;
119
120 #define _CEI_TO_EI(_cei_)               (&(_cei_)->ei)
121 #define _CEI_TO_STATE(_cei_, _func_)    (_cei_)->states.s_##_func_
122
123 #define _FUNCNAME(m)                    _citrus_BIG5_##m
124 #define _ENCODING_INFO                  _BIG5EncodingInfo
125 #define _CTYPE_INFO                     _BIG5CTypeInfo
126 #define _ENCODING_STATE                 _BIG5State
127 #define _ENCODING_MB_CUR_MAX(_ei_)      2
128 #define _ENCODING_IS_STATE_DEPENDENT    0
129 #define _STATE_NEEDS_EXPLICIT_INIT(_ps_)        0
130
131
132 static __inline void
133 /*ARGSUSED*/
134 _citrus_BIG5_init_state(_BIG5EncodingInfo * __restrict ei,
135                         _BIG5State * __restrict s)
136 {
137         memset(s, 0, sizeof(*s));
138 }
139
140 static __inline void
141 /*ARGSUSED*/
142 _citrus_BIG5_pack_state(_BIG5EncodingInfo * __restrict ei,
143                         void * __restrict pspriv,
144                         const _BIG5State * __restrict s)
145 {
146         memcpy(pspriv, (const void *)s, sizeof(*s));
147 }
148
149 static __inline void
150 /*ARGSUSED*/
151 _citrus_BIG5_unpack_state(_BIG5EncodingInfo * __restrict ei,
152                           _BIG5State * __restrict s,
153                           const void * __restrict pspriv)
154 {
155         memcpy((void *)s, pspriv, sizeof(*s));
156 }
157
158 static __inline int
159 _citrus_BIG5_check(_BIG5EncodingInfo *ei, u_int c)
160 {
161         _DIAGASSERT(ei != NULL);
162
163         return (ei->cell[c & 0xFF] & 0x1) ? 2 : 1;
164 }
165
166 static __inline int
167 _citrus_BIG5_check2(_BIG5EncodingInfo *ei, u_int c)
168 {
169         _DIAGASSERT(ei != NULL);
170
171         return (ei->cell[c & 0xFF] & 0x2) ? 1 : 0;
172 }
173
174 static __inline int
175 _citrus_BIG5_check_excludes(_BIG5EncodingInfo *ei, wint_t c)
176 {
177         _BIG5Exclude *exclude;
178
179         _DIAGASSERT(ei != NULL);
180
181         TAILQ_FOREACH(exclude, &ei->excludes, entry) {
182                 if (c >= exclude->start && c <= exclude->end)
183                         return EILSEQ;
184         }
185         return 0;
186 }
187
188 static int
189 _citrus_BIG5_fill_rowcol(void ** __restrict ctx, const char * __restrict s,
190         uint64_t start, uint64_t end)
191 {
192         _BIG5EncodingInfo *ei;
193         int i;
194         uint64_t n;
195
196         _DIAGASSERT(ctx != NULL && *ctx != NULL);
197
198         if (start > 0xFF || end > 0xFF)
199                 return EINVAL;
200         ei = (_BIG5EncodingInfo *)*ctx;
201         i = strcmp("row", s) ? 1 : 0;
202         i = 1 << i;
203         for (n = start; n <= end; ++n)
204                 ei->cell[n & 0xFF] |= i;
205         return 0;
206 }
207
208 static int
209 /*ARGSUSED*/
210 _citrus_BIG5_fill_excludes(void ** __restrict ctx, const char * __restrict s,
211         uint64_t start, uint64_t end)
212 {
213         _BIG5EncodingInfo *ei;
214         _BIG5Exclude *exclude;
215
216         _DIAGASSERT(ctx != NULL && *ctx != NULL);
217
218         if (start > 0xFFFF || end > 0xFFFF)
219                 return EINVAL;
220         ei = (_BIG5EncodingInfo *)*ctx;
221         exclude = TAILQ_LAST(&ei->excludes, _BIG5ExcludeList);
222         if (exclude != NULL && (wint_t)start <= exclude->end)
223                 return EINVAL;
224         exclude = (void *)malloc(sizeof(*exclude));
225         if (exclude == NULL)
226                 return ENOMEM;
227         exclude->start = (wint_t)start;
228         exclude->end = (wint_t)end;
229         TAILQ_INSERT_TAIL(&ei->excludes, exclude, entry);
230
231         return 0;
232 }
233
234 static const _citrus_prop_hint_t root_hints[] = {
235     _CITRUS_PROP_HINT_NUM("row", &_citrus_BIG5_fill_rowcol),
236     _CITRUS_PROP_HINT_NUM("col", &_citrus_BIG5_fill_rowcol),
237     _CITRUS_PROP_HINT_NUM("excludes", &_citrus_BIG5_fill_excludes),
238     _CITRUS_PROP_HINT_END
239 };
240
241 static void
242 /*ARGSUSED*/
243 _citrus_BIG5_encoding_module_uninit(_BIG5EncodingInfo *ei)
244 {
245         _BIG5Exclude *exclude;
246
247         _DIAGASSERT(ei != NULL);
248
249         while ((exclude = TAILQ_FIRST(&ei->excludes)) != NULL) {
250                 TAILQ_REMOVE(&ei->excludes, exclude, entry);
251                 free(exclude);
252         }
253 }
254
255 static int
256 /*ARGSUSED*/
257 _citrus_BIG5_encoding_module_init(_BIG5EncodingInfo * __restrict ei,
258                                   const void * __restrict var, size_t lenvar)
259 {
260         int err;
261         const char *s;
262
263         _DIAGASSERT(ei != NULL);
264
265         memset((void *)ei, 0, sizeof(*ei));
266         TAILQ_INIT(&ei->excludes);
267
268         if (lenvar > 0 && var != NULL) {
269                 s = _bcs_skip_ws_len((const char *)var, &lenvar);
270                 if (lenvar > 0 && *s != '\0') {
271                         err = _citrus_prop_parse_variable(
272                             root_hints, (void *)ei, s, lenvar);
273                         if (err == 0)
274                                 return 0;
275
276                         _citrus_BIG5_encoding_module_uninit(ei);
277                         memset((void *)ei, 0, sizeof(*ei));
278                         TAILQ_INIT(&ei->excludes);
279                 }
280         }
281
282         /* fallback Big5-1984, for backward compatibility. */
283         _citrus_BIG5_fill_rowcol((void **)&ei, "row", 0xA1, 0xFE);
284         _citrus_BIG5_fill_rowcol((void **)&ei, "col", 0x40, 0x7E);
285         _citrus_BIG5_fill_rowcol((void **)&ei, "col", 0xA1, 0xFE);
286
287         return 0;
288 }
289
290 static int
291 /*ARGSUSED*/
292 _citrus_BIG5_mbrtowc_priv(_BIG5EncodingInfo * __restrict ei,
293                           wchar_t * __restrict pwc,
294                           const char ** __restrict s, size_t n,
295                           _BIG5State * __restrict psenc,
296                           size_t * __restrict nresult)
297 {
298         wchar_t wchar;
299         int c;
300         int chlenbak;
301         const char *s0;
302
303         _DIAGASSERT(nresult != NULL);
304         _DIAGASSERT(ei != NULL);
305         _DIAGASSERT(psenc != NULL);
306         _DIAGASSERT(s != NULL && *s != NULL);
307
308         s0 = *s;
309
310         if (s0 == NULL) {
311                 _citrus_BIG5_init_state(ei, psenc);
312                 *nresult = 0;
313                 return (0);
314         }
315
316         chlenbak = psenc->chlen;
317
318         /* make sure we have the first byte in the buffer */
319         switch (psenc->chlen) {
320         case 0:
321                 if (n < 1)
322                         goto restart;
323                 psenc->ch[0] = *s0++;
324                 psenc->chlen = 1;
325                 n--;
326                 break;
327         case 1:
328                 break;
329         default:
330                 /* illegal state */
331                 goto ilseq;
332         }
333
334         c = _citrus_BIG5_check(ei, psenc->ch[0] & 0xff);
335         if (c == 0)
336                 goto ilseq;
337         while (psenc->chlen < c) {
338                 if (n < 1) {
339                         goto restart;
340                 }
341                 psenc->ch[psenc->chlen] = *s0++;
342                 psenc->chlen++;
343                 n--;
344         }
345
346         switch (c) {
347         case 1:
348                 wchar = psenc->ch[0] & 0xff;
349                 break;
350         case 2:
351                 if (!_citrus_BIG5_check2(ei, psenc->ch[1] & 0xff))
352                         goto ilseq;
353                 wchar = ((psenc->ch[0] & 0xff) << 8) | (psenc->ch[1] & 0xff);
354                 break;
355         default:
356                 /* illegal state */
357                 goto ilseq;
358         }
359
360         if (_citrus_BIG5_check_excludes(ei, (wint_t)wchar) != 0)
361                 goto ilseq;
362
363         *s = s0;
364         psenc->chlen = 0;
365         if (pwc)
366                 *pwc = wchar;
367         if (!wchar)
368                 *nresult = 0;
369         else
370                 *nresult = c - chlenbak;
371
372         return (0);
373
374 ilseq:
375         psenc->chlen = 0;
376         *nresult = (size_t)-1;
377         return (EILSEQ);
378
379 restart:
380         *s = s0;
381         *nresult = (size_t)-2;
382         return (0);
383 }
384
385 static int
386 /*ARGSUSED*/
387 _citrus_BIG5_wcrtomb_priv(_BIG5EncodingInfo * __restrict ei,
388                           char * __restrict s,
389                           size_t n, wchar_t wc, _BIG5State * __restrict psenc,
390                           size_t * __restrict nresult)
391 {
392         int l, ret;
393
394         _DIAGASSERT(ei != NULL);
395         _DIAGASSERT(nresult != NULL);
396         _DIAGASSERT(s != NULL);
397
398         /* check invalid sequence */
399         if (wc & ~0xffff ||
400             _citrus_BIG5_check_excludes(ei, (wint_t)wc) != 0) {
401                 ret = EILSEQ;
402                 goto err;
403         }
404
405         if (wc & 0x8000) {
406                 if (_citrus_BIG5_check(ei, (wc >> 8) & 0xff) != 2 ||
407                     !_citrus_BIG5_check2(ei, wc & 0xff)) {
408                         ret = EILSEQ;
409                         goto err;
410                 }
411                 l = 2;
412         } else {
413                 if (wc & ~0xff || !_citrus_BIG5_check(ei, wc & 0xff)) {
414                         ret = EILSEQ;
415                         goto err;
416                 }
417                 l = 1;
418         }
419
420         if (n < l) {
421                 /* bound check failure */
422                 ret = E2BIG;
423                 goto err;
424         }
425
426         if (l == 2) {
427                 s[0] = (wc >> 8) & 0xff;
428                 s[1] = wc & 0xff;
429         } else
430                 s[0] = wc & 0xff;
431
432         *nresult = l;
433
434         return 0;
435
436 err:
437         *nresult = (size_t)-1;
438         return ret;
439 }
440
441 static __inline int
442 /*ARGSUSED*/
443 _citrus_BIG5_stdenc_wctocs(_BIG5EncodingInfo * __restrict ei,
444                            _csid_t * __restrict csid,
445                            _index_t * __restrict idx, wchar_t wc)
446 {
447
448         _DIAGASSERT(csid != NULL && idx != NULL);
449
450         *csid = (wc < 0x100) ? 0 : 1;
451         *idx = (_index_t)wc;
452
453         return 0;
454 }
455
456 static __inline int
457 /*ARGSUSED*/
458 _citrus_BIG5_stdenc_cstowc(_BIG5EncodingInfo * __restrict ei,
459                            wchar_t * __restrict wc,
460                            _csid_t csid, _index_t idx)
461 {
462         _DIAGASSERT(wc != NULL);
463
464         switch (csid) {
465         case 0:
466         case 1:
467                 *wc = (wchar_t)idx;
468                 break;
469         default:
470                 return EILSEQ;
471         }
472
473         return 0;
474 }
475
476 static __inline int
477 /*ARGSUSED*/
478 _citrus_BIG5_stdenc_get_state_desc_generic(_BIG5EncodingInfo * __restrict ei,
479                                            _BIG5State * __restrict psenc,
480                                            int * __restrict rstate)
481 {
482
483         if (psenc->chlen == 0)
484                 *rstate = _STDENC_SDGEN_INITIAL;
485         else
486                 *rstate = _STDENC_SDGEN_INCOMPLETE_CHAR;
487
488         return 0;
489 }
490
491 /* ----------------------------------------------------------------------
492  * public interface for ctype
493  */
494
495 _CITRUS_CTYPE_DECLS(BIG5);
496 _CITRUS_CTYPE_DEF_OPS(BIG5);
497
498 #include "citrus_ctype_template.h"
499
500
501 /* ----------------------------------------------------------------------
502  * public interface for stdenc
503  */
504
505 _CITRUS_STDENC_DECLS(BIG5);
506 _CITRUS_STDENC_DEF_OPS(BIG5);
507
508 #include "citrus_stdenc_template.h"