108974a972709f68dfa83ed7de52ffe7482aaaeb
[kopensolaris-gnu/glibc.git] / iconvdata / euccn.c
1 /* Mapping tables for EUC-CN 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 <gb2312.h>
27
28 /* Direction of the transformation.  */
29 enum direction
30 {
31   illegal,
32   to_euccn,
33   from_euccn
34 };
35
36 struct euccn_data
37 {
38   enum direction dir;
39 };
40
41
42 int
43 gconv_init (struct gconv_step *step)
44 {
45   /* Determine which direction.  */
46   struct euccn_data *new_data;
47   enum direction dir;
48   int result;
49
50   if (strcasestr (step->from_name, "EUC-CN") != NULL)
51     dir = from_euccn;
52   else if (strcasestr (step->to_name, "EUC-CN") != NULL)
53     dir = to_euccn;
54   else
55     dir = illegal;
56
57   result = GCONV_NOCONV;
58   if (dir != illegal
59       && ((new_data
60            = (struct euccn_data *) malloc (sizeof (struct euccn_data)))
61           != NULL))
62     {
63       new_data->dir = dir;
64       step->data = new_data;
65       result = GCONV_OK;
66     }
67
68   return result;
69 }
70
71
72 void
73 gconv_end (struct gconv_step *data)
74 {
75   free (data->data);
76 }
77
78
79 int
80 gconv (struct gconv_step *step, struct gconv_step_data *data,
81        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
82 {
83   struct gconv_step *next_step = step + 1;
84   struct gconv_step_data *next_data = data + 1;
85   gconv_fct fct = next_step->fct;
86   size_t do_write;
87   int result;
88
89   /* If the function is called with no input this means we have to reset
90      to the initial state.  The possibly partly converted input is
91      dropped.  */
92   if (do_flush)
93     {
94       do_write = 0;
95
96       /* Call the steps down the chain if there are any.  */
97       if (data->is_last)
98         result = GCONV_OK;
99       else
100         {
101           struct gconv_step *next_step = step + 1;
102           struct gconv_step_data *next_data = data + 1;
103
104           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
105
106           /* Clear output buffer.  */
107           data->outbufavail = 0;
108         }
109     }
110   else
111     {
112       enum direction dir = ((struct euccn_data *) step->data)->dir;
113
114       do_write = 0;
115
116       do
117         {
118           result = GCONV_OK;
119
120           if (dir == from_euccn)
121             {
122               size_t inchars = *inbufsize;
123               size_t outwchars = data->outbufavail;
124               char *outbuf = data->outbuf;
125               size_t cnt = 0;
126
127               while (cnt < inchars
128                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
129                 {
130                   int inchar = (unsigned char) inbuf[cnt];
131                   wchar_t ch;
132
133                   if (inchar <= 0x7f)
134                     ch = (wchar_t) inchar;
135                   else if ((inchar <= 0xa0 || inchar > 0xfe)
136                            && inchar != 0x8e && inchar != 0x8f)
137                       /* This is illegal.  */
138                       ch = L'\0';
139                   else
140                     {
141                       /* Two or more byte character.  First test whether the
142                          next character is also available.  */
143                       const char *endp;
144                       int inchar2;
145
146                       if (cnt + 1 >= 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)
159                         {
160                           /* This is an illegal character.  */
161                           --cnt;
162                           result = GCONV_ILLEGAL_INPUT;
163                           break;
164                         }
165
166                       /* This is code set 1: GB 2312-80.  */
167                       endp = &inbuf[cnt - 1];
168
169                       ch = gb2312_to_ucs4 (&endp, 2, 0x80);
170                       if (ch != L'\0')
171                         ++cnt;
172
173                       if (ch == UNKNOWN_10646_CHAR)
174                          ch = L'\0';
175
176                       if (ch == L'\0')
177                         --cnt;
178                     }
179
180                   if (ch == L'\0' && inbuf[cnt] != '\0')
181                     {
182                       /* This is an illegal character.  */
183                       result = GCONV_ILLEGAL_INPUT;
184                       break;
185                     }
186
187                   *((wchar_t *) (outbuf + outwchars)) = ch;
188                   ++do_write;
189                   outwchars += sizeof (wchar_t);
190                   ++cnt;
191                 }
192               *inbufsize -= cnt;
193               data->outbufavail = outwchars;
194             }
195           else
196             {
197               size_t inwchars = *inbufsize;
198               size_t outchars = data->outbufavail;
199               char *outbuf = data->outbuf;
200               size_t cnt = 0;
201               int extra = 0;
202
203               while (inwchars >= cnt + sizeof (wchar_t)
204                      && outchars < data->outbufsize)
205                 {
206                   wchar_t ch = *((wchar_t *) (inbuf + cnt));
207
208                   if (ch <= L'\x7f')
209                     /* It's plain ASCII.  */
210                     outbuf[outchars] = ch;
211                   else
212                     {
213                       /* Try the JIS character sets.  */
214                       size_t found;
215
216                       found = ucs4_to_gb2312 (ch, &outbuf[outchars],
217                                               (data->outbufsize
218                                                - outchars));
219                       if (found > 0)
220                         {
221                           /* It's a GB 2312 character, adjust it for
222                              EUC-CN.  */
223                           outbuf[outchars++] += 0x80;
224                           outbuf[outchars] += 0x80;
225                         }
226                       else if (found == 0)
227                         {
228                           /* We ran out of space.  */
229                           extra = 2;
230                           break;
231                         }
232                       else
233                         /* Illegal character.  */
234                         break;
235                     }
236
237                   ++do_write;
238                   ++outchars;
239                   cnt += sizeof (wchar_t);
240                 }
241               *inbufsize -= cnt;
242               data->outbufavail = outchars;
243
244               if (outchars + extra < data->outbufsize)
245                 {
246                   /* If there is still room in the output buffer something
247                      is wrong with the input.  */
248                   if (inwchars >= cnt + sizeof (wchar_t))
249                     {
250                       /* An error occurred.  */
251                       result = GCONV_ILLEGAL_INPUT;
252                       break;
253                     }
254                   if (inwchars != cnt)
255                     {
256                       /* There are some unprocessed bytes at the end of the
257                          input buffer.  */
258                       result = GCONV_INCOMPLETE_INPUT;
259                       break;
260                     }
261                 }
262             }
263
264           if (result != GCONV_OK)
265             break;
266
267           if (data->is_last)
268             {
269               /* This is the last step.  */
270               result = (*inbufsize > (dir == from_euccn
271                                       ? 0 : sizeof (wchar_t) - 1)
272                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
273               break;
274             }
275
276           /* Status so far.  */
277           result = GCONV_EMPTY_INPUT;
278
279           if (data->outbufavail > 0)
280             {
281               /* Call the functions below in the chain.  */
282               size_t newavail = data->outbufavail;
283
284               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
285                                written, 0);
286
287               /* Correct the output buffer.  */
288               if (newavail != data->outbufavail && newavail > 0)
289                 {
290                   memmove (data->outbuf,
291                            &data->outbuf[data->outbufavail - newavail],
292                            newavail);
293                   data->outbufavail = newavail;
294                 }
295             }
296         }
297       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
298     }
299
300   if (written != NULL && data->is_last)
301     *written = do_write;
302
303   return result;
304 }