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