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