62dd2c47bfd9085995bb452a46211a47ee466cf2
[kopensolaris-gnu/glibc.git] / iconvdata / euckr.c
1 /* Mapping tables for EUC-KR handling.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Jungshik Shin <jshin@pantheon.yale.edu>, 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 <ksc5601.h>
27
28 /* Direction of the transformation.  */
29 enum direction
30 {
31   illegal,
32   to_euckr,
33   from_euckr
34 };
35
36 struct euckr_data
37 {
38   enum direction dir;
39 };
40
41
42
43 static inline void
44 euckr_from_ucs4(wchar_t ch, unsigned char *cp)
45 {
46   if (ch > 0x7f)
47     {
48       uint16_t idx=0;
49
50       if (ucs4_to_ksc5601 (ch, &idx))
51         idx |= 0x8080;
52
53       *cp = (unsigned char) (idx/256);
54       *(cp+1) = (unsigned char) (idx & 0xff) ;
55     }
56   /* think about 0x5c ; '\' */
57   else
58     {
59       *cp = (unsigned char) (0x7f & ch) ;
60       *(cp+1) = (unsigned char) 0;
61     }
62 }
63
64
65 int
66 gconv_init (struct gconv_step *step)
67 {
68   /* Determine which direction.  */
69   struct euckr_data *new_data;
70   enum direction dir;
71   int result;
72
73   if (strcasestr (step->from_name, "EUC-KR") != NULL)
74     dir = from_euckr;
75   else if (strcasestr (step->to_name, "EUC-KR") != NULL)
76     dir = to_euckr;
77   else
78     dir = illegal;
79
80   result = GCONV_NOCONV;
81   if (dir != illegal
82       && ((new_data
83            = (struct euckr_data *) malloc (sizeof (struct euckr_data)))
84           != NULL))
85     {
86       new_data->dir = dir;
87       step->data = new_data;
88       result = GCONV_OK;
89     }
90
91   return result;
92 }
93
94
95 void
96 gconv_end (struct gconv_step *data)
97 {
98   free (data->data);
99 }
100
101
102 int
103 gconv (struct gconv_step *step, struct gconv_step_data *data,
104        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
105 {
106   struct gconv_step *next_step = step + 1;
107   struct gconv_step_data *next_data = data + 1;
108   gconv_fct fct = next_step->fct;
109   size_t do_write;
110   int result;
111
112   /* If the function is called with no input this means we have to reset
113      to the initial state.  The possibly partly converted input is
114      dropped.  */
115   if (do_flush)
116     {
117       do_write = 0;
118
119       /* Call the steps down the chain if there are any.  */
120       if (data->is_last)
121         result = GCONV_OK;
122       else
123         {
124           struct gconv_step *next_step = step + 1;
125           struct gconv_step_data *next_data = data + 1;
126
127           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
128
129           /* Clear output buffer.  */
130           data->outbufavail = 0;
131         }
132     }
133   else
134     {
135       enum direction dir = ((struct euckr_data *) step->data)->dir;
136
137       do_write = 0;
138
139       do
140         {
141           result = GCONV_OK;
142
143           if (dir == from_euckr)
144             {
145               size_t inchars = *inbufsize;
146               size_t outwchars = data->outbufavail;
147               char *outbuf = data->outbuf;
148               size_t cnt = 0;
149
150               while (cnt < inchars
151                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
152                 {
153                   int inchar = (unsigned char) inbuf[cnt];
154                   wchar_t ch;
155
156                   /*
157                     half-width Korean Currency WON sign
158
159                     if (inchar == 0x5c)
160                       ch =  0x20a9;
161                     else if (inchar <= 0x7f)
162                       ch = (wchar_t) inchar;
163                   */
164
165                   if (inchar <= 0x7f)
166                     ch = (wchar_t) inchar;
167
168
169 /* 0xfe(->0x7e : row 94) and 0xc9(->0x59 : row 41) are user-defined areas */
170
171                   else if ( inchar <= 0xa0 || inchar > 0xfe || inchar == 0xc9)
172                       /* This is illegal.  */
173                       ch = L'\0';
174                   else
175                     {
176                       /* Two-byte character.  First test whether the next
177                          character is also available.  */
178                       int inchar2;
179
180                       if (cnt + 1 >= inchars)
181                         {
182                           /* The second character is not available.  Store
183                              the intermediate result.  */
184                           result = GCONV_INCOMPLETE_INPUT;
185                           break;
186                         }
187
188                       inchar2 = (unsigned char) inbuf[++cnt];
189
190                       ch = ksc5601_to_ucs4 ((uint16_t) (inchar * 256 + inchar2)
191                                             & 0x7f7f);
192                       if (ch == UNKNOWN_10646_CHAR)
193                          ch = L'\0';
194
195                       if (ch == L'\0')
196                         --cnt;
197                     }
198
199                   if (ch == L'\0' && inbuf[cnt] != '\0')
200                     {
201                       /* This is an illegal character.  */
202                       result = GCONV_ILLEGAL_INPUT;
203                       break;
204                     }
205
206                   *((wchar_t *) (outbuf + outwchars)) = ch;
207                   ++do_write;
208                   outwchars += sizeof (wchar_t);
209                   ++cnt;
210                 }
211               *inbufsize -= cnt;
212               data->outbufavail = outwchars;
213             }
214           else
215             {
216               size_t inwchars = *inbufsize;
217               size_t outchars = data->outbufavail;
218               char *outbuf = data->outbuf;
219               size_t cnt = 0;
220               int extra = 0;
221
222               while (inwchars >= cnt + sizeof (wchar_t)
223                      && outchars < data->outbufsize)
224                 {
225                   wchar_t ch = *((wchar_t *) (inbuf + cnt));
226                   unsigned char cp[2];
227
228 /* decomposing Hangul syllables not available in KS C 5601 into Jamos
229    should be considered either here or in euckr_from_ucs4() */
230
231                   euckr_from_ucs4(ch,cp) ;
232
233                   if (cp[0] == '\0' && ch != 0)
234                     /* Illegal character.  */
235                     break;
236
237                   outbuf[outchars] = cp[0];
238                   /* Now test for a possible second byte and write this
239                      if possible.  */
240                   if (cp[1] != '\0')
241                     {
242                       if (outchars + 1 >= data->outbufsize)
243                         {
244                           /* The result does not fit into the buffer.  */
245                           extra = 1;
246                           break;
247                         }
248                       outbuf[++outchars] = cp[1];
249                     }
250
251                   ++do_write;
252                   ++outchars;
253                   cnt += sizeof (wchar_t);
254                 }
255               *inbufsize -= cnt;
256               data->outbufavail = outchars;
257
258               if (outchars + extra < data->outbufsize)
259                 {
260                   /* If there is still room in the output buffer something
261                      is wrong with the input.  */
262                   if (inwchars >= cnt + sizeof (wchar_t))
263                     {
264                       /* An error occurred.  */
265                       result = GCONV_ILLEGAL_INPUT;
266                       break;
267                     }
268                   if (inwchars != cnt)
269                     {
270                       /* There are some unprocessed bytes at the end of the
271                          input buffer.  */
272                       result = GCONV_INCOMPLETE_INPUT;
273                       break;
274                     }
275                 }
276             }
277
278           if (result != GCONV_OK)
279             break;
280
281           if (data->is_last)
282             {
283               /* This is the last step.  */
284               result = (*inbufsize > (dir == from_euckr
285                                       ? 0 : sizeof (wchar_t) - 1)
286                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
287               break;
288             }
289
290           /* Status so far.  */
291           result = GCONV_EMPTY_INPUT;
292
293           if (data->outbufavail > 0)
294             {
295               /* Call the functions below in the chain.  */
296               size_t newavail = data->outbufavail;
297
298               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
299                                written, 0);
300
301               /* Correct the output buffer.  */
302               if (newavail != data->outbufavail && newavail > 0)
303                 {
304                   memmove (data->outbuf,
305                            &data->outbuf[data->outbufavail - newavail],
306                            newavail);
307                   data->outbufavail = newavail;
308                 }
309             }
310         }
311       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
312     }
313
314   if (written != NULL && data->is_last)
315     *written = do_write;
316
317   return result;
318 }