Simplify step data handling.
[kopensolaris-gnu/glibc.git] / iconvdata / iso8859-1.c
1 /* Conversion to and from ISO 8859-1.
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 /* Direction of the transformation.  */
25 static int to_iso88591_object;
26 static int from_iso88591_object;
27
28
29 int
30 gconv_init (struct gconv_step *step)
31 {
32   /* Determine which direction.  */
33   if (strcasestr (step->from_name, "ISO-8859-1") != NULL)
34     step->data = &from_iso88591_object;
35   else if (strcasestr (step->to_name, "ISO-8859-1") != NULL)
36     step->data = &to_iso88591_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_iso88591_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 *) (outbuf + outwchars)) =
101                     (unsigned char) inbuf[cnt];
102                   ++do_write;
103                   outwchars += sizeof (wchar_t);
104                   ++cnt;
105                 }
106               *inbufsize -= cnt;
107               data->outbufavail = outwchars;
108             }
109           else
110             {
111               size_t inwchars = *inbufsize;
112               size_t outchars = data->outbufavail;
113               char *outbuf = data->outbuf;
114               size_t cnt = 0;
115
116               while (inwchars >= cnt + sizeof (wchar_t)
117                      && outchars < data->outbufsize)
118                 {
119                   if (*((wchar_t *) (inbuf + cnt)) >= L'\0'
120                       && *((wchar_t *) (inbuf + cnt)) <= L'\377')
121                     outbuf[outchars] = *((wchar_t *) (inbuf + cnt));
122                   else
123                     /* Here is where the transliteration would enter the
124                        scene.  */
125                     break;
126
127                   ++do_write;
128                   ++outchars;
129                   cnt += sizeof (wchar_t);
130                 }
131               *inbufsize -= cnt;
132               data->outbufavail = outchars;
133
134               if (outchars < data->outbufsize)
135                 {
136                   /* If there is still room in the output buffer something
137                      is wrong with the input.  */
138                   if (inwchars >= cnt + sizeof (wchar_t))
139                     {
140                       /* An error occurred.  */
141                       result = GCONV_ILLEGAL_INPUT;
142                       break;
143                     }
144                   if (inwchars != cnt)
145                     {
146                       /* There are some unprocessed bytes at the end of the
147                          input buffer.  */
148                       result = GCONV_INCOMPLETE_INPUT;
149                       break;
150                     }
151                 }
152             }
153
154           if (result != GCONV_OK)
155             break;
156
157           if (data->is_last)
158             {
159               /* This is the last step.  */
160               result = (*inbufsize > (step->data == &from_iso88591_object
161                                       ? 0 : sizeof (wchar_t) - 1)
162                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
163               break;
164             }
165
166           /* Status so far.  */
167           result = GCONV_EMPTY_INPUT;
168
169           if (data->outbufavail > 0)
170             {
171               /* Call the functions below in the chain.  */
172               size_t newavail = data->outbufavail;
173
174               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
175                                written, 0);
176
177               /* Correct the output buffer.  */
178               if (newavail != data->outbufavail && newavail > 0)
179                 {
180                   memmove (data->outbuf,
181                            &data->outbuf[data->outbufavail - newavail],
182                            newavail);
183                   data->outbufavail = newavail;
184                 }
185             }
186         }
187       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
188     }
189
190   if (written != NULL && data->is_last)
191     *written = do_write;
192
193   return result;
194 }