33da1ce84c81d4fa8fbd2d3352077c633403c808
[kopensolaris-gnu/glibc.git] / iconvdata / euctw.c
1 /* Mapping tables for EUC-TW handling.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public
17    License along with the GNU C Library; see the file COPYING.LIB.  If not,
18    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19    Boston, MA 02111-1307, USA.  */
20
21 #include <gconv.h>
22 #include <stdint.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <wchar.h>
26 #include <cns11643l1.h>
27 #include <cns11643.h>
28
29 /* Direction of the transformation.  */
30 enum direction
31 {
32   illegal,
33   to_euctw,
34   from_euctw
35 };
36
37 struct euctw_data
38 {
39   enum direction dir;
40 };
41
42
43 int
44 gconv_init (struct gconv_step *step)
45 {
46   /* Determine which direction.  */
47   struct euctw_data *new_data;
48   enum direction dir;
49   int result;
50
51   if (strcasestr (step->from_name, "EUC-TW") != NULL)
52     dir = from_euctw;
53   else if (strcasestr (step->to_name, "EUC-TW") != NULL)
54     dir = to_euctw;
55   else
56     dir = illegal;
57
58   result = GCONV_NOCONV;
59   if (dir != illegal
60       && ((new_data
61            = (struct euctw_data *) malloc (sizeof (struct euctw_data)))
62           != NULL))
63     {
64       new_data->dir = dir;
65       step->data = new_data;
66       result = GCONV_OK;
67     }
68
69   return result;
70 }
71
72
73 void
74 gconv_end (struct gconv_step *data)
75 {
76   free (data->data);
77 }
78
79
80 int
81 gconv (struct gconv_step *step, struct gconv_step_data *data,
82        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
83 {
84   struct gconv_step *next_step = step + 1;
85   struct gconv_step_data *next_data = data + 1;
86   gconv_fct fct = next_step->fct;
87   size_t do_write;
88   int result;
89
90   /* If the function is called with no input this means we have to reset
91      to the initial state.  The possibly partly converted input is
92      dropped.  */
93   if (do_flush)
94     {
95       do_write = 0;
96
97       /* Call the steps down the chain if there are any.  */
98       if (data->is_last)
99         result = GCONV_OK;
100       else
101         {
102           struct gconv_step *next_step = step + 1;
103           struct gconv_step_data *next_data = data + 1;
104
105           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
106
107           /* Clear output buffer.  */
108           data->outbufavail = 0;
109         }
110     }
111   else
112     {
113       enum direction dir = ((struct euctw_data *) step->data)->dir;
114
115       do_write = 0;
116
117       do
118         {
119           result = GCONV_OK;
120
121           if (dir == from_euctw)
122             {
123               size_t inchars = *inbufsize;
124               size_t outwchars = data->outbufavail;
125               char *outbuf = data->outbuf;
126               size_t cnt = 0;
127
128               while (cnt < inchars
129                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
130                 {
131                   int inchar = (unsigned char) inbuf[cnt];
132                   wchar_t ch;
133
134                   if (inchar <= 0x7f)
135                     ch = (wchar_t) inchar;
136                   else if ((inchar <= 0xa0 || inchar > 0xfe)
137                            && inchar != 0x8e)
138                       /* This is illegal.  */
139                       ch = L'\0';
140                   else
141                     {
142                       /* Two or more byte character.  First test whether the
143                          next character is also available.  */
144                       int inchar2;
145
146                       if (cnt + 1 + (inchar == 0x8e ? 2 : 0) >= inchars)
147                         {
148                           /* The second character is not available.  Store
149                              the intermediate result.  */
150                           result = GCONV_INCOMPLETE_INPUT;
151                           break;
152                         }
153
154                       inchar2 = (unsigned char) inbuf[++cnt];
155
156                       /* All second bytes of a multibyte character must be
157                          >= 0xa1. */
158                       if (inchar2 < 0xa1 && inchar2 == 0xff)
159                         {
160                           /* This is an illegal character.  */
161                           --cnt;
162                           result = GCONV_ILLEGAL_INPUT;
163                           break;
164                         }
165
166                       if (inchar == '\x8e')
167                         {
168                           /* This is code set 2: CNS 11643, planes 1 to 16.  */
169                           const char *endp = &inbuf[cnt];
170
171                           ch = cns11643_to_ucs4 (&endp, 2 + inchars - cnt,
172                                                  0x80);
173
174                           if (ch == UNKNOWN_10646_CHAR)
175                             ch = L'\0';
176                           if (ch != L'\0')
177                             cnt += 2;
178                         }
179                       else
180                         {
181                           /* This is code set 1: CNS 11643, plane 1.  */
182                           const char *endp = &inbuf[cnt - 1];
183
184                           ch = cns11643l1_to_ucs4 (&endp, 2 + inchars - cnt,
185                                                    0x80);
186
187                           if (ch == UNKNOWN_10646_CHAR)
188                             ch = L'\0';
189                           if (ch != L'\0')
190                             ++cnt;
191                         }
192
193                       if (ch == L'\0')
194                         --cnt;
195                     }
196
197                   if (ch == L'\0' && inbuf[cnt] != '\0')
198                     {
199                       /* This is an illegal character.  */
200                       result = GCONV_ILLEGAL_INPUT;
201                       break;
202                     }
203
204                   *((wchar_t *) (outbuf + outwchars)) = ch;
205                   ++do_write;
206                   outwchars += sizeof (wchar_t);
207                   ++cnt;
208                 }
209               *inbufsize -= cnt;
210               data->outbufavail = outwchars;
211             }
212           else
213             {
214               size_t inwchars = *inbufsize;
215               size_t outchars = data->outbufavail;
216               char *outbuf = data->outbuf;
217               size_t cnt = 0;
218               int extra = 0;
219
220               while (inwchars >= cnt + sizeof (wchar_t)
221                      && outchars < data->outbufsize)
222                 {
223                   wchar_t ch = *((wchar_t *) (inbuf + cnt));
224
225                   if (ch <= L'\x7f')
226                     /* It's plain ASCII.  */
227                     outbuf[outchars] = ch;
228                   else
229                     {
230                       /* Try the JIS character sets.  */
231                       size_t found;
232
233                       found = ucs4_to_cns11643l1 (ch, &outbuf[outchars],
234                                                   (data->outbufsize
235                                                      - outchars));
236                       if (found == 0)
237                         {
238                           /* We ran out of space.  */
239                           extra = 2;
240                           break;
241                         }
242                       else if (found != UNKNOWN_10646_CHAR)
243                         {
244                           /* It's a CNS 11643, plane 1 character, adjust it
245                              for EUC-TW.  */
246                           outbuf[outchars++] += 0x80;
247                           outbuf[outchars] += 0x80;
248                         }
249                       else
250                         {
251                           /* No CNS 11643, plane 1 character.  */
252                           outbuf[outchars] = '\x8e';
253
254                           found = ucs4_to_cns11643 (ch, &outbuf[outchars + 1],
255                                                     (data->outbufsize
256                                                      - outchars - 1));
257                           if (found > 0)
258                             {
259                               /* It's a CNS 11643 character, adjust it for
260                                  EUC-TW.  */
261                               outbuf[++outchars] += 0xa0;
262                               outbuf[++outchars] += 0x80;
263                               outbuf[outchars] += 0x80;
264                             }
265                           else if (found == 0)
266                             {
267                               /* We ran out of space.  */
268                               extra = 4;
269                               break;
270                             }
271                           else
272                             /* Illegal character.  */
273                             break;
274                         }
275                     }
276
277                   ++do_write;
278                   ++outchars;
279                   cnt += sizeof (wchar_t);
280                 }
281               *inbufsize -= cnt;
282               data->outbufavail = outchars;
283
284               if (outchars + extra < data->outbufsize)
285                 {
286                   /* If there is still room in the output buffer something
287                      is wrong with the input.  */
288                   if (inwchars >= cnt + sizeof (wchar_t))
289                     {
290                       /* An error occurred.  */
291                       result = GCONV_ILLEGAL_INPUT;
292                       break;
293                     }
294                   if (inwchars != cnt)
295                     {
296                       /* There are some unprocessed bytes at the end of the
297                          input buffer.  */
298                       result = GCONV_INCOMPLETE_INPUT;
299                       break;
300                     }
301                 }
302             }
303
304           if (result != GCONV_OK)
305             break;
306
307           if (data->is_last)
308             {
309               /* This is the last step.  */
310               result = (*inbufsize > (dir == from_euctw
311                                       ? 0 : sizeof (wchar_t) - 1)
312                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
313               break;
314             }
315
316           /* Status so far.  */
317           result = GCONV_EMPTY_INPUT;
318
319           if (data->outbufavail > 0)
320             {
321               /* Call the functions below in the chain.  */
322               size_t newavail = data->outbufavail;
323
324               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
325                                written, 0);
326
327               /* Correct the output buffer.  */
328               if (newavail != data->outbufavail && newavail > 0)
329                 {
330                   memmove (data->outbuf,
331                            &data->outbuf[data->outbufavail - newavail],
332                            newavail);
333                   data->outbufavail = newavail;
334                 }
335             }
336         }
337       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
338     }
339
340   if (written != NULL && data->is_last)
341     *written = do_write;
342
343   return result;
344 }