Update.
[platform/upstream/glibc.git] / iconv / gconv_simple.c
1 /* Simple transformations functions.
2    Copyright (C) 1997 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 #include <wchar.h>
25 #include <sys/param.h>
26
27
28 int
29 __gconv_transform_dummy (struct gconv_step *step, struct gconv_step_data *data,
30                          const char *inbuf, size_t *inlen, size_t *written,
31                          int do_flush)
32 {
33   size_t do_write;
34
35   /* We have no stateful encoding.  So we don't have to do anything
36      special.  */
37   if (do_flush)
38     do_write = 0;
39   else
40     {
41       do_write = MIN (*inlen, data->outbufsize - data->outbufavail);
42
43       memcpy (data->outbuf, inbuf, do_write);
44
45       *inlen -= do_write;
46       data->outbufavail += do_write;
47     }
48
49   /* ### TODO Actually, this number must be devided according to the
50      size of the input charset.  I.e., if the input is in UCS4 the
51      number of copied bytes must be divided by 4.  */
52   if (written != NULL)
53     *written = do_write;
54
55   return GCONV_OK;
56 }
57
58
59 int
60 __gconv_transform_init_rstate (struct gconv_step *step,
61                                struct gconv_step_data *data)
62 {
63   /* We have to provide the transformation function an correctly initialized
64      object of type `mbstate_t'.  This must be dynamically allocated.  */
65   data->data = calloc (1, sizeof (mbstate_t));
66
67   return data->data == NULL ? GCONV_NOMEM : GCONV_OK;
68 }
69
70
71 void
72 __gconv_transform_end_rstate (struct gconv_step_data *data)
73 {
74   if (data->data != NULL)
75     free (data->data);
76 }
77
78
79 int
80 __gconv_transform_ucs4_utf8 (struct gconv_step *step,
81                              struct gconv_step_data *data, const char *inbuf,
82                              size_t *inlen, size_t *written, int do_flush)
83 {
84   struct gconv_step *next_step = step + 1;
85   struct gconv_step_data *next_data = data + 1;
86   gconv_fct fct = next_step->fct;
87   size_t do_write;
88   int result;
89
90   /* If the function is called with no input this means we have to reset
91      to the initial state.  The possibly partly converted input is
92      dropped.  */
93   if (do_flush)
94     {
95       /* Clear the state.  */
96       memset (data->data, '\0', sizeof (mbstate_t));
97       do_write = 0;
98
99       /* Call the steps down the chain if there are any.  */
100       if (data->is_last)
101         result = GCONV_OK;
102       else
103         {
104           struct gconv_step *next_step = step + 1;
105           struct gconv_step_data *next_data = data + 1;
106
107           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
108
109           /* Clear output buffer.  */
110           data->outbufavail = 0;
111         }
112     }
113   else
114     {
115       do_write = 0;
116
117       do
118         {
119           const char *newinbuf = inbuf;
120           size_t actually = __wmemrtombs (&data->outbuf[data->outbufavail],
121                                           (const wchar_t **) &newinbuf,
122                                           *inlen / sizeof (wchar_t),
123                                           data->outbufsize - data->outbufavail,
124                                           (mbstate_t *) data->data);
125
126           /* Remember how much we converted.  */
127           do_write += newinbuf - inbuf;
128           *inlen -= (newinbuf - inbuf) * sizeof (wchar_t);
129
130           data->outbufavail += actually;
131
132           if (data->is_last)
133             {
134               /* This is the last step.  */
135               result = (*inlen < sizeof (wchar_t)
136                         ? GCONV_EMPTY_INPUT : GCONV_FULL_OUTPUT);
137               break;
138             }
139
140           /* Status so far.  */
141           result = GCONV_EMPTY_INPUT;
142
143           if (data->outbufavail > 0)
144             {
145               /* Call the functions below in the chain.  */
146               size_t newavail = data->outbufavail;
147
148               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
149                                written, 0);
150
151               /* Correct the output buffer.  */
152               if (newavail != data->outbufavail && newavail > 0)
153                 {
154                   memmove (data->outbuf,
155                            &data->outbuf[data->outbufavail - newavail],
156                            newavail);
157                   data->outbufavail = newavail;
158                 }
159             }
160         }
161       while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
162     }
163
164   if (written != NULL && data->is_last)
165     *written = do_write / sizeof (wchar_t);
166
167   return result;
168 }
169
170
171 int
172 __gconv_transform_utf8_ucs4 (struct gconv_step *step,
173                              struct gconv_step_data *data, const char *inbuf,
174                              size_t *inlen, size_t *written, int do_flush)
175 {
176   struct gconv_step *next_step = step + 1;
177   struct gconv_step_data *next_data = data + 1;
178   gconv_fct fct = next_step->fct;
179   size_t do_write;
180   int result;
181
182   /* If the function is called with no input this means we have to reset
183      to the initial state.  The possibly partly converted input is
184      dropped.  */
185   if (do_flush)
186     {
187       /* Clear the state.  */
188       memset (data->data, '\0', sizeof (mbstate_t));
189       do_write = 0;
190
191       /* Call the steps down the chain if there are any.  */
192       if (data->is_last)
193         result = GCONV_OK;
194       else
195         {
196           struct gconv_step *next_step = step + 1;
197           struct gconv_step_data *next_data = data + 1;
198
199           result = (*fct) (next_step, next_data, NULL, 0, written, 1);
200         }
201     }
202   else
203     {
204       do_write = 0;
205
206       do
207         {
208           const char *newinbuf = inbuf;
209           size_t actually = __wmemrtowcs ((wchar_t *) &data->outbuf[data->outbufavail],
210                                           &newinbuf, *inlen,
211                                           ((data->outbufsize
212                                             - data->outbufavail)
213                                            / sizeof (wchar_t)),
214                                           (mbstate_t *) data->data);
215
216           /* Remember how much we converted.  */
217           do_write += actually;
218           *inlen -= newinbuf - inbuf;
219
220           data->outbufavail += actually * sizeof (wchar_t);
221
222           if (data->is_last)
223             {
224               /* This is the last step.  */
225               result = (data->outbufavail + sizeof (wchar_t) > data->outbufsize
226                         ? GCONV_FULL_OUTPUT : GCONV_EMPTY_INPUT);
227               break;
228             }
229
230           /* Status so far.  */
231           result = GCONV_EMPTY_INPUT;
232
233           if (data->outbufavail > 0)
234             {
235               /* Call the functions below in the chain.  */
236               size_t newavail = data->outbufavail;
237
238               result = (*fct) (next_step, next_data, data->outbuf, &newavail,
239                                written, 0);
240
241               /* Correct the output buffer.  */
242               if (newavail != data->outbufavail && newavail > 0)
243                 {
244                   memmove (data->outbuf,
245                            &data->outbuf[data->outbufavail - newavail],
246                            newavail);
247                   data->outbufavail = newavail;
248                 }
249             }
250         }
251       while (*inlen > 0 && result == GCONV_EMPTY_INPUT);
252     }
253
254   if (written != NULL && data->is_last)
255     *written = do_write;
256
257   return result;
258 }