]> git.ipfire.org Git - thirdparty/glibc.git/blame - iconv/gconv_db.c
Cleanup of configuration options
[thirdparty/glibc.git] / iconv / gconv_db.c
CommitLineData
6973fc01 1/* Provide access to the collection of available transformation modules.
7b503bcc
UD
2 Copyright (C) 1997-2003, 2004, 2005, 2006, 2007
3 Free Software Foundation, Inc.
6973fc01
UD
4 This file is part of the GNU C Library.
5 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6
7 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
6973fc01
UD
11
12 The GNU C Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 15 Lesser General Public License for more details.
6973fc01 16
41bdb6e2
AJ
17 You should have received a copy of the GNU Lesser General Public
18 License along with the GNU C Library; if not, write to the Free
19 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA. */
6973fc01 21
915a6c51 22#include <assert.h>
2bd60880 23#include <limits.h>
6973fc01
UD
24#include <search.h>
25#include <stdlib.h>
26#include <string.h>
2bd60880 27#include <sys/param.h>
6973fc01 28#include <bits/libc-lock.h>
7c11c4a1 29#include <locale/localeinfo.h>
816e6eb5 30
b3fc5f84 31#include <dlfcn.h>
e62c19f1 32#include <gconv_int.h>
915a6c51 33#include <sysdep.h>
e62c19f1 34
6973fc01
UD
35
36/* Simple data structure for alias mapping. We have two names, `from'
37 and `to'. */
38void *__gconv_alias_db;
39
40/* Array with available modules. */
2bd60880 41struct gconv_module *__gconv_modules_db;
6973fc01 42
0d9f6793 43/* We modify global data. */
9e26f129 44__libc_lock_define_initialized (, __gconv_lock)
0d9f6793 45
6973fc01 46
230491f0
UD
47/* Provide access to module database. */
48struct gconv_module *
49__gconv_get_modules_db (void)
50{
51 return __gconv_modules_db;
52}
53
54void *
55__gconv_get_alias_db (void)
56{
57 return __gconv_alias_db;
58}
59
60
6973fc01
UD
61/* Function for searching alias. */
62int
63__gconv_alias_compare (const void *p1, const void *p2)
64{
17427edd
UD
65 const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
66 const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
bd4848fb 67 return strcmp (s1->fromname, s2->fromname);
6973fc01
UD
68}
69
70
71/* To search for a derivation we create a list of intermediate steps.
72 Each element contains a pointer to the element which precedes it
73 in the derivation order. */
74struct derivation_step
75{
76 const char *result_set;
2bd60880
UD
77 size_t result_set_len;
78 int cost_lo;
79 int cost_hi;
6973fc01
UD
80 struct gconv_module *code;
81 struct derivation_step *last;
82 struct derivation_step *next;
83};
84
2bd60880 85#define NEW_STEP(result, hi, lo, module, last_mod) \
6973fc01
UD
86 ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
87 newp->result_set = result; \
2bd60880
UD
88 newp->result_set_len = strlen (result); \
89 newp->cost_hi = hi; \
90 newp->cost_lo = lo; \
6973fc01
UD
91 newp->code = module; \
92 newp->last = last_mod; \
93 newp->next = NULL; \
94 newp; })
95
96
97/* If a specific transformation is used more than once we should not need
98 to start looking for it again. Instead cache each successful result. */
99struct known_derivation
100{
101 const char *from;
102 const char *to;
d64b6ad0 103 struct __gconv_step *steps;
6973fc01
UD
104 size_t nsteps;
105};
106
107/* Compare function for database of found derivations. */
108static int
109derivation_compare (const void *p1, const void *p2)
110{
17427edd
UD
111 const struct known_derivation *s1 = (const struct known_derivation *) p1;
112 const struct known_derivation *s2 = (const struct known_derivation *) p2;
6973fc01
UD
113 int result;
114
115 result = strcmp (s1->from, s2->from);
116 if (result == 0)
117 result = strcmp (s1->to, s2->to);
118 return result;
119}
120
121/* The search tree for known derivations. */
122static void *known_derivations;
123
124/* Look up whether given transformation was already requested before. */
125static int
e62c19f1 126internal_function
6973fc01 127derivation_lookup (const char *fromset, const char *toset,
d64b6ad0 128 struct __gconv_step **handle, size_t *nsteps)
6973fc01
UD
129{
130 struct known_derivation key = { fromset, toset, NULL, 0 };
bd687f7a 131 struct known_derivation **result;
6973fc01 132
e34b0f29 133 result = __tfind (&key, &known_derivations, derivation_compare);
6973fc01
UD
134
135 if (result == NULL)
d64b6ad0 136 return __GCONV_NOCONV;
6973fc01 137
bd687f7a
UD
138 *handle = (*result)->steps;
139 *nsteps = (*result)->nsteps;
6973fc01
UD
140
141 /* Please note that we return GCONV_OK even if the last search for
142 this transformation was unsuccessful. */
d64b6ad0 143 return __GCONV_OK;
6973fc01
UD
144}
145
146/* Add new derivation to list of known ones. */
147static void
e62c19f1 148internal_function
6973fc01 149add_derivation (const char *fromset, const char *toset,
d64b6ad0 150 struct __gconv_step *handle, size_t nsteps)
6973fc01
UD
151{
152 struct known_derivation *new_deriv;
153 size_t fromset_len = strlen (fromset) + 1;
154 size_t toset_len = strlen (toset) + 1;
155
156 new_deriv = (struct known_derivation *)
157 malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
158 if (new_deriv != NULL)
159 {
390500b1
UD
160 new_deriv->from = (char *) (new_deriv + 1);
161 new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
6973fc01
UD
162 toset, toset_len);
163
164 new_deriv->steps = handle;
165 new_deriv->nsteps = nsteps;
166
390500b1
UD
167 if (__tsearch (new_deriv, &known_derivations, derivation_compare)
168 == NULL)
169 /* There is some kind of memory allocation problem. */
170 free (new_deriv);
6973fc01
UD
171 }
172 /* Please note that we don't complain if the allocation failed. This
173 is not tragically but in case we use the memory debugging facilities
174 not all memory will be freed. */
175}
176
7c11c4a1 177static void __libc_freeres_fn_section
6973fc01
UD
178free_derivation (void *p)
179{
180 struct known_derivation *deriv = (struct known_derivation *) p;
0d9f6793
UD
181 size_t cnt;
182
183 for (cnt = 0; cnt < deriv->nsteps; ++cnt)
b79f74cd
UD
184 if (deriv->steps[cnt].__counter > 0
185 && deriv->steps[cnt].__end_fct != NULL)
915a6c51
UD
186 {
187 assert (deriv->steps[cnt].__shlib_handle != NULL);
188
189 __gconv_end_fct end_fct = deriv->steps[cnt].__end_fct;
190#ifdef PTR_DEMANGLE
191 PTR_DEMANGLE (end_fct);
192#endif
193 DL_CALL_FCT (end_fct, (&deriv->steps[cnt]));
194 }
6973fc01 195
74454183 196 /* Free the name strings. */
d64b6ad0
UD
197 free ((char *) deriv->steps[0].__from_name);
198 free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
74454183 199
d64b6ad0 200 free ((struct __gconv_step *) deriv->steps);
6973fc01
UD
201 free (deriv);
202}
203
204
b79f74cd 205/* Decrement the reference count for a single step in a steps array. */
6b98979f
UD
206void
207internal_function
208__gconv_release_step (struct __gconv_step *step)
b79f74cd 209{
915a6c51
UD
210 /* Skip builtin modules; they are not reference counted. */
211 if (step->__shlib_handle != NULL && --step->__counter == 0)
b79f74cd
UD
212 {
213 /* Call the destructor. */
214 if (step->__end_fct != NULL)
a334319f 215 {
915a6c51
UD
216 assert (step->__shlib_handle != NULL);
217
218 __gconv_end_fct end_fct = step->__end_fct;
219#ifdef PTR_DEMANGLE
220 PTR_DEMANGLE (end_fct);
221#endif
222 DL_CALL_FCT (end_fct, (step));
a334319f 223 }
915a6c51
UD
224
225#ifndef STATIC_GCONV
226 /* Release the loaded module. */
227 __gconv_release_shlib (step->__shlib_handle);
228 step->__shlib_handle = NULL;
b79f74cd
UD
229#endif
230 }
74ed1159 231 else if (step->__shlib_handle == NULL)
915a6c51
UD
232 /* Builtin modules should not have end functions. */
233 assert (step->__end_fct == NULL);
b79f74cd
UD
234}
235
6973fc01
UD
236static int
237internal_function
238gen_steps (struct derivation_step *best, const char *toset,
d64b6ad0 239 const char *fromset, struct __gconv_step **handle, size_t *nsteps)
6973fc01
UD
240{
241 size_t step_cnt = 0;
d64b6ad0 242 struct __gconv_step *result;
6973fc01 243 struct derivation_step *current;
d64b6ad0 244 int status = __GCONV_NOMEM;
6973fc01
UD
245
246 /* First determine number of steps. */
247 for (current = best; current->last != NULL; current = current->last)
248 ++step_cnt;
249
d64b6ad0
UD
250 result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
251 * step_cnt);
6973fc01
UD
252 if (result != NULL)
253 {
e34b0f29
UD
254 int failed = 0;
255
d64b6ad0 256 status = __GCONV_OK;
e34b0f29 257 *nsteps = step_cnt;
6973fc01
UD
258 current = best;
259 while (step_cnt-- > 0)
260 {
d64b6ad0
UD
261 result[step_cnt].__from_name = (step_cnt == 0
262 ? __strdup (fromset)
8a0746ae 263 : (char *)current->last->result_set);
d64b6ad0
UD
264 result[step_cnt].__to_name = (step_cnt + 1 == *nsteps
265 ? __strdup (current->result_set)
266 : result[step_cnt + 1].__from_name);
6973fc01 267
6b98979f
UD
268 result[step_cnt].__counter = 1;
269 result[step_cnt].__data = NULL;
270
8619129f 271#ifndef STATIC_GCONV
6973fc01
UD
272 if (current->code->module_name[0] == '/')
273 {
274 /* Load the module, return handle for it. */
d64b6ad0 275 struct __gconv_loaded_object *shlib_handle =
6973fc01
UD
276 __gconv_find_shlib (current->code->module_name);
277
278 if (shlib_handle == NULL)
e34b0f29
UD
279 {
280 failed = 1;
281 break;
282 }
6973fc01 283
d64b6ad0
UD
284 result[step_cnt].__shlib_handle = shlib_handle;
285 result[step_cnt].__modname = shlib_handle->name;
d64b6ad0
UD
286 result[step_cnt].__fct = shlib_handle->fct;
287 result[step_cnt].__init_fct = shlib_handle->init_fct;
288 result[step_cnt].__end_fct = shlib_handle->end_fct;
6b98979f 289
f9ad060c
UD
290 /* These settings can be overridden by the init function. */
291 result[step_cnt].__btowc_fct = NULL;
292
6b98979f 293 /* Call the init function. */
915a6c51
UD
294 __gconv_init_fct init_fct = result[step_cnt].__init_fct;
295 if (init_fct != NULL)
6b98979f 296 {
915a6c51
UD
297 assert (result[step_cnt].__shlib_handle != NULL);
298
299# ifdef PTR_DEMANGLE
300 PTR_DEMANGLE (init_fct);
301# endif
302 status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
6b98979f
UD
303
304 if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
305 {
306 failed = 1;
307 /* Make sure we unload this modules. */
308 --step_cnt;
309 result[step_cnt].__end_fct = NULL;
310 break;
311 }
915a6c51
UD
312
313# ifdef PTR_MANGLE
314 if (result[step_cnt].__btowc_fct != NULL)
315 PTR_MANGLE (result[step_cnt].__btowc_fct);
316# endif
6b98979f 317 }
6973fc01
UD
318 }
319 else
8619129f 320#endif
6973fc01
UD
321 /* It's a builtin transformation. */
322 __gconv_get_builtin_trans (current->code->module_name,
323 &result[step_cnt]);
324
325 current = current->last;
326 }
327
5ea1a82d 328 if (__builtin_expect (failed, 0) != 0)
6973fc01
UD
329 {
330 /* Something went wrong while initializing the modules. */
e34b0f29 331 while (++step_cnt < *nsteps)
6b98979f 332 __gconv_release_step (&result[step_cnt]);
6973fc01 333 free (result);
e34b0f29 334 *nsteps = 0;
7a68c94a 335 *handle = NULL;
d64b6ad0
UD
336 if (status == __GCONV_OK)
337 status = __GCONV_NOCONV;
6973fc01
UD
338 }
339 else
c7ec9d75 340 *handle = result;
6973fc01 341 }
7a68c94a
UD
342 else
343 {
344 *nsteps = 0;
345 *handle = NULL;
346 }
6973fc01
UD
347
348 return status;
349}
350
351
76a2102b
UD
352#ifndef STATIC_GCONV
353static int
354internal_function
355increment_counter (struct __gconv_step *steps, size_t nsteps)
356{
357 /* Increment the user counter. */
358 size_t cnt = nsteps;
359 int result = __GCONV_OK;
360
361 while (cnt-- > 0)
b79f74cd
UD
362 {
363 struct __gconv_step *step = &steps[cnt];
364
365 if (step->__counter++ == 0)
366 {
367 /* Skip builtin modules. */
368 if (step->__modname != NULL)
369 {
370 /* Reopen a previously used module. */
371 step->__shlib_handle = __gconv_find_shlib (step->__modname);
372 if (step->__shlib_handle == NULL)
373 {
374 /* Oops, this is the second time we use this module
375 (after unloading) and this time loading failed!? */
376 --step->__counter;
377 while (++cnt < nsteps)
6b98979f 378 __gconv_release_step (&steps[cnt]);
b79f74cd
UD
379 result = __GCONV_NOCONV;
380 break;
381 }
382
383 /* The function addresses defined by the module may
384 have changed. */
385 step->__fct = step->__shlib_handle->fct;
386 step->__init_fct = step->__shlib_handle->init_fct;
387 step->__end_fct = step->__shlib_handle->end_fct;
f9ad060c
UD
388
389 /* These settings can be overridden by the init function. */
390 step->__btowc_fct = NULL;
b79f74cd
UD
391 }
392
f9ad060c 393 /* Call the init function. */
915a6c51
UD
394 __gconv_init_fct init_fct = step->__init_fct;
395 if (init_fct != NULL)
396 {
397#ifdef PTR_DEMANGLE
398 PTR_DEMANGLE (init_fct);
399#endif
400 DL_CALL_FCT (init_fct, (step));
401
402#ifdef PTR_MANGLE
403 if (step->__btowc_fct != NULL)
404 PTR_MANGLE (step->__btowc_fct);
405#endif
406 }
b79f74cd
UD
407 }
408 }
76a2102b
UD
409 return result;
410}
411#endif
412
413
6973fc01
UD
414/* The main function: find a possible derivation from the `fromset' (either
415 the given name or the alias) to the `toset' (again with alias). */
416static int
e34b0f29 417internal_function
6973fc01
UD
418find_derivation (const char *toset, const char *toset_expand,
419 const char *fromset, const char *fromset_expand,
d64b6ad0 420 struct __gconv_step **handle, size_t *nsteps)
6973fc01 421{
2bd60880
UD
422 struct derivation_step *first, *current, **lastp, *solution = NULL;
423 int best_cost_hi = INT_MAX;
424 int best_cost_lo = INT_MAX;
6973fc01
UD
425 int result;
426
b79f74cd
UD
427 /* Look whether an earlier call to `find_derivation' has already
428 computed a possible derivation. If so, return it immediately. */
6973fc01
UD
429 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
430 handle, nsteps);
d64b6ad0 431 if (result == __GCONV_OK)
3c5edd4d 432 {
76a2102b
UD
433#ifndef STATIC_GCONV
434 result = increment_counter (*handle, *nsteps);
435#endif
3c5edd4d
UD
436 return result;
437 }
6973fc01 438
b79f74cd
UD
439 /* The task is to find a sequence of transformations, backed by the
440 existing modules - whether builtin or dynamically loadable -,
441 starting at `fromset' (or `fromset_expand') and ending at `toset'
442 (or `toset_expand'), and with minimal cost.
443
444 For computer scientists, this is a shortest path search in the
445 graph where the nodes are all possible charsets and the edges are
446 the transformations listed in __gconv_modules_db.
447
448 For now we use a simple algorithm with quadratic runtime behaviour.
449 A breadth-first search, starting at `fromset' and `fromset_expand'.
450 The list starting at `first' contains all nodes that have been
451 visited up to now, in the order in which they have been visited --
452 excluding the goal nodes `toset' and `toset_expand' which get
453 managed in the list starting at `solution'.
454 `current' walks through the list starting at `first' and looks
455 which nodes are reachable from the current node, adding them to
456 the end of the list [`first' or `solution' respectively] (if
457 they are visited the first time) or updating them in place (if
458 they have have already been visited).
459 In each node of either list, cost_lo and cost_hi contain the
460 minimum cost over any paths found up to now, starting at `fromset'
461 or `fromset_expand', ending at that node. best_cost_lo and
462 best_cost_hi represent the minimum over the elements of the
463 `solution' list. */
464
6973fc01
UD
465 if (fromset_expand != NULL)
466 {
2bd60880
UD
467 first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
468 first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 469 lastp = &first->next->next;
6973fc01
UD
470 }
471 else
472 {
2bd60880 473 first = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 474 lastp = &first->next;
6973fc01
UD
475 }
476
2bd60880 477 for (current = first; current != NULL; current = current->next)
6973fc01
UD
478 {
479 /* Now match all the available module specifications against the
480 current charset name. If any of them matches check whether
481 we already have a derivation for this charset. If yes, use the
482 one with the lower costs. Otherwise add the new charset at the
2bd60880
UD
483 end.
484
e8b1163e
AJ
485 The module database is organized in a tree form which allows
486 searching for prefixes. So we search for the first entry with a
2bd60880
UD
487 matching prefix and any other matching entry can be found from
488 this place. */
b79f74cd 489 struct gconv_module *node;
2bd60880
UD
490
491 /* Maybe it is not necessary anymore to look for a solution for
b79f74cd 492 this entry since the cost is already as high (or higher) as
2bd60880
UD
493 the cost for the best solution so far. */
494 if (current->cost_hi > best_cost_hi
495 || (current->cost_hi == best_cost_hi
496 && current->cost_lo >= best_cost_lo))
497 continue;
498
b79f74cd 499 node = __gconv_modules_db;
2bd60880
UD
500 while (node != NULL)
501 {
d2dfc5de 502 int cmpres = strcmp (current->result_set, node->from_string);
2bd60880 503 if (cmpres == 0)
6973fc01 504 {
2bd60880
UD
505 /* Walk through the list of modules with this prefix and
506 try to match the name. */
507 struct gconv_module *runp;
508
2bd60880
UD
509 /* Check all the modules with this prefix. */
510 runp = node;
511 do
6973fc01 512 {
d2dfc5de
UD
513 const char *result_set = (strcmp (runp->to_string, "-") == 0
514 ? (toset_expand ?: toset)
515 : runp->to_string);
516 int cost_hi = runp->cost_hi + current->cost_hi;
517 int cost_lo = runp->cost_lo + current->cost_lo;
518 struct derivation_step *step;
519
520 /* We managed to find a derivation. First see whether
b79f74cd 521 we have reached one of the goal nodes. */
d2dfc5de
UD
522 if (strcmp (result_set, toset) == 0
523 || (toset_expand != NULL
524 && strcmp (result_set, toset_expand) == 0))
2bd60880 525 {
b79f74cd
UD
526 /* Append to the `solution' list if there
527 is no entry with this name. */
528 for (step = solution; step != NULL; step = step->next)
529 if (strcmp (result_set, step->result_set) == 0)
530 break;
531
532 if (step == NULL)
533 {
534 step = NEW_STEP (result_set,
535 cost_hi, cost_lo,
536 runp, current);
537 step->next = solution;
538 solution = step;
539 }
540 else if (step->cost_hi > cost_hi
541 || (step->cost_hi == cost_hi
542 && step->cost_lo > cost_lo))
543 {
544 /* A better path was found for the node,
545 on the `solution' list. */
546 step->code = runp;
547 step->last = current;
548 step->cost_hi = cost_hi;
549 step->cost_lo = cost_lo;
550 }
551
552 /* Update best_cost accordingly. */
553 if (cost_hi < best_cost_hi
d2dfc5de
UD
554 || (cost_hi == best_cost_hi
555 && cost_lo < best_cost_lo))
2bd60880 556 {
d2dfc5de
UD
557 best_cost_hi = cost_hi;
558 best_cost_lo = cost_lo;
2bd60880 559 }
6973fc01 560 }
d2dfc5de
UD
561 else if (cost_hi < best_cost_hi
562 || (cost_hi == best_cost_hi
563 && cost_lo < best_cost_lo))
6973fc01 564 {
b79f74cd
UD
565 /* Append at the end of the `first' list if there
566 is no entry with this name. */
d2dfc5de
UD
567 for (step = first; step != NULL; step = step->next)
568 if (strcmp (result_set, step->result_set) == 0)
569 break;
2bd60880 570
d2dfc5de
UD
571 if (step == NULL)
572 {
573 *lastp = NEW_STEP (result_set,
574 cost_hi, cost_lo,
575 runp, current);
576 lastp = &(*lastp)->next;
2bd60880 577 }
d2dfc5de
UD
578 else if (step->cost_hi > cost_hi
579 || (step->cost_hi == cost_hi
580 && step->cost_lo > cost_lo))
2bd60880 581 {
b79f74cd
UD
582 /* A better path was found for the node,
583 on the `first' list. */
d2dfc5de
UD
584 step->code = runp;
585 step->last = current;
2bd60880 586
d2dfc5de
UD
587 /* Update the cost for all steps. */
588 for (step = first; step != NULL;
589 step = step->next)
b79f74cd
UD
590 /* But don't update the start nodes. */
591 if (step->code != NULL)
592 {
593 struct derivation_step *back;
594 int hi, lo;
595
596 hi = step->code->cost_hi;
597 lo = step->code->cost_lo;
598
599 for (back = step->last; back->code != NULL;
600 back = back->last)
601 {
602 hi += back->code->cost_hi;
603 lo += back->code->cost_lo;
604 }
605
606 step->cost_hi = hi;
607 step->cost_lo = lo;
608 }
609
610 /* Likewise for the nodes on the solution list.
611 Also update best_cost accordingly. */
d2dfc5de
UD
612 for (step = solution; step != NULL;
613 step = step->next)
614 {
615 step->cost_hi = (step->code->cost_hi
616 + step->last->cost_hi);
617 step->cost_lo = (step->code->cost_lo
618 + step->last->cost_lo);
619
620 if (step->cost_hi < best_cost_hi
621 || (step->cost_hi == best_cost_hi
622 && step->cost_lo < best_cost_lo))
2bd60880 623 {
d2dfc5de
UD
624 best_cost_hi = step->cost_hi;
625 best_cost_lo = step->cost_lo;
2bd60880
UD
626 }
627 }
628 }
6973fc01 629 }
2bd60880
UD
630
631 runp = runp->same;
6973fc01 632 }
2bd60880 633 while (runp != NULL);
e34b0f29 634
d2dfc5de 635 break;
6973fc01 636 }
2bd60880
UD
637 else if (cmpres < 0)
638 node = node->left;
639 else
640 node = node->right;
6973fc01
UD
641 }
642 }
643
2bd60880 644 if (solution != NULL)
b79f74cd
UD
645 {
646 /* We really found a way to do the transformation. */
647
648 /* Choose the best solution. This is easy because we know that
649 the solution list has at most length 2 (one for every possible
650 goal node). */
651 if (solution->next != NULL)
652 {
653 struct derivation_step *solution2 = solution->next;
654
655 if (solution2->cost_hi < solution->cost_hi
656 || (solution2->cost_hi == solution->cost_hi
657 && solution2->cost_lo < solution->cost_lo))
658 solution = solution2;
659 }
660
661 /* Now build a data structure describing the transformation steps. */
662 result = gen_steps (solution, toset_expand ?: toset,
663 fromset_expand ?: fromset, handle, nsteps);
664 }
6973fc01
UD
665 else
666 {
667 /* We haven't found a transformation. Clear the result values. */
668 *handle = NULL;
669 *nsteps = 0;
670 }
671
672 /* Add result in any case to list of known derivations. */
673 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
674 *handle, *nsteps);
675
6973fc01
UD
676 return result;
677}
678
679
e7f21fa6
UD
680/* Control of initialization. */
681__libc_once_define (static, once);
682
683
684static const char *
685do_lookup_alias (const char *name)
686{
687 struct gconv_alias key;
688 struct gconv_alias **found;
689
690 key.fromname = (char *) name;
691 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
692 return found != NULL ? (*found)->toname : NULL;
693}
694
695
9a018f6c
UD
696int
697internal_function
698__gconv_compare_alias (const char *name1, const char *name2)
e7f21fa6 699{
9a018f6c
UD
700 int result;
701
e7f21fa6
UD
702 /* Ensure that the configuration data is read. */
703 __libc_once (once, __gconv_read_conf);
704
9a018f6c
UD
705 if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
706 result = strcmp (do_lookup_alias (name1) ?: name1,
707 do_lookup_alias (name2) ?: name2);
708
709 return result;
e7f21fa6
UD
710}
711
712
6973fc01 713int
e62c19f1 714internal_function
6973fc01 715__gconv_find_transform (const char *toset, const char *fromset,
60c53a12
UD
716 struct __gconv_step **handle, size_t *nsteps,
717 int flags)
6973fc01 718{
6b98979f
UD
719 const char *fromset_expand;
720 const char *toset_expand;
6973fc01
UD
721 int result;
722
723 /* Ensure that the configuration data is read. */
724 __libc_once (once, __gconv_read_conf);
725
0d9f6793 726 /* Acquire the lock. */
9e26f129 727 __libc_lock_lock (__gconv_lock);
0d9f6793 728
6b98979f
UD
729 result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
730 if (result != __GCONV_NODB)
731 {
732 /* We have a cache and could resolve the request, successful or not. */
9e26f129 733 __libc_lock_unlock (__gconv_lock);
6b98979f
UD
734 return result;
735 }
736
6973fc01
UD
737 /* If we don't have a module database return with an error. */
738 if (__gconv_modules_db == NULL)
3c5edd4d 739 {
9e26f129 740 __libc_lock_unlock (__gconv_lock);
d64b6ad0 741 return __GCONV_NOCONV;
3c5edd4d 742 }
6973fc01
UD
743
744 /* See whether the names are aliases. */
6b98979f
UD
745 fromset_expand = do_lookup_alias (fromset);
746 toset_expand = do_lookup_alias (toset);
6973fc01 747
5ea1a82d 748 if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
60c53a12
UD
749 /* We are not supposed to create a pseudo transformation (means
750 copying) when the input and output character set are the same. */
751 && (strcmp (toset, fromset) == 0
752 || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
753 || (fromset_expand != NULL
754 && (strcmp (toset, fromset_expand) == 0
755 || (toset_expand != NULL
756 && strcmp (toset_expand, fromset_expand) == 0)))))
757 {
758 /* Both character sets are the same. */
9e26f129 759 __libc_lock_unlock (__gconv_lock);
7b503bcc 760 return __GCONV_NULCONV;
60c53a12
UD
761 }
762
6973fc01
UD
763 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
764 handle, nsteps);
765
0d9f6793 766 /* Release the lock. */
9e26f129 767 __libc_lock_unlock (__gconv_lock);
0d9f6793 768
6973fc01
UD
769 /* The following code is necessary since `find_derivation' will return
770 GCONV_OK even when no derivation was found but the same request
771 was processed before. I.e., negative results will also be cached. */
d64b6ad0
UD
772 return (result == __GCONV_OK
773 ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
6973fc01
UD
774 : result);
775}
776
777
778/* Release the entries of the modules list. */
779int
e62c19f1 780internal_function
d64b6ad0 781__gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
6973fc01 782{
d64b6ad0 783 int result = __GCONV_OK;
0db59742 784 size_t cnt;
6973fc01 785
0d9f6793 786 /* Acquire the lock. */
9e26f129 787 __libc_lock_lock (__gconv_lock);
0d9f6793 788
0db59742
UD
789#ifndef STATIC_GCONV
790 cnt = nsteps;
791 while (cnt-- > 0)
792 __gconv_release_step (&steps[cnt]);
793#endif
794
795 /* If we use the cache we free a bit more since we don't keep any
796 transformation records around, they are cheap enough to
797 recreate. */
798 __gconv_release_cache (steps, nsteps);
6973fc01 799
0d9f6793 800 /* Release the lock. */
9e26f129 801 __libc_lock_unlock (__gconv_lock);
0d9f6793 802
6973fc01
UD
803 return result;
804}
805
806
2bd60880
UD
807/* Free the modules mentioned. */
808static void
7c11c4a1 809internal_function __libc_freeres_fn_section
2bd60880
UD
810free_modules_db (struct gconv_module *node)
811{
812 if (node->left != NULL)
813 free_modules_db (node->left);
814 if (node->right != NULL)
815 free_modules_db (node->right);
2bd60880
UD
816 do
817 {
818 struct gconv_module *act = node;
d2dfc5de 819 node = node->same;
8ce63ec0
UD
820 if (act->module_name[0] == '/')
821 free (act);
2bd60880
UD
822 }
823 while (node != NULL);
824}
825
826
6973fc01 827/* Free all resources if necessary. */
c877418f 828libc_freeres_fn (free_mem)
6973fc01 829{
7c11c4a1
UD
830 /* First free locale memory. This needs to be done before freeing derivations,
831 as ctype cleanup functions dereference steps arrays which we free below. */
832 _nl_locale_subfreeres ();
833
9e365fe7
UD
834 /* finddomain.c has similar problem. */
835 extern void _nl_finddomain_subfreeres (void) attribute_hidden;
836 _nl_finddomain_subfreeres ();
837
6973fc01
UD
838 if (__gconv_alias_db != NULL)
839 __tdestroy (__gconv_alias_db, free);
840
0dbbad29
UD
841 if (__gconv_modules_db != NULL)
842 free_modules_db (__gconv_modules_db);
6973fc01
UD
843
844 if (known_derivations != NULL)
845 __tdestroy (known_derivations, free_derivation);
846}