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