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