Also reset converted counter in case of an error.
[kopensolaris-gnu/glibc.git] / iconv / skeleton.c
1 /* Skeleton for a converison module.
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 /* This file can be included to provide definitions of several things
22    many modules have in common.  It can be customized using the following
23    macros:
24
25      DEFINE_INIT        define the default initializer.  This requires the
26                         following symbol to be defined.
27
28      CHARSET_NAME       string with official name of the coded character
29                         set (in all-caps)
30
31      DEFINE_FINI        define the default destructor function.
32
33      MIN_NEEDED_FROM    minimal number of bytes needed for the from-charset.
34      MIN_NEEDED_TO      likewise for the to-charset.
35
36      MAX_NEEDED_FROM    maximal number of bytes needed for the from-charset.
37                         This macro is optional, it defaults to MIN_NEEDED_FROM.
38      MAX_NEEDED_TO      likewise for the to-charset.
39
40      DEFINE_DIRECTION_OBJECTS
41                         two objects will be defined to be used when the
42                         `gconv' function must only distinguish two
43                         directions.  This is implied by DEFINE_INIT.
44                         If this macro is not defined the following
45                         macro must be available.
46
47      FROM_DIRECTION     this macro is supposed to return a value != 0
48                         if we convert from the current character set,
49                         otherwise it return 0.
50
51      EMIT_SHIFT_TO_INIT this symbol is optional.  If it is defined it
52                         defines some code which writes out a sequence
53                         of characters which bring the current state into
54                         the initial state.
55
56      FROM_LOOP          name of the function implementing the conversion
57                         from the current characters.
58      TO_LOOP            likewise for the other direction
59
60      RESET_STATE        in case of an error we must reset the state for
61                         the rerun so this macro must be defined for
62                         stateful encodings.  It takes an argument which
63                         is nonzero when saving.
64
65      RESET_INPUT_BUFFER If the input character sets allow this the macro
66                         can be defined to reset the input buffer pointers
67                         to cover only those characters up to the error.
68
69      FUNCTION_NAME      if not set the conversion function is named `gconv'.
70  */
71
72 #include <assert.h>
73 #include <gconv.h>
74 #include <string.h>
75 #define __need_size_t
76 #define __need_NULL
77 #include <stddef.h>
78
79
80 /* The direction objects.  */
81 #if DEFINE_DIRECTION_OBJECTS || DEFINE_INIT
82 static int from_object;
83 static int to_object;
84
85 # ifndef FROM_DIRECTION
86 #  define FROM_DIRECTION step->data == &from_object
87 # endif
88 #else
89 # ifndef FROM_DIRECTION
90 #  error "FROM_DIRECTION must be provided if direction objects are not used"
91 # endif
92 #endif
93
94
95 /* How many bytes are needed at most for the from-charset.  */
96 #ifndef MAX_NEEDED_FROM
97 # define MAX_NEEDED_FROM        MIN_NEEDED_FROM
98 #endif
99
100 /* Same for the to-charset.  */
101 #ifndef MAX_NEEDED_TO
102 # define MAX_NEEDED_TO          MIN_NEEDED_TO
103 #endif
104
105
106 /* For conversions from a fixed width character sets to another fixed width
107    character set we we can define RESET_INPUT_BUFFER is necessary.  */
108 #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE
109 # if MIN_NEEDED_FROM == MAX_NEEDED_FROM && MIN_NEEDED_TO == MAX_NEEDED_TO
110 /* We have to used these `if's here since the compiler cannot know that
111    (outbuf - outerr) is always divisible by MIN_NEEDED_TO.  */
112 #  define RESET_INPUT_BUFFER \
113   if (MIN_NEEDED_FROM % MIN_NEEDED_TO == 0)                                   \
114     *inbuf -= (outbuf - outerr) * (MIN_NEEDED_FROM / MIN_NEEDED_TO);          \
115   else if (MIN_NEEDED_TO % MIN_NEEDED_FROM == 0)                              \
116     *inbuf -= (outbuf - outerr) / (MIN_NEEDED_TO / MIN_NEEDED_FROM);          \
117   else                                                                        \
118     *inbuf -= ((outbuf - outerr) / MIN_NEEDED_TO) * MIN_NEEDED_FROM
119 # endif
120 #endif
121
122
123 /* The default init function.  It simply matches the name and initializes
124    the step data to point to one of the objects above.  */
125 #if DEFINE_INIT
126 # ifndef CHARSET_NAME
127 #  error "CHARSET_NAME not defined"
128 # endif
129
130 int
131 gconv_init (struct gconv_step *step)
132 {
133   /* Determine which direction.  */
134   if (__strcasestr (step->from_name, CHARSET_NAME) != NULL)
135     step->data = &from_object;
136   else if (__strcasestr (step->to_name, CHARSET_NAME) != NULL)
137     step->data = &to_object;
138   else
139     return GCONV_NOCONV;
140
141   step->min_needed_from = MIN_NEEDED_FROM;
142   step->max_needed_from = MAX_NEEDED_FROM;
143   step->min_needed_to = MIN_NEEDED_TO;
144   step->max_needed_to = MAX_NEEDED_TO;
145
146   return GCONV_OK;
147 }
148 #endif
149
150
151 /* The default destructor function does nothing in the moment and so
152    be define it at all.  But we still provide the macro just in case
153    we need it some day.  */
154 #if DEFINE_FINI
155 #endif
156
157
158 /* This is the actual conversion function.  */
159 #ifndef FUNCTION_NAME
160 # define FUNCTION_NAME  gconv
161 #endif
162
163 int
164 FUNCTION_NAME (struct gconv_step *step, struct gconv_step_data *data,
165                const char **inbuf, const char *inbufend, size_t *written,
166                int do_flush)
167 {
168   struct gconv_step *next_step = step + 1;
169   struct gconv_step_data *next_data = data + 1;
170   gconv_fct fct = next_step->fct;
171   int status;
172
173   /* If the function is called with no input this means we have to reset
174      to the initial state.  The possibly partly converted input is
175      dropped.  */
176   if (do_flush)
177     {
178       /* Call the steps down the chain if there are any.  */
179       if (data->is_last)
180         status = GCONV_OK;
181       else
182         {
183 #ifdef EMIT_SHIFT_TO_INIT
184           status = GCONV_OK;
185
186           EMIT_SHIFT_TO_INIT;
187
188           if (status == GCONV_OK)
189 #endif
190             /* Give the modules below the same chance.  */
191             status = (*fct) (next_step, next_data, NULL, NULL, written, 1);
192         }
193     }
194   else
195     {
196       /* This variable is used to count the number of characters we
197          actually converted.  */
198       size_t converted = 0;
199       size_t last_converted;
200
201       /* We preserve the initial values of the pointer variables.  */
202       const char *inptr = *inbuf;
203       char *outbuf = data->outbuf;
204       char *outend = data->outbufend;
205       char *outptr;
206
207       do
208         {
209           /* Remember the start value for this round.  */
210           inptr = *inbuf;
211           /* The outbuf buffer is empty.  */
212           outptr = outbuf;
213
214           /* Save the state.  */
215           last_converted = converted;
216 #ifdef SAVE_RESET_STATE
217           SAVE_RESET_STATE (1);
218 #endif
219
220           if (FROM_DIRECTION)
221             /* Run the conversion loop.  */
222             status = FROM_LOOP ((const unsigned char **) inbuf,
223                                 (const unsigned char *) inbufend,
224                                 (unsigned char **) &outbuf,
225                                 (unsigned char *) outend,
226                                 data->statep, step->data, &converted);
227           else
228             /* Run the conversion loop.  */
229             status = TO_LOOP ((const unsigned char **) inbuf,
230                               (const unsigned char *) inbufend,
231                               (unsigned char **) &outbuf,
232                               (unsigned char *) outend,
233                               data->statep, step->data, &converted);
234
235           /* If this is the last step leave the loop, there is nothgin
236              we can do.  */
237           if (data->is_last)
238             {
239               /* Store information about how many bytes are available.  */
240               data->outbuf = outbuf;
241               break;
242             }
243
244           /* Write out all output which was produced.  */
245           if (outbuf > outptr)
246             {
247               const char *outerr = data->outbuf;
248               int result;
249
250               result = (*fct) (next_step, next_data, &outerr, outbuf,
251                                written, 0);
252
253               if (result != GCONV_EMPTY_INPUT)
254                 {
255                   if (outerr != outbuf)
256                     {
257 #ifdef RESET_INPUT_BUFFER
258                       RESET_INPUT_BUFFER;
259 #else
260                       /* We have a problem with the in on of the functions
261                          below.  Undo the conversion upto the error point.  */
262                       size_t nstatus;
263
264                       /* Reload the pointers.  */
265                       *inbuf = inptr;
266                       outbuf = outptr;
267
268                       /* Reset the state.  */
269                       converted = last_converted;
270 # ifdef SAVE_RESET_STATE
271                       SAVE_RESET_STATE (0);
272 # endif
273
274                       if (FROM_DIRECTION)
275                         /* Run the conversion loop.  */
276                         nstatus = FROM_LOOP ((const unsigned char **) inbuf,
277                                              (const unsigned char *) inbufend,
278                                              (unsigned char **) &outbuf,
279                                              (unsigned char *) outerr,
280                                              data->statep, step->data,
281                                              &converted);
282                       else
283                         /* Run the conversion loop.  */
284                         nstatus = TO_LOOP ((const unsigned char **) inbuf,
285                                            (const unsigned char *) inbufend,
286                                            (unsigned char **) &outbuf,
287                                            (unsigned char *) outerr,
288                                            data->statep, step->data,
289                                            &converted);
290
291                       /* We must run out of output buffer space in this
292                          rerun.  */
293                       assert (outbuf == outerr);
294                       assert (nstatus == GCONV_FULL_OUTPUT);
295 #endif  /* reset input buffer */
296                     }
297
298                   /* Change the status.  */
299                   status = result;
300                 }
301               else
302                 /* All the output is consumed, we can make another run
303                    if everything was ok.  */
304                 if (status == GCONV_FULL_OUTPUT)
305                   status = GCONV_OK;
306             }
307         }
308       while (status == GCONV_OK);
309
310       /* Remember how many characters we converted.  */
311       *written += converted;
312     }
313
314   return status;
315 }
316
317 #undef DEFINE_INIT
318 #undef CHARSET_NAME
319 #undef DEFINE_FINI
320 #undef MIN_NEEDED_FROM
321 #undef MIN_NEEDED_TO
322 #undef MAX_NEEDED_FROM
323 #undef MAX_NEEDED_TO
324 #undef DEFINE_DIRECTION_OBJECTS
325 #undef FROM_DIRECTION
326 #undef EMIT_SHIFT_TO_INIT
327 #undef FROM_LOOP
328 #undef TO_LOOP
329 #undef RESET_STATE
330 #undef RESET_INPUT_BUFFER
331 #undef FUNCTION_NAME