Sync Citrus iconv support with NetBSD.
[dragonfly.git] / lib / libc / citrus / modules / citrus_zw.c
1 /* $NetBSD: citrus_zw.c,v 1.3 2006/11/24 17:27:52 tnozaki Exp $ */
2 /* $DragonFly: src/lib/libc/citrus/modules/citrus_zw.c,v 1.1 2008/04/10 10:21:02 hasso 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/types.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <stdio.h>
36 #include <stdint.h>
37 #include <stdlib.h>
38 #include <stddef.h>
39 #include <locale.h>
40 #include <wchar.h>
41 #include <limits.h>
42
43 #include "citrus_namespace.h"
44 #include "citrus_types.h"
45 #include "citrus_module.h"
46 #include "citrus_ctype.h"
47 #include "citrus_stdenc.h"
48 #include "citrus_zw.h"
49
50 /* ----------------------------------------------------------------------
51  * private stuffs used by templates
52  */
53
54 typedef struct {
55         int dummy;
56 } _ZWEncodingInfo;
57
58 typedef enum {
59         NONE, AMBIGIOUS, ASCII, GB2312
60 } _ZWCharset;
61
62 typedef struct {
63         int             chlen;
64         char            ch[4];
65         _ZWCharset      charset;
66 } _ZWState;
67
68 typedef struct {
69         _ZWEncodingInfo ei;
70         struct {
71                 /* for future multi-locale facility */
72                 _ZWState        s_mblen;
73                 _ZWState        s_mbrlen;
74                 _ZWState        s_mbrtowc;
75                 _ZWState        s_mbtowc;
76                 _ZWState        s_mbsrtowcs;
77                 _ZWState        s_wcrtomb;
78                 _ZWState        s_wcsrtombs;
79                 _ZWState        s_wctomb;
80         } states;
81 } _ZWCTypeInfo;
82
83 #define _CEI_TO_EI(_cei_)               (&(_cei_)->ei)
84 #define _CEI_TO_STATE(_cei_, _func_)    (_cei_)->states.s_##_func_
85
86 #define _FUNCNAME(m)                    _citrus_ZW_##m
87 #define _ENCODING_INFO                  _ZWEncodingInfo
88 #define _CTYPE_INFO                     _ZWCTypeInfo
89 #define _ENCODING_STATE                 _ZWState
90 #define _ENCODING_MB_CUR_MAX(_ei_)      MB_LEN_MAX
91 #define _ENCODING_IS_STATE_DEPENDENT            1
92 #define _STATE_NEEDS_EXPLICIT_INIT(_ps_)        ((_ps_)->charset != NONE)
93
94 static __inline void
95 /*ARGSUSED*/
96 _citrus_ZW_init_state(_ZWEncodingInfo * __restrict ei,
97         _ZWState * __restrict psenc)
98 {
99         /* ei my be unused */
100         _DIAGASSERT(psenc != NULL);
101
102         psenc->chlen = 0;
103         psenc->charset = NONE;
104 }
105
106 static __inline void
107 /*ARGSUSED*/
108 _citrus_ZW_pack_state(_ZWEncodingInfo * __restrict ei,
109         void *__restrict pspriv, const _ZWState * __restrict psenc)
110 {
111         /* ei may be unused */
112         _DIAGASSERT(pspriv != NULL);
113         _DIAGASSERT(psenc != NULL);
114
115         memcpy(pspriv, (const void *)psenc, sizeof(*psenc));
116 }
117
118 static __inline void
119 /*ARGSUSED*/
120 _citrus_ZW_unpack_state(_ZWEncodingInfo * __restrict ei,
121         _ZWState * __restrict psenc, const void * __restrict pspriv)
122 {
123         /* ei may be unused */
124         _DIAGASSERT(psenc != NULL);
125         _DIAGASSERT(pspriv != NULL);
126
127         memcpy((void *)psenc, pspriv, sizeof(*psenc));
128 }
129
130 static int
131 _citrus_ZW_mbrtowc_priv(_ZWEncodingInfo * __restrict ei,
132         wchar_t * __restrict pwc, const char **__restrict s, size_t n,
133         _ZWState * __restrict psenc, size_t * __restrict nresult)
134 {
135         const char *s0;
136         int ch, len;
137         wchar_t  wc;
138
139         /* ei may be unused */
140         /* pwc may be null */
141         _DIAGASSERT(s != NULL);
142         _DIAGASSERT(psenc != NULL);
143         _DIAGASSERT(nresult != NULL);
144
145         if (*s == NULL) {
146                 _citrus_ZW_init_state(ei, psenc);
147                 *nresult = (size_t)_ENCODING_IS_STATE_DEPENDENT;
148                 return 0;
149         }
150         s0 = *s;
151         len = 0;
152
153 #define STORE                           \
154 do {                                    \
155         if (n-- < 1) {                  \
156                 *nresult = (size_t)-2;  \
157                 *s = s0;                \
158                 return 0;               \
159         }                               \
160         ch = (unsigned char)*s0++;      \
161         if (len++ > MB_LEN_MAX || ch > 0x7F)\
162                 goto ilseq;             \
163         psenc->ch[psenc->chlen++] = ch; \
164 } while (/*CONSTCOND*/0)
165
166 loop:
167         switch (psenc->charset) {
168         case ASCII:
169                 switch (psenc->chlen) {
170                 case 0:
171                         STORE;
172                         switch (psenc->ch[0]) {
173                         case '\0': case '\n':
174                                 psenc->charset = NONE;
175                         }
176                 /*FALLTHROUGH*/
177                 case 1:
178                         break;
179                 default:
180                         return EINVAL;
181                 }
182                 ch = (unsigned char)psenc->ch[0];
183                 if (ch > 0x7F)
184                         goto ilseq;
185                 wc = (wchar_t)ch;
186                 psenc->chlen = 0;
187                 break;
188         case NONE:
189                 if (psenc->chlen != 0)
190                         return EINVAL;
191                 STORE;
192                 ch = (unsigned char)psenc->ch[0];
193                 if (ch != 'z') {
194                         if (ch != '\n' && ch != '\0')
195                                 psenc->charset = ASCII;
196                         wc = (wchar_t)ch;
197                         psenc->chlen = 0;
198                         break;
199                 }
200                 psenc->charset = AMBIGIOUS;
201                 psenc->chlen = 0;
202         /* FALLTHROUGH */
203         case AMBIGIOUS:
204                 if (psenc->chlen != 0)
205                         return EINVAL;
206                 STORE;
207                 if (psenc->ch[0] != 'W') {
208                         psenc->charset = ASCII;
209                         wc = L'z';
210                         break;
211                 }
212                 psenc->charset = GB2312;
213                 psenc->chlen = 0;
214         /* FALLTHROUGH */
215         case GB2312:
216                 switch (psenc->chlen) {
217                 case 0:
218                         STORE;
219                         ch = (unsigned char)psenc->ch[0];
220                         if (ch == '\0') {
221                                 psenc->charset = NONE;
222                                 wc = (wchar_t)ch;
223                                 psenc->chlen = 0;
224                                 break;
225                         } else if (ch == '\n') {
226                                 psenc->charset = NONE;
227                                 psenc->chlen = 0;
228                                 goto loop;
229                         }
230                 /*FALLTHROUGH*/
231                 case 1:
232                         STORE;
233                         if (psenc->ch[0] == ' ') {
234                                 ch = (unsigned char)psenc->ch[1];
235                                 wc = (wchar_t)ch;
236                                 psenc->chlen = 0;
237                                 break;
238                         } else if (psenc->ch[0] == '#') {
239                                 ch = (unsigned char)psenc->ch[1];
240                                 if (ch == '\n') {
241                                         psenc->charset = NONE;
242                                         wc = (wchar_t)ch;
243                                         psenc->chlen = 0;
244                                         break;
245                                 } else if (ch == ' ') {
246                                         wc = (wchar_t)ch;
247                                         psenc->chlen = 0;
248                                         break;
249                                 }
250                         }
251                         ch = (unsigned char)psenc->ch[0];
252                         if (ch < 0x21 || ch > 0x7E)
253                                 goto ilseq;
254                         wc = (wchar_t)(ch << 8);
255                         ch = (unsigned char)psenc->ch[1];
256                         if (ch < 0x21 || ch > 0x7E) {
257 ilseq:
258                                 *nresult = (size_t)-1;
259                                 return EILSEQ;
260                         }
261                         wc |= (wchar_t)ch;
262                         psenc->chlen = 0;
263                         break;
264                 default:
265                         return EINVAL;
266                 }
267                 break;
268         default:
269                 return EINVAL;
270         }
271         if (pwc != NULL)
272                 *pwc = wc;
273
274         *nresult = (size_t)(wc == 0 ? 0 : len);
275         *s = s0;
276
277         return 0;
278 }
279
280 static int
281 /*ARGSUSED*/
282 _citrus_ZW_wcrtomb_priv(_ZWEncodingInfo * __restrict ei,
283         char *__restrict s, size_t n, wchar_t wc,
284         _ZWState * __restrict psenc, size_t * __restrict nresult)
285 {
286         int ch;
287
288         /* ei may be null */
289         _DIAGASSERT(s != NULL);
290         _DIAGASSERT(psenc != NULL);
291         _DIAGASSERT(nresult != NULL);
292
293         if (psenc->chlen != 0)
294                 return EINVAL;
295         if ((uint32_t)wc <= 0x7F) {
296                 ch = (unsigned char)wc;
297                 switch (psenc->charset) {
298                 case NONE:
299                         if (ch == '\0' || ch == '\n') {
300                                 psenc->ch[psenc->chlen++] = ch;
301                         } else {
302                                 if (n < 4)
303                                         return E2BIG;
304                                 n -= 4;
305                                 psenc->ch[psenc->chlen++] = 'z';
306                                 psenc->ch[psenc->chlen++] = 'W';
307                                 psenc->ch[psenc->chlen++] = ' ';
308                                 psenc->ch[psenc->chlen++] = ch;
309                                 psenc->charset = GB2312;
310                         }
311                         break;
312                 case GB2312:
313                         if (n < 2)
314                                 return E2BIG;
315                         n -= 2;
316                         if (ch == '\0') {
317                                 psenc->ch[psenc->chlen++] = '\n';
318                                 psenc->ch[psenc->chlen++] = '\0';
319                                 psenc->charset = NONE;
320                         } else if (ch == '\n') {
321                                 psenc->ch[psenc->chlen++] = '#';
322                                 psenc->ch[psenc->chlen++] = '\n';
323                                 psenc->charset = NONE;
324                         } else {
325                                 psenc->ch[psenc->chlen++] = ' ';
326                                 psenc->ch[psenc->chlen++] = ch;
327                         }
328                         break;
329                 default:
330                         return EINVAL;
331                 }
332         } else if ((uint32_t)wc <= 0x7E7E) {
333                 switch (psenc->charset) {
334                 case NONE:
335                         if (n < 2)
336                                 return E2BIG;
337                         n -= 2;
338                         psenc->ch[psenc->chlen++] = 'z';
339                         psenc->ch[psenc->chlen++] = 'W';
340                         psenc->charset = GB2312;
341                 /* FALLTHROUGH*/
342                 case GB2312:
343                         if (n < 2)
344                                 return E2BIG;
345                         n -= 2;
346                         ch = (wc >> 8) & 0xFF;
347                         if (ch < 0x21 || ch > 0x7E)
348                                 goto ilseq;
349                         psenc->ch[psenc->chlen++] = ch;
350                         ch = wc & 0xFF;
351                         if (ch < 0x21 || ch > 0x7E)
352                                 goto ilseq;
353                         psenc->ch[psenc->chlen++] = ch;
354                         break;
355                 default:
356                         return EINVAL;
357                 }
358         } else {
359 ilseq:
360                 *nresult = (size_t)-1;
361                 return EILSEQ;
362         }
363         memcpy(s, psenc->ch, psenc->chlen);
364         *nresult = psenc->chlen;
365         psenc->chlen = 0;
366
367         return 0;
368 }
369
370 static int
371 /*ARGSUSED*/
372 _citrus_ZW_put_state_reset(_ZWEncodingInfo * __restrict ei,
373         char * __restrict s, size_t n,
374         _ZWState * __restrict psenc, size_t * __restrict nresult)
375 {
376         /* ei may be unused */
377         _DIAGASSERT(s != NULL);
378         _DIAGASSERT(psenc != NULL);
379         _DIAGASSERT(nresult != NULL);
380
381         if (psenc->chlen != 0)
382                 return EINVAL;
383         switch (psenc->charset) {
384         case GB2312:
385                 if (n-- < 1)
386                         return E2BIG;
387                 psenc->ch[psenc->chlen++] = '\n';
388                 psenc->charset = NONE;
389         /*FALLTHROUGH*/
390         case NONE:
391                 *nresult = psenc->chlen;
392                 if (psenc->chlen > 0) {
393                         memcpy(s, psenc->ch, psenc->chlen);
394                         psenc->chlen = 0;
395                 }
396                 break;
397         default:
398                 return EINVAL;
399         }
400
401         return 0;
402 }
403
404 static __inline int
405 /*ARGSUSED*/
406 _citrus_ZW_stdenc_get_state_desc_generic(_ZWEncodingInfo * __restrict ei,
407         _ZWState * __restrict psenc, int * __restrict rstate)
408 {
409         /* ei may be unused */
410         _DIAGASSERT(psenc != NULL);
411         _DIAGASSERT(rstate != NULL);
412
413         switch (psenc->charset) {
414         case NONE:
415                 if (psenc->chlen != 0)
416                         return EINVAL;
417                 *rstate = _STDENC_SDGEN_INITIAL;
418                 break;
419         case AMBIGIOUS:
420                 if (psenc->chlen != 0)
421                         return EINVAL;
422                 *rstate = _STDENC_SDGEN_INCOMPLETE_SHIFT;
423                 break;
424         case ASCII:
425         case GB2312:
426                 switch (psenc->chlen) {
427                 case 0:
428                         *rstate = _STDENC_SDGEN_STABLE;
429                         break;
430                 case 1:
431                         *rstate = (psenc->ch[0] == '#')
432                             ? _STDENC_SDGEN_INCOMPLETE_SHIFT
433                             : _STDENC_SDGEN_INCOMPLETE_CHAR;
434                         break;
435                 default:
436                         return EINVAL;
437                 }
438                 break;
439         default:
440                 return EINVAL;
441         }
442         return 0;
443 }
444
445 static __inline int
446 /*ARGSUSED*/
447 _citrus_ZW_stdenc_wctocs(_ZWEncodingInfo * __restrict ei,
448         _csid_t * __restrict csid, _index_t * __restrict idx, wchar_t wc)
449 {
450         /* ei seems to be unused */
451         _DIAGASSERT(csid != NULL);
452         _DIAGASSERT(idx != NULL);
453
454         *csid = (_csid_t)(wc <= 0x7FU) ? 0 : 1;
455         *idx = (_index_t)wc;
456
457         return 0;
458 }
459
460 static __inline int
461 /*ARGSUSED*/
462 _citrus_ZW_stdenc_cstowc(_ZWEncodingInfo * __restrict ei,
463          wchar_t * __restrict wc, _csid_t csid, _index_t idx)
464 {
465         /* ei seems to be unused */
466         _DIAGASSERT(wc != NULL);
467
468         switch (csid) {
469         case 0: case 1:
470                 break;
471         default:
472                 return EINVAL;
473         }
474         *wc = (wchar_t)idx;
475
476         return 0;
477 }
478
479 static void
480 /*ARGSUSED*/
481 _citrus_ZW_encoding_module_uninit(_ZWEncodingInfo *ei)
482 {
483 }
484
485 static int
486 /*ARGSUSED*/
487 _citrus_ZW_encoding_module_init(_ZWEncodingInfo * __restrict ei,
488         const void *__restrict var, size_t lenvar)
489 {
490         return 0;
491 }
492
493 /* ----------------------------------------------------------------------
494  * public interface for ctype
495  */
496
497 _CITRUS_CTYPE_DECLS(ZW);
498 _CITRUS_CTYPE_DEF_OPS(ZW);
499
500 #include "citrus_ctype_template.h"
501
502 /* ----------------------------------------------------------------------
503  * public interface for stdenc
504  */
505
506 _CITRUS_STDENC_DECLS(ZW);
507 _CITRUS_STDENC_DEF_OPS(ZW);
508
509 #include "citrus_stdenc_template.h"