1 /* Find matching transformation algorithms and initialize steps.
2 Copyright (C) 1997, 1998, 1999, 2000, 2001 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 Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the 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 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with the GNU C Library; if not, write to the Free
18 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA. */
20
21 #include <errno.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <gconv_int.h>
27
28 int
29 internal_function
__gconv_open(const char * toset,const char * fromset,__gconv_t * handle,int flags)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 struct trans_struct *trans = NULL;
42 char old_locale[20], *old_locale_p;
43 char *old, *new;
44 size_t len;
45
46 /* Find out whether any error handling method is specified. */
47 errhand = strchr (toset, '/');
48 if (errhand != NULL)
49 errhand = strchr (errhand + 1, '/');
50 if (__builtin_expect (errhand != NULL, 1))
51 {
52 if (*++errhand == '\0')
53 errhand = NULL;
54 else
55 {
56 /* Make copy without the error handling description. */
57 char *newtoset = (char *) alloca (errhand - toset + 1);
58 char *tok;
59 char *ptr;
60
61 newtoset[errhand - toset] = '\0';
62 toset = memcpy (newtoset, toset, errhand - toset);
63
64 /* Find the appropriate transliteration handlers. */
65 old = (char *)(errhand);
66 len = strlen (old) + 1;
67 new = (char *) alloca (len);
68 tok = (char *) memcpy (new, old, len);
69
70 tok = strtok_r (tok, ",", &ptr);
71
72 /* Set locale to default C locale. */
73 old_locale_p = setlocale(LC_ALL, "C");
74 strncpy(old_locale, old_locale_p, 20);
75
76 while (tok != NULL)
77 {
78 if (strcasecmp (tok, "TRANSLIT") == 0)
79 {
80 /* It's the builtin transliteration handling. We only
81 support it for working on the internal encoding. */
82 static const char *internal_trans_names[1] = { "INTERNAL" };
83 struct trans_struct *lastp = NULL;
84 struct trans_struct *runp;
85
86 for (runp = trans; runp != NULL; runp = runp->next)
87 if (runp->trans_fct == __gconv_transliterate)
88 break;
89 else
90 lastp = runp;
91
92 if (runp == NULL)
93 {
94 struct trans_struct *newp;
95
96 newp = (struct trans_struct *) alloca (sizeof (*newp));
97 memset (newp, '\0', sizeof (*newp));
98
99 /* We leave the `name' field zero to signal that
100 this is an internal transliteration step. */
101 newp->csnames = internal_trans_names;
102 newp->ncsnames = 1;
103 newp->trans_fct = __gconv_transliterate;
104
105 if (lastp == NULL)
106 trans = newp;
107 else
108 lastp->next = newp;
109 }
110 }
111 else if (strcasecmp (tok, "IGNORE") == 0)
112 /* Set the flag to ignore all errors. */
113 conv_flags |= __GCONV_IGNORE_ERRORS;
114 else
115 {
116 /* `tok' is possibly a module name. We'll see later
117 whether we can find it. But first see that we do
118 not already a module of this name. */
119 struct trans_struct *lastp = NULL;
120 struct trans_struct *runp;
121
122 for (runp = trans; runp != NULL; runp = runp->next)
123 if (runp->name != NULL
124 && strcasecmp (tok, runp->name) == 0)
125 break;
126 else
127 lastp = runp;
128
129 if (runp == NULL)
130 {
131 struct trans_struct *newp;
132
133 newp = (struct trans_struct *) alloca (sizeof (*newp));
134 memset (newp, '\0', sizeof (*newp));
135 newp->name = tok;
136
137 if (lastp == NULL)
138 trans = newp;
139 else
140 lastp->next = newp;
141 }
142 }
143
144 tok = strtok_r (NULL, ",", &ptr);
145 }
146 }
147 }
148
149 /* For the source character set we ignore the error handler specification.
150 XXX Is this really always the best? */
151 ignore = strchr (fromset, '/');
152 if (ignore != NULL && (ignore = strchr (ignore + 1, '/')) != NULL
153 && *++ignore != '\0')
154 {
155 char *newfromset = (char *) alloca (ignore - fromset + 1);
156
157 newfromset[ignore - fromset] = '\0';
158 fromset = memcpy (newfromset, fromset, ignore - fromset);
159 }
160
161 res = __gconv_find_transform (toset, fromset, &steps, &nsteps, flags);
162 if (res == __GCONV_OK)
163 {
164 /* Find the modules. */
165 struct trans_struct *lastp = NULL;
166 struct trans_struct *runp;
167
168 for (runp = trans; runp != NULL; runp = runp->next)
169 {
170 if (runp->name == NULL
171 || __builtin_expect (__gconv_translit_find (runp), 0) == 0)
172 lastp = runp;
173 else
174 /* This means we haven't found the module. Remove it. */
175 (lastp == NULL ? trans : lastp->next) = runp->next;
176 }
177
178 /* Allocate room for handle. */
179 result = (__gconv_t) malloc (sizeof (struct __gconv_info)
180 + (nsteps
181 * sizeof (struct __gconv_step_data)));
182 if (result == NULL)
183 res = __GCONV_NOMEM;
184 else
185 {
186 size_t n;
187
188 /* Remember the list of steps. */
189 result->__steps = steps;
190 result->__nsteps = nsteps;
191
192 /* Clear the array for the step data. */
193 memset (result->__data, '\0',
194 nsteps * sizeof (struct __gconv_step_data));
195
196 /* Call all initialization functions for the transformation
197 step implementations. */
198 for (cnt = 0; cnt < nsteps; ++cnt)
199 {
200 size_t size;
201
202 /* Would have to be done if we would not clear the whole
203 array above. */
204 #if 0
205 /* Reset the counter. */
206 result->__data[cnt].__invocation_counter = 0;
207
208 /* It's a regular use. */
209 result->__data[cnt].__internal_use = 0;
210 #endif
211
212 /* We use the `mbstate_t' member in DATA. */
213 result->__data[cnt].__statep = &result->__data[cnt].__state;
214
215 /* Now see whether we can use any of the transliteration
216 modules for this step. */
217 for (runp = trans; runp != NULL; runp = runp->next)
218 for (n = 0; n < runp->ncsnames; ++n)
219 if (strcasecmp (steps[cnt].__from_name, runp->csnames[n]) == 0)
220 {
221 void *data = NULL;
222
223 /* Match! Now try the initializer. */
224 if (runp->trans_init_fct == NULL
225 || (runp->trans_init_fct (&data,
226 steps[cnt].__to_name)
227 == __GCONV_OK))
228 {
229 /* Append at the end of the list. */
230 struct __gconv_trans_data *newp;
231 struct __gconv_trans_data **lastp;
232
233 newp = (struct __gconv_trans_data *)
234 malloc (sizeof (struct __gconv_trans_data));
235 if (newp == NULL)
236 {
237 res = __GCONV_NOMEM;
238 goto bail;
239 }
240
241 newp->__trans_fct = runp->trans_fct;
242 newp->__trans_context_fct = runp->trans_context_fct;
243 newp->__trans_end_fct = runp->trans_end_fct;
244 newp->__data = data;
245 newp->__next = NULL;
246
247 lastp = &result->__data[cnt].__trans;
248 while (*lastp != NULL)
249 lastp = &(*lastp)->__next;
250
251 *lastp = newp;
252 }
253 break;
254 }
255
256 /* If this is the last step we must not allocate an
257 output buffer. */
258 if (cnt < nsteps - 1)
259 {
260 result->__data[cnt].__flags = conv_flags;
261
262 /* Allocate the buffer. */
263 size = (GCONV_NCHAR_GOAL * steps[cnt].__max_needed_to);
264
265 result->__data[cnt].__outbuf = (char *) malloc (size);
266 if (result->__data[cnt].__outbuf == NULL)
267 {
268 res = __GCONV_NOMEM;
269 goto bail;
270 }
271
272 result->__data[cnt].__outbufend =
273 result->__data[cnt].__outbuf + size;
274 }
275 else
276 {
277 /* Handle the last entry. */
278 result->__data[cnt].__flags = conv_flags | __GCONV_IS_LAST;
279
280 break;
281 }
282 }
283 }
284
285 if (res != __GCONV_OK)
286 {
287 /* Something went wrong. Free all the resources. */
288 int serrno;
289 bail:
290 serrno = errno;
291
292 if (result != NULL)
293 {
294 while (cnt-- > 0)
295 {
296 struct __gconv_trans_data *transp;
297
298 transp = result->__data[cnt].__trans;
299 while (transp != NULL)
300 {
301 struct __gconv_trans_data *curp = transp;
302 transp = transp->__next;
303
304 if (__builtin_expect (curp->__trans_end_fct != NULL, 0))
305 curp->__trans_end_fct (curp->__data);
306
307 free (curp);
308 }
309
310 free (result->__data[cnt].__outbuf);
311 }
312
313 free (result);
314 result = NULL;
315 }
316
317 __gconv_close_transform (steps, nsteps);
318
319 __set_errno (serrno);
320 }
321 }
322
323 *handle = result;
324 setlocale(LC_ALL, old_locale);
325 return res;
326 }
327