]> 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.
2bd60880 2 Copyright (C) 1997, 1998, 1999 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{
47 struct gconv_alias *s1 = (struct gconv_alias *) p1;
48 struct gconv_alias *s2 = (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{
93 struct known_derivation *s1 = (struct known_derivation *) p1;
94 struct known_derivation *s2 = (struct known_derivation *) p2;
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)
d64b6ad0 166 if (deriv->steps[cnt].__end_fct)
94e365c6 167 DL_CALL_FCT (deriv->steps[cnt].__end_fct, (&deriv->steps[cnt]));
6973fc01 168
74454183 169 /* Free the name strings. */
d64b6ad0
UD
170 free ((char *) deriv->steps[0].__from_name);
171 free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
74454183 172
d64b6ad0 173 free ((struct __gconv_step *) deriv->steps);
6973fc01
UD
174 free (deriv);
175}
176
177
178static int
179internal_function
180gen_steps (struct derivation_step *best, const char *toset,
d64b6ad0 181 const char *fromset, struct __gconv_step **handle, size_t *nsteps)
6973fc01
UD
182{
183 size_t step_cnt = 0;
d64b6ad0 184 struct __gconv_step *result;
6973fc01 185 struct derivation_step *current;
d64b6ad0 186 int status = __GCONV_NOMEM;
6973fc01
UD
187
188 /* First determine number of steps. */
189 for (current = best; current->last != NULL; current = current->last)
190 ++step_cnt;
191
d64b6ad0
UD
192 result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
193 * step_cnt);
6973fc01
UD
194 if (result != NULL)
195 {
e34b0f29
UD
196 int failed = 0;
197
d64b6ad0 198 status = __GCONV_OK;
e34b0f29 199 *nsteps = step_cnt;
6973fc01
UD
200 current = best;
201 while (step_cnt-- > 0)
202 {
d64b6ad0
UD
203 result[step_cnt].__from_name = (step_cnt == 0
204 ? __strdup (fromset)
205 : current->last->result_set);
206 result[step_cnt].__to_name = (step_cnt + 1 == *nsteps
207 ? __strdup (current->result_set)
208 : result[step_cnt + 1].__from_name);
6973fc01 209
8619129f 210#ifndef STATIC_GCONV
6973fc01
UD
211 if (current->code->module_name[0] == '/')
212 {
213 /* Load the module, return handle for it. */
d64b6ad0 214 struct __gconv_loaded_object *shlib_handle =
6973fc01
UD
215 __gconv_find_shlib (current->code->module_name);
216
217 if (shlib_handle == NULL)
e34b0f29
UD
218 {
219 failed = 1;
220 break;
221 }
6973fc01 222
d64b6ad0
UD
223 result[step_cnt].__shlib_handle = shlib_handle;
224 result[step_cnt].__modname = shlib_handle->name;
76a2102b 225 result[step_cnt].__counter = 1;
d64b6ad0
UD
226 result[step_cnt].__fct = shlib_handle->fct;
227 result[step_cnt].__init_fct = shlib_handle->init_fct;
228 result[step_cnt].__end_fct = shlib_handle->end_fct;
6973fc01
UD
229 }
230 else
8619129f 231#endif
6973fc01
UD
232 /* It's a builtin transformation. */
233 __gconv_get_builtin_trans (current->code->module_name,
234 &result[step_cnt]);
235
0d9f6793 236 /* Call the init function. */
d64b6ad0 237 if (result[step_cnt].__init_fct != NULL)
c7ec9d75 238 {
94e365c6 239 status = DL_CALL_FCT (result[step_cnt].__init_fct,
c7ec9d75 240 (&result[step_cnt]));
2bd60880 241
d64b6ad0 242 if (status != __GCONV_OK)
c7ec9d75
UD
243 {
244 failed = 1;
245 /* Make sure we unload this modules. */
246 --step_cnt;
247 break;
248 }
249 }
0d9f6793 250
6973fc01
UD
251 current = current->last;
252 }
253
e34b0f29 254 if (failed != 0)
6973fc01
UD
255 {
256 /* Something went wrong while initializing the modules. */
e34b0f29 257 while (++step_cnt < *nsteps)
0d9f6793 258 {
d64b6ad0 259 if (result[step_cnt].__end_fct != NULL)
94e365c6 260 DL_CALL_FCT (result[step_cnt].__end_fct, (&result[step_cnt]));
8619129f 261#ifndef STATIC_GCONV
d64b6ad0 262 __gconv_release_shlib (result[step_cnt].__shlib_handle);
8619129f 263#endif
0d9f6793 264 }
6973fc01 265 free (result);
e34b0f29 266 *nsteps = 0;
7a68c94a 267 *handle = NULL;
d64b6ad0
UD
268 if (status == __GCONV_OK)
269 status = __GCONV_NOCONV;
6973fc01
UD
270 }
271 else
c7ec9d75 272 *handle = result;
6973fc01 273 }
7a68c94a
UD
274 else
275 {
276 *nsteps = 0;
277 *handle = NULL;
278 }
6973fc01
UD
279
280 return status;
281}
282
283
76a2102b
UD
284#ifndef STATIC_GCONV
285static int
286internal_function
287increment_counter (struct __gconv_step *steps, size_t nsteps)
288{
289 /* Increment the user counter. */
290 size_t cnt = nsteps;
291 int result = __GCONV_OK;
292
293 while (cnt-- > 0)
294 if (steps[cnt].__counter++ == 0)
295 {
296 steps[cnt].__shlib_handle =
297 __gconv_find_shlib (steps[cnt].__modname);
298 if (steps[cnt].__shlib_handle == NULL)
299 {
300 /* Oops, this is the second time we use this module (after
301 unloading) and this time loading failed!? */
302 while (++cnt < nsteps)
303 __gconv_release_shlib (steps[cnt].__shlib_handle);
304 result = __GCONV_NOCONV;
305 break;
306 }
307 }
308 return result;
309}
310#endif
311
312
6973fc01
UD
313/* The main function: find a possible derivation from the `fromset' (either
314 the given name or the alias) to the `toset' (again with alias). */
315static int
e34b0f29 316internal_function
6973fc01
UD
317find_derivation (const char *toset, const char *toset_expand,
318 const char *fromset, const char *fromset_expand,
d64b6ad0 319 struct __gconv_step **handle, size_t *nsteps)
6973fc01 320{
2bd60880
UD
321 struct derivation_step *first, *current, **lastp, *solution = NULL;
322 int best_cost_hi = INT_MAX;
323 int best_cost_lo = INT_MAX;
6973fc01
UD
324 int result;
325
6973fc01
UD
326 /* There is a small chance that this derivation is meanwhile found. This
327 can happen if in `find_derivation' we look for this derivation, didn't
328 find it but at the same time another thread looked for this derivation. */
329 result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
330 handle, nsteps);
d64b6ad0 331 if (result == __GCONV_OK)
3c5edd4d 332 {
76a2102b
UD
333#ifndef STATIC_GCONV
334 result = increment_counter (*handle, *nsteps);
335#endif
3c5edd4d
UD
336 return result;
337 }
6973fc01 338
2bd60880 339 /* For now we use a simple algorithm with quadratic runtime behaviour.
0d9f6793
UD
340 The task is to match the `toset' with any of the available rules,
341 starting from FROMSET. */
6973fc01
UD
342 if (fromset_expand != NULL)
343 {
2bd60880
UD
344 first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
345 first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 346 lastp = &first->next->next;
6973fc01
UD
347 }
348 else
349 {
2bd60880 350 first = NEW_STEP (fromset, 0, 0, NULL, NULL);
e34b0f29 351 lastp = &first->next;
6973fc01
UD
352 }
353
2bd60880 354 for (current = first; current != NULL; current = current->next)
6973fc01
UD
355 {
356 /* Now match all the available module specifications against the
357 current charset name. If any of them matches check whether
358 we already have a derivation for this charset. If yes, use the
359 one with the lower costs. Otherwise add the new charset at the
2bd60880
UD
360 end.
361
362 The module database is organized in a tree form which allows to
363 search for prefixes. So we search for the first entry with a
364 matching prefix and any other matching entry can be found from
365 this place. */
366 struct gconv_module *node = __gconv_modules_db;
367
368 /* Maybe it is not necessary anymore to look for a solution for
369 this entry since the cost is already as high (or heigher) as
370 the cost for the best solution so far. */
371 if (current->cost_hi > best_cost_hi
372 || (current->cost_hi == best_cost_hi
373 && current->cost_lo >= best_cost_lo))
374 continue;
375
376 while (node != NULL)
377 {
378 int cmpres = strncmp (current->result_set, node->from_constpfx,
379 MIN (current->result_set_len,
380 node->from_constpfx_len));
6973fc01 381
2bd60880 382 if (cmpres == 0)
6973fc01 383 {
2bd60880
UD
384 /* Walk through the list of modules with this prefix and
385 try to match the name. */
386 struct gconv_module *runp;
387
388 if (current->result_set_len < node->from_constpfx_len)
389 /* Cannot possibly match. */
390 break;
391
392 /* Check all the modules with this prefix. */
393 runp = node;
394 do
6973fc01 395 {
2bd60880
UD
396 const char *result_set = NULL;
397
398 if (runp->from_pattern == NULL)
399 {
400 /* This is a simple entry and therefore we have a
401 found an matching entry if the strings are really
402 equal. */
403 if (current->result_set_len == runp->from_constpfx_len)
404 {
405 if (strcmp (runp->to_string, "-") == 0)
406 result_set = toset_expand ?: toset;
407 else
408 result_set = runp->to_string;
409 }
410 }
6973fc01 411 else
2bd60880
UD
412 {
413 /* Compile the regular expression if necessary. */
414 if (runp->from_regex == NULL)
415 {
416 if (__regcomp (&runp->from_regex_mem,
417 runp->from_pattern,
418 REG_EXTENDED | REG_ICASE) != 0)
419 /* Something is wrong. Remember this. */
420 runp->from_regex = (regex_t *) -1L;
421 else
422 runp->from_regex = &runp->from_regex_mem;
423 }
424
425 if (runp->from_regex != (regex_t *) -1L)
426 {
427 regmatch_t match[4];
428
429 /* Try to match the regular expression. */
430 if (__regexec (runp->from_regex, current->result_set,
431 4, match, 0) == 0
432 && match[0].rm_so == 0
433 && current->result_set[match[0].rm_eo] == '\0')
434 {
435 /* At least the whole <from> string is matched.
436 We must now match sed-like possible
437 subexpressions from the match to the
438 toset expression. */
6973fc01
UD
439#define ENSURE_LEN(LEN) \
440 if (wp + (LEN) >= constr + len - 1) \
441 { \
442 char *newp = alloca (len += 128); \
2bd60880 443 wp = __mempcpy (newp, constr, wp - constr); \
6973fc01
UD
444 constr = newp; \
445 }
2bd60880
UD
446 size_t len = 128;
447 char *constr = alloca (len);
448 char *wp = constr;
449 const char *cp = runp->to_string;
450
451 while (*cp != '\0')
452 {
453 if (*cp != '\\')
454 {
455 ENSURE_LEN (1);
456 *wp++ = *cp++;
457 }
458 else if (cp[1] == '\0')
459 /* Backslash at end of string. */
460 break;
461 else
462 {
463 ++cp;
464 if (*cp == '\\')
465 {
466 *wp++ = *cp++;
467 ENSURE_LEN (1);
468 }
469 else if (*cp < '1' || *cp > '3')
470 break;
471 else
472 {
473 int idx = *cp - '0';
474 if (match[idx].rm_so == -1)
475 /* No match. */
476 break;
477
478 ENSURE_LEN (match[idx].rm_eo
479 - match[idx].rm_so);
480 wp = __mempcpy (wp,
481 &current->result_set[match[idx].rm_so],
482 match[idx].rm_eo
483 - match[idx].rm_so);
484 ++cp;
485 }
486 }
487 }
488 if (*cp == '\0' && wp != constr)
489 {
490 /* Terminate the constructed string. */
491 *wp = '\0';
492 result_set = constr;
493 }
494 }
495 }
6973fc01 496 }
2bd60880
UD
497
498 if (result_set != NULL)
6973fc01 499 {
2bd60880
UD
500 int cost_hi = runp->cost_hi + current->cost_hi;
501 int cost_lo = runp->cost_lo + current->cost_lo;
502 struct derivation_step *step;
503
504 /* We managed to find a derivation. First see whether
505 this is what we are looking for. */
bd4848fb 506 if (strcmp (result_set, toset) == 0
2bd60880 507 || (toset_expand != NULL
bd4848fb 508 && strcmp (result_set, toset_expand) == 0))
2bd60880
UD
509 {
510 if (solution == NULL || cost_hi < best_cost_hi
511 || (cost_hi == best_cost_hi
512 && cost_lo < best_cost_lo))
513 {
514 best_cost_hi = cost_hi;
515 best_cost_lo = cost_lo;
516 }
517
518 /* Append this solution to list. */
519 if (solution == NULL)
520 solution = NEW_STEP (result_set, 0, 0, runp,
521 current);
522 else
523 {
524 while (solution->next != NULL)
525 solution = solution->next;
526
527 solution->next = NEW_STEP (result_set, 0, 0,
528 runp, current);
529 }
530 }
531 else if (cost_hi < best_cost_hi
532 || (cost_hi == best_cost_hi
533 && cost_lo < best_cost_lo))
534 {
535 /* Append at the end if there is no entry with
536 this name. */
537 for (step = first; step != NULL; step = step->next)
bd4848fb 538 if (strcmp (result_set, step->result_set) == 0)
2bd60880
UD
539 break;
540
541 if (step == NULL)
542 {
543 *lastp = NEW_STEP (result_set,
544 cost_hi, cost_lo,
545 runp, current);
546 lastp = &(*lastp)->next;
547 }
548 else if (step->cost_hi > cost_hi
549 || (step->cost_hi == cost_hi
550 && step->cost_lo > cost_lo))
551 {
552 step->code = runp;
553 step->last = current;
554
555 /* Update the cost for all steps. */
556 for (step = first; step != NULL;
557 step = step->next)
558 {
559 struct derivation_step *back;
560
561 if (step->code == NULL)
562 /* This is one of the entries we started
563 from. */
564 continue;
565
566 step->cost_hi = step->code->cost_hi;
567 step->cost_lo = step->code->cost_lo;
568
569 for (back = step->last; back->code != NULL;
570 back = back->last)
571 {
572 step->cost_hi += back->code->cost_hi;
573 step->cost_lo += back->code->cost_lo;
574 }
575 }
576
577 for (step = solution; step != NULL;
578 step = step->next)
579 {
580 step->cost_hi = (step->code->cost_hi
581 + step->last->cost_hi);
582 step->cost_lo = (step->code->cost_lo
583 + step->last->cost_lo);
584
585 if (step->cost_hi < best_cost_hi
586 || (step->cost_hi == best_cost_hi
587 && step->cost_lo < best_cost_lo))
588 {
589 solution = step;
590 best_cost_hi = step->cost_hi;
591 best_cost_lo = step->cost_lo;
592 }
593 }
594 }
595 }
6973fc01 596 }
2bd60880
UD
597
598 runp = runp->same;
6973fc01 599 }
2bd60880 600 while (runp != NULL);
e34b0f29 601
2bd60880
UD
602 if (current->result_set_len == node->from_constpfx_len)
603 break;
e34b0f29 604
2bd60880 605 node = node->matching;
6973fc01 606 }
2bd60880
UD
607 else if (cmpres < 0)
608 node = node->left;
609 else
610 node = node->right;
6973fc01
UD
611 }
612 }
613
2bd60880 614 if (solution != NULL)
6973fc01
UD
615 /* We really found a way to do the transformation. Now build a data
616 structure describing the transformation steps.*/
2bd60880
UD
617 result = gen_steps (solution, toset_expand ?: toset,
618 fromset_expand ?: fromset, handle, nsteps);
6973fc01
UD
619 else
620 {
621 /* We haven't found a transformation. Clear the result values. */
622 *handle = NULL;
623 *nsteps = 0;
624 }
625
626 /* Add result in any case to list of known derivations. */
627 add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
628 *handle, *nsteps);
629
6973fc01
UD
630 return result;
631}
632
633
634int
e62c19f1 635internal_function
6973fc01 636__gconv_find_transform (const char *toset, const char *fromset,
60c53a12
UD
637 struct __gconv_step **handle, size_t *nsteps,
638 int flags)
6973fc01
UD
639{
640 __libc_once_define (static, once);
641 const char *fromset_expand = NULL;
642 const char *toset_expand = NULL;
643 int result;
644
645 /* Ensure that the configuration data is read. */
646 __libc_once (once, __gconv_read_conf);
647
0d9f6793
UD
648 /* Acquire the lock. */
649 __libc_lock_lock (lock);
650
6973fc01
UD
651 /* If we don't have a module database return with an error. */
652 if (__gconv_modules_db == NULL)
3c5edd4d
UD
653 {
654 __libc_lock_unlock (lock);
d64b6ad0 655 return __GCONV_NOCONV;
3c5edd4d 656 }
6973fc01
UD
657
658 /* See whether the names are aliases. */
659 if (__gconv_alias_db != NULL)
660 {
661 struct gconv_alias key;
e34b0f29 662 struct gconv_alias **found;
6973fc01
UD
663
664 key.fromname = fromset;
e34b0f29
UD
665 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
666 fromset_expand = found != NULL ? (*found)->toname : NULL;
6973fc01
UD
667
668 key.fromname = toset;
e34b0f29
UD
669 found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
670 toset_expand = found != NULL ? (*found)->toname : NULL;
6973fc01
UD
671 }
672
60c53a12
UD
673 if ((flags & GCONV_AVOID_NOCONV)
674 /* We are not supposed to create a pseudo transformation (means
675 copying) when the input and output character set are the same. */
676 && (strcmp (toset, fromset) == 0
677 || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
678 || (fromset_expand != NULL
679 && (strcmp (toset, fromset_expand) == 0
680 || (toset_expand != NULL
681 && strcmp (toset_expand, fromset_expand) == 0)))))
682 {
683 /* Both character sets are the same. */
684 __libc_lock_unlock (lock);
685 return __GCONV_NOCONV;
686 }
687
6973fc01
UD
688 result = find_derivation (toset, toset_expand, fromset, fromset_expand,
689 handle, nsteps);
690
0d9f6793
UD
691 /* Release the lock. */
692 __libc_lock_unlock (lock);
693
6973fc01
UD
694 /* The following code is necessary since `find_derivation' will return
695 GCONV_OK even when no derivation was found but the same request
696 was processed before. I.e., negative results will also be cached. */
d64b6ad0
UD
697 return (result == __GCONV_OK
698 ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
6973fc01
UD
699 : result);
700}
701
702
703/* Release the entries of the modules list. */
704int
e62c19f1 705internal_function
d64b6ad0 706__gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
6973fc01 707{
d64b6ad0 708 int result = __GCONV_OK;
6973fc01 709
8619129f 710#ifndef STATIC_GCONV
0d9f6793
UD
711 /* Acquire the lock. */
712 __libc_lock_lock (lock);
713
6973fc01 714 while (nsteps-- > 0)
d64b6ad0
UD
715 if (steps[nsteps].__shlib_handle != NULL
716 && --steps[nsteps].__counter == 0)
6973fc01 717 {
d64b6ad0
UD
718 result = __gconv_release_shlib (steps[nsteps].__shlib_handle);
719 if (result != __GCONV_OK)
6973fc01 720 break;
d64b6ad0 721 steps[nsteps].__shlib_handle = NULL;
6973fc01
UD
722 }
723
0d9f6793
UD
724 /* Release the lock. */
725 __libc_lock_unlock (lock);
8619129f 726#endif
0d9f6793 727
6973fc01
UD
728 return result;
729}
730
731
2bd60880
UD
732/* Free the modules mentioned. */
733static void
734internal_function
735free_modules_db (struct gconv_module *node)
736{
737 if (node->left != NULL)
738 free_modules_db (node->left);
739 if (node->right != NULL)
740 free_modules_db (node->right);
741 if (node->same != NULL)
742 free_modules_db (node->same);
743 do
744 {
745 struct gconv_module *act = node;
746 node = node->matching;
8ce63ec0
UD
747 if (act->module_name[0] == '/')
748 free (act);
2bd60880
UD
749 }
750 while (node != NULL);
751}
752
753
6973fc01
UD
754/* Free all resources if necessary. */
755static void __attribute__ ((unused))
756free_mem (void)
757{
6973fc01
UD
758 if (__gconv_alias_db != NULL)
759 __tdestroy (__gconv_alias_db, free);
760
0dbbad29
UD
761 if (__gconv_modules_db != NULL)
762 free_modules_db (__gconv_modules_db);
6973fc01
UD
763
764 if (known_derivations != NULL)
765 __tdestroy (known_derivations, free_derivation);
766}
767
768text_set_element (__libc_subfreeres, free_mem);
This page took 0.391177 seconds and 5 git commands to generate.