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