Update.
[platform/upstream/glibc.git] / iconv / gconv_open.c
1 /* Find matching transformation algorithms and initialize steps.
2    Copyright (C) 1997, 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>, 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 <errno.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <gconv_int.h>
26
27
28 int
29 internal_function
30 __gconv_open (const char *toset, const char *fromset, __gconv_t *handle,
31               int flags)
32 {
33   struct __gconv_step *steps;
34   size_t nsteps;
35   __gconv_t result = NULL;
36   size_t cnt = 0;
37   int res;
38   int conv_flags = 0;
39   const char *errhand;
40   const char *ignore;
41
42   /* Find out whether any error handling method is specified.  */
43   errhand = strchr (toset, '/');
44   if (errhand != NULL)
45     errhand = strchr (errhand + 1, '/');
46   if (__builtin_expect (errhand != NULL, 1))
47     {
48       if (*++errhand == '\0')
49         errhand = NULL;
50       else
51         {
52           /* Make copy without the error handling description.  */
53           char *newtoset = (char *) alloca (errhand - toset + 1);
54
55           newtoset[errhand - toset] = '\0';
56           toset = memcpy (newtoset, toset, errhand - toset);
57
58           flags = __GCONV_IGNORE_ERRORS;
59
60           if (__strcasecmp (errhand, "IGNORE") == 0)
61             {
62               /* Found it.  This means we should ignore conversion errors.  */
63               flags = __GCONV_IGNORE_ERRORS;
64               errhand = NULL;
65             }
66         }
67     }
68
69   /* For the source character set we ignore the error handler specification.
70      XXX Is this really always the best?  */
71   ignore = strchr (fromset, '/');
72   if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
73       && *++ignore != '\0')
74     {
75       char *newfromset = (char *) alloca (ignore - fromset + 1);
76
77       newfromset[ignore - fromset] = '\0';
78       fromset = memcpy (newfromset, fromset, ignore - fromset);
79     }
80
81   res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
82   if (res == __GCONV_OK)
83     {
84       const char **csnames = NULL;
85       size_t ncsnames = 0;
86       __gconv_trans_fct trans_fct = NULL;
87       __gconv_trans_context_fct trans_context_fct = NULL;
88       __gconv_trans_init_fct trans_init_fct = NULL;
89       __gconv_trans_end_fct trans_end_fct = NULL;
90
91       if (errhand != NULL)
92         {
93           /* Find the appropriate transliteration handling.  */
94           if (__strcasecmp (errhand, "TRANSLIT") == 0)
95             {
96               /* It's the builtin transliteration handling.  We only
97                  suport for it working on the internal encoding.  */
98               static const char *internal_trans_names[1] = { "INTERNAL" };
99
100               csnames = internal_trans_names;
101               ncsnames = 1;
102               trans_fct = __gconv_transliterate;
103               /* No context, init, or end function.  */
104             }
105           else if (__strcasecmp (errhand, "WORK AROUND A GCC BUG") == 0)
106             {
107               trans_init_fct = (__gconv_trans_init_fct) 1;
108             }
109         }
110
111       /* Allocate room for handle.  */
112       result = (__gconv_t) malloc (sizeof (struct __gconv_info)
113                                    + (nsteps
114                                       * sizeof (struct __gconv_step_data)));
115       if (result == NULL)
116         res = __GCONV_NOMEM;
117       else
118         {
119           size_t n;
120
121           /* Remember the list of steps.  */
122           result->__steps = steps;
123           result->__nsteps = nsteps;
124
125           /* Clear the array for the step data.  */
126           memset (result->__data, '\0',
127                   nsteps * sizeof (struct __gconv_step_data));
128
129           /* Call all initialization functions for the transformation
130              step implementations.  */
131           for (cnt = 0; cnt < nsteps - 1; ++cnt)
132             {
133               size_t size;
134
135               /* Would have to be done if we would not clear the whole
136                  array above.  */
137               /* If this is the last step we must not allocate an
138                  output buffer.  */
139               result->__data[cnt].__flags = conv_flags;
140
141 #if 0
142               /* Reset the counter.  */
143               result->__data[cnt].__invocation_counter = 0;
144
145               /* It's a regular use.  */
146               result->__data[cnt].__internal_use = 0;
147 #endif
148
149               /* We use the `mbstate_t' member in DATA.  */
150               result->__data[cnt].__statep = &result->__data[cnt].__state;
151
152               /* Allocate the buffer.  */
153               size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
154
155               result->__data[cnt].__outbuf = (char *) malloc (size);
156               if (result->__data[cnt].__outbuf == NULL)
157                 {
158                   res = __GCONV_NOMEM;
159                   break;
160                 }
161               result->__data[cnt].__outbufend =
162                 result->__data[cnt].__outbuf + size;
163
164               /* Now see whether we can use the transliteration module
165                  for this step.  */
166               for (n = 0; n < ncsnames; ++n)
167                 if (__strcasecmp (steps[cnt].__from_name, csnames[n]) == 0)
168                   {
169                     /* Match!  Now try the initializer.  */
170                     if (trans_init_fct == NULL
171                         || (trans_init_fct (&result->__data[cnt].__trans.__data,
172                                             steps[cnt].__to_name)
173                             == __GCONV_OK))
174                       {
175                         result->__data[cnt].__trans.__trans_fct = trans_fct;
176                         result->__data[cnt].__trans.__trans_context_fct =
177                           trans_context_fct;
178                         result->__data[cnt].__trans.__trans_end_fct =
179                           trans_end_fct;
180                       }
181                     break;
182                   }
183             }
184
185           /* Now handle the last entry.  */
186           result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
187           /* Would have to be done if we would not clear the whole
188              array above.  */
189 #if 0
190           result->__data[cnt].__invocation_counter = 0;
191           result->__data[cnt].__internal_use = 0;
192 #endif
193           result->__data[cnt].__statep = &result->__data[cnt].__state;
194
195           /* Now see whether we can use the transliteration module
196              for this step.  */
197           for (n = 0; n < ncsnames; ++n)
198             if (__strcasecmp (steps[cnt].__from_name, csnames[n]) == 0)
199               {
200                 /* Match!  Now try the initializer.  */
201                 if (trans_init_fct == NULL
202                     || trans_init_fct (&result->__data[cnt].__trans.__data,
203                                        steps[cnt].__to_name)
204                     == __GCONV_OK)
205                   {
206                     result->__data[cnt].__trans.__trans_fct = trans_fct;
207                     result->__data[cnt].__trans.__trans_context_fct =
208                       trans_context_fct;
209                     result->__data[cnt].__trans.__trans_end_fct =
210                       trans_end_fct;
211                   }
212                 break;
213               }
214         }
215
216       if (res != __GCONV_OK)
217         {
218           /* Something went wrong.  Free all the resources.  */
219           int serrno = errno;
220
221           if (result != NULL)
222             {
223               while (cnt-- > 0)
224                 free (result->__data[cnt].__outbuf);
225
226               free (result);
227               result = NULL;
228             }
229
230           __gconv_close_transform (steps, nsteps);
231
232           __set_errno (serrno);
233         }
234     }
235
236   *handle = result;
237   return res;
238 }