Blame wcsmbs/wcsmbsload.c

Packit 6c4009
/* Copyright (C) 1998-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>, 1998.
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 <ctype.h>
Packit 6c4009
#include <langinfo.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include <locale/localeinfo.h>
Packit 6c4009
#include <wcsmbsload.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* These are the descriptions for the default conversion functions.  */
Packit 6c4009
static const struct __gconv_step to_wc =
Packit 6c4009
{
Packit 6c4009
  .__shlib_handle = NULL,
Packit 6c4009
  .__modname = NULL,
Packit 6c4009
  .__counter = INT_MAX,
Packit 6c4009
  .__from_name = (char *) "ANSI_X3.4-1968//TRANSLIT",
Packit 6c4009
  .__to_name = (char *) "INTERNAL",
Packit 6c4009
  .__fct = __gconv_transform_ascii_internal,
Packit 6c4009
  .__btowc_fct = __gconv_btwoc_ascii,
Packit 6c4009
  .__init_fct = NULL,
Packit 6c4009
  .__end_fct = NULL,
Packit 6c4009
  .__min_needed_from = 1,
Packit 6c4009
  .__max_needed_from = 1,
Packit 6c4009
  .__min_needed_to = 4,
Packit 6c4009
  .__max_needed_to = 4,
Packit 6c4009
  .__stateful = 0,
Packit 6c4009
  .__data = NULL
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
static const struct __gconv_step to_mb =
Packit 6c4009
{
Packit 6c4009
  .__shlib_handle = NULL,
Packit 6c4009
  .__modname = NULL,
Packit 6c4009
  .__counter = INT_MAX,
Packit 6c4009
  .__from_name = (char *) "INTERNAL",
Packit 6c4009
  .__to_name = (char *) "ANSI_X3.4-1968//TRANSLIT",
Packit 6c4009
  .__fct = __gconv_transform_internal_ascii,
Packit 6c4009
  .__btowc_fct = NULL,
Packit 6c4009
  .__init_fct = NULL,
Packit 6c4009
  .__end_fct = NULL,
Packit 6c4009
  .__min_needed_from = 4,
Packit 6c4009
  .__max_needed_from = 4,
Packit 6c4009
  .__min_needed_to = 1,
Packit 6c4009
  .__max_needed_to = 1,
Packit 6c4009
  .__stateful = 0,
Packit 6c4009
  .__data = NULL
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* For the default locale we only have to handle ANSI_X3.4-1968.  */
Packit 6c4009
const struct gconv_fcts __wcsmbs_gconv_fcts_c =
Packit 6c4009
{
Packit 6c4009
  .towc = (struct __gconv_step *) &to_wc,
Packit 6c4009
  .towc_nsteps = 1,
Packit 6c4009
  .tomb = (struct __gconv_step *) &to_mb,
Packit 6c4009
  .tomb_nsteps = 1,
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
Packit 6c4009
attribute_hidden
Packit 6c4009
struct __gconv_step *
Packit 6c4009
__wcsmbs_getfct (const char *to, const char *from, size_t *nstepsp)
Packit 6c4009
{
Packit 6c4009
  size_t nsteps;
Packit 6c4009
  struct __gconv_step *result;
Packit 6c4009
#if 0
Packit 6c4009
  size_t nstateful;
Packit 6c4009
  size_t cnt;
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  if (__gconv_find_transform (to, from, &result, &nsteps, 0) != __GCONV_OK)
Packit 6c4009
    /* Loading the conversion step is not possible.  */
Packit 6c4009
    return NULL;
Packit 6c4009
Packit 6c4009
  /* Maybe it is someday necessary to allow more than one step.
Packit 6c4009
     Currently this is not the case since the conversions handled here
Packit 6c4009
     are from and to INTERNAL and there always is a converted for
Packit 6c4009
     that.  It the directly following code is enabled the libio
Packit 6c4009
     functions will have to allocate appropriate __gconv_step_data
Packit 6c4009
     elements instead of only one.  */
Packit 6c4009
#if 0
Packit 6c4009
  /* Count the number of stateful conversions.  Since we will only
Packit 6c4009
     have one 'mbstate_t' object available we can only deal with one
Packit 6c4009
     stateful conversion.  */
Packit 6c4009
  nstateful = 0;
Packit 6c4009
  for (cnt = 0; cnt < nsteps; ++cnt)
Packit 6c4009
    if (result[cnt].__stateful)
Packit 6c4009
      ++nstateful;
Packit 6c4009
  if (nstateful > 1)
Packit 6c4009
#else
Packit 6c4009
  if (nsteps > 1)
Packit 6c4009
#endif
Packit 6c4009
    {
Packit 6c4009
      /* We cannot handle this case.  */
Packit 6c4009
      __gconv_close_transform (result, nsteps);
Packit 6c4009
      result = NULL;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    *nstepsp = nsteps;
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Extract from the given locale name the character set portion.  Since
Packit 6c4009
   only the XPG form of the name includes this information we don't have
Packit 6c4009
   to take care for the CEN form.  */
Packit 6c4009
#define extract_charset_name(str) \
Packit 6c4009
  ({									      \
Packit 6c4009
    const char *cp = str;						      \
Packit 6c4009
    char *result = NULL;						      \
Packit 6c4009
									      \
Packit 6c4009
    cp += strcspn (cp, "@.+,");						      \
Packit 6c4009
    if (*cp == '.')							      \
Packit 6c4009
      {									      \
Packit 6c4009
	const char *endp = ++cp;					      \
Packit 6c4009
	while (*endp != '\0' && *endp != '@')				      \
Packit 6c4009
	  ++endp;							      \
Packit 6c4009
	if (endp != cp)							      \
Packit 6c4009
	  result = strndupa (cp, endp - cp);				      \
Packit 6c4009
      }									      \
Packit 6c4009
    result;								      \
Packit 6c4009
  })
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Some of the functions here must not be used while setlocale is called.  */
Packit 6c4009
__libc_rwlock_define (extern, __libc_setlocale_lock attribute_hidden)
Packit 6c4009
Packit 6c4009
/* Load conversion functions for the currently selected locale.  */
Packit 6c4009
void
Packit 6c4009
__wcsmbs_load_conv (struct __locale_data *new_category)
Packit 6c4009
{
Packit 6c4009
  /* Acquire the lock.  */
Packit 6c4009
  __libc_rwlock_wrlock (__libc_setlocale_lock);
Packit 6c4009
Packit 6c4009
  /* We should repeat the test since while we waited some other thread
Packit 6c4009
     might have run this function.  */
Packit 6c4009
  if (__glibc_likely (new_category->private.ctype == NULL))
Packit 6c4009
    {
Packit 6c4009
      /* We must find the real functions.  */
Packit 6c4009
      const char *charset_name;
Packit 6c4009
      const char *complete_name;
Packit 6c4009
      struct gconv_fcts *new_fcts;
Packit 6c4009
      int use_translit;
Packit 6c4009
Packit 6c4009
      /* Allocate the gconv_fcts structure.  */
Packit 6c4009
      new_fcts = calloc (1, sizeof *new_fcts);
Packit 6c4009
      if (new_fcts == NULL)
Packit 6c4009
	goto failed;
Packit 6c4009
Packit 6c4009
      /* Get name of charset of the locale.  */
Packit 6c4009
      charset_name = new_category->values[_NL_ITEM_INDEX(CODESET)].string;
Packit 6c4009
Packit 6c4009
      /* Does the user want transliteration?  */
Packit 6c4009
      use_translit = new_category->use_translit;
Packit 6c4009
Packit 6c4009
      /* Normalize the name and add the slashes necessary for a
Packit 6c4009
	 complete lookup.  */
Packit 6c4009
      complete_name = norm_add_slashes (charset_name,
Packit 6c4009
					use_translit ? "TRANSLIT" : "");
Packit 6c4009
Packit 6c4009
      /* It is not necessary to use transliteration in this direction
Packit 6c4009
	 since the internal character set is supposed to be able to
Packit 6c4009
	 represent all others.  */
Packit 6c4009
      new_fcts->towc = __wcsmbs_getfct ("INTERNAL", complete_name,
Packit 6c4009
					&new_fcts->towc_nsteps);
Packit 6c4009
      if (new_fcts->towc != NULL)
Packit 6c4009
	new_fcts->tomb = __wcsmbs_getfct (complete_name, "INTERNAL",
Packit 6c4009
					  &new_fcts->tomb_nsteps);
Packit 6c4009
Packit 6c4009
      /* If any of the conversion functions is not available we don't
Packit 6c4009
	 use any since this would mean we cannot convert back and
Packit 6c4009
	 forth.  NB: NEW_FCTS was allocated with calloc.  */
Packit 6c4009
      if (new_fcts->tomb == NULL)
Packit 6c4009
	{
Packit 6c4009
	  if (new_fcts->towc != NULL)
Packit 6c4009
	    __gconv_close_transform (new_fcts->towc, new_fcts->towc_nsteps);
Packit 6c4009
Packit 6c4009
	  free (new_fcts);
Packit 6c4009
Packit 6c4009
	failed:
Packit 6c4009
	  new_category->private.ctype = &__wcsmbs_gconv_fcts_c;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  new_category->private.ctype = new_fcts;
Packit 6c4009
	  new_category->private.cleanup = &_nl_cleanup_ctype;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Clone the current conversion function set.  */
Packit 6c4009
void
Packit 6c4009
__wcsmbs_clone_conv (struct gconv_fcts *copy)
Packit 6c4009
{
Packit 6c4009
  const struct gconv_fcts *orig;
Packit 6c4009
Packit 6c4009
  orig = get_gconv_fcts (_NL_CURRENT_DATA (LC_CTYPE));
Packit 6c4009
Packit 6c4009
  /* Copy the data.  */
Packit 6c4009
  *copy = *orig;
Packit 6c4009
Packit 6c4009
  /* Now increment the usage counters.
Packit 6c4009
     Note: This assumes copy->*_nsteps == 1.  */
Packit 6c4009
  if (copy->towc->__shlib_handle != NULL)
Packit 6c4009
    ++copy->towc->__counter;
Packit 6c4009
  if (copy->tomb->__shlib_handle != NULL)
Packit 6c4009
    ++copy->tomb->__counter;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Get converters for named charset.  */
Packit 6c4009
int
Packit 6c4009
__wcsmbs_named_conv (struct gconv_fcts *copy, const char *name)
Packit 6c4009
{
Packit 6c4009
  copy->towc = __wcsmbs_getfct ("INTERNAL", name, &copy->towc_nsteps);
Packit 6c4009
  if (copy->towc == NULL)
Packit 6c4009
    return 1;
Packit 6c4009
Packit 6c4009
  copy->tomb = __wcsmbs_getfct (name, "INTERNAL", &copy->tomb_nsteps);
Packit 6c4009
  if (copy->tomb == NULL)
Packit 6c4009
    {
Packit 6c4009
      __gconv_close_transform (copy->towc, copy->towc_nsteps);
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_nl_cleanup_ctype (struct __locale_data *locale)
Packit 6c4009
{
Packit 6c4009
  const struct gconv_fcts *const data = locale->private.ctype;
Packit 6c4009
  if (data != NULL)
Packit 6c4009
    {
Packit 6c4009
      locale->private.ctype = NULL;
Packit 6c4009
      locale->private.cleanup = NULL;
Packit 6c4009
Packit 6c4009
      /* Free the old conversions.  */
Packit 6c4009
      __gconv_close_transform (data->tomb, data->tomb_nsteps);
Packit 6c4009
      __gconv_close_transform (data->towc, data->towc_nsteps);
Packit 6c4009
      free ((char *) data);
Packit 6c4009
    }
Packit 6c4009
}