]> 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.
f7a9f785 2 Copyright (C) 1997-2016 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>
ec999b8e 26#include <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;
be64c2ef
PP
246 char *from_name = NULL;
247 char *to_name = NULL;
6973fc01
UD
248
249 /* First determine number of steps. */
250 for (current = best; current->last != NULL; current = current->last)
251 ++step_cnt;
252
d64b6ad0
UD
253 result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
254 * step_cnt);
6973fc01
UD
255 if (result != NULL)
256 {
e34b0f29
UD
257 int failed = 0;
258
d64b6ad0 259 status = __GCONV_OK;
e34b0f29 260 *nsteps = step_cnt;
6973fc01
UD
261 current = best;
262 while (step_cnt-- > 0)
263 {
be64c2ef
PP
264 if (step_cnt == 0)
265 {
266 result[step_cnt].__from_name = from_name = __strdup (fromset);
267 if (from_name == NULL)
268 {
269 failed = 1;
270 break;
271 }
272 }
273 else
274 result[step_cnt].__from_name = (char *)current->last->result_set;
275
276 if (step_cnt + 1 == *nsteps)
277 {
278 result[step_cnt].__to_name = to_name
279 = __strdup (current->result_set);
280 if (to_name == NULL)
281 {
282 failed = 1;
283 break;
284 }
285 }
286 else
287 result[step_cnt].__to_name = result[step_cnt + 1].__from_name;
6973fc01 288
6b98979f
UD
289 result[step_cnt].__counter = 1;
290 result[step_cnt].__data = NULL;
291
8619129f 292#ifndef STATIC_GCONV
6973fc01
UD
293 if (current->code->module_name[0] == '/')
294 {
295 /* Load the module, return handle for it. */
d64b6ad0 296 struct __gconv_loaded_object *shlib_handle =
6973fc01
UD
297 __gconv_find_shlib (current->code->module_name);
298
299 if (shlib_handle == NULL)
e34b0f29
UD
300 {
301 failed = 1;
302 break;
303 }
6973fc01 304
d64b6ad0
UD
305 result[step_cnt].__shlib_handle = shlib_handle;
306 result[step_cnt].__modname = shlib_handle->name;
d64b6ad0
UD
307 result[step_cnt].__fct = shlib_handle->fct;
308 result[step_cnt].__init_fct = shlib_handle->init_fct;
309 result[step_cnt].__end_fct = shlib_handle->end_fct;
6b98979f 310
f9ad060c
UD
311 /* These settings can be overridden by the init function. */
312 result[step_cnt].__btowc_fct = NULL;
313
6b98979f 314 /* Call the init function. */
915a6c51
UD
315 __gconv_init_fct init_fct = result[step_cnt].__init_fct;
316 if (init_fct != NULL)
6b98979f 317 {
915a6c51
UD
318 assert (result[step_cnt].__shlib_handle != NULL);
319
320# ifdef PTR_DEMANGLE
321 PTR_DEMANGLE (init_fct);
322# endif
323 status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
6b98979f
UD
324
325 if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
326 {
327 failed = 1;
328 /* Make sure we unload this modules. */
329 --step_cnt;
330 result[step_cnt].__end_fct = NULL;
331 break;
332 }
915a6c51
UD
333
334# ifdef PTR_MANGLE
335 if (result[step_cnt].__btowc_fct != NULL)
336 PTR_MANGLE (result[step_cnt].__btowc_fct);
337# endif
6b98979f 338 }
6973fc01
UD
339 }
340 else
8619129f 341#endif
6973fc01
UD
342 /* It's a builtin transformation. */
343 __gconv_get_builtin_trans (current->code->module_name,
344 &result[step_cnt]);
345
346 current = current->last;
347 }
348
5ea1a82d 349 if (__builtin_expect (failed, 0) != 0)
6973fc01
UD
350 {
351 /* Something went wrong while initializing the modules. */
e34b0f29 352 while (++step_cnt < *nsteps)
6b98979f 353 __gconv_release_step (&result[step_cnt]);
6973fc01 354 free (result);
be64c2ef
PP
355 free (from_name);
356 free (to_name);
e34b0f29 357 *nsteps = 0;
7a68c94a 358 *handle = NULL;
d64b6ad0
UD
359 if (status == __GCONV_OK)
360 status = __GCONV_NOCONV;
6973fc01
UD
361 }
362 else
c7ec9d75 363 *handle = result;
6973fc01 364 }
7a68c94a
UD
365 else
366 {
367 *nsteps = 0;
368 *handle = NULL;
369 }
6973fc01
UD
370
371 return status;
372}
373
374
76a2102b
UD
375#ifndef STATIC_GCONV
376static int
377internal_function
378increment_counter (struct __gconv_step *steps, size_t nsteps)
379{
380 /* Increment the user counter. */
381 size_t cnt = nsteps;
382 int result = __GCONV_OK;
383
384 while (cnt-- > 0)
b79f74cd
UD
385 {
386 struct __gconv_step *step = &steps[cnt];
387
388 if (step->__counter++ == 0)
389 {
390 /* Skip builtin modules. */
391 if (step->__modname != NULL)
392 {
393 /* Reopen a previously used module. */
394 step->__shlib_handle = __gconv_find_shlib (step->__modname);
395 if (step->__shlib_handle == NULL)
396 {
397 /* Oops, this is the second time we use this module
398 (after unloading) and this time loading failed!? */
399 --step->__counter;
400 while (++cnt < nsteps)
6b98979f 401 __gconv_release_step (&steps[cnt]);
b79f74cd
UD
402 result = __GCONV_NOCONV;
403 break;
404 }
405
406 /* The function addresses defined by the module may
407 have changed. */
408 step->__fct = step->__shlib_handle->fct;
409 step->__init_fct = step->__shlib_handle->init_fct;
410 step->__end_fct = step->__shlib_handle->end_fct;
f9ad060c
UD
411
412 /* These settings can be overridden by the init function. */
413 step->__btowc_fct = NULL;
b79f74cd
UD
414 }
415
f9ad060c 416 /* Call the init function. */
915a6c51
UD
417 __gconv_init_fct init_fct = step->__init_fct;
418 if (init_fct != NULL)
419 {
420#ifdef PTR_DEMANGLE
421 PTR_DEMANGLE (init_fct);
422#endif
423 DL_CALL_FCT (init_fct, (step));
424
425#ifdef PTR_MANGLE
426 if (step->__btowc_fct != NULL)
427 PTR_MANGLE (step->__btowc_fct);
428#endif
429 }
b79f74cd
UD
430 }
431 }
76a2102b
UD
432 return result;
433}
434#endif
435
436
6973fc01
UD
437/* The main function: find a possible derivation from the `fromset' (either
438 the given name or the alias) to the `toset' (again with alias). */
439static int
e34b0f29 440internal_function
6973fc01
UD
441find_derivation (const char *toset, const char *toset_expand,
442 const char *fromset, const char *fromset_expand,
d64b6ad0 443 struct __gconv_step **handle, size_t *nsteps)
6973fc01 444{
2bd60880
UD
445 struct derivation_step *first, *current, **lastp, *solution = NULL;
446 int best_cost_hi = INT_MAX;
447 int best_cost_lo = INT_MAX;
6973fc01
UD
448 int result;
449
b79f74cd
UD
450 /* Look whether an earlier call to `find_derivation' has already
451 computed a possible derivation. If so, return it immediately. */
6973fc01
UD
452 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
453 handle, nsteps);
d64b6ad0 454 if (result == __GCONV_OK)
3c5edd4d 455 {
76a2102b
UD
456#ifndef STATIC_GCONV
457 result = increment_counter (*handle, *nsteps);
458#endif
3c5edd4d
UD
459 return result;
460 }
6973fc01 461
b79f74cd
UD
462 /* The task is to find a sequence of transformations, backed by the
463 existing modules - whether builtin or dynamically loadable -,
464 starting at `fromset' (or `fromset_expand') and ending at `toset'
465 (or `toset_expand'), and with minimal cost.
466
467 For computer scientists, this is a shortest path search in the
468 graph where the nodes are all possible charsets and the edges are
469 the transformations listed in __gconv_modules_db.
470
471 For now we use a simple algorithm with quadratic runtime behaviour.
472 A breadth-first search, starting at `fromset' and `fromset_expand'.
473 The list starting at `first' contains all nodes that have been
474 visited up to now, in the order in which they have been visited --
475 excluding the goal nodes `toset' and `toset_expand' which get
476 managed in the list starting at `solution'.
477 `current' walks through the list starting at `first' and looks
478 which nodes are reachable from the current node, adding them to
479 the end of the list [`first' or `solution' respectively] (if
480 they are visited the first time) or updating them in place (if
481 they have have already been visited).
482 In each node of either list, cost_lo and cost_hi contain the
483 minimum cost over any paths found up to now, starting at `fromset'
484 or `fromset_expand', ending at that node. best_cost_lo and
485 best_cost_hi represent the minimum over the elements of the
486 `solution' list. */
487
6973fc01
UD
488 if (fromset_expand != NULL)
489 {
2bd60880
UD
490 first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
491 first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 492 lastp = &first->next->next;
6973fc01
UD
493 }
494 else
495 {
2bd60880 496 first = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 497 lastp = &first->next;
6973fc01
UD
498 }
499
2bd60880 500 for (current = first; current != NULL; current = current->next)
6973fc01
UD
501 {
502 /* Now match all the available module specifications against the
503 current charset name. If any of them matches check whether
504 we already have a derivation for this charset. If yes, use the
505 one with the lower costs. Otherwise add the new charset at the
2bd60880
UD
506 end.
507
e8b1163e
AJ
508 The module database is organized in a tree form which allows
509 searching for prefixes. So we search for the first entry with a
2bd60880
UD
510 matching prefix and any other matching entry can be found from
511 this place. */
b79f74cd 512 struct gconv_module *node;
2bd60880
UD
513
514 /* Maybe it is not necessary anymore to look for a solution for
b79f74cd 515 this entry since the cost is already as high (or higher) as
2bd60880
UD
516 the cost for the best solution so far. */
517 if (current->cost_hi > best_cost_hi
518 || (current->cost_hi == best_cost_hi
519 && current->cost_lo >= best_cost_lo))
520 continue;
521
b79f74cd 522 node = __gconv_modules_db;
2bd60880
UD
523 while (node != NULL)
524 {
d2dfc5de 525 int cmpres = strcmp (current->result_set, node->from_string);
2bd60880 526 if (cmpres == 0)
6973fc01 527 {
2bd60880
UD
528 /* Walk through the list of modules with this prefix and
529 try to match the name. */
530 struct gconv_module *runp;
531
2bd60880
UD
532 /* Check all the modules with this prefix. */
533 runp = node;
534 do
6973fc01 535 {
d2dfc5de
UD
536 const char *result_set = (strcmp (runp->to_string, "-") == 0
537 ? (toset_expand ?: toset)
538 : runp->to_string);
539 int cost_hi = runp->cost_hi + current->cost_hi;
540 int cost_lo = runp->cost_lo + current->cost_lo;
541 struct derivation_step *step;
542
543 /* We managed to find a derivation. First see whether
b79f74cd 544 we have reached one of the goal nodes. */
d2dfc5de
UD
545 if (strcmp (result_set, toset) == 0
546 || (toset_expand != NULL
547 && strcmp (result_set, toset_expand) == 0))
2bd60880 548 {
b79f74cd
UD
549 /* Append to the `solution' list if there
550 is no entry with this name. */
551 for (step = solution; step != NULL; step = step->next)
552 if (strcmp (result_set, step->result_set) == 0)
553 break;
554
555 if (step == NULL)
556 {
557 step = NEW_STEP (result_set,
558 cost_hi, cost_lo,
559 runp, current);
560 step->next = solution;
561 solution = step;
562 }
563 else if (step->cost_hi > cost_hi
564 || (step->cost_hi == cost_hi
565 && step->cost_lo > cost_lo))
566 {
567 /* A better path was found for the node,
568 on the `solution' list. */
569 step->code = runp;
570 step->last = current;
571 step->cost_hi = cost_hi;
572 step->cost_lo = cost_lo;
573 }
574
575 /* Update best_cost accordingly. */
576 if (cost_hi < best_cost_hi
d2dfc5de
UD
577 || (cost_hi == best_cost_hi
578 && cost_lo < best_cost_lo))
2bd60880 579 {
d2dfc5de
UD
580 best_cost_hi = cost_hi;
581 best_cost_lo = cost_lo;
2bd60880 582 }
6973fc01 583 }
d2dfc5de
UD
584 else if (cost_hi < best_cost_hi
585 || (cost_hi == best_cost_hi
586 && cost_lo < best_cost_lo))
6973fc01 587 {
b79f74cd
UD
588 /* Append at the end of the `first' list if there
589 is no entry with this name. */
d2dfc5de
UD
590 for (step = first; step != NULL; step = step->next)
591 if (strcmp (result_set, step->result_set) == 0)
592 break;
2bd60880 593
d2dfc5de
UD
594 if (step == NULL)
595 {
596 *lastp = NEW_STEP (result_set,
597 cost_hi, cost_lo,
598 runp, current);
599 lastp = &(*lastp)->next;
2bd60880 600 }
d2dfc5de
UD
601 else if (step->cost_hi > cost_hi
602 || (step->cost_hi == cost_hi
603 && step->cost_lo > cost_lo))
2bd60880 604 {
b79f74cd
UD
605 /* A better path was found for the node,
606 on the `first' list. */
d2dfc5de
UD
607 step->code = runp;
608 step->last = current;
2bd60880 609
d2dfc5de
UD
610 /* Update the cost for all steps. */
611 for (step = first; step != NULL;
612 step = step->next)
b79f74cd
UD
613 /* But don't update the start nodes. */
614 if (step->code != NULL)
615 {
616 struct derivation_step *back;
617 int hi, lo;
618
619 hi = step->code->cost_hi;
620 lo = step->code->cost_lo;
621
622 for (back = step->last; back->code != NULL;
623 back = back->last)
624 {
625 hi += back->code->cost_hi;
626 lo += back->code->cost_lo;
627 }
628
629 step->cost_hi = hi;
630 step->cost_lo = lo;
631 }
632
633 /* Likewise for the nodes on the solution list.
634 Also update best_cost accordingly. */
d2dfc5de
UD
635 for (step = solution; step != NULL;
636 step = step->next)
637 {
638 step->cost_hi = (step->code->cost_hi
639 + step->last->cost_hi);
640 step->cost_lo = (step->code->cost_lo
641 + step->last->cost_lo);
642
643 if (step->cost_hi < best_cost_hi
644 || (step->cost_hi == best_cost_hi
645 && step->cost_lo < best_cost_lo))
2bd60880 646 {
d2dfc5de
UD
647 best_cost_hi = step->cost_hi;
648 best_cost_lo = step->cost_lo;
2bd60880
UD
649 }
650 }
651 }
6973fc01 652 }
2bd60880
UD
653
654 runp = runp->same;
6973fc01 655 }
2bd60880 656 while (runp != NULL);
e34b0f29 657
d2dfc5de 658 break;
6973fc01 659 }
2bd60880
UD
660 else if (cmpres < 0)
661 node = node->left;
662 else
663 node = node->right;
6973fc01
UD
664 }
665 }
666
2bd60880 667 if (solution != NULL)
b79f74cd
UD
668 {
669 /* We really found a way to do the transformation. */
670
671 /* Choose the best solution. This is easy because we know that
672 the solution list has at most length 2 (one for every possible
673 goal node). */
674 if (solution->next != NULL)
675 {
676 struct derivation_step *solution2 = solution->next;
677
678 if (solution2->cost_hi < solution->cost_hi
679 || (solution2->cost_hi == solution->cost_hi
680 && solution2->cost_lo < solution->cost_lo))
681 solution = solution2;
682 }
683
684 /* Now build a data structure describing the transformation steps. */
685 result = gen_steps (solution, toset_expand ?: toset,
686 fromset_expand ?: fromset, handle, nsteps);
687 }
6973fc01
UD
688 else
689 {
690 /* We haven't found a transformation. Clear the result values. */
691 *handle = NULL;
692 *nsteps = 0;
693 }
694
695 /* Add result in any case to list of known derivations. */
696 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
697 *handle, *nsteps);
698
6973fc01
UD
699 return result;
700}
701
702
e7f21fa6
UD
703/* Control of initialization. */
704__libc_once_define (static, once);
705
706
707static const char *
708do_lookup_alias (const char *name)
709{
710 struct gconv_alias key;
711 struct gconv_alias **found;
712
713 key.fromname = (char *) name;
714 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
715 return found != NULL ? (*found)->toname : NULL;
716}
717
718
9a018f6c
UD
719int
720internal_function
721__gconv_compare_alias (const char *name1, const char *name2)
e7f21fa6 722{
9a018f6c
UD
723 int result;
724
e7f21fa6
UD
725 /* Ensure that the configuration data is read. */
726 __libc_once (once, __gconv_read_conf);
727
9a018f6c
UD
728 if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
729 result = strcmp (do_lookup_alias (name1) ?: name1,
730 do_lookup_alias (name2) ?: name2);
731
732 return result;
e7f21fa6
UD
733}
734
735
6973fc01 736int
e62c19f1 737internal_function
6973fc01 738__gconv_find_transform (const char *toset, const char *fromset,
60c53a12
UD
739 struct __gconv_step **handle, size_t *nsteps,
740 int flags)
6973fc01 741{
6b98979f
UD
742 const char *fromset_expand;
743 const char *toset_expand;
6973fc01
UD
744 int result;
745
746 /* Ensure that the configuration data is read. */
747 __libc_once (once, __gconv_read_conf);
748
0d9f6793 749 /* Acquire the lock. */
9e26f129 750 __libc_lock_lock (__gconv_lock);
0d9f6793 751
6b98979f
UD
752 result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
753 if (result != __GCONV_NODB)
754 {
755 /* We have a cache and could resolve the request, successful or not. */
9e26f129 756 __libc_lock_unlock (__gconv_lock);
6b98979f
UD
757 return result;
758 }
759
6973fc01
UD
760 /* If we don't have a module database return with an error. */
761 if (__gconv_modules_db == NULL)
3c5edd4d 762 {
9e26f129 763 __libc_lock_unlock (__gconv_lock);
d64b6ad0 764 return __GCONV_NOCONV;
3c5edd4d 765 }
6973fc01
UD
766
767 /* See whether the names are aliases. */
6b98979f
UD
768 fromset_expand = do_lookup_alias (fromset);
769 toset_expand = do_lookup_alias (toset);
6973fc01 770
5ea1a82d 771 if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
60c53a12
UD
772 /* We are not supposed to create a pseudo transformation (means
773 copying) when the input and output character set are the same. */
774 && (strcmp (toset, fromset) == 0
775 || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
776 || (fromset_expand != NULL
777 && (strcmp (toset, fromset_expand) == 0
778 || (toset_expand != NULL
779 && strcmp (toset_expand, fromset_expand) == 0)))))
780 {
781 /* Both character sets are the same. */
9e26f129 782 __libc_lock_unlock (__gconv_lock);
7b503bcc 783 return __GCONV_NULCONV;
60c53a12
UD
784 }
785
6973fc01
UD
786 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
787 handle, nsteps);
788
0d9f6793 789 /* Release the lock. */
9e26f129 790 __libc_lock_unlock (__gconv_lock);
0d9f6793 791
6973fc01
UD
792 /* The following code is necessary since `find_derivation' will return
793 GCONV_OK even when no derivation was found but the same request
794 was processed before. I.e., negative results will also be cached. */
d64b6ad0
UD
795 return (result == __GCONV_OK
796 ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
6973fc01
UD
797 : result);
798}
799
800
801/* Release the entries of the modules list. */
802int
e62c19f1 803internal_function
d64b6ad0 804__gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
6973fc01 805{
d64b6ad0 806 int result = __GCONV_OK;
0db59742 807 size_t cnt;
6973fc01 808
0d9f6793 809 /* Acquire the lock. */
9e26f129 810 __libc_lock_lock (__gconv_lock);
0d9f6793 811
0db59742
UD
812#ifndef STATIC_GCONV
813 cnt = nsteps;
814 while (cnt-- > 0)
815 __gconv_release_step (&steps[cnt]);
816#endif
817
818 /* If we use the cache we free a bit more since we don't keep any
819 transformation records around, they are cheap enough to
820 recreate. */
821 __gconv_release_cache (steps, nsteps);
6973fc01 822
0d9f6793 823 /* Release the lock. */
9e26f129 824 __libc_lock_unlock (__gconv_lock);
0d9f6793 825
6973fc01
UD
826 return result;
827}
828
829
2bd60880
UD
830/* Free the modules mentioned. */
831static void
7c11c4a1 832internal_function __libc_freeres_fn_section
2bd60880
UD
833free_modules_db (struct gconv_module *node)
834{
835 if (node->left != NULL)
836 free_modules_db (node->left);
837 if (node->right != NULL)
838 free_modules_db (node->right);
2bd60880
UD
839 do
840 {
841 struct gconv_module *act = node;
d2dfc5de 842 node = node->same;
8ce63ec0
UD
843 if (act->module_name[0] == '/')
844 free (act);
2bd60880
UD
845 }
846 while (node != NULL);
847}
848
849
6973fc01 850/* Free all resources if necessary. */
c877418f 851libc_freeres_fn (free_mem)
6973fc01 852{
be64c2ef
PP
853 /* First free locale memory. This needs to be done before freeing
854 derivations, as ctype cleanup functions dereference steps arrays which we
855 free below. */
7c11c4a1
UD
856 _nl_locale_subfreeres ();
857
9e365fe7
UD
858 /* finddomain.c has similar problem. */
859 extern void _nl_finddomain_subfreeres (void) attribute_hidden;
860 _nl_finddomain_subfreeres ();
861
6973fc01
UD
862 if (__gconv_alias_db != NULL)
863 __tdestroy (__gconv_alias_db, free);
864
0dbbad29
UD
865 if (__gconv_modules_db != NULL)
866 free_modules_db (__gconv_modules_db);
6973fc01
UD
867
868 if (known_derivations != NULL)
869 __tdestroy (known_derivations, free_derivation);
870}