Initial revision
[TestXSLT.git] / libiconv / lib / loop_unicode.h
1 /*
2  * Copyright (C) 1999-2002 Free Software Foundation, Inc.
3  * This file is part of the GNU LIBICONV Library.
4  *
5  * The GNU LIBICONV Library is free software; you can redistribute it
6  * and/or modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * The GNU LIBICONV Library is distributed in the hope that it will be
11  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with the GNU LIBICONV Library; see the file COPYING.LIB.
17  * If not, write to the Free Software Foundation, Inc., 59 Temple Place -
18  * Suite 330, Boston, MA 02111-1307, USA.
19  */
20
21 /* This file defines the conversion loop via Unicode as a pivot encoding. */
22
23 /* Attempt to transliterate wc. Return code as in xxx_wctomb. */
24 static int unicode_transliterate (conv_t cd, ucs4_t wc,
25                                   unsigned char* outptr, size_t outleft)
26 {
27   if (cd->oflags & HAVE_HANGUL_JAMO) {
28     /* Decompose Hangul into Jamo. Use double-width Jamo (contained
29        in all Korean encodings and ISO-2022-JP-2), not half-width Jamo
30        (contained in Unicode only). */
31     ucs4_t buf[3];
32     int ret = johab_hangul_decompose(cd,buf,wc);
33     if (ret != RET_ILUNI) {
34       /* we know 1 <= ret <= 3 */
35       state_t backup_state = cd->ostate;
36       unsigned char* backup_outptr = outptr;
37       size_t backup_outleft = outleft;
38       int i, sub_outcount;
39       for (i = 0; i < ret; i++) {
40         if (outleft == 0) {
41           sub_outcount = RET_TOOSMALL;
42           goto johab_hangul_failed;
43         }
44         sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,buf[i],outleft);
45         if (sub_outcount <= RET_ILUNI)
46           goto johab_hangul_failed;
47         if (!(sub_outcount <= outleft)) abort();
48         outptr += sub_outcount; outleft -= sub_outcount;
49       }
50       return outptr-backup_outptr;
51     johab_hangul_failed:
52       cd->ostate = backup_state;
53       outptr = backup_outptr;
54       outleft = backup_outleft;
55       if (sub_outcount < 0)
56         return RET_TOOSMALL;
57     }
58   }
59   {
60     /* Try to use a variant, but postfix it with
61        U+303E IDEOGRAPHIC VARIATION INDICATOR
62        (cf. Ken Lunde's "CJKV information processing", p. 188). */
63     int indx = -1;
64     if (wc == 0x3006)
65       indx = 0;
66     else if (wc == 0x30f6)
67       indx = 1;
68     else if (wc >= 0x4e00 && wc < 0xa000)
69       indx = cjk_variants_indx[wc-0x4e00];
70     if (indx >= 0) {
71       for (;; indx++) {
72         ucs4_t buf[2];
73         unsigned short variant = cjk_variants[indx];
74         unsigned short last = variant & 0x8000;
75         variant &= 0x7fff;
76         variant += 0x3000;
77         buf[0] = variant; buf[1] = 0x303e;
78         {
79           state_t backup_state = cd->ostate;
80           unsigned char* backup_outptr = outptr;
81           size_t backup_outleft = outleft;
82           int i, sub_outcount;
83           for (i = 0; i < 2; i++) {
84             if (outleft == 0) {
85               sub_outcount = RET_TOOSMALL;
86               goto variant_failed;
87             }
88             sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,buf[i],outleft);
89             if (sub_outcount <= RET_ILUNI)
90               goto variant_failed;
91             if (!(sub_outcount <= outleft)) abort();
92             outptr += sub_outcount; outleft -= sub_outcount;
93           }
94           return outptr-backup_outptr;
95         variant_failed:
96           cd->ostate = backup_state;
97           outptr = backup_outptr;
98           outleft = backup_outleft;
99           if (sub_outcount < 0)
100             return RET_TOOSMALL;
101         }
102         if (last)
103           break;
104       }
105     }
106   }
107   if (wc >= 0x2018 && wc <= 0x201a) {
108     /* Special case for quotation marks 0x2018, 0x2019, 0x201a */
109     ucs4_t substitute =
110       (cd->oflags & HAVE_QUOTATION_MARKS
111        ? (wc == 0x201a ? 0x2018 : wc)
112        : (cd->oflags & HAVE_ACCENTS
113           ? (wc==0x2019 ? 0x00b4 : 0x0060) /* use accents */
114           : 0x0027 /* use apostrophe */
115       )  );
116     int outcount = cd->ofuncs.xxx_wctomb(cd,outptr,substitute,outleft);
117     if (outcount != RET_ILUNI)
118       return outcount;
119   }
120   {
121     /* Use the transliteration table. */
122     int indx = translit_index(wc);
123     if (indx >= 0) {
124       const unsigned short * cp = &translit_data[indx];
125       unsigned int num = *cp++;
126       state_t backup_state = cd->ostate;
127       unsigned char* backup_outptr = outptr;
128       size_t backup_outleft = outleft;
129       unsigned int i;
130       int sub_outcount;
131       for (i = 0; i < num; i++) {
132         if (outleft == 0) {
133           sub_outcount = RET_TOOSMALL;
134           goto translit_failed;
135         }
136         sub_outcount = cd->ofuncs.xxx_wctomb(cd,outptr,cp[i],outleft);
137         if (sub_outcount <= RET_ILUNI)
138           goto translit_failed;
139         if (!(sub_outcount <= outleft)) abort();
140         outptr += sub_outcount; outleft -= sub_outcount;
141       }
142       return outptr-backup_outptr;
143     translit_failed:
144       cd->ostate = backup_state;
145       outptr = backup_outptr;
146       outleft = backup_outleft;
147       if (sub_outcount != RET_ILUNI)
148         return RET_TOOSMALL;
149     }
150   }
151   return RET_ILUNI;
152 }
153
154 static size_t unicode_loop_convert (iconv_t icd,
155                                     const char* * inbuf, size_t *inbytesleft,
156                                     char* * outbuf, size_t *outbytesleft)
157 {
158   conv_t cd = (conv_t) icd;
159   size_t result = 0;
160   const unsigned char* inptr = (const unsigned char*) *inbuf;
161   size_t inleft = *inbytesleft;
162   unsigned char* outptr = (unsigned char*) *outbuf;
163   size_t outleft = *outbytesleft;
164   while (inleft > 0) {
165     state_t last_istate = cd->istate;
166     ucs4_t wc;
167     int incount;
168     int outcount;
169     incount = cd->ifuncs.xxx_mbtowc(cd,&wc,inptr,inleft);
170     if (incount < 0) {
171       if (incount == RET_ILSEQ) {
172         /* Case 1: invalid input */
173         if (cd->discard_ilseq) {
174           switch (cd->iindex) {
175             case ei_ucs4: case ei_ucs4be: case ei_ucs4le:
176             case ei_utf32: case ei_utf32be: case ei_utf32le:
177             case ei_ucs4internal: case ei_ucs4swapped:
178               incount = 4; break;
179             case ei_ucs2: case ei_ucs2be: case ei_ucs2le:
180             case ei_utf16: case ei_utf16be: case ei_utf16le:
181             case ei_ucs2internal: case ei_ucs2swapped:
182               incount = 2; break;
183             default:
184               incount = 1; break;
185           }
186           goto outcount_zero;
187         }
188         errno = EILSEQ;
189         result = -1;
190         break;
191       }
192       if (incount == RET_TOOFEW(0)) {
193         /* Case 2: not enough bytes available to detect anything */
194         errno = EINVAL;
195         result = -1;
196         break;
197       }
198       /* Case 3: k bytes read, but only a shift sequence */
199       incount = -2-incount;
200     } else {
201       /* Case 4: k bytes read, making up a wide character */
202       if (outleft == 0) {
203         cd->istate = last_istate;
204         errno = E2BIG;
205         result = -1;
206         break;
207       }
208       outcount = cd->ofuncs.xxx_wctomb(cd,outptr,wc,outleft);
209       if (outcount != RET_ILUNI)
210         goto outcount_ok;
211       /* Handle Unicode tag characters (range U+E0000..U+E007F). */
212       if ((wc >> 7) == (0xe0000 >> 7))
213         goto outcount_zero;
214       /* Try transliteration. */
215       result++;
216       if (cd->transliterate) {
217         outcount = unicode_transliterate(cd,wc,outptr,outleft);
218         if (outcount != RET_ILUNI)
219           goto outcount_ok;
220       }
221       if (cd->discard_ilseq)
222         goto outcount_zero;
223       outcount = cd->ofuncs.xxx_wctomb(cd,outptr,0xFFFD,outleft);
224       if (outcount != RET_ILUNI)
225         goto outcount_ok;
226       cd->istate = last_istate;
227       errno = EILSEQ;
228       result = -1;
229       break;
230     outcount_ok:
231       if (outcount < 0) {
232         cd->istate = last_istate;
233         errno = E2BIG;
234         result = -1;
235         break;
236       }
237       if (!(outcount <= outleft)) abort();
238       outptr += outcount; outleft -= outcount;
239     }
240   outcount_zero:
241     if (!(incount <= inleft)) abort();
242     inptr += incount; inleft -= incount;
243   }
244   *inbuf = (const char*) inptr;
245   *inbytesleft = inleft;
246   *outbuf = (char*) outptr;
247   *outbytesleft = outleft;
248   return result;
249 }
250
251 static size_t unicode_loop_reset (iconv_t icd,
252                                   char* * outbuf, size_t *outbytesleft)
253 {
254   conv_t cd = (conv_t) icd;
255   if (outbuf == NULL || *outbuf == NULL) {
256     /* Reset the states. */
257     memset(&cd->istate,'\0',sizeof(state_t));
258     memset(&cd->ostate,'\0',sizeof(state_t));
259     return 0;
260   } else {
261     size_t result = 0;
262     if (cd->ifuncs.xxx_flushwc) {
263       state_t last_istate = cd->istate;
264       ucs4_t wc;
265       if (cd->ifuncs.xxx_flushwc(cd, &wc)) {
266         unsigned char* outptr = (unsigned char*) *outbuf;
267         size_t outleft = *outbytesleft;
268         int outcount = cd->ofuncs.xxx_wctomb(cd,outptr,wc,outleft);
269         if (outcount != RET_ILUNI)
270           goto outcount_ok;
271         /* Handle Unicode tag characters (range U+E0000..U+E007F). */
272         if ((wc >> 7) == (0xe0000 >> 7))
273           goto outcount_zero;
274         /* Try transliteration. */
275         result++;
276         if (cd->transliterate) {
277           outcount = unicode_transliterate(cd,wc,outptr,outleft);
278           if (outcount != RET_ILUNI)
279             goto outcount_ok;
280         }
281         if (cd->discard_ilseq)
282           goto outcount_zero;
283         outcount = cd->ofuncs.xxx_wctomb(cd,outptr,0xFFFD,outleft);
284         if (outcount != RET_ILUNI)
285           goto outcount_ok;
286         cd->istate = last_istate;
287         errno = EILSEQ;
288         return -1;
289       outcount_ok:
290         if (outcount < 0) {
291           cd->istate = last_istate;
292           errno = E2BIG;
293           return -1;
294         }
295         if (!(outcount <= outleft)) abort();
296         outptr += outcount;
297         outleft -= outcount;
298       outcount_zero:
299         *outbuf = (char*) outptr;
300         *outbytesleft = outleft;
301       }
302     }
303     if (cd->ofuncs.xxx_reset) {
304       unsigned char* outptr = (unsigned char*) *outbuf;
305       size_t outleft = *outbytesleft;
306       int outcount = cd->ofuncs.xxx_reset(cd,outptr,outleft);
307       if (outcount < 0) {
308         errno = E2BIG;
309         return -1;
310       }
311       if (!(outcount <= outleft)) abort();
312       *outbuf = (char*) (outptr + outcount);
313       *outbytesleft = outleft - outcount;
314     }
315     memset(&cd->istate,'\0',sizeof(state_t));
316     memset(&cd->ostate,'\0',sizeof(state_t));
317     return result;
318   }
319 }