Blame locale/newlocale.c

Packit 6c4009
/* Return a reference to locale information record.
Packit 6c4009
   Copyright (C) 1996-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>, 1996.
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 <argz.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <locale.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include "localeinfo.h"
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Lock for protecting global data.  */
Packit 6c4009
__libc_rwlock_define (extern , __libc_setlocale_lock attribute_hidden)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Use this when we come along an error.  */
Packit 6c4009
#define ERROR_RETURN							      \
Packit 6c4009
  do {									      \
Packit 6c4009
    __set_errno (EINVAL);						      \
Packit 6c4009
    return NULL;							      \
Packit 6c4009
  } while (0)
Packit 6c4009
Packit 6c4009
Packit 6c4009
locale_t
Packit 6c4009
__newlocale (int category_mask, const char *locale, locale_t base)
Packit 6c4009
{
Packit 6c4009
  /* Intermediate memory for result.  */
Packit 6c4009
  const char *newnames[__LC_LAST];
Packit 6c4009
  struct __locale_struct result;
Packit 6c4009
  locale_t result_ptr;
Packit 6c4009
  char *locale_path;
Packit 6c4009
  size_t locale_path_len;
Packit 6c4009
  const char *locpath_var;
Packit 6c4009
  int cnt;
Packit 6c4009
  size_t names_len;
Packit 6c4009
Packit 6c4009
  /* We treat LC_ALL in the same way as if all bits were set.  */
Packit 6c4009
  if (category_mask == 1 << LC_ALL)
Packit 6c4009
    category_mask = (1 << __LC_LAST) - 1 - (1 << LC_ALL);
Packit 6c4009
Packit 6c4009
  /* Sanity check for CATEGORY argument.  */
Packit 6c4009
  if ((category_mask & ~((1 << __LC_LAST) - 1 - (1 << LC_ALL))) != 0)
Packit 6c4009
    ERROR_RETURN;
Packit 6c4009
Packit 6c4009
  /* `newlocale' does not support asking for the locale name. */
Packit 6c4009
  if (locale == NULL)
Packit 6c4009
    ERROR_RETURN;
Packit 6c4009
Packit 6c4009
  if (base == _nl_C_locobj_ptr)
Packit 6c4009
    /* We're to modify BASE, returned for a previous call with "C".
Packit 6c4009
       We can't really modify the read-only structure, so instead
Packit 6c4009
       start over by copying it.  */
Packit 6c4009
    base = NULL;
Packit 6c4009
Packit 6c4009
  if ((base == NULL || category_mask == (1 << __LC_LAST) - 1 - (1 << LC_ALL))
Packit 6c4009
      && (category_mask == 0 || !strcmp (locale, "C")))
Packit 6c4009
    /* Asking for the "C" locale needn't allocate a new object.  */
Packit 6c4009
    return _nl_C_locobj_ptr;
Packit 6c4009
Packit 6c4009
  /* Allocate memory for the result.  */
Packit 6c4009
  if (base != NULL)
Packit 6c4009
    result = *base;
Packit 6c4009
  else
Packit 6c4009
    /* Fill with pointers to C locale data.  */
Packit 6c4009
    result = _nl_C_locobj;
Packit 6c4009
Packit 6c4009
  /* If no category is to be set we return BASE if available or a
Packit 6c4009
     dataset using the C locale data.  */
Packit 6c4009
  if (category_mask == 0)
Packit 6c4009
    {
Packit 6c4009
      result_ptr = (locale_t) malloc (sizeof (struct __locale_struct));
Packit 6c4009
      if (result_ptr == NULL)
Packit 6c4009
	return NULL;
Packit 6c4009
      *result_ptr = result;
Packit 6c4009
Packit 6c4009
      goto update;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We perhaps really have to load some data.  So we determine the
Packit 6c4009
     path in which to look for the data now.  The environment variable
Packit 6c4009
     `LOCPATH' must only be used when the binary has no SUID or SGID
Packit 6c4009
     bit set.  If using the default path, we tell _nl_find_locale
Packit 6c4009
     by passing null and it can check the canonical locale archive.  */
Packit 6c4009
  locale_path = NULL;
Packit 6c4009
  locale_path_len = 0;
Packit 6c4009
Packit 6c4009
  locpath_var = getenv ("LOCPATH");
Packit 6c4009
  if (locpath_var != NULL && locpath_var[0] != '\0')
Packit 6c4009
    {
Packit 6c4009
      if (__argz_create_sep (locpath_var, ':',
Packit 6c4009
			     &locale_path, &locale_path_len) != 0)
Packit 6c4009
	return NULL;
Packit 6c4009
Packit 6c4009
      if (__argz_add_sep (&locale_path, &locale_path_len,
Packit 6c4009
			  _nl_default_locale_path, ':') != 0)
Packit 6c4009
	return NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Get the names for the locales we are interested in.  We either
Packit 6c4009
     allow a composite name or a single name.  */
Packit 6c4009
  for (cnt = 0; cnt < __LC_LAST; ++cnt)
Packit 6c4009
    if (cnt != LC_ALL)
Packit 6c4009
      newnames[cnt] = locale;
Packit 6c4009
  if (strchr (locale, ';') != NULL)
Packit 6c4009
    {
Packit 6c4009
      /* This is a composite name.  Make a copy and split it up.  */
Packit 6c4009
      char *np = strdupa (locale);
Packit 6c4009
      char *cp;
Packit 6c4009
      int specified_mask = 0;
Packit 6c4009
Packit 6c4009
      while ((cp = strchr (np, '=')) != NULL)
Packit 6c4009
	{
Packit 6c4009
	  for (cnt = 0; cnt < __LC_LAST; ++cnt)
Packit 6c4009
	    if (cnt != LC_ALL
Packit 6c4009
		&& (size_t) (cp - np) == _nl_category_name_sizes[cnt]
Packit 6c4009
		&& memcmp (np, (_nl_category_names.str
Packit 6c4009
				+ _nl_category_name_idxs[cnt]), cp - np) == 0)
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	  if (cnt == __LC_LAST)
Packit 6c4009
	    /* Bogus category name.  */
Packit 6c4009
	    ERROR_RETURN;
Packit 6c4009
Packit 6c4009
	  /* Found the category this clause sets.  */
Packit 6c4009
	  specified_mask |= 1 << cnt;
Packit 6c4009
	  newnames[cnt] = ++cp;
Packit 6c4009
	  cp = strchr (cp, ';');
Packit 6c4009
	  if (cp != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      /* Examine the next clause.  */
Packit 6c4009
	      *cp = '\0';
Packit 6c4009
	      np = cp + 1;
Packit 6c4009
	    }
Packit 6c4009
	  else
Packit 6c4009
	    /* This was the last clause.  We are done.  */
Packit 6c4009
	    break;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      if (category_mask &~ specified_mask)
Packit 6c4009
	/* The composite name did not specify all categories we need.  */
Packit 6c4009
	ERROR_RETURN;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Protect global data.  */
Packit 6c4009
  __libc_rwlock_wrlock (__libc_setlocale_lock);
Packit 6c4009
Packit 6c4009
  /* Now process all categories we are interested in.  */
Packit 6c4009
  names_len = 0;
Packit 6c4009
  for (cnt = 0; cnt < __LC_LAST; ++cnt)
Packit 6c4009
    {
Packit 6c4009
      if ((category_mask & 1 << cnt) != 0)
Packit 6c4009
	{
Packit 6c4009
	  result.__locales[cnt] = _nl_find_locale (locale_path,
Packit 6c4009
						   locale_path_len,
Packit 6c4009
						   cnt, &newnames[cnt]);
Packit 6c4009
	  if (result.__locales[cnt] == NULL)
Packit 6c4009
	    {
Packit 6c4009
	    free_cnt_data_and_exit:
Packit 6c4009
	      while (cnt-- > 0)
Packit 6c4009
		if (((category_mask & 1 << cnt) != 0)
Packit 6c4009
		    && result.__locales[cnt]->usage_count != UNDELETABLE)
Packit 6c4009
		  /* We can remove the data.  */
Packit 6c4009
		  _nl_remove_locale (cnt, result.__locales[cnt]);
Packit 6c4009
Packit 6c4009
              /* Critical section left.  */
Packit 6c4009
              __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
	      return NULL;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  if (newnames[cnt] != _nl_C_name)
Packit 6c4009
	    names_len += strlen (newnames[cnt]) + 1;
Packit 6c4009
	}
Packit 6c4009
      else if (cnt != LC_ALL && result.__names[cnt] != _nl_C_name)
Packit 6c4009
	/* Tally up the unchanged names from BASE as well.  */
Packit 6c4009
	names_len += strlen (result.__names[cnt]) + 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* We successfully loaded all required data.  Allocate a new structure.
Packit 6c4009
     We can't just reuse the BASE pointer, because the name strings are
Packit 6c4009
     changing and we need the old name string area intact so we can copy
Packit 6c4009
     out of it into the new one without overlap problems should some
Packit 6c4009
     category's name be getting longer.  */
Packit 6c4009
  result_ptr = malloc (sizeof (struct __locale_struct) + names_len);
Packit 6c4009
  if (result_ptr == NULL)
Packit 6c4009
    {
Packit 6c4009
      cnt = __LC_LAST;
Packit 6c4009
      goto free_cnt_data_and_exit;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (base == NULL)
Packit 6c4009
    {
Packit 6c4009
      /* Fill in this new structure from scratch.  */
Packit 6c4009
Packit 6c4009
      char *namep = (char *) (result_ptr + 1);
Packit 6c4009
Packit 6c4009
      /* Install copied new names in the new structure's __names array.
Packit 6c4009
	 If resolved to "C", that is already in RESULT.__names to start.  */
Packit 6c4009
      for (cnt = 0; cnt < __LC_LAST; ++cnt)
Packit 6c4009
	if ((category_mask & 1 << cnt) != 0 && newnames[cnt] != _nl_C_name)
Packit 6c4009
	  {
Packit 6c4009
	    result.__names[cnt] = namep;
Packit 6c4009
	    namep = __stpcpy (namep, newnames[cnt]) + 1;
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      *result_ptr = result;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* We modify the base structure.  */
Packit 6c4009
Packit 6c4009
      char *namep = (char *) (result_ptr + 1);
Packit 6c4009
Packit 6c4009
      for (cnt = 0; cnt < __LC_LAST; ++cnt)
Packit 6c4009
	if ((category_mask & 1 << cnt) != 0)
Packit 6c4009
	  {
Packit 6c4009
	    if (base->__locales[cnt]->usage_count != UNDELETABLE)
Packit 6c4009
	      /* We can remove the old data.  */
Packit 6c4009
	      _nl_remove_locale (cnt, base->__locales[cnt]);
Packit 6c4009
	    result_ptr->__locales[cnt] = result.__locales[cnt];
Packit 6c4009
Packit 6c4009
	    if (newnames[cnt] == _nl_C_name)
Packit 6c4009
	      result_ptr->__names[cnt] = _nl_C_name;
Packit 6c4009
	    else
Packit 6c4009
	      {
Packit 6c4009
		result_ptr->__names[cnt] = namep;
Packit 6c4009
		namep = __stpcpy (namep, newnames[cnt]) + 1;
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
	else if (cnt != LC_ALL)
Packit 6c4009
	  {
Packit 6c4009
	    /* The RESULT members point into the old BASE structure.  */
Packit 6c4009
	    result_ptr->__locales[cnt] = result.__locales[cnt];
Packit 6c4009
	    if (result.__names[cnt] == _nl_C_name)
Packit 6c4009
	      result_ptr->__names[cnt] = _nl_C_name;
Packit 6c4009
	    else
Packit 6c4009
	      {
Packit 6c4009
		result_ptr->__names[cnt] = namep;
Packit 6c4009
		namep = __stpcpy (namep, result.__names[cnt]) + 1;
Packit 6c4009
	      }
Packit 6c4009
	  }
Packit 6c4009
Packit 6c4009
      free (base);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Critical section left.  */
Packit 6c4009
  __libc_rwlock_unlock (__libc_setlocale_lock);
Packit 6c4009
Packit 6c4009
  /* Update the special members.  */
Packit 6c4009
 update:
Packit 6c4009
  {
Packit 6c4009
    union locale_data_value *ctypes = result_ptr->__locales[LC_CTYPE]->values;
Packit 6c4009
    result_ptr->__ctype_b = (const unsigned short int *)
Packit 6c4009
      ctypes[_NL_ITEM_INDEX (_NL_CTYPE_CLASS)].string + 128;
Packit 6c4009
    result_ptr->__ctype_tolower = (const int *)
Packit 6c4009
      ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOLOWER)].string + 128;
Packit 6c4009
    result_ptr->__ctype_toupper = (const int *)
Packit 6c4009
      ctypes[_NL_ITEM_INDEX (_NL_CTYPE_TOUPPER)].string + 128;
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  return result_ptr;
Packit 6c4009
}
Packit 6c4009
weak_alias (__newlocale, newlocale)