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