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