Blame locale/newlocale.c

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