3b4864ef11c9fbfccebf8c7e1361efc7f4bb5b4d
[kopensolaris-gnu/glibc.git] / iconvdata / iso646.c
1 /* Conversion to and from the various ISO 646 CCS.
2    Copyright (C) 1998 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
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 /* The implementation of the conversion which can be performed by this
22    module are not very sophisticated and not tuned at all.  There are
23    zillions of ISO 646 derivates and supporting them all in a separate
24    module is overkill since these coded character sets are hardly ever
25    used anymore (except ANSI_X3.4-1968 == ASCII, which is compatible
26    with ISO 8859-1).  The European variants are superceded by the
27    various ISO 8859-? standards and the Asian variants are embedded in
28    larger character sets.  Therefore this implementation is simply
29    here to make it possible to do the conversion if it is necessary.
30    The cost in the gconv-modules file is set to `2' and therefore
31    allows one to easily provide a tuned implementation in case this
32    proofs to be necessary.  */
33
34 #include <gconv.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 /* Direction of the transformation.  */
39 enum direction
40 {
41   illegal_dir,
42   to_iso646,
43   from_iso646
44 };
45
46 enum variant
47 {
48   illegal_var,
49   US,           /* ANSI_X3.4-1968 */
50   GB,           /* BS_4730 */
51 };
52
53 struct iso646_data
54 {
55   enum direction dir;
56   enum variant var;
57 };
58
59
60 int
61 gconv_init (struct gconv_step *step, struct gconv_step_data *data)
62 {
63   /* Determine which direction.  */
64   struct iso646_data *new_data;
65   enum direction dir;
66   enum variant var;
67   int result;
68
69   if (strcasestr (step->from_name, "ANSI_X3.4-1968") != NULL)
70     {
71       dir = from_iso646;
72       var = US;
73     }
74   else if (strcasestr (step->from_name, "BS_4730") != NULL)
75     {
76       dir = from_iso646;
77       var = GB;
78     }
79   else if (strcasestr (step->to_name, "ANSI_X3.4-1968") != NULL)
80     {
81       dir = to_iso646;
82       var = US;
83     }
84   else if (strcasestr (step->to_name, "BS_4730") != NULL)
85     {
86       dir = to_iso646;
87       var = GB;
88     }
89   else
90     {
91       dir = illegal_dir;
92       var = illegal_var;
93     }
94
95   result = GCONV_NOCONV;
96   if (dir != illegal_dir
97       && ((new_data
98            = (struct iso646_data *) malloc (sizeof (struct iso646_data)))
99           != NULL))
100     {
101       new_data->dir = dir;
102       new_data->var = var;
103       data->data = new_data;
104       result = GCONV_OK;
105     }
106
107   return result;
108 }
109
110
111 void
112 gconv_end (struct gconv_step_data *data)
113 {
114   free (data->data);
115 }
116
117
118 int
119 gconv (struct gconv_step *step, struct gconv_step_data *data,
120        const char *inbuf, size_t *inbufsize, size_t *written, int do_flush)
121 {
122   struct gconv_step *next_step = step + 1;
123   struct gconv_step_data *next_data = data + 1;
124   gconv_fct fct = next_step->fct;
125   size_t do_write;
126   int result;
127
128   /* If the function is called with no input this means we have to reset
129      to the initial state.  The possibly partly converted input is
130      dropped.  */
131   if (do_flush)
132     {
133       do_write = 0;
134
135       /* Call the steps down the chain if there are any.  */
136       if (data->is_last)
137         result = GCONV_OK;
138       else
139         {
140           struct gconv_step *next_step = step + 1;
141           struct gconv_step_data *next_data = data + 1;
142
143           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
144
145           /* Clear output buffer.  */
146           data->outbufavail = 0;
147         }
148     }
149   else
150     {
151       enum direction dir = ((struct iso646_data *) data->data)->dir;
152       enum variant var = ((struct iso646_data *) data->data)->var;
153
154       do_write = 0;
155
156       do
157         {
158           result = GCONV_OK;
159
160           if (dir == from_iso646)
161             {
162               size_t inchars = *inbufsize;
163               size_t outwchars = data->outbufavail;
164               char *outbuf = data->outbuf;
165               size_t cnt = 0;
166
167               while (cnt < inchars
168                      && (outwchars + sizeof (wchar_t) <= data->outbufsize))
169                 {
170                   switch ((unsigned char) inbuf[cnt])
171                     {
172                     case '\x23':
173                       if (var == GB)
174                         *((wchar_t *) (outbuf + outwchars)) = 0xa3;
175                       else
176                         *((wchar_t *) (outbuf + outwchars)) = 0x23;
177                       break;
178                     case '\x75':
179                       if (var == GB)
180                         *((wchar_t *) (outbuf + outwchars)) = 0x203e;
181                       else
182                         *((wchar_t *) (outbuf + outwchars)) = 0x75;
183                       break;
184                     default:
185                       *((wchar_t *) (outbuf + outwchars)) =
186                         (unsigned char) inbuf[cnt];
187                     case '\x80' ... '\xff':
188                       /* Illegal character.  */
189                       result = GCONV_ILLEGAL_INPUT;
190                       goto out_from;
191                     }
192                   ++do_write;
193                   outwchars += sizeof (wchar_t);
194                   ++cnt;
195                 }
196             out_from:
197               *inbufsize -= cnt;
198               data->outbufavail = outwchars;
199             }
200           else
201             {
202               size_t inwchars = *inbufsize;
203               size_t outchars = data->outbufavail;
204               unsigned char *outbuf = data->outbuf;
205               size_t cnt = 0;
206
207               while (inwchars >= cnt + sizeof (wchar_t)
208                      && outchars < data->outbufsize)
209                 {
210                   switch (*((wchar_t *) (inbuf + cnt)))
211                     {
212                     case 0x23:
213                       if (var == GB)
214                         goto out_to;
215                       outbuf[outchars] = 0x23;
216                       break;
217                     case 0x75:
218                       if (var == GB)
219                         goto out_to;
220                       outbuf[outchars] = 0x75;
221                       break;
222                     case 0xa3:
223                       if (var != GB)
224                         goto out_to;
225                       outbuf[outchars] = 0x23;
226                       break;
227                     case 0x203e:
228                       if (var != GB)
229                         goto out_to;
230                       outbuf[outchars] = 0x75;
231                       break;
232                     default:
233                       if (*((wchar_t *) (inbuf + cnt)) > 0x7f)
234                         goto out_to;
235                       outbuf[outchars] =
236                         (unsigned char) *((wchar_t *) (inbuf + cnt));
237                       break;
238                     }
239
240                   ++do_write;
241                   ++outchars;
242                   cnt += sizeof (wchar_t);
243                 }
244             out_to:
245               *inbufsize -= cnt;
246               data->outbufavail = outchars;
247
248               if (outchars < data->outbufsize)
249                 {
250                   /* If there is still room in the output buffer something
251                      is wrong with the input.  */
252                   if (inwchars >= cnt + sizeof (wchar_t))
253                     {
254                       /* An error occurred.  */
255                       result = GCONV_ILLEGAL_INPUT;
256                       break;
257                     }
258                   if (inwchars != cnt)
259                     {
260                       /* There are some unprocessed bytes at the end of the
261                          input buffer.  */
262                       result = GCONV_INCOMPLETE_INPUT;
263                       break;
264                     }
265                 }
266             }
267
268           if (result != GCONV_OK)
269             break;
270
271           if (data->is_last)
272             {
273               /* This is the last step.  */
274               result = (*inbufsize > (dir == from_iso646
275                                       ? 0 : sizeof (wchar_t) - 1)
276                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
277               break;
278             }
279
280           /* Status so far.  */
281           result = GCONV_EMPTY_INPUT;
282
283           if (data->outbufavail > 0)
284             {
285               /* Call the functions below in the chain.  */
286               size_t newavail = data->outbufavail;
287
288               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
289                                written, 0);
290
291               /* Correct the output buffer.  */
292               if (newavail != data->outbufavail && newavail > 0)
293                 {
294                   memmove (data->outbuf,
295                            &data->outbuf[data->outbufavail - newavail],
296                            newavail);
297                   data->outbufavail = newavail;
298                 }
299             }
300         }
301       while (*inbufsize > 0 && result == GCONV_EMPTY_INPUT);
302     }
303
304   if (written != NULL && data->is_last)
305     *written = do_write;
306
307   return result;
308 }