Update.
[jlayton/glibc.git] / iconv / skeleton.c
1 /* Skeleton for a conversion module.
2    Copyright (C) 1998, 1999, 2000 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      PREPARE_LOOP       optional code preparing the conversion loop.  Can
72                         contain variable definitions.
73      END_LOOP           also optional, may be used to store information
74
75      EXTRA_LOOP_ARGS    optional macro specifying extra arguments passed
76                         to loop function.
77  */
78
79 #include <assert.h>
80 #include <gconv.h>
81 #include <string.h>
82 #define __need_size_t
83 #define __need_NULL
84 #include <stddef.h>
85
86 #ifndef STATIC_GCONV
87 # include <dlfcn.h>
88 #endif
89
90 #ifndef DL_CALL_FCT
91 # define DL_CALL_FCT(fct, args) fct args
92 #endif
93
94 /* The direction objects.  */
95 #if DEFINE_DIRECTION_OBJECTS || DEFINE_INIT
96 static int from_object;
97 static int to_object;
98
99 # ifndef FROM_DIRECTION
100 #  define FROM_DIRECTION (step->__data == &from_object)
101 # endif
102 #else
103 # ifndef FROM_DIRECTION
104 #  error "FROM_DIRECTION must be provided if direction objects are not used"
105 # endif
106 #endif
107
108
109 /* How many bytes are needed at most for the from-charset.  */
110 #ifndef MAX_NEEDED_FROM
111 # define MAX_NEEDED_FROM        MIN_NEEDED_FROM
112 #endif
113
114 /* Same for the to-charset.  */
115 #ifndef MAX_NEEDED_TO
116 # define MAX_NEEDED_TO          MIN_NEEDED_TO
117 #endif
118
119
120 /* For conversions from a fixed width character sets to another fixed width
121    character set we we can define RESET_INPUT_BUFFER is necessary.  */
122 #if !defined RESET_INPUT_BUFFER && !defined SAVE_RESET_STATE
123 # if MIN_NEEDED_FROM == MAX_NEEDED_FROM && MIN_NEEDED_TO == MAX_NEEDED_TO
124 /* We have to use these `if's here since the compiler cannot know that
125    (outbuf - outerr) is always divisible by MIN_NEEDED_TO.  */
126 #  define RESET_INPUT_BUFFER \
127   if (MIN_NEEDED_FROM % MIN_NEEDED_TO == 0)                                   \
128     *inbuf -= (outbuf - outerr) * (MIN_NEEDED_FROM / MIN_NEEDED_TO);          \
129   else if (MIN_NEEDED_TO % MIN_NEEDED_FROM == 0)                              \
130     *inbuf -= (outbuf - outerr) / (MIN_NEEDED_TO / MIN_NEEDED_FROM);          \
131   else                                                                        \
132     *inbuf -= ((outbuf - outerr) / MIN_NEEDED_TO) * MIN_NEEDED_FROM
133 # endif
134 #endif
135
136
137 /* The default init function.  It simply matches the name and initializes
138    the step data to point to one of the objects above.  */
139 #if DEFINE_INIT
140 # ifndef CHARSET_NAME
141 #  error "CHARSET_NAME not defined"
142 # endif
143
144 int
145 gconv_init (struct __gconv_step *step)
146 {
147   /* Determine which direction.  */
148   if (strcmp (step->__from_name, CHARSET_NAME) == 0)
149     {
150       step->__data = &from_object;
151
152       step->__min_needed_from = MIN_NEEDED_FROM;
153       step->__max_needed_from = MAX_NEEDED_FROM;
154       step->__min_needed_to = MIN_NEEDED_TO;
155       step->__max_needed_to = MAX_NEEDED_TO;
156     }
157   else if (strcmp (step->__to_name, CHARSET_NAME) == 0)
158     {
159       step->__data = &to_object;
160
161       step->__min_needed_from = MIN_NEEDED_TO;
162       step->__max_needed_from = MAX_NEEDED_TO;
163       step->__min_needed_to = MIN_NEEDED_FROM;
164       step->__max_needed_to = MAX_NEEDED_FROM;
165     }
166   else
167     return __GCONV_NOCONV;
168
169 #ifdef RESET_STATE
170   step->__stateful = 1;
171 #else
172   step->__stateful = 0;
173 #endif
174
175   return __GCONV_OK;
176 }
177 #endif
178
179
180 /* The default destructor function does nothing in the moment and so
181    be define it at all.  But we still provide the macro just in case
182    we need it some day.  */
183 #if DEFINE_FINI
184 #endif
185
186
187 /* If no arguments have to passed to the loop function define the macro
188    as empty.  */
189 #ifndef EXTRA_LOOP_ARGS
190 # define EXTRA_LOOP_ARGS
191 #endif
192
193
194 /* This is the actual conversion function.  */
195 #ifndef FUNCTION_NAME
196 # define FUNCTION_NAME  gconv
197 #endif
198
199 int
200 FUNCTION_NAME (struct __gconv_step *step, struct __gconv_step_data *data,
201                const unsigned char **inbuf, const unsigned char *inbufend,
202                size_t *written, int do_flush)
203 {
204   struct __gconv_step *next_step = step + 1;
205   struct __gconv_step_data *next_data = data + 1;
206   __gconv_fct fct = data->__is_last ? NULL : next_step->__fct;
207   int status;
208
209   /* If the function is called with no input this means we have to reset
210      to the initial state.  The possibly partly converted input is
211      dropped.  */
212   if (do_flush)
213     {
214       status = __GCONV_OK;
215
216 #ifdef EMIT_SHIFT_TO_INIT
217       /* Emit the escape sequence to reset the state.  */
218       EMIT_SHIFT_TO_INIT;
219 #endif
220       /* Call the steps down the chain if there are any but only if we
221          successfully emitted the escape sequence.  */
222       if (status == __GCONV_OK && ! data->__is_last)
223         status = DL_CALL_FCT (fct, (next_step, next_data, NULL, NULL,
224                                     written, 1));
225     }
226   else
227     {
228       /* We preserve the initial values of the pointer variables.  */
229       const unsigned char *inptr = *inbuf;
230       unsigned char *outbuf = data->__outbuf;
231       unsigned char *outend = data->__outbufend;
232       unsigned char *outstart;
233
234       /* This variable is used to count the number of characters we
235          actually converted.  */
236       size_t converted = 0;
237
238 #ifdef PREPARE_LOOP
239       PREPARE_LOOP
240 #endif
241
242       do
243         {
244           /* Remember the start value for this round.  */
245           inptr = *inbuf;
246           /* The outbuf buffer is empty.  */
247           outstart = outbuf;
248
249 #ifdef SAVE_RESET_STATE
250           SAVE_RESET_STATE (1);
251 #endif
252
253           if (FROM_DIRECTION)
254             /* Run the conversion loop.  */
255             status = FROM_LOOP (inbuf, inbufend, &outbuf, outend,
256                                 data->__statep, step->__data, &converted
257                                 EXTRA_LOOP_ARGS);
258           else
259             /* Run the conversion loop.  */
260             status = TO_LOOP (inbuf, inbufend, &outbuf, outend,
261                               data->__statep, step->__data, &converted
262                               EXTRA_LOOP_ARGS);
263
264           /* If this is the last step leave the loop, there is nothing
265              we can do.  */
266           if (data->__is_last)
267             {
268               /* Store information about how many bytes are available.  */
269               data->__outbuf = outbuf;
270
271               /* Remember how many non-identical characters we converted.  */
272               *written += converted;
273
274               break;
275             }
276
277           /* Write out all output which was produced.  */
278           if (outbuf > outstart)
279             {
280               const unsigned char *outerr = data->__outbuf;
281               int result;
282
283               result = DL_CALL_FCT (fct, (next_step, next_data, &outerr,
284                                           outbuf, written, 0));
285
286               if (result != __GCONV_EMPTY_INPUT)
287                 {
288                   if (outerr != outbuf)
289                     {
290 #ifdef RESET_INPUT_BUFFER
291                       RESET_INPUT_BUFFER;
292 #else
293                       /* We have a problem with the in on of the functions
294                          below.  Undo the conversion upto the error point.  */
295                       size_t nstatus;
296
297                       /* Reload the pointers.  */
298                       *inbuf = inptr;
299                       outbuf = outstart;
300
301                       /* Reset the state.  */
302 # ifdef SAVE_RESET_STATE
303                       SAVE_RESET_STATE (0);
304 # endif
305
306                       if (FROM_DIRECTION)
307                         /* Run the conversion loop.  */
308                         nstatus = FROM_LOOP ((const unsigned char **) inbuf,
309                                              (const unsigned char *) inbufend,
310                                              (unsigned char **) &outbuf,
311                                              (unsigned char *) outerr,
312                                              data->__statep, step->__data,
313                                              &converted EXTRA_LOOP_ARGS);
314                       else
315                         /* Run the conversion loop.  */
316                         nstatus = TO_LOOP ((const unsigned char **) inbuf,
317                                            (const unsigned char *) inbufend,
318                                            (unsigned char **) &outbuf,
319                                            (unsigned char *) outerr,
320                                            data->__statep, step->__data,
321                                            &converted EXTRA_LOOP_ARGS);
322
323                       /* We must run out of output buffer space in this
324                          rerun.  */
325                       assert (outbuf == outerr);
326                       assert (nstatus == __GCONV_FULL_OUTPUT);
327 #endif  /* reset input buffer */
328                     }
329
330                   /* Change the status.  */
331                   status = result;
332                 }
333               else
334                 /* All the output is consumed, we can make another run
335                    if everything was ok.  */
336                 if (status == __GCONV_FULL_OUTPUT)
337                   status = __GCONV_OK;
338             }
339
340           /* We finished one use of the loops.  */
341           ++data->__invocation_counter;
342         }
343       while (status == __GCONV_OK);
344
345 #ifdef END_LOOP
346       END_LOOP
347 #endif
348     }
349
350   return status;
351 }
352
353 #undef DEFINE_INIT
354 #undef CHARSET_NAME
355 #undef DEFINE_FINI
356 #undef MIN_NEEDED_FROM
357 #undef MIN_NEEDED_TO
358 #undef MAX_NEEDED_FROM
359 #undef MAX_NEEDED_TO
360 #undef DEFINE_DIRECTION_OBJECTS
361 #undef FROM_DIRECTION
362 #undef EMIT_SHIFT_TO_INIT
363 #undef FROM_LOOP
364 #undef TO_LOOP
365 #undef RESET_STATE
366 #undef RESET_INPUT_BUFFER
367 #undef FUNCTION_NAME
368 #undef PREPARE_LOOP
369 #undef END_LOOP