Simplify step data handling.
[kopensolaris-gnu/glibc.git] / iconvdata / 8bit-gap.c
1 /* Generic conversion to and from 8bit charsets,
2    converting from UCS using gaps.
3    Copyright (C) 1997, 1998 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 <stdint.h>
24 #include <string.h>
25
26
27 struct gap
28 {
29   uint16_t start;
30   uint16_t end;
31   int16_t idx;
32 };
33
34 /* Now we can include the tables.  */
35 #include TABLES
36
37 /* We use three objects to describe the operation mode.  */
38 static int from_8bit_object;
39 static int to_8bit_object;
40
41
42 int
43 gconv_init (struct gconv_step *step)
44 {
45   /* Determine which direction.  */
46   if (strcasestr (step->from_name, NAME) != NULL)
47     step->data = &from_8bit_object;
48   else if (strcasestr (step->to_name, NAME) != NULL)
49     step->data = &to_8bit_object;
50   else
51     return GCONV_NOCONV;
52
53   return GCONV_OK;
54 }
55
56
57 void
58 gconv_end (struct gconv_step *data)
59 {
60   /* Nothing to do.  */
61 }
62
63
64 int
65 gconv (struct gconv_step *step, struct gconv_step_data *data,
66        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
67 {
68   struct gconv_step *next_step = step + 1;
69   struct gconv_step_data *next_data = data + 1;
70   gconv_fct fct = next_step->fct;
71   size_t do_write;
72   int result;
73
74   /* If the function is called with no input this means we have to reset
75      to the initial state.  The possibly partly converted input is
76      dropped.  */
77   if (do_flush)
78     {
79       do_write = 0;
80
81       /* Call the steps down the chain if there are any.  */
82       if (data->is_last)
83         result = GCONV_OK;
84       else
85         {
86           struct gconv_step *next_step = step + 1;
87           struct gconv_step_data *next_data = data + 1;
88
89           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
90
91           /* Clear output buffer.  */
92           data->outbufavail = 0;
93         }
94     }
95   else
96     {
97       do_write = 0;
98
99       do
100         {
101           result = GCONV_OK;
102
103           if (step->data == &from_8bit_object)
104             {
105               size_t inchars = *inbufsize;
106               size_t outwchars = data->outbufavail;
107               char *outbuf = data->outbuf;
108               size_t cnt = 0;
109
110               while (cnt < inchars
111                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
112                 {
113                   wchar_t ch = to_ucs4[(unsigned int) inbuf[cnt]];
114
115                   if (ch == L'\0' && inbuf[cnt] != '\0')
116                     {
117                       /* This is an illegal character.  */
118                       result = GCONV_ILLEGAL_INPUT;
119                       break;
120                     }
121
122                   *((wchar_t *) (outbuf + outwchars)) = ch;
123                   ++do_write;
124                   outwchars += sizeof (wchar_t);
125                   ++cnt;
126                 }
127               *inbufsize -= cnt;
128               data->outbufavail = outwchars;
129             }
130           else
131             {
132               size_t inwchars = *inbufsize;
133               size_t outchars = data->outbufavail;
134               char *outbuf = data->outbuf;
135               size_t cnt = 0;
136
137               while (inwchars >= cnt + sizeof (wchar_t)
138                      && outchars < data->outbufsize)
139                 {
140                   const struct gap *rp = from_idx;
141                   unsigned int ch = *((wchar_t *) (inbuf + cnt));
142                   char res;
143
144                   while (ch > rp->end)
145                     ++rp;
146                   if (ch < rp->start)
147                     /* No valid character.  */
148                     break;
149
150                   res = from_ucs4[ch + rp->idx];
151                   if (res == '\0' && ch != 0)
152                     /* No valid character.  */
153                     break;
154
155                   outbuf[outchars] = res;
156                   ++do_write;
157                   ++outchars;
158                   cnt += sizeof (wchar_t);
159                 }
160               *inbufsize -= cnt;
161               data->outbufavail = outchars;
162
163               if (outchars < data->outbufsize)
164                 {
165                   /* If there is still room in the output buffer something
166                      is wrong with the input.  */
167                   if (inwchars >= cnt + sizeof (wchar_t))
168                     {
169                       /* An error occurred.  */
170                       result = GCONV_ILLEGAL_INPUT;
171                       break;
172                     }
173                   if (inwchars != cnt)
174                     {
175                       /* There are some unprocessed bytes at the end of the
176                          input buffer.  */
177                       result = GCONV_INCOMPLETE_INPUT;
178                       break;
179                     }
180                 }
181             }
182
183           if (result != GCONV_OK)
184             break;
185
186           if (data->is_last)
187             {
188               /* This is the last step.  */
189               result = (*inbufsize > (step->data == &from_8bit_object
190                                       ? 0 : sizeof (wchar_t) - 1)
191                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
192               break;
193             }
194
195           /* Status so far.  */
196           result = GCONV_EMPTY_INPUT;
197
198           if (data->outbufavail > 0)
199             {
200               /* Call the functions below in the chain.  */
201               size_t newavail = data->outbufavail;
202
203               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
204                                written, 0);
205
206               /* Correct the output buffer.  */
207               if (newavail != data->outbufavail && newavail > 0)
208                 {
209                   memmove (data->outbuf,
210                            &data->outbuf[data->outbufavail - newavail],
211                            newavail);
212                   data->outbufavail = newavail;
213                 }
214             }
215         }
216       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
217     }
218
219   if (written != NULL && data->is_last)
220     *written = do_write;
221
222   return result;
223 }