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