Blame iconv/gconv_db.c

Packit 6c4009
/* Provide access to the collection of available transformation modules.
Packit 6c4009
   Copyright (C) 1997-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <search.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <locale/localeinfo.h>
Packit 6c4009
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <gconv_int.h>
Packit 6c4009
#include <sysdep.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Simple data structure for alias mapping.  We have two names, `from'
Packit 6c4009
   and `to'.  */
Packit 6c4009
void *__gconv_alias_db;
Packit 6c4009
Packit 6c4009
/* Array with available modules.  */
Packit 6c4009
struct gconv_module *__gconv_modules_db;
Packit 6c4009
Packit 6c4009
/* We modify global data.   */
Packit 6c4009
__libc_lock_define_initialized (, __gconv_lock)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Provide access to module database.  */
Packit 6c4009
struct gconv_module *
Packit 6c4009
__gconv_get_modules_db (void)
Packit 6c4009
{
Packit 6c4009
  return __gconv_modules_db;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void *
Packit 6c4009
__gconv_get_alias_db (void)
Packit 6c4009
{
Packit 6c4009
  return __gconv_alias_db;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Function for searching alias.  */
Packit 6c4009
int
Packit 6c4009
__gconv_alias_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct gconv_alias *s1 = (const struct gconv_alias *) p1;
Packit 6c4009
  const struct gconv_alias *s2 = (const struct gconv_alias *) p2;
Packit 6c4009
  return strcmp (s1->fromname, s2->fromname);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* To search for a derivation we create a list of intermediate steps.
Packit 6c4009
   Each element contains a pointer to the element which precedes it
Packit 6c4009
   in the derivation order.  */
Packit 6c4009
struct derivation_step
Packit 6c4009
{
Packit 6c4009
  const char *result_set;
Packit 6c4009
  size_t result_set_len;
Packit 6c4009
  int cost_lo;
Packit 6c4009
  int cost_hi;
Packit 6c4009
  struct gconv_module *code;
Packit 6c4009
  struct derivation_step *last;
Packit 6c4009
  struct derivation_step *next;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
#define NEW_STEP(result, hi, lo, module, last_mod) \
Packit 6c4009
  ({ struct derivation_step *newp = alloca (sizeof (struct derivation_step)); \
Packit 6c4009
     newp->result_set = result;						      \
Packit 6c4009
     newp->result_set_len = strlen (result);				      \
Packit 6c4009
     newp->cost_hi = hi;						      \
Packit 6c4009
     newp->cost_lo = lo;						      \
Packit 6c4009
     newp->code = module;						      \
Packit 6c4009
     newp->last = last_mod;						      \
Packit 6c4009
     newp->next = NULL;							      \
Packit 6c4009
     newp; })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* If a specific transformation is used more than once we should not need
Packit 6c4009
   to start looking for it again.  Instead cache each successful result.  */
Packit 6c4009
struct known_derivation
Packit 6c4009
{
Packit 6c4009
  const char *from;
Packit 6c4009
  const char *to;
Packit 6c4009
  struct __gconv_step *steps;
Packit 6c4009
  size_t nsteps;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
/* Compare function for database of found derivations.  */
Packit 6c4009
static int
Packit 6c4009
derivation_compare (const void *p1, const void *p2)
Packit 6c4009
{
Packit 6c4009
  const struct known_derivation *s1 = (const struct known_derivation *) p1;
Packit 6c4009
  const struct known_derivation *s2 = (const struct known_derivation *) p2;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  result = strcmp (s1->from, s2->from);
Packit 6c4009
  if (result == 0)
Packit 6c4009
    result = strcmp (s1->to, s2->to);
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* The search tree for known derivations.  */
Packit 6c4009
static void *known_derivations;
Packit 6c4009
Packit 6c4009
/* Look up whether given transformation was already requested before.  */
Packit 6c4009
static int
Packit 6c4009
derivation_lookup (const char *fromset, const char *toset,
Packit 6c4009
		   struct __gconv_step **handle, size_t *nsteps)
Packit 6c4009
{
Packit 6c4009
  struct known_derivation key = { fromset, toset, NULL, 0 };
Packit 6c4009
  struct known_derivation **result;
Packit 6c4009
Packit 6c4009
  result = __tfind (&key, &known_derivations, derivation_compare);
Packit 6c4009
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    return __GCONV_NOCONV;
Packit 6c4009
Packit 6c4009
  *handle = (*result)->steps;
Packit 6c4009
  *nsteps = (*result)->nsteps;
Packit 6c4009
Packit 6c4009
  /* Please note that we return GCONV_OK even if the last search for
Packit 6c4009
     this transformation was unsuccessful.  */
Packit 6c4009
  return __GCONV_OK;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Add new derivation to list of known ones.  */
Packit 6c4009
static void
Packit 6c4009
add_derivation (const char *fromset, const char *toset,
Packit 6c4009
		struct __gconv_step *handle, size_t nsteps)
Packit 6c4009
{
Packit 6c4009
  struct known_derivation *new_deriv;
Packit 6c4009
  size_t fromset_len = strlen (fromset) + 1;
Packit 6c4009
  size_t toset_len = strlen (toset) + 1;
Packit 6c4009
Packit 6c4009
  new_deriv = (struct known_derivation *)
Packit 6c4009
    malloc (sizeof (struct known_derivation) + fromset_len + toset_len);
Packit 6c4009
  if (new_deriv != NULL)
Packit 6c4009
    {
Packit 6c4009
      new_deriv->from = (char *) (new_deriv + 1);
Packit 6c4009
      new_deriv->to = memcpy (__mempcpy (new_deriv + 1, fromset, fromset_len),
Packit 6c4009
			      toset, toset_len);
Packit 6c4009
Packit 6c4009
      new_deriv->steps = handle;
Packit 6c4009
      new_deriv->nsteps = nsteps;
Packit 6c4009
Packit 6c4009
      if (__tsearch (new_deriv, &known_derivations, derivation_compare)
Packit 6c4009
	  == NULL)
Packit 6c4009
	/* There is some kind of memory allocation problem.  */
Packit 6c4009
	free (new_deriv);
Packit 6c4009
    }
Packit 6c4009
  /* Please note that we don't complain if the allocation failed.  This
Packit 6c4009
     is not tragically but in case we use the memory debugging facilities
Packit 6c4009
     not all memory will be freed.  */
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void __libc_freeres_fn_section
Packit 6c4009
free_derivation (void *p)
Packit 6c4009
{
Packit 6c4009
  struct known_derivation *deriv = (struct known_derivation *) p;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  for (cnt = 0; cnt < deriv->nsteps; ++cnt)
Packit 6c4009
    if (deriv->steps[cnt].__counter > 0
Packit 6c4009
	&& deriv->steps[cnt].__shlib_handle != NULL)
Packit 6c4009
      {
Packit 6c4009
	__gconv_end_fct end_fct = deriv->steps[cnt].__end_fct;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
	PTR_DEMANGLE (end_fct);
Packit 6c4009
#endif
Packit 6c4009
	if (end_fct != NULL)
Packit 6c4009
	  DL_CALL_FCT (end_fct, (&deriv->steps[cnt]));
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  /* Free the name strings.  */
Packit 6c4009
  if (deriv->steps != NULL)
Packit 6c4009
    {
Packit 6c4009
      free ((char *) deriv->steps[0].__from_name);
Packit 6c4009
      free ((char *) deriv->steps[deriv->nsteps - 1].__to_name);
Packit 6c4009
      free ((struct __gconv_step *) deriv->steps);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  free (deriv);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Decrement the reference count for a single step in a steps array.  */
Packit 6c4009
void
Packit 6c4009
__gconv_release_step (struct __gconv_step *step)
Packit 6c4009
{
Packit 6c4009
  /* Skip builtin modules; they are not reference counted.  */
Packit 6c4009
  if (step->__shlib_handle != NULL && --step->__counter == 0)
Packit 6c4009
    {
Packit 6c4009
      /* Call the destructor.  */
Packit 6c4009
	__gconv_end_fct end_fct = step->__end_fct;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
	PTR_DEMANGLE (end_fct);
Packit 6c4009
#endif
Packit 6c4009
      if (end_fct != NULL)
Packit 6c4009
	DL_CALL_FCT (end_fct, (step));
Packit 6c4009
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
      /* Release the loaded module.  */
Packit 6c4009
      __gconv_release_shlib (step->__shlib_handle);
Packit 6c4009
      step->__shlib_handle = NULL;
Packit 6c4009
#endif
Packit 6c4009
    }
Packit 6c4009
  else if (step->__shlib_handle == NULL)
Packit 6c4009
    /* Builtin modules should not have end functions.  */
Packit 6c4009
    assert (step->__end_fct == NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
gen_steps (struct derivation_step *best, const char *toset,
Packit 6c4009
	   const char *fromset, struct __gconv_step **handle, size_t *nsteps)
Packit 6c4009
{
Packit 6c4009
  size_t step_cnt = 0;
Packit 6c4009
  struct __gconv_step *result;
Packit 6c4009
  struct derivation_step *current;
Packit 6c4009
  int status = __GCONV_NOMEM;
Packit 6c4009
  char *from_name = NULL;
Packit 6c4009
  char *to_name = NULL;
Packit 6c4009
Packit 6c4009
  /* First determine number of steps.  */
Packit 6c4009
  for (current = best; current->last != NULL; current = current->last)
Packit 6c4009
    ++step_cnt;
Packit 6c4009
Packit 6c4009
  result = (struct __gconv_step *) malloc (sizeof (struct __gconv_step)
Packit 6c4009
					   * step_cnt);
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    {
Packit 6c4009
      int failed = 0;
Packit 6c4009
Packit 6c4009
      status = __GCONV_OK;
Packit 6c4009
      *nsteps = step_cnt;
Packit 6c4009
      current = best;
Packit 6c4009
      while (step_cnt-- > 0)
Packit 6c4009
	{
Packit 6c4009
	  if (step_cnt == 0)
Packit 6c4009
	    {
Packit 6c4009
	      result[step_cnt].__from_name = from_name = __strdup (fromset);
Packit 6c4009
	      if (from_name == NULL)
Packit 6c4009
		{
Packit 6c4009
		  failed = 1;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    result[step_cnt].__from_name = (char *)current->last->result_set;
Packit 6c4009
Packit 6c4009
	  if (step_cnt + 1 == *nsteps)
Packit 6c4009
	    {
Packit 6c4009
	      result[step_cnt].__to_name = to_name
Packit 6c4009
		= __strdup (current->result_set);
Packit 6c4009
	      if (to_name == NULL)
Packit 6c4009
		{
Packit 6c4009
		  failed = 1;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    result[step_cnt].__to_name = result[step_cnt + 1].__from_name;
Packit 6c4009
Packit 6c4009
	  result[step_cnt].__counter = 1;
Packit 6c4009
	  result[step_cnt].__data = NULL;
Packit 6c4009
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
	  if (current->code->module_name[0] == '/')
Packit 6c4009
	    {
Packit 6c4009
	      /* Load the module, return handle for it.  */
Packit 6c4009
	      struct __gconv_loaded_object *shlib_handle =
Packit 6c4009
		__gconv_find_shlib (current->code->module_name);
Packit 6c4009
Packit 6c4009
	      if (shlib_handle == NULL)
Packit 6c4009
		{
Packit 6c4009
		  failed = 1;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      result[step_cnt].__shlib_handle = shlib_handle;
Packit 6c4009
	      result[step_cnt].__modname = shlib_handle->name;
Packit 6c4009
	      result[step_cnt].__fct = shlib_handle->fct;
Packit 6c4009
	      result[step_cnt].__init_fct = shlib_handle->init_fct;
Packit 6c4009
	      result[step_cnt].__end_fct = shlib_handle->end_fct;
Packit 6c4009
Packit 6c4009
	      /* These settings can be overridden by the init function.  */
Packit 6c4009
	      result[step_cnt].__btowc_fct = NULL;
Packit 6c4009
Packit 6c4009
	      /* Call the init function.  */
Packit 6c4009
	      __gconv_init_fct init_fct = result[step_cnt].__init_fct;
Packit 6c4009
# ifdef PTR_DEMANGLE
Packit 6c4009
	      PTR_DEMANGLE (init_fct);
Packit 6c4009
# endif
Packit 6c4009
	      if (init_fct != NULL)
Packit 6c4009
		{
Packit 6c4009
		  status = DL_CALL_FCT (init_fct, (&result[step_cnt]));
Packit 6c4009
Packit 6c4009
		  if (__builtin_expect (status, __GCONV_OK) != __GCONV_OK)
Packit 6c4009
		    {
Packit 6c4009
		      failed = 1;
Packit 6c4009
		      /* Do not call the end function because the init
Packit 6c4009
			 function has failed.  */
Packit 6c4009
		      result[step_cnt].__end_fct = NULL;
Packit 6c4009
# ifdef PTR_MANGLE
Packit 6c4009
		      PTR_MANGLE (result[step_cnt].__end_fct);
Packit 6c4009
# endif
Packit 6c4009
		      /* Make sure we unload this module.  */
Packit 6c4009
		      --step_cnt;
Packit 6c4009
		      break;
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
# ifdef PTR_MANGLE
Packit 6c4009
	      PTR_MANGLE (result[step_cnt].__btowc_fct);
Packit 6c4009
# endif
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
#endif
Packit 6c4009
	    /* It's a builtin transformation.  */
Packit 6c4009
	    __gconv_get_builtin_trans (current->code->module_name,
Packit 6c4009
				       &result[step_cnt]);
Packit 6c4009
Packit 6c4009
	  current = current->last;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (__builtin_expect (failed, 0) != 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Something went wrong while initializing the modules.  */
Packit 6c4009
	  while (++step_cnt < *nsteps)
Packit 6c4009
	    __gconv_release_step (&result[step_cnt]);
Packit 6c4009
	  free (result);
Packit 6c4009
	  free (from_name);
Packit 6c4009
	  free (to_name);
Packit 6c4009
	  *nsteps = 0;
Packit 6c4009
	  *handle = NULL;
Packit 6c4009
	  if (status == __GCONV_OK)
Packit 6c4009
	    status = __GCONV_NOCONV;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	*handle = result;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      *nsteps = 0;
Packit 6c4009
      *handle = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
static int
Packit 6c4009
increment_counter (struct __gconv_step *steps, size_t nsteps)
Packit 6c4009
{
Packit 6c4009
  /* Increment the user counter.  */
Packit 6c4009
  size_t cnt = nsteps;
Packit 6c4009
  int result = __GCONV_OK;
Packit 6c4009
Packit 6c4009
  while (cnt-- > 0)
Packit 6c4009
    {
Packit 6c4009
      struct __gconv_step *step = &steps[cnt];
Packit 6c4009
Packit 6c4009
      if (step->__counter++ == 0)
Packit 6c4009
	{
Packit 6c4009
	  /* Skip builtin modules.  */
Packit 6c4009
	  if (step->__modname != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Reopen a previously used module.  */
Packit 6c4009
	      step->__shlib_handle = __gconv_find_shlib (step->__modname);
Packit 6c4009
	      if (step->__shlib_handle == NULL)
Packit 6c4009
		{
Packit 6c4009
		  /* Oops, this is the second time we use this module
Packit 6c4009
		     (after unloading) and this time loading failed!?  */
Packit 6c4009
		  --step->__counter;
Packit 6c4009
		  while (++cnt < nsteps)
Packit 6c4009
		    __gconv_release_step (&steps[cnt]);
Packit 6c4009
		  result = __GCONV_NOCONV;
Packit 6c4009
		  break;
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      /* The function addresses defined by the module may
Packit 6c4009
		 have changed.  */
Packit 6c4009
	      step->__fct = step->__shlib_handle->fct;
Packit 6c4009
	      step->__init_fct = step->__shlib_handle->init_fct;
Packit 6c4009
	      step->__end_fct = step->__shlib_handle->end_fct;
Packit 6c4009
Packit 6c4009
	      /* These settings can be overridden by the init function.  */
Packit 6c4009
	      step->__btowc_fct = NULL;
Packit 6c4009
Packit 6c4009
	      /* Call the init function.  */
Packit 6c4009
	      __gconv_init_fct init_fct = step->__init_fct;
Packit 6c4009
#ifdef PTR_DEMANGLE
Packit 6c4009
	      PTR_DEMANGLE (init_fct);
Packit 6c4009
#endif
Packit 6c4009
	      if (init_fct != NULL)
Packit 6c4009
		DL_CALL_FCT (init_fct, (step));
Packit 6c4009
Packit 6c4009
#ifdef PTR_MANGLE
Packit 6c4009
	      PTR_MANGLE (step->__btowc_fct);
Packit 6c4009
#endif
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* The main function: find a possible derivation from the `fromset' (either
Packit 6c4009
   the given name or the alias) to the `toset' (again with alias).  */
Packit 6c4009
static int
Packit 6c4009
find_derivation (const char *toset, const char *toset_expand,
Packit 6c4009
		 const char *fromset, const char *fromset_expand,
Packit 6c4009
		 struct __gconv_step **handle, size_t *nsteps)
Packit 6c4009
{
Packit 6c4009
  struct derivation_step *first, *current, **lastp, *solution = NULL;
Packit 6c4009
  int best_cost_hi = INT_MAX;
Packit 6c4009
  int best_cost_lo = INT_MAX;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  /* Look whether an earlier call to `find_derivation' has already
Packit 6c4009
     computed a possible derivation.  If so, return it immediately.  */
Packit 6c4009
  result = derivation_lookup (fromset_expand ?: fromset, toset_expand ?: toset,
Packit 6c4009
			      handle, nsteps);
Packit 6c4009
  if (result == __GCONV_OK)
Packit 6c4009
    {
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
      result = increment_counter (*handle, *nsteps);
Packit 6c4009
#endif
Packit 6c4009
      return result;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* The task is to find a sequence of transformations, backed by the
Packit 6c4009
     existing modules - whether builtin or dynamically loadable -,
Packit 6c4009
     starting at `fromset' (or `fromset_expand') and ending at `toset'
Packit 6c4009
     (or `toset_expand'), and with minimal cost.
Packit 6c4009
Packit 6c4009
     For computer scientists, this is a shortest path search in the
Packit 6c4009
     graph where the nodes are all possible charsets and the edges are
Packit 6c4009
     the transformations listed in __gconv_modules_db.
Packit 6c4009
Packit 6c4009
     For now we use a simple algorithm with quadratic runtime behaviour.
Packit 6c4009
     A breadth-first search, starting at `fromset' and `fromset_expand'.
Packit 6c4009
     The list starting at `first' contains all nodes that have been
Packit 6c4009
     visited up to now, in the order in which they have been visited --
Packit 6c4009
     excluding the goal nodes `toset' and `toset_expand' which get
Packit 6c4009
     managed in the list starting at `solution'.
Packit 6c4009
     `current' walks through the list starting at `first' and looks
Packit 6c4009
     which nodes are reachable from the current node, adding them to
Packit 6c4009
     the end of the list [`first' or `solution' respectively] (if
Packit 6c4009
     they are visited the first time) or updating them in place (if
Packit 6c4009
     they have have already been visited).
Packit 6c4009
     In each node of either list, cost_lo and cost_hi contain the
Packit 6c4009
     minimum cost over any paths found up to now, starting at `fromset'
Packit 6c4009
     or `fromset_expand', ending at that node.  best_cost_lo and
Packit 6c4009
     best_cost_hi represent the minimum over the elements of the
Packit 6c4009
     `solution' list.  */
Packit 6c4009
Packit 6c4009
  if (fromset_expand != NULL)
Packit 6c4009
    {
Packit 6c4009
      first = NEW_STEP (fromset_expand, 0, 0, NULL, NULL);
Packit 6c4009
      first->next = NEW_STEP (fromset, 0, 0, NULL, NULL);
Packit 6c4009
      lastp = &first->next->next;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      first = NEW_STEP (fromset, 0, 0, NULL, NULL);
Packit 6c4009
      lastp = &first->next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  for (current = first; current != NULL; current = current->next)
Packit 6c4009
    {
Packit 6c4009
      /* Now match all the available module specifications against the
Packit 6c4009
         current charset name.  If any of them matches check whether
Packit 6c4009
         we already have a derivation for this charset.  If yes, use the
Packit 6c4009
         one with the lower costs.  Otherwise add the new charset at the
Packit 6c4009
         end.
Packit 6c4009
Packit 6c4009
	 The module database is organized in a tree form which allows
Packit 6c4009
	 searching for prefixes.  So we search for the first entry with a
Packit 6c4009
	 matching prefix and any other matching entry can be found from
Packit 6c4009
	 this place.  */
Packit 6c4009
      struct gconv_module *node;
Packit 6c4009
Packit 6c4009
      /* Maybe it is not necessary anymore to look for a solution for
Packit 6c4009
	 this entry since the cost is already as high (or higher) as
Packit 6c4009
	 the cost for the best solution so far.  */
Packit 6c4009
      if (current->cost_hi > best_cost_hi
Packit 6c4009
	  || (current->cost_hi == best_cost_hi
Packit 6c4009
	      && current->cost_lo >= best_cost_lo))
Packit 6c4009
	continue;
Packit 6c4009
Packit 6c4009
      node = __gconv_modules_db;
Packit 6c4009
      while (node != NULL)
Packit 6c4009
	{
Packit 6c4009
	  int cmpres = strcmp (current->result_set, node->from_string);
Packit 6c4009
	  if (cmpres == 0)
Packit 6c4009
	    {
Packit 6c4009
	      /* Walk through the list of modules with this prefix and
Packit 6c4009
		 try to match the name.  */
Packit 6c4009
	      struct gconv_module *runp;
Packit 6c4009
Packit 6c4009
	      /* Check all the modules with this prefix.  */
Packit 6c4009
	      runp = node;
Packit 6c4009
	      do
Packit 6c4009
		{
Packit 6c4009
		  const char *result_set = (strcmp (runp->to_string, "-") == 0
Packit 6c4009
					    ? (toset_expand ?: toset)
Packit 6c4009
					    : runp->to_string);
Packit 6c4009
		  int cost_hi = runp->cost_hi + current->cost_hi;
Packit 6c4009
		  int cost_lo = runp->cost_lo + current->cost_lo;
Packit 6c4009
		  struct derivation_step *step;
Packit 6c4009
Packit 6c4009
		  /* We managed to find a derivation.  First see whether
Packit 6c4009
		     we have reached one of the goal nodes.  */
Packit 6c4009
		  if (strcmp (result_set, toset) == 0
Packit 6c4009
		      || (toset_expand != NULL
Packit 6c4009
			  && strcmp (result_set, toset_expand) == 0))
Packit 6c4009
		    {
Packit 6c4009
		      /* Append to the `solution' list if there
Packit 6c4009
			 is no entry with this name.  */
Packit 6c4009
		      for (step = solution; step != NULL; step = step->next)
Packit 6c4009
			if (strcmp (result_set, step->result_set) == 0)
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
		      if (step == NULL)
Packit 6c4009
			{
Packit 6c4009
			  step = NEW_STEP (result_set,
Packit 6c4009
					   cost_hi, cost_lo,
Packit 6c4009
					   runp, current);
Packit 6c4009
			  step->next = solution;
Packit 6c4009
			  solution = step;
Packit 6c4009
			}
Packit 6c4009
		      else if (step->cost_hi > cost_hi
Packit 6c4009
			       || (step->cost_hi == cost_hi
Packit 6c4009
				   && step->cost_lo > cost_lo))
Packit 6c4009
			{
Packit 6c4009
			  /* A better path was found for the node,
Packit 6c4009
			     on the `solution' list.  */
Packit 6c4009
			  step->code = runp;
Packit 6c4009
			  step->last = current;
Packit 6c4009
			  step->cost_hi = cost_hi;
Packit 6c4009
			  step->cost_lo = cost_lo;
Packit 6c4009
			}
Packit 6c4009
Packit 6c4009
		      /* Update best_cost accordingly.  */
Packit 6c4009
		      if (cost_hi < best_cost_hi
Packit 6c4009
			  || (cost_hi == best_cost_hi
Packit 6c4009
			      && cost_lo < best_cost_lo))
Packit 6c4009
			{
Packit 6c4009
			  best_cost_hi = cost_hi;
Packit 6c4009
			  best_cost_lo = cost_lo;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
		  else if (cost_hi < best_cost_hi
Packit 6c4009
			   || (cost_hi == best_cost_hi
Packit 6c4009
			       && cost_lo < best_cost_lo))
Packit 6c4009
		    {
Packit 6c4009
		      /* Append at the end of the `first' list if there
Packit 6c4009
			 is no entry with this name.  */
Packit 6c4009
		      for (step = first; step != NULL; step = step->next)
Packit 6c4009
			if (strcmp (result_set, step->result_set) == 0)
Packit 6c4009
			  break;
Packit 6c4009
Packit 6c4009
		      if (step == NULL)
Packit 6c4009
			{
Packit 6c4009
			  *lastp = NEW_STEP (result_set,
Packit 6c4009
					     cost_hi, cost_lo,
Packit 6c4009
					     runp, current);
Packit 6c4009
			  lastp = &(*lastp)->next;
Packit 6c4009
			}
Packit 6c4009
		      else if (step->cost_hi > cost_hi
Packit 6c4009
			       || (step->cost_hi == cost_hi
Packit 6c4009
				   && step->cost_lo > cost_lo))
Packit 6c4009
			{
Packit 6c4009
			  /* A better path was found for the node,
Packit 6c4009
			     on the `first' list.  */
Packit 6c4009
			  step->code = runp;
Packit 6c4009
			  step->last = current;
Packit 6c4009
Packit 6c4009
			  /* Update the cost for all steps.  */
Packit 6c4009
			  for (step = first; step != NULL;
Packit 6c4009
			       step = step->next)
Packit 6c4009
			    /* But don't update the start nodes.  */
Packit 6c4009
			    if (step->code != NULL)
Packit 6c4009
			      {
Packit 6c4009
				struct derivation_step *back;
Packit 6c4009
				int hi, lo;
Packit 6c4009
Packit 6c4009
				hi = step->code->cost_hi;
Packit 6c4009
				lo = step->code->cost_lo;
Packit 6c4009
Packit 6c4009
				for (back = step->last; back->code != NULL;
Packit 6c4009
				     back = back->last)
Packit 6c4009
				  {
Packit 6c4009
				    hi += back->code->cost_hi;
Packit 6c4009
				    lo += back->code->cost_lo;
Packit 6c4009
				  }
Packit 6c4009
Packit 6c4009
				step->cost_hi = hi;
Packit 6c4009
				step->cost_lo = lo;
Packit 6c4009
			      }
Packit 6c4009
Packit 6c4009
			  /* Likewise for the nodes on the solution list.
Packit 6c4009
			     Also update best_cost accordingly.  */
Packit 6c4009
			  for (step = solution; step != NULL;
Packit 6c4009
			       step = step->next)
Packit 6c4009
			    {
Packit 6c4009
			      step->cost_hi = (step->code->cost_hi
Packit 6c4009
					       + step->last->cost_hi);
Packit 6c4009
			      step->cost_lo = (step->code->cost_lo
Packit 6c4009
					       + step->last->cost_lo);
Packit 6c4009
Packit 6c4009
			      if (step->cost_hi < best_cost_hi
Packit 6c4009
				  || (step->cost_hi == best_cost_hi
Packit 6c4009
				      && step->cost_lo < best_cost_lo))
Packit 6c4009
				{
Packit 6c4009
				  best_cost_hi = step->cost_hi;
Packit 6c4009
				  best_cost_lo = step->cost_lo;
Packit 6c4009
				}
Packit 6c4009
			    }
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
Packit 6c4009
		  runp = runp->same;
Packit 6c4009
		}
Packit 6c4009
	      while (runp != NULL);
Packit 6c4009
Packit 6c4009
	      break;
Packit 6c4009
	    }
Packit 6c4009
	  else if (cmpres < 0)
Packit 6c4009
	    node = node->left;
Packit 6c4009
	  else
Packit 6c4009
	    node = node->right;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (solution != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* We really found a way to do the transformation.  */
Packit 6c4009
Packit 6c4009
      /* Choose the best solution.  This is easy because we know that
Packit 6c4009
	 the solution list has at most length 2 (one for every possible
Packit 6c4009
	 goal node).  */
Packit 6c4009
      if (solution->next != NULL)
Packit 6c4009
	{
Packit 6c4009
	  struct derivation_step *solution2 = solution->next;
Packit 6c4009
Packit 6c4009
	  if (solution2->cost_hi < solution->cost_hi
Packit 6c4009
	      || (solution2->cost_hi == solution->cost_hi
Packit 6c4009
		  && solution2->cost_lo < solution->cost_lo))
Packit 6c4009
	    solution = solution2;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* Now build a data structure describing the transformation steps.  */
Packit 6c4009
      result = gen_steps (solution, toset_expand ?: toset,
Packit 6c4009
			  fromset_expand ?: fromset, handle, nsteps);
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We haven't found a transformation.  Clear the result values.  */
Packit 6c4009
      *handle = NULL;
Packit 6c4009
      *nsteps = 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Add result in any case to list of known derivations.  */
Packit 6c4009
  add_derivation (fromset_expand ?: fromset, toset_expand ?: toset,
Packit 6c4009
		  *handle, *nsteps);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Control of initialization.  */
Packit 6c4009
__libc_once_define (static, once);
Packit 6c4009
Packit 6c4009
Packit 6c4009
static const char *
Packit 6c4009
do_lookup_alias (const char *name)
Packit 6c4009
{
Packit 6c4009
  struct gconv_alias key;
Packit 6c4009
  struct gconv_alias **found;
Packit 6c4009
Packit 6c4009
  key.fromname = (char *) name;
Packit 6c4009
  found = __tfind (&key, &__gconv_alias_db, __gconv_alias_compare);
Packit 6c4009
  return found != NULL ? (*found)->toname : NULL;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__gconv_compare_alias (const char *name1, const char *name2)
Packit 6c4009
{
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  /* Ensure that the configuration data is read.  */
Packit 6c4009
  __libc_once (once, __gconv_read_conf);
Packit 6c4009
Packit 6c4009
  if (__gconv_compare_alias_cache (name1, name2, &result) != 0)
Packit 6c4009
    result = strcmp (do_lookup_alias (name1) ?: name1,
Packit 6c4009
		     do_lookup_alias (name2) ?: name2);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
__gconv_find_transform (const char *toset, const char *fromset,
Packit 6c4009
			struct __gconv_step **handle, size_t *nsteps,
Packit 6c4009
			int flags)
Packit 6c4009
{
Packit 6c4009
  const char *fromset_expand;
Packit 6c4009
  const char *toset_expand;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
  /* Ensure that the configuration data is read.  */
Packit 6c4009
  __libc_once (once, __gconv_read_conf);
Packit 6c4009
Packit 6c4009
  /* Acquire the lock.  */
Packit 6c4009
  __libc_lock_lock (__gconv_lock);
Packit 6c4009
Packit 6c4009
  result = __gconv_lookup_cache (toset, fromset, handle, nsteps, flags);
Packit 6c4009
  if (result != __GCONV_NODB)
Packit 6c4009
    {
Packit 6c4009
      /* We have a cache and could resolve the request, successful or not.  */
Packit 6c4009
      __libc_lock_unlock (__gconv_lock);
Packit 6c4009
      return result;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we don't have a module database return with an error.  */
Packit 6c4009
  if (__gconv_modules_db == NULL)
Packit 6c4009
    {
Packit 6c4009
      __libc_lock_unlock (__gconv_lock);
Packit 6c4009
      return __GCONV_NOCONV;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* See whether the names are aliases.  */
Packit 6c4009
  fromset_expand = do_lookup_alias (fromset);
Packit 6c4009
  toset_expand = do_lookup_alias (toset);
Packit 6c4009
Packit 6c4009
  if (__builtin_expect (flags & GCONV_AVOID_NOCONV, 0)
Packit 6c4009
      /* We are not supposed to create a pseudo transformation (means
Packit 6c4009
	 copying) when the input and output character set are the same.  */
Packit 6c4009
      && (strcmp (toset, fromset) == 0
Packit 6c4009
	  || (toset_expand != NULL && strcmp (toset_expand, fromset) == 0)
Packit 6c4009
	  || (fromset_expand != NULL
Packit 6c4009
	      && (strcmp (toset, fromset_expand) == 0
Packit 6c4009
		  || (toset_expand != NULL
Packit 6c4009
		      && strcmp (toset_expand, fromset_expand) == 0)))))
Packit 6c4009
    {
Packit 6c4009
      /* Both character sets are the same.  */
Packit 6c4009
      __libc_lock_unlock (__gconv_lock);
Packit 6c4009
      return __GCONV_NULCONV;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  result = find_derivation (toset, toset_expand, fromset, fromset_expand,
Packit 6c4009
			    handle, nsteps);
Packit 6c4009
Packit 6c4009
  /* Release the lock.  */
Packit 6c4009
  __libc_lock_unlock (__gconv_lock);
Packit 6c4009
Packit 6c4009
  /* The following code is necessary since `find_derivation' will return
Packit 6c4009
     GCONV_OK even when no derivation was found but the same request
Packit 6c4009
     was processed before.  I.e., negative results will also be cached.  */
Packit 6c4009
  return (result == __GCONV_OK
Packit 6c4009
	  ? (*handle == NULL ? __GCONV_NOCONV : __GCONV_OK)
Packit 6c4009
	  : result);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Release the entries of the modules list.  */
Packit 6c4009
int
Packit 6c4009
__gconv_close_transform (struct __gconv_step *steps, size_t nsteps)
Packit 6c4009
{
Packit 6c4009
  int result = __GCONV_OK;
Packit 6c4009
  size_t cnt;
Packit 6c4009
Packit 6c4009
  /* Acquire the lock.  */
Packit 6c4009
  __libc_lock_lock (__gconv_lock);
Packit 6c4009
Packit 6c4009
#ifndef STATIC_GCONV
Packit 6c4009
  cnt = nsteps;
Packit 6c4009
  while (cnt-- > 0)
Packit 6c4009
    __gconv_release_step (&steps[cnt]);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* If we use the cache we free a bit more since we don't keep any
Packit 6c4009
     transformation records around, they are cheap enough to
Packit 6c4009
     recreate.  */
Packit 6c4009
  __gconv_release_cache (steps, nsteps);
Packit 6c4009
Packit 6c4009
  /* Release the lock.  */
Packit 6c4009
  __libc_lock_unlock (__gconv_lock);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Free the modules mentioned.  */
Packit 6c4009
static void
Packit 6c4009
__libc_freeres_fn_section
Packit 6c4009
free_modules_db (struct gconv_module *node)
Packit 6c4009
{
Packit 6c4009
  if (node->left != NULL)
Packit 6c4009
    free_modules_db (node->left);
Packit 6c4009
  if (node->right != NULL)
Packit 6c4009
    free_modules_db (node->right);
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      struct gconv_module *act = node;
Packit 6c4009
      node = node->same;
Packit 6c4009
      if (act->module_name[0] == '/')
Packit 6c4009
	free (act);
Packit 6c4009
    }
Packit 6c4009
  while (node != NULL);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Free all resources if necessary.  */
Packit 6c4009
libc_freeres_fn (free_mem)
Packit 6c4009
{
Packit 6c4009
  /* First free locale memory.  This needs to be done before freeing
Packit 6c4009
     derivations, as ctype cleanup functions dereference steps arrays which we
Packit 6c4009
     free below.  */
Packit 6c4009
  _nl_locale_subfreeres ();
Packit 6c4009
Packit 6c4009
  /* finddomain.c has similar problem.  */
Packit 6c4009
  extern void _nl_finddomain_subfreeres (void) attribute_hidden;
Packit 6c4009
  _nl_finddomain_subfreeres ();
Packit 6c4009
Packit 6c4009
  if (__gconv_alias_db != NULL)
Packit 6c4009
    __tdestroy (__gconv_alias_db, free);
Packit 6c4009
Packit 6c4009
  if (__gconv_modules_db != NULL)
Packit 6c4009
    free_modules_db (__gconv_modules_db);
Packit 6c4009
Packit 6c4009
  if (known_derivations != NULL)
Packit 6c4009
    __tdestroy (known_derivations, free_derivation);
Packit 6c4009
}