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