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