0bcc91ba28775b7f37da14d3b4db334ea3b8ccbe
[platform/upstream/glibc.git] / iconvdata / 8bit-gap.c
1 /* Generic conversion to and from 8bit charsets,
2    converting from UCS using gaps.
3    Copyright (C) 1997 Free Software Foundation, Inc.
4    This file is part of the GNU C Library.
5    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6
7    The GNU C Library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public License as
9    published by the Free Software Foundation; either version 2 of the
10    License, or (at your option) any later version.
11
12    The GNU C Library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public
18    License along with the GNU C Library; see the file COPYING.LIB.  If not,
19    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20    Boston, MA 02111-1307, USA.  */
21
22 #include <gconv.h>
23 #include <inttypes.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27
28 struct gap
29 {
30   uint16_t start;
31   uint16_t end;
32   int16_t idx;
33 };
34
35 /* Now we can include the tables.  */
36 #include TABLES
37
38 /* Direction of the transformation.  */
39 enum direction
40 {
41   illegal,
42   to_8bit,
43   from_8bit
44 };
45
46 struct s_8bit_data
47 {
48   enum direction dir;
49 };
50
51
52 int
53 gconv_init (struct gconv_step *step, struct gconv_step_data *data)
54 {
55   /* Determine which direction.  */
56   struct s_8bit_data *new_data;
57   enum direction dir;
58   int result;
59
60   if (__strcasestr (step->from_name, NAME) != NULL)
61     dir = from_8bit;
62   else if (__strcasestr (step->to_name, NAME) != NULL)
63     dir = to_8bit;
64   else
65     dir = illegal;
66
67   result = GCONV_NOCONV;
68   if (dir != illegal
69       && ((new_data
70            = (struct s_8bit_data *) malloc (sizeof (struct s_8bit_data)))
71           != NULL))
72     {
73       new_data->dir = dir;
74       data->data = new_data;
75       result = GCONV_OK;
76     }
77
78   return result;
79 }
80
81
82 void
83 gconv_end (struct gconv_step_data *data)
84 {
85   free (data->data);
86 }
87
88
89 int
90 gconv (struct gconv_step *step, struct gconv_step_data *data,
91        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
92 {
93   struct gconv_step *next_step = step + 1;
94   struct gconv_step_data *next_data = data + 1;
95   gconv_fct fct = next_step->fct;
96   size_t do_write;
97   int result;
98
99   /* If the function is called with no input this means we have to reset
100      to the initial state.  The possibly partly converted input is
101      dropped.  */
102   if (do_flush)
103     {
104       do_write = 0;
105
106       /* Call the steps down the chain if there are any.  */
107       if (data->is_last)
108         result = GCONV_OK;
109       else
110         {
111           struct gconv_step *next_step = step + 1;
112           struct gconv_step_data *next_data = data + 1;
113
114           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
115
116           /* Clear output buffer.  */
117           data->outbufavail = 0;
118         }
119     }
120   else
121     {
122       enum direction dir = ((struct s_8bit_data *) data->data)->dir;
123
124       do_write = 0;
125
126       do
127         {
128           result = GCONV_OK;
129
130           if (dir == from_8bit)
131             {
132               size_t inchars = *inbufsize;
133               size_t outwchars = data->outbufavail;
134               char *outbuf = data->outbuf;
135               size_t cnt = 0;
136
137               while (cnt < inchars
138                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
139                 {
140                   wchar_t ch = to_ucs4[(unsigned int) inbuf[cnt]];
141
142                   if (ch == L'\0' && inbuf[cnt] != '\0')
143                     {
144                       /* This is an illegal character.  */
145                       result = GCONV_ILLEGAL_INPUT;
146                       break;
147                     }
148
149                   *((wchar_t *) (outbuf + outwchars)) = ch;
150                   ++do_write;
151                   outwchars += sizeof (wchar_t);
152                   ++cnt;
153                 }
154               *inbufsize -= cnt;
155               data->outbufavail = outwchars;
156             }
157           else
158             {
159               size_t inwchars = *inbufsize;
160               size_t outchars = data->outbufavail;
161               char *outbuf = data->outbuf;
162               size_t cnt = 0;
163
164               while (inwchars >= cnt + sizeof (wchar_t)
165                      && outchars < data->outbufsize)
166                 {
167                   const struct gap *rp = from_idx;
168                   unsigned int ch = *((wchar_t *) (inbuf + cnt));
169                   char res;
170
171                   while (ch > rp->end)
172                     ++rp;
173                   if (ch < rp->start)
174                     /* No valid character.  */
175                     break;
176
177                   res = from_ucs4[ch + rp->idx];
178                   if (res == '\0' && ch != 0)
179                     /* No valid character.  */
180                     break;
181
182                   outbuf[outchars] = res;
183                   ++do_write;
184                   ++outchars;
185                   cnt += sizeof (wchar_t);
186                 }
187               *inbufsize -= cnt;
188               data->outbufavail = outchars;
189
190               if (outchars < data->outbufsize)
191                 {
192                   /* If there is still room in the output buffer something
193                      is wrong with the input.  */
194                   if (inwchars >= cnt + sizeof (wchar_t))
195                     {
196                       /* An error occurred.  */
197                       result = GCONV_ILLEGAL_INPUT;
198                       break;
199                     }
200                   if (inwchars != cnt)
201                     {
202                       /* There are some unprocessed bytes at the end of the
203                          input buffer.  */
204                       result = GCONV_INCOMPLETE_INPUT;
205                       break;
206                     }
207                 }
208             }
209
210           if (result != GCONV_OK)
211             break;
212
213           if (data->is_last)
214             {
215               /* This is the last step.  */
216               result = (*inbufsize > (dir == from_8bit
217                                       ? 0 : sizeof (wchar_t) - 1)
218                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
219               break;
220             }
221
222           /* Status so far.  */
223           result = GCONV_EMPTY_INPUT;
224
225           if (data->outbufavail > 0)
226             {
227               /* Call the functions below in the chain.  */
228               size_t newavail = data->outbufavail;
229
230               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
231                                written, 0);
232
233               /* Correct the output buffer.  */
234               if (newavail != data->outbufavail && newavail > 0)
235                 {
236                   memmove (data->outbuf,
237                            &data->outbuf[data->outbufavail - newavail],
238                            newavail);
239                   data->outbufavail = newavail;
240                 }
241             }
242         }
243       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
244     }
245
246   if (written != NULL && data->is_last)
247     *written = do_write;
248
249   return result;
250 }