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