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