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