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