Back to index

glibc  2.9
gconv_db.c
Go to the documentation of this file.
00001 /* Provide access to the collection of available transformation modules.
00002    Copyright (C) 1997-2003, 2004, 2005, 2006, 2007
00003    Free Software Foundation, Inc.
00004    This file is part of the GNU C Library.
00005    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #include <assert.h>
00023 #include <limits.h>
00024 #include <search.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <sys/param.h>
00028 #include <bits/libc-lock.h>
00029 #include <locale/localeinfo.h>
00030 
00031 #include <dlfcn.h>
00032 #include <gconv_int.h>
00033 #include <sysdep.h>
00034 
00035 
00036 /* Simple data structure for alias mapping.  We have two names, `from'
00037    and `to'.  */
00038 void *__gconv_alias_db;
00039 
00040 /* Array with available modules.  */
00041 struct gconv_module *__gconv_modules_db;
00042 
00043 /* We modify global data.   */
00044 __libc_lock_define_initialized (, __gconv_lock)
00045 
00046 
00047 /* Provide access to module database.  */
00048 struct gconv_module *
00049 __gconv_get_modules_db (void)
00050 {
00051   return __gconv_modules_db;
00052 }
00053 
00054 void *
00055 __gconv_get_alias_db (void)
00056 {
00057   return __gconv_alias_db;
00058 }
00059 
00060 
00061 /* Function for searching alias.  */
00062 int
00063 __gconv_alias_compare (const void *p1, const void *p2)
00064 {
00065   const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
00066   const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
00067   return strcmp (s1->fromname, s2->fromname);
00068 }
00069 
00070 
00071 /* To search for a derivation we create a list of intermediate steps.
00072    Each element contains a pointer to the element which precedes it
00073    in the derivation order.  */
00074 struct derivation_step
00075 {
00076   const char *result_set;
00077   size_t result_set_len;
00078   int cost_lo;
00079   int cost_hi;
00080   struct gconv_module *code;
00081   struct derivation_step *last;
00082   struct derivation_step *next;
00083 };
00084 
00085 #define NEW_STEP(result, hi, lo, module, last_mod) \
00086   ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
00087      newp->result_set = result;                                             \
00088      newp->result_set_len = strlen (result);                                \
00089      newp->cost_hi = hi;                                             \
00090      newp->cost_lo = lo;                                             \
00091      newp->code = module;                                            \
00092      newp->last = last_mod;                                          \
00093      newp->next = NULL;                                                     \
00094      newp; })
00095 
00096 
00097 /* If a specific transformation is used more than once we should not need
00098    to start looking for it again.  Instead cache each successful result.  */
00099 struct known_derivation
00100 {
00101   const char *from;
00102   const char *to;
00103   struct __gconv_step *steps;
00104   size_t nsteps;
00105 };
00106 
00107 /* Compare function for database of found derivations.  */
00108 static int
00109 derivation_compare (const void *p1, const void *p2)
00110 {
00111   const struct known_derivation *s1 = (const struct known_derivation *) p1;
00112   const struct known_derivation *s2 = (const struct known_derivation *) p2;
00113   int result;
00114 
00115   result = strcmp (s1->from, s2->from);
00116   if (result == 0)
00117     result = strcmp (s1->to, s2->to);
00118   return result;
00119 }
00120 
00121 /* The search tree for known derivations.  */
00122 static void *known_derivations;
00123 
00124 /* Look up whether given transformation was already requested before.  */
00125 static int
00126 internal_function
00127 derivation_lookup (const char *fromset, const char *toset,
00128                  struct __gconv_step **handle, size_t *nsteps)
00129 {
00130   struct known_derivation key = { fromset, toset, NULL, 0 };
00131   struct known_derivation **result;
00132 
00133   result = __tfind (&key, &known_derivations, derivation_compare);
00134 
00135   if (result == NULL)
00136     return __GCONV_NOCONV;
00137 
00138   *handle = (*result)->steps;
00139   *nsteps = (*result)->nsteps;
00140 
00141   /* Please note that we return GCONV_OK even if the last search for
00142      this transformation was unsuccessful.  */
00143   return __GCONV_OK;
00144 }
00145 
00146 /* Add new derivation to list of known ones.  */
00147 static void
00148 internal_function
00149 add_derivation (const char *fromset, const char *toset,
00150               struct __gconv_step *handle, size_t nsteps)
00151 {
00152   struct known_derivation *new_deriv;
00153   size_t fromset_len = strlen (fromset) + 1;
00154   size_t toset_len = strlen (toset) + 1;
00155 
00156   new_deriv = (struct known_derivation *)
00157     malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
00158   if (new_deriv != NULL)
00159     {
00160       new_deriv->from = (char *) (new_deriv + 1);
00161       new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
00162                            toset, toset_len);
00163 
00164       new_deriv->steps = handle;
00165       new_deriv->nsteps = nsteps;
00166 
00167       if (__tsearch (new_deriv, &known_derivations, derivation_compare)
00168          == NULL)
00169        /* There is some kind of memory allocation problem.  */
00170        free (new_deriv);
00171     }
00172   /* Please note that we don't complain if the allocation failed.  This
00173      is not tragically but in case we use the memory debugging facilities
00174      not all memory will be freed.  */
00175 }
00176 
00177 static void __libc_freeres_fn_section
00178 free_derivation (void *p)
00179 {
00180   struct known_derivation *deriv = (struct known_derivation *) p;
00181   size_t cnt;
00182 
00183   for (cnt = 0; cnt < deriv->nsteps; ++cnt)
00184     if (deriv->steps[cnt].__counter > 0
00185        && deriv->steps[cnt].__end_fct != NULL)
00186       {
00187        assert (deriv->steps[cnt].__shlib_handle != NULL);
00188 
00189        __gconv_end_fct end_fct = deriv->steps[cnt].__end_fct;
00190 #ifdef PTR_DEMANGLE
00191        PTR_DEMANGLE (end_fct);
00192 #endif
00193        DL_CALL_FCT (end_fct, (&deriv->steps[cnt]));
00194       }
00195 
00196   /* Free the name strings.  */
00197   free ((char *) deriv->steps[0].__from_name);
00198   free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
00199 
00200   free ((struct __gconv_step *) deriv->steps);
00201   free (deriv);
00202 }
00203 
00204 
00205 /* Decrement the reference count for a single step in a steps array.  */
00206 void
00207 internal_function
00208 __gconv_release_step (struct __gconv_step *step)
00209 {
00210   /* Skip builtin modules; they are not reference counted.  */
00211   if (step->__shlib_handle != NULL && --step->__counter == 0)
00212     {
00213       /* Call the destructor.  */
00214       if (step->__end_fct != NULL)
00215        {
00216          assert (step->__shlib_handle != NULL);
00217 
00218          __gconv_end_fct end_fct = step->__end_fct;
00219 #ifdef PTR_DEMANGLE
00220          PTR_DEMANGLE (end_fct);
00221 #endif
00222          DL_CALL_FCT (end_fct, (step));
00223        }
00224 
00225 #ifndef STATIC_GCONV
00226       /* Release the loaded module.  */
00227       __gconv_release_shlib (step->__shlib_handle);
00228       step->__shlib_handle = NULL;
00229 #endif
00230     }
00231   else if (step->__shlib_handle == NULL)
00232     /* Builtin modules should not have end functions.  */
00233     assert (step->__end_fct == NULL);
00234 }
00235 
00236 static int
00237 internal_function
00238 gen_steps (struct derivation_step *best, const char *toset,
00239           const char *fromset, struct __gconv_step **handle, size_t *nsteps)
00240 {
00241   size_t step_cnt = 0;
00242   struct __gconv_step *result;
00243   struct derivation_step *current;
00244   int status = __GCONV_NOMEM;
00245 
00246   /* First determine number of steps.  */
00247   for (current = best; current->last != NULL; current = current->last)
00248     ++step_cnt;
00249 
00250   result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
00251                                       * step_cnt);
00252   if (result != NULL)
00253     {
00254       int failed = 0;
00255 
00256       status = __GCONV_OK;
00257       *nsteps = step_cnt;
00258       current = best;
00259       while (step_cnt-- > 0)
00260        {
00261          result[step_cnt].__from_name = (step_cnt == 0
00262                                      ? __strdup (fromset)
00263                                      : (char *)current->last->result_set);
00264          result[step_cnt].__to_name = (step_cnt + 1 == *nsteps
00265                                    ? __strdup (current->result_set)
00266                                    : result[step_cnt + 1].__from_name);
00267 
00268          result[step_cnt].__counter = 1;
00269          result[step_cnt].__data = NULL;
00270 
00271 #ifndef STATIC_GCONV
00272          if (current->code->module_name[0] == '/')
00273            {
00274              /* Load the module, return handle for it.  */
00275              struct __gconv_loaded_object *shlib_handle =
00276               __gconv_find_shlib (current->code->module_name);
00277 
00278              if (shlib_handle == NULL)
00279               {
00280                 failed = 1;
00281                 break;
00282               }
00283 
00284              result[step_cnt].__shlib_handle = shlib_handle;
00285              result[step_cnt].__modname = shlib_handle->name;
00286              result[step_cnt].__fct = shlib_handle->fct;
00287              result[step_cnt].__init_fct = shlib_handle->init_fct;
00288              result[step_cnt].__end_fct = shlib_handle->end_fct;
00289 
00290              /* These settings can be overridden by the init function.  */
00291              result[step_cnt].__btowc_fct = NULL;
00292 
00293              /* Call the init function.  */
00294              __gconv_init_fct init_fct = result[step_cnt].__init_fct;
00295              if (init_fct != NULL)
00296               {
00297                 assert (result[step_cnt].__shlib_handle != NULL);
00298 
00299 # ifdef PTR_DEMANGLE
00300                 PTR_DEMANGLE (init_fct);
00301 # endif
00302                 status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
00303 
00304                 if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
00305                   {
00306                     failed = 1;
00307                     /* Make sure we unload this modules.  */
00308                     --step_cnt;
00309                     result[step_cnt].__end_fct = NULL;
00310                     break;
00311                   }
00312 
00313 # ifdef PTR_MANGLE
00314                 if (result[step_cnt].__btowc_fct != NULL)
00315                   PTR_MANGLE (result[step_cnt].__btowc_fct);
00316 # endif
00317               }
00318            }
00319          else
00320 #endif
00321            /* It's a builtin transformation.  */
00322            __gconv_get_builtin_trans (current->code->module_name,
00323                                    &result[step_cnt]);
00324 
00325          current = current->last;
00326        }
00327 
00328       if (__builtin_expect (failed, 0) != 0)
00329        {
00330          /* Something went wrong while initializing the modules.  */
00331          while (++step_cnt < *nsteps)
00332            __gconv_release_step (&result[step_cnt]);
00333          free (result);
00334          *nsteps = 0;
00335          *handle = NULL;
00336          if (status == __GCONV_OK)
00337            status = __GCONV_NOCONV;
00338        }
00339       else
00340        *handle = result;
00341     }
00342   else
00343     {
00344       *nsteps = 0;
00345       *handle = NULL;
00346     }
00347 
00348   return status;
00349 }
00350 
00351 
00352 #ifndef STATIC_GCONV
00353 static int
00354 internal_function
00355 increment_counter (struct __gconv_step *steps, size_t nsteps)
00356 {
00357   /* Increment the user counter.  */
00358   size_t cnt = nsteps;
00359   int result = __GCONV_OK;
00360 
00361   while (cnt-- > 0)
00362     {
00363       struct __gconv_step *step = &steps[cnt];
00364 
00365       if (step->__counter++ == 0)
00366        {
00367          /* Skip builtin modules.  */
00368          if (step->__modname != NULL)
00369            {
00370              /* Reopen a previously used module.  */
00371              step->__shlib_handle = __gconv_find_shlib (step->__modname);
00372              if (step->__shlib_handle == NULL)
00373               {
00374                 /* Oops, this is the second time we use this module
00375                    (after unloading) and this time loading failed!?  */
00376                 --step->__counter;
00377                 while (++cnt < nsteps)
00378                   __gconv_release_step (&steps[cnt]);
00379                 result = __GCONV_NOCONV;
00380                 break;
00381               }
00382 
00383              /* The function addresses defined by the module may
00384                have changed.  */
00385              step->__fct = step->__shlib_handle->fct;
00386              step->__init_fct = step->__shlib_handle->init_fct;
00387              step->__end_fct = step->__shlib_handle->end_fct;
00388 
00389              /* These settings can be overridden by the init function.  */
00390              step->__btowc_fct = NULL;
00391            }
00392 
00393          /* Call the init function.  */
00394          __gconv_init_fct init_fct = step->__init_fct;
00395          if (init_fct != NULL)
00396            {
00397 #ifdef PTR_DEMANGLE
00398              PTR_DEMANGLE (init_fct);
00399 #endif
00400              DL_CALL_FCT (init_fct, (step));
00401 
00402 #ifdef PTR_MANGLE
00403              if (step->__btowc_fct != NULL)
00404               PTR_MANGLE (step->__btowc_fct);
00405 #endif
00406            }
00407        }
00408     }
00409   return result;
00410 }
00411 #endif
00412 
00413 
00414 /* The main function: find a possible derivation from the `fromset' (either
00415    the given name or the alias) to the `toset' (again with alias).  */
00416 static int
00417 internal_function
00418 find_derivation (const char *toset, const char *toset_expand,
00419                const char *fromset, const char *fromset_expand,
00420                struct __gconv_step **handle, size_t *nsteps)
00421 {
00422   struct derivation_step *first, *current, **lastp, *solution = NULL;
00423   int best_cost_hi = INT_MAX;
00424   int best_cost_lo = INT_MAX;
00425   int result;
00426 
00427   /* Look whether an earlier call to `find_derivation' has already
00428      computed a possible derivation.  If so, return it immediately.  */
00429   result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
00430                            handle, nsteps);
00431   if (result == __GCONV_OK)
00432     {
00433 #ifndef STATIC_GCONV
00434       result = increment_counter (*handle, *nsteps);
00435 #endif
00436       return result;
00437     }
00438 
00439   /* The task is to find a sequence of transformations, backed by the
00440      existing modules - whether builtin or dynamically loadable -,
00441      starting at `fromset' (or `fromset_expand') and ending at `toset'
00442      (or `toset_expand'), and with minimal cost.
00443 
00444      For computer scientists, this is a shortest path search in the
00445      graph where the nodes are all possible charsets and the edges are
00446      the transformations listed in __gconv_modules_db.
00447 
00448      For now we use a simple algorithm with quadratic runtime behaviour.
00449      A breadth-first search, starting at `fromset' and `fromset_expand'.
00450      The list starting at `first' contains all nodes that have been
00451      visited up to now, in the order in which they have been visited --
00452      excluding the goal nodes `toset' and `toset_expand' which get
00453      managed in the list starting at `solution'.
00454      `current' walks through the list starting at `first' and looks
00455      which nodes are reachable from the current node, adding them to
00456      the end of the list [`first' or `solution' respectively] (if
00457      they are visited the first time) or updating them in place (if
00458      they have have already been visited).
00459      In each node of either list, cost_lo and cost_hi contain the
00460      minimum cost over any paths found up to now, starting at `fromset'
00461      or `fromset_expand', ending at that node.  best_cost_lo and
00462      best_cost_hi represent the minimum over the elements of the
00463      `solution' list.  */
00464 
00465   if (fromset_expand != NULL)
00466     {
00467       first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
00468       first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
00469       lastp = &first->next->next;
00470     }
00471   else
00472     {
00473       first = NEW_STEP (fromset, 0, 0, NULL, NULL);
00474       lastp = &first->next;
00475     }
00476 
00477   for (current = first; current != NULL; current = current->next)
00478     {
00479       /* Now match all the available module specifications against the
00480          current charset name.  If any of them matches check whether
00481          we already have a derivation for this charset.  If yes, use the
00482          one with the lower costs.  Otherwise add the new charset at the
00483          end.
00484 
00485         The module database is organized in a tree form which allows
00486         searching for prefixes.  So we search for the first entry with a
00487         matching prefix and any other matching entry can be found from
00488         this place.  */
00489       struct gconv_module *node;
00490 
00491       /* Maybe it is not necessary anymore to look for a solution for
00492         this entry since the cost is already as high (or higher) as
00493         the cost for the best solution so far.  */
00494       if (current->cost_hi > best_cost_hi
00495          || (current->cost_hi == best_cost_hi
00496              && current->cost_lo >= best_cost_lo))
00497        continue;
00498 
00499       node = __gconv_modules_db;
00500       while (node != NULL)
00501        {
00502          int cmpres = strcmp (current->result_set, node->from_string);
00503          if (cmpres == 0)
00504            {
00505              /* Walk through the list of modules with this prefix and
00506                try to match the name.  */
00507              struct gconv_module *runp;
00508 
00509              /* Check all the modules with this prefix.  */
00510              runp = node;
00511              do
00512               {
00513                 const char *result_set = (strcmp (runp->to_string, "-") == 0
00514                                        ? (toset_expand ?: toset)
00515                                        : runp->to_string);
00516                 int cost_hi = runp->cost_hi + current->cost_hi;
00517                 int cost_lo = runp->cost_lo + current->cost_lo;
00518                 struct derivation_step *step;
00519 
00520                 /* We managed to find a derivation.  First see whether
00521                    we have reached one of the goal nodes.  */
00522                 if (strcmp (result_set, toset) == 0
00523                     || (toset_expand != NULL
00524                        && strcmp (result_set, toset_expand) == 0))
00525                   {
00526                     /* Append to the `solution' list if there
00527                       is no entry with this name.  */
00528                     for (step = solution; step != NULL; step = step->next)
00529                      if (strcmp (result_set, step->result_set) == 0)
00530                        break;
00531 
00532                     if (step == NULL)
00533                      {
00534                        step = NEW_STEP (result_set,
00535                                       cost_hi, cost_lo,
00536                                       runp, current);
00537                        step->next = solution;
00538                        solution = step;
00539                      }
00540                     else if (step->cost_hi > cost_hi
00541                             || (step->cost_hi == cost_hi
00542                                && step->cost_lo > cost_lo))
00543                      {
00544                        /* A better path was found for the node,
00545                           on the `solution' list.  */
00546                        step->code = runp;
00547                        step->last = current;
00548                        step->cost_hi = cost_hi;
00549                        step->cost_lo = cost_lo;
00550                      }
00551 
00552                     /* Update best_cost accordingly.  */
00553                     if (cost_hi < best_cost_hi
00554                        || (cost_hi == best_cost_hi
00555                            && cost_lo < best_cost_lo))
00556                      {
00557                        best_cost_hi = cost_hi;
00558                        best_cost_lo = cost_lo;
00559                      }
00560                   }
00561                 else if (cost_hi < best_cost_hi
00562                         || (cost_hi == best_cost_hi
00563                             && cost_lo < best_cost_lo))
00564                   {
00565                     /* Append at the end of the `first' list if there
00566                       is no entry with this name.  */
00567                     for (step = first; step != NULL; step = step->next)
00568                      if (strcmp (result_set, step->result_set) == 0)
00569                        break;
00570 
00571                     if (step == NULL)
00572                      {
00573                        *lastp = NEW_STEP (result_set,
00574                                         cost_hi, cost_lo,
00575                                         runp, current);
00576                        lastp = &(*lastp)->next;
00577                      }
00578                     else if (step->cost_hi > cost_hi
00579                             || (step->cost_hi == cost_hi
00580                                && step->cost_lo > cost_lo))
00581                      {
00582                        /* A better path was found for the node,
00583                           on the `first' list.  */
00584                        step->code = runp;
00585                        step->last = current;
00586 
00587                        /* Update the cost for all steps.  */
00588                        for (step = first; step != NULL;
00589                             step = step->next)
00590                          /* But don't update the start nodes.  */
00591                          if (step->code != NULL)
00592                            {
00593                             struct derivation_step *back;
00594                             int hi, lo;
00595 
00596                             hi = step->code->cost_hi;
00597                             lo = step->code->cost_lo;
00598 
00599                             for (back = step->last; back->code != NULL;
00600                                  back = back->last)
00601                               {
00602                                 hi += back->code->cost_hi;
00603                                 lo += back->code->cost_lo;
00604                               }
00605 
00606                             step->cost_hi = hi;
00607                             step->cost_lo = lo;
00608                            }
00609 
00610                        /* Likewise for the nodes on the solution list.
00611                           Also update best_cost accordingly.  */
00612                        for (step = solution; step != NULL;
00613                             step = step->next)
00614                          {
00615                            step->cost_hi = (step->code->cost_hi
00616                                           + step->last->cost_hi);
00617                            step->cost_lo = (step->code->cost_lo
00618                                           + step->last->cost_lo);
00619 
00620                            if (step->cost_hi < best_cost_hi
00621                               || (step->cost_hi == best_cost_hi
00622                                   && step->cost_lo < best_cost_lo))
00623                             {
00624                               best_cost_hi = step->cost_hi;
00625                               best_cost_lo = step->cost_lo;
00626                             }
00627                          }
00628                      }
00629                   }
00630 
00631                 runp = runp->same;
00632               }
00633              while (runp != NULL);
00634 
00635              break;
00636            }
00637          else if (cmpres < 0)
00638            node = node->left;
00639          else
00640            node = node->right;
00641        }
00642     }
00643 
00644   if (solution != NULL)
00645     {
00646       /* We really found a way to do the transformation.  */
00647 
00648       /* Choose the best solution.  This is easy because we know that
00649         the solution list has at most length 2 (one for every possible
00650         goal node).  */
00651       if (solution->next != NULL)
00652        {
00653          struct derivation_step *solution2 = solution->next;
00654 
00655          if (solution2->cost_hi < solution->cost_hi
00656              || (solution2->cost_hi == solution->cost_hi
00657                 && solution2->cost_lo < solution->cost_lo))
00658            solution = solution2;
00659        }
00660 
00661       /* Now build a data structure describing the transformation steps.  */
00662       result = gen_steps (solution, toset_expand ?: toset,
00663                        fromset_expand ?: fromset, handle, nsteps);
00664     }
00665   else
00666     {
00667       /* We haven't found a transformation.  Clear the result values.  */
00668       *handle = NULL;
00669       *nsteps = 0;
00670     }
00671 
00672   /* Add result in any case to list of known derivations.  */
00673   add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
00674                 *handle, *nsteps);
00675 
00676   return result;
00677 }
00678 
00679 
00680 /* Control of initialization.  */
00681 __libc_once_define (static, once);
00682 
00683 
00684 static const char *
00685 do_lookup_alias (const char *name)
00686 {
00687   struct gconv_alias key;
00688   struct gconv_alias **found;
00689 
00690   key.fromname = (char *) name;
00691   found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
00692   return found != NULL ? (*found)->toname : NULL;
00693 }
00694 
00695 
00696 int
00697 internal_function
00698 __gconv_compare_alias (const char *name1, const char *name2)
00699 {
00700   int result;
00701 
00702   /* Ensure that the configuration data is read.  */
00703   __libc_once (once, __gconv_read_conf);
00704 
00705   if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
00706     result = strcmp (do_lookup_alias (name1) ?: name1,
00707                    do_lookup_alias (name2) ?: name2);
00708 
00709   return result;
00710 }
00711 
00712 
00713 int
00714 internal_function
00715 __gconv_find_transform (const char *toset, const char *fromset,
00716                      struct __gconv_step **handle, size_t *nsteps,
00717                      int flags)
00718 {
00719   const char *fromset_expand;
00720   const char *toset_expand;
00721   int result;
00722 
00723   /* Ensure that the configuration data is read.  */
00724   __libc_once (once, __gconv_read_conf);
00725 
00726   /* Acquire the lock.  */
00727   __libc_lock_lock (__gconv_lock);
00728 
00729   result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
00730   if (result != __GCONV_NODB)
00731     {
00732       /* We have a cache and could resolve the request, successful or not.  */
00733       __libc_lock_unlock (__gconv_lock);
00734       return result;
00735     }
00736 
00737   /* If we don't have a module database return with an error.  */
00738   if (__gconv_modules_db == NULL)
00739     {
00740       __libc_lock_unlock (__gconv_lock);
00741       return __GCONV_NOCONV;
00742     }
00743 
00744   /* See whether the names are aliases.  */
00745   fromset_expand = do_lookup_alias (fromset);
00746   toset_expand = do_lookup_alias (toset);
00747 
00748   if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
00749       /* We are not supposed to create a pseudo transformation (means
00750         copying) when the input and output character set are the same.  */
00751       && (strcmp (toset, fromset) == 0
00752          || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
00753          || (fromset_expand != NULL
00754              && (strcmp (toset, fromset_expand) == 0
00755                 || (toset_expand != NULL
00756                     && strcmp (toset_expand, fromset_expand) == 0)))))
00757     {
00758       /* Both character sets are the same.  */
00759       __libc_lock_unlock (__gconv_lock);
00760       return __GCONV_NULCONV;
00761     }
00762 
00763   result = find_derivation (toset, toset_expand, fromset, fromset_expand,
00764                          handle, nsteps);
00765 
00766   /* Release the lock.  */
00767   __libc_lock_unlock (__gconv_lock);
00768 
00769   /* The following code is necessary since `find_derivation' will return
00770      GCONV_OK even when no derivation was found but the same request
00771      was processed before.  I.e., negative results will also be cached.  */
00772   return (result == __GCONV_OK
00773          ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
00774          : result);
00775 }
00776 
00777 
00778 /* Release the entries of the modules list.  */
00779 int
00780 internal_function
00781 __gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
00782 {
00783   int result = __GCONV_OK;
00784   size_t cnt;
00785 
00786   /* Acquire the lock.  */
00787   __libc_lock_lock (__gconv_lock);
00788 
00789 #ifndef STATIC_GCONV
00790   cnt = nsteps;
00791   while (cnt-- > 0)
00792     __gconv_release_step (&steps[cnt]);
00793 #endif
00794 
00795   /* If we use the cache we free a bit more since we don't keep any
00796      transformation records around, they are cheap enough to
00797      recreate.  */
00798   __gconv_release_cache (steps, nsteps);
00799 
00800   /* Release the lock.  */
00801   __libc_lock_unlock (__gconv_lock);
00802 
00803   return result;
00804 }
00805 
00806 
00807 /* Free the modules mentioned.  */
00808 static void
00809 internal_function __libc_freeres_fn_section
00810 free_modules_db (struct gconv_module *node)
00811 {
00812   if (node->left != NULL)
00813     free_modules_db (node->left);
00814   if (node->right != NULL)
00815     free_modules_db (node->right);
00816   do
00817     {
00818       struct gconv_module *act = node;
00819       node = node->same;
00820       if (act->module_name[0] == '/')
00821        free (act);
00822     }
00823   while (node != NULL);
00824 }
00825 
00826 
00827 /* Free all resources if necessary.  */
00828 libc_freeres_fn (free_mem)
00829 {
00830   /* First free locale memory.  This needs to be done before freeing derivations,
00831      as ctype cleanup functions dereference steps arrays which we free below.  */
00832   _nl_locale_subfreeres ();
00833 
00834   /* finddomain.c has similar problem.  */
00835   extern void _nl_finddomain_subfreeres (void) attribute_hidden;
00836   _nl_finddomain_subfreeres ();
00837 
00838   if (__gconv_alias_db != NULL)
00839     __tdestroy (__gconv_alias_db, free);
00840 
00841   if (__gconv_modules_db != NULL)
00842     free_modules_db (__gconv_modules_db);
00843 
00844   if (known_derivations != NULL)
00845     __tdestroy (known_derivations, free_derivation);
00846 }