cdeb526d7a3d34ef6dd6b66325718faef3751620
[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 <stdlib.h>
23 #include <string.h>
24
25 /* Direction of the transformation.  */
26 enum direction
27 {
28   illegal,
29   to_iso88591,
30   from_iso88591
31 };
32
33 struct iso88591_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 iso88591_data *new_data;
44   enum direction dir;
45   int result;
46
47   if (strcasestr (step->from_name, "ISO-8859-1") != NULL)
48     dir = from_iso88591;
49   else if (strcasestr (step->to_name, "ISO-8859-1") != NULL)
50     dir = to_iso88591;
51   else
52     dir = illegal;
53
54   result = GCONV_NOCONV;
55   if (dir != illegal
56       && ((new_data
57            = (struct iso88591_data *) malloc (sizeof (struct iso88591_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 iso88591_data *) step->data)->dir;
110
111       do_write = 0;
112
113       do
114         {
115           result = GCONV_OK;
116
117           if (dir == from_iso88591)
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 *) (outbuf + outwchars)) =
128                     (unsigned char) inbuf[cnt];
129                   ++do_write;
130                   outwchars += sizeof (wchar_t);
131                   ++cnt;
132                 }
133               *inbufsize -= cnt;
134               data->outbufavail = outwchars;
135             }
136           else
137             {
138               size_t inwchars = *inbufsize;
139               size_t outchars = data->outbufavail;
140               char *outbuf = data->outbuf;
141               size_t cnt = 0;
142
143               while (inwchars >= cnt + sizeof (wchar_t)
144                      && outchars < data->outbufsize)
145                 {
146                   if (*((wchar_t *) (inbuf + cnt)) >= L'\0'
147                       && *((wchar_t *) (inbuf + cnt)) <= L'\377')
148                     outbuf[outchars] = *((wchar_t *) (inbuf + cnt));
149                   else
150                     /* Here is where the transliteration would enter the
151                        scene.  */
152                     break;
153
154                   ++do_write;
155                   ++outchars;
156                   cnt += sizeof (wchar_t);
157                 }
158               *inbufsize -= cnt;
159               data->outbufavail = outchars;
160
161               if (outchars < data->outbufsize)
162                 {
163                   /* If there is still room in the output buffer something
164                      is wrong with the input.  */
165                   if (inwchars >= cnt + sizeof (wchar_t))
166                     {
167                       /* An error occurred.  */
168                       result = GCONV_ILLEGAL_INPUT;
169                       break;
170                     }
171                   if (inwchars != cnt)
172                     {
173                       /* There are some unprocessed bytes at the end of the
174                          input buffer.  */
175                       result = GCONV_INCOMPLETE_INPUT;
176                       break;
177                     }
178                 }
179             }
180
181           if (result != GCONV_OK)
182             break;
183
184           if (data->is_last)
185             {
186               /* This is the last step.  */
187               result = (*inbufsize > (dir == from_iso88591
188                                       ? 0 : sizeof (wchar_t) - 1)
189                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
190               break;
191             }
192
193           /* Status so far.  */
194           result = GCONV_EMPTY_INPUT;
195
196           if (data->outbufavail > 0)
197             {
198               /* Call the functions below in the chain.  */
199               size_t newavail = data->outbufavail;
200
201               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
202                                written, 0);
203
204               /* Correct the output buffer.  */
205               if (newavail != data->outbufavail && newavail > 0)
206                 {
207                   memmove (data->outbuf,
208                            &data->outbuf[data->outbufavail - newavail],
209                            newavail);
210                   data->outbufavail = newavail;
211                 }
212             }
213         }
214       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
215     }
216
217   if (written != NULL && data->is_last)
218     *written = do_write;
219
220   return result;
221 }