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