| /* Copyright (C) 1991-2016 Free Software Foundation, Inc. | 
 |    This file is part of the GNU C Library. | 
 |  | 
 |    The GNU C Library is free software; you can redistribute it and/or | 
 |    modify it under the terms of the GNU Lesser General Public | 
 |    License as published by the Free Software Foundation; either | 
 |    version 2.1 of the License, or (at your option) any later version. | 
 |  | 
 |    The GNU C Library is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 |    Lesser General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU Lesser General Public | 
 |    License along with the GNU C Library; if not, see | 
 |    <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #include <alloca.h> | 
 | #include <argz.h> | 
 | #include <errno.h> | 
 | #include <libc-lock.h> | 
 | #include <locale.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include "localeinfo.h" | 
 |  | 
 | #ifdef NL_CURRENT_INDIRECT | 
 |  | 
 | /* For each category declare a special external symbol | 
 |    _nl_current_CATEGORY_used with a weak reference. | 
 |    This symbol will is defined in lc-CATEGORY.c and will be linked in | 
 |    if anything uses _nl_current_CATEGORY (also defined in that module). | 
 |    Also use a weak reference for the _nl_current_CATEGORY thread variable.  */ | 
 |  | 
 | # define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 |     extern char _nl_current_##category##_used; \ | 
 |     weak_extern (_nl_current_##category##_used) \ | 
 |     weak_extern (_nl_current_##category) | 
 | # include "categories.def" | 
 | # undef	DEFINE_CATEGORY | 
 |  | 
 | /* Now define a table of flags based on those special weak symbols' values. | 
 |    _nl_current_used[CATEGORY] will be zero if _nl_current_CATEGORY is not | 
 |    linked in.  */ | 
 | static char *const _nl_current_used[] = | 
 |   { | 
 | # define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 |     [category] = &_nl_current_##category##_used, | 
 | # include "categories.def" | 
 | # undef	DEFINE_CATEGORY | 
 |   }; | 
 |  | 
 | # define CATEGORY_USED(category)	(_nl_current_used[category] != 0) | 
 |  | 
 | #else | 
 |  | 
 | /* The shared library always loads all the categories, | 
 |    and the current global settings are kept in _nl_global_locale.  */ | 
 |  | 
 | # define CATEGORY_USED(category)	(1) | 
 |  | 
 | #endif | 
 |  | 
 |  | 
 | /* Define an array of category names (also the environment variable names).  */ | 
 | const union catnamestr_t _nl_category_names attribute_hidden = | 
 |   { | 
 |     { | 
 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 |       category_name, | 
 | #include "categories.def" | 
 | #undef DEFINE_CATEGORY | 
 |     } | 
 |   }; | 
 |  | 
 | const uint8_t _nl_category_name_idxs[__LC_LAST] attribute_hidden = | 
 |   { | 
 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 |     [category] = offsetof (union catnamestr_t, CATNAMEMF (__LINE__)), | 
 | #include "categories.def" | 
 | #undef DEFINE_CATEGORY | 
 |   }; | 
 |  | 
 | /* An array of their lengths, for convenience.  */ | 
 | const uint8_t _nl_category_name_sizes[] attribute_hidden = | 
 |   { | 
 | #define DEFINE_CATEGORY(category, category_name, items, a) \ | 
 |     [category] = sizeof (category_name) - 1, | 
 | #include "categories.def" | 
 | #undef	DEFINE_CATEGORY | 
 |     [LC_ALL] = sizeof ("LC_ALL") - 1 | 
 |   }; | 
 |  | 
 |  | 
 | #ifdef NL_CURRENT_INDIRECT | 
 | # define WEAK_POSTLOAD(postload) weak_extern (postload) | 
 | #else | 
 | # define WEAK_POSTLOAD(postload) /* Need strong refs in static linking.  */ | 
 | #endif | 
 |  | 
 | /* Declare the postload functions used below.  */ | 
 | #undef	NO_POSTLOAD | 
 | #define NO_POSTLOAD _nl_postload_ctype /* Harmless thing known to exist.  */ | 
 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ | 
 | extern void postload (void); WEAK_POSTLOAD (postload) | 
 | #include "categories.def" | 
 | #undef	DEFINE_CATEGORY | 
 | #undef	NO_POSTLOAD | 
 |  | 
 | /* Define an array indexed by category of postload functions to call after | 
 |    loading and installing that category's data.  */ | 
 | static void (*const _nl_category_postload[]) (void) = | 
 |   { | 
 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ | 
 |     [category] = postload, | 
 | #include "categories.def" | 
 | #undef	DEFINE_CATEGORY | 
 |   }; | 
 |  | 
 |  | 
 | /* Lock for protecting global data.  */ | 
 | __libc_rwlock_define_initialized (, __libc_setlocale_lock attribute_hidden) | 
 |  | 
 | /* Defined in loadmsgcat.c.  */ | 
 | extern int _nl_msg_cat_cntr; | 
 |  | 
 |  | 
 | /* Use this when we come along an error.  */ | 
 | #define ERROR_RETURN							      \ | 
 |   do {									      \ | 
 |     __set_errno (EINVAL);						      \ | 
 |     return NULL;							      \ | 
 |   } while (0) | 
 |  | 
 |  | 
 | /* Construct a new composite name.  */ | 
 | static char * | 
 | new_composite_name (int category, const char *newnames[__LC_LAST]) | 
 | { | 
 |   size_t last_len = 0; | 
 |   size_t cumlen = 0; | 
 |   int i; | 
 |   char *new, *p; | 
 |   int same = 1; | 
 |  | 
 |   for (i = 0; i < __LC_LAST; ++i) | 
 |     if (i != LC_ALL) | 
 |       { | 
 | 	const char *name = (category == LC_ALL ? newnames[i] : | 
 | 			    category == i ? newnames[0] : | 
 | 			    _nl_global_locale.__names[i]); | 
 | 	last_len = strlen (name); | 
 | 	cumlen += _nl_category_name_sizes[i] + 1 + last_len + 1; | 
 | 	if (same && name != newnames[0] && strcmp (name, newnames[0]) != 0) | 
 | 	  same = 0; | 
 |       } | 
 |  | 
 |   if (same) | 
 |     { | 
 |       /* All the categories use the same name.  */ | 
 |       if (strcmp (newnames[0], _nl_C_name) == 0 | 
 | 	  || strcmp (newnames[0], _nl_POSIX_name) == 0) | 
 | 	return (char *) _nl_C_name; | 
 |  | 
 |       new = malloc (last_len + 1); | 
 |  | 
 |       return new == NULL ? NULL : memcpy (new, newnames[0], last_len + 1); | 
 |     } | 
 |  | 
 |   new = malloc (cumlen); | 
 |   if (new == NULL) | 
 |     return NULL; | 
 |   p = new; | 
 |   for (i = 0; i < __LC_LAST; ++i) | 
 |     if (i != LC_ALL) | 
 |       { | 
 | 	/* Add "CATEGORY=NAME;" to the string.  */ | 
 | 	const char *name = (category == LC_ALL ? newnames[i] : | 
 | 			    category == i ? newnames[0] : | 
 | 			    _nl_global_locale.__names[i]); | 
 | 	p = __stpcpy (p, _nl_category_names.str + _nl_category_name_idxs[i]); | 
 | 	*p++ = '='; | 
 | 	p = __stpcpy (p, name); | 
 | 	*p++ = ';'; | 
 |       } | 
 |   p[-1] = '\0';		/* Clobber the last ';'.  */ | 
 |   return new; | 
 | } | 
 |  | 
 |  | 
 | /* Put NAME in _nl_global_locale.__names.  */ | 
 | static void | 
 | setname (int category, const char *name) | 
 | { | 
 |   if (_nl_global_locale.__names[category] == name) | 
 |     return; | 
 |  | 
 |   if (_nl_global_locale.__names[category] != _nl_C_name) | 
 |     free ((char *) _nl_global_locale.__names[category]); | 
 |  | 
 |   _nl_global_locale.__names[category] = name; | 
 | } | 
 |  | 
 | /* Put DATA in *_nl_current[CATEGORY].  */ | 
 | static void | 
 | setdata (int category, struct __locale_data *data) | 
 | { | 
 |   if (CATEGORY_USED (category)) | 
 |     { | 
 |       _nl_global_locale.__locales[category] = data; | 
 |       if (_nl_category_postload[category]) | 
 | 	(*_nl_category_postload[category]) (); | 
 |     } | 
 | } | 
 |  | 
 | char * | 
 | setlocale (int category, const char *locale) | 
 | { | 
 |   char *locale_path; | 
 |   size_t locale_path_len; | 
 |   const char *locpath_var; | 
 |   char *composite; | 
 |  | 
 |   /* Sanity check for CATEGORY argument.  */ | 
 |   if (__builtin_expect (category, 0) < 0 | 
 |       || __builtin_expect (category, 0) >= __LC_LAST) | 
 |     ERROR_RETURN; | 
 |  | 
 |   /* Does user want name of current locale?  */ | 
 |   if (locale == NULL) | 
 |     return (char *) _nl_global_locale.__names[category]; | 
 |  | 
 |   /* Protect global data.  */ | 
 |   __libc_rwlock_wrlock (__libc_setlocale_lock); | 
 |  | 
 |   if (strcmp (locale, _nl_global_locale.__names[category]) == 0) | 
 |     { | 
 |       /* Changing to the same thing.  */ | 
 |       __libc_rwlock_unlock (__libc_setlocale_lock); | 
 |  | 
 |       return (char *) _nl_global_locale.__names[category]; | 
 |     } | 
 |  | 
 |   /* We perhaps really have to load some data.  So we determine the | 
 |      path in which to look for the data now.  The environment variable | 
 |      `LOCPATH' must only be used when the binary has no SUID or SGID | 
 |      bit set.  If using the default path, we tell _nl_find_locale | 
 |      by passing null and it can check the canonical locale archive.  */ | 
 |   locale_path = NULL; | 
 |   locale_path_len = 0; | 
 |  | 
 |   locpath_var = getenv ("LOCPATH"); | 
 |   if (locpath_var != NULL && locpath_var[0] != '\0') | 
 |     { | 
 |       if (__argz_create_sep (locpath_var, ':', | 
 | 			     &locale_path, &locale_path_len) != 0 | 
 | 	  || __argz_add_sep (&locale_path, &locale_path_len, | 
 | 			     _nl_default_locale_path, ':') != 0) | 
 | 	{ | 
 | 	  __libc_rwlock_unlock (__libc_setlocale_lock); | 
 | 	  return NULL; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (category == LC_ALL) | 
 |     { | 
 |       /* The user wants to set all categories.  The desired locales | 
 | 	 for the individual categories can be selected by using a | 
 | 	 composite locale name.  This is a semi-colon separated list | 
 | 	 of entries of the form `CATEGORY=VALUE'.  */ | 
 |       const char *newnames[__LC_LAST]; | 
 |       struct __locale_data *newdata[__LC_LAST]; | 
 |       /* Copy of the locale argument, for in-place splitting.  */ | 
 |       char *locale_copy = NULL; | 
 |  | 
 |       /* Set all name pointers to the argument name.  */ | 
 |       for (category = 0; category < __LC_LAST; ++category) | 
 | 	if (category != LC_ALL) | 
 | 	  newnames[category] = (char *) locale; | 
 |  | 
 |       if (__glibc_unlikely (strchr (locale, ';') != NULL)) | 
 | 	{ | 
 | 	  /* This is a composite name.  Make a copy and split it up.  */ | 
 | 	  locale_copy = strdup (locale); | 
 | 	  if (__glibc_unlikely (locale_copy == NULL)) | 
 | 	    { | 
 | 	      __libc_rwlock_unlock (__libc_setlocale_lock); | 
 | 	      return NULL; | 
 | 	    } | 
 | 	  char *np = locale_copy; | 
 | 	  char *cp; | 
 | 	  int cnt; | 
 |  | 
 | 	  while ((cp = strchr (np, '=')) != NULL) | 
 | 	    { | 
 | 	      for (cnt = 0; cnt < __LC_LAST; ++cnt) | 
 | 		if (cnt != LC_ALL | 
 | 		    && (size_t) (cp - np) == _nl_category_name_sizes[cnt] | 
 | 		    && (memcmp (np, (_nl_category_names.str | 
 | 				     + _nl_category_name_idxs[cnt]), cp - np) | 
 | 			== 0)) | 
 | 		  break; | 
 |  | 
 | 	      if (cnt == __LC_LAST) | 
 | 		{ | 
 | 		error_return: | 
 | 		  __libc_rwlock_unlock (__libc_setlocale_lock); | 
 | 		  free (locale_copy); | 
 |  | 
 | 		  /* Bogus category name.  */ | 
 | 		  ERROR_RETURN; | 
 | 		} | 
 |  | 
 | 	      /* Found the category this clause sets.  */ | 
 | 	      newnames[cnt] = ++cp; | 
 | 	      cp = strchr (cp, ';'); | 
 | 	      if (cp != NULL) | 
 | 		{ | 
 | 		  /* Examine the next clause.  */ | 
 | 		  *cp = '\0'; | 
 | 		  np = cp + 1; | 
 | 		} | 
 | 	      else | 
 | 		/* This was the last clause.  We are done.  */ | 
 | 		break; | 
 | 	    } | 
 |  | 
 | 	  for (cnt = 0; cnt < __LC_LAST; ++cnt) | 
 | 	    if (cnt != LC_ALL && newnames[cnt] == locale) | 
 | 	      /* The composite name did not specify all categories.  */ | 
 | 	      goto error_return; | 
 | 	} | 
 |  | 
 |       /* Load the new data for each category.  */ | 
 |       while (category-- > 0) | 
 | 	if (category != LC_ALL) | 
 | 	  { | 
 | 	    newdata[category] = _nl_find_locale (locale_path, locale_path_len, | 
 | 						 category, | 
 | 						 &newnames[category]); | 
 |  | 
 | 	    if (newdata[category] == NULL) | 
 | 	      { | 
 | #ifdef NL_CURRENT_INDIRECT | 
 | 		if (newnames[category] == _nl_C_name) | 
 | 		  /* Null because it's the weak value of _nl_C_LC_FOO.  */ | 
 | 		  continue; | 
 | #endif | 
 | 		break; | 
 | 	      } | 
 |  | 
 | 	    /* We must not simply free a global locale since we have | 
 | 	       no control over the usage.  So we mark it as | 
 | 	       un-deletable.  And yes, the 'if' is needed, the data | 
 | 	       might be in read-only memory.  */ | 
 | 	    if (newdata[category]->usage_count != UNDELETABLE) | 
 | 	      newdata[category]->usage_count = UNDELETABLE; | 
 |  | 
 | 	    /* Make a copy of locale name.  */ | 
 | 	    if (newnames[category] != _nl_C_name) | 
 | 	      { | 
 | 		if (strcmp (newnames[category], | 
 | 			    _nl_global_locale.__names[category]) == 0) | 
 | 		  newnames[category] = _nl_global_locale.__names[category]; | 
 | 		else | 
 | 		  { | 
 | 		    newnames[category] = __strdup (newnames[category]); | 
 | 		    if (newnames[category] == NULL) | 
 | 		      break; | 
 | 		  } | 
 | 	      } | 
 | 	  } | 
 |  | 
 |       /* Create new composite name.  */ | 
 |       composite = (category >= 0 | 
 | 		   ? NULL : new_composite_name (LC_ALL, newnames)); | 
 |       if (composite != NULL) | 
 | 	{ | 
 | 	  /* Now we have loaded all the new data.  Put it in place.  */ | 
 | 	  for (category = 0; category < __LC_LAST; ++category) | 
 | 	    if (category != LC_ALL) | 
 | 	      { | 
 | 		setdata (category, newdata[category]); | 
 | 		setname (category, newnames[category]); | 
 | 	      } | 
 | 	  setname (LC_ALL, composite); | 
 |  | 
 | 	  /* We successfully loaded a new locale.  Let the message catalog | 
 | 	     functions know about this.  */ | 
 | 	  ++_nl_msg_cat_cntr; | 
 | 	} | 
 |       else | 
 | 	for (++category; category < __LC_LAST; ++category) | 
 | 	  if (category != LC_ALL && newnames[category] != _nl_C_name | 
 | 	      && newnames[category] != _nl_global_locale.__names[category]) | 
 | 	    free ((char *) newnames[category]); | 
 |  | 
 |       /* Critical section left.  */ | 
 |       __libc_rwlock_unlock (__libc_setlocale_lock); | 
 |  | 
 |       /* Free the resources.  */ | 
 |       free (locale_path); | 
 |       free (locale_copy); | 
 |  | 
 |       return composite; | 
 |     } | 
 |   else | 
 |     { | 
 |       struct __locale_data *newdata = NULL; | 
 |       const char *newname[1] = { locale }; | 
 |  | 
 |       if (CATEGORY_USED (category)) | 
 | 	{ | 
 | 	  /* Only actually load the data if anything will use it.  */ | 
 | 	  newdata = _nl_find_locale (locale_path, locale_path_len, category, | 
 | 				     &newname[0]); | 
 | 	  if (newdata == NULL) | 
 | 	    goto abort_single; | 
 |  | 
 | 	  /* We must not simply free a global locale since we have no | 
 | 	     control over the usage.  So we mark it as un-deletable. | 
 |  | 
 | 	     Note: do not remove the `if', it's necessary to cope with | 
 | 	     the builtin locale data.  */ | 
 | 	  if (newdata->usage_count != UNDELETABLE) | 
 | 	    newdata->usage_count = UNDELETABLE; | 
 | 	} | 
 |  | 
 |       /* Make a copy of locale name.  */ | 
 |       if (newname[0] != _nl_C_name) | 
 | 	{ | 
 | 	  newname[0] = __strdup (newname[0]); | 
 | 	  if (newname[0] == NULL) | 
 | 	    goto abort_single; | 
 | 	} | 
 |  | 
 |       /* Create new composite name.  */ | 
 |       composite = new_composite_name (category, newname); | 
 |       if (composite == NULL) | 
 | 	{ | 
 | 	  if (newname[0] != _nl_C_name) | 
 | 	    free ((char *) newname[0]); | 
 |  | 
 | 	  /* Say that we don't have any data loaded.  */ | 
 | 	abort_single: | 
 | 	  newname[0] = NULL; | 
 | 	} | 
 |       else | 
 | 	{ | 
 | 	  if (CATEGORY_USED (category)) | 
 | 	    setdata (category, newdata); | 
 |  | 
 | 	  setname (category, newname[0]); | 
 | 	  setname (LC_ALL, composite); | 
 |  | 
 | 	  /* We successfully loaded a new locale.  Let the message catalog | 
 | 	     functions know about this.  */ | 
 | 	  ++_nl_msg_cat_cntr; | 
 | 	} | 
 |  | 
 |       /* Critical section left.  */ | 
 |       __libc_rwlock_unlock (__libc_setlocale_lock); | 
 |  | 
 |       /* Free the resources (the locale path variable.  */ | 
 |       free (locale_path); | 
 |  | 
 |       return (char *) newname[0]; | 
 |     } | 
 | } | 
 | libc_hidden_def (setlocale) | 
 |  | 
 | static void __libc_freeres_fn_section | 
 | free_category (int category, | 
 | 	       struct __locale_data *here, struct __locale_data *c_data) | 
 | { | 
 |   struct loaded_l10nfile *runp = _nl_locale_file_list[category]; | 
 |  | 
 |   /* If this category is already "C" don't do anything.  */ | 
 |   if (here != c_data) | 
 |     { | 
 |       /* We have to be prepared that sometime later we still | 
 | 	 might need the locale information.  */ | 
 |       setdata (category, c_data); | 
 |       setname (category, _nl_C_name); | 
 |     } | 
 |  | 
 |   while (runp != NULL) | 
 |     { | 
 |       struct loaded_l10nfile *curr = runp; | 
 |       struct __locale_data *data = (struct __locale_data *) runp->data; | 
 |  | 
 |       if (data != NULL && data != c_data) | 
 | 	_nl_unload_locale (data); | 
 |       runp = runp->next; | 
 |       free ((char *) curr->filename); | 
 |       free (curr); | 
 |     } | 
 | } | 
 |  | 
 | /* This is called from iconv/gconv_db.c's free_mem, as locales must | 
 |    be freed before freeing gconv steps arrays.  */ | 
 | void __libc_freeres_fn_section | 
 | _nl_locale_subfreeres (void) | 
 | { | 
 | #ifdef NL_CURRENT_INDIRECT | 
 |   /* We don't use the loop because we want to have individual weak | 
 |      symbol references here.  */ | 
 | # define DEFINE_CATEGORY(category, category_name, items, a)		      \ | 
 |   if (CATEGORY_USED (category))						      \ | 
 |     {									      \ | 
 |       extern struct __locale_data _nl_C_##category;			      \ | 
 |       weak_extern (_nl_C_##category)					      \ | 
 |       free_category (category, *_nl_current_##category, &_nl_C_##category);   \ | 
 |     } | 
 | # include "categories.def" | 
 | # undef	DEFINE_CATEGORY | 
 | #else | 
 |   int category; | 
 |  | 
 |   for (category = 0; category < __LC_LAST; ++category) | 
 |     if (category != LC_ALL) | 
 |       free_category (category, _NL_CURRENT_DATA (category), | 
 | 		     _nl_C_locobj.__locales[category]); | 
 | #endif | 
 |  | 
 |   setname (LC_ALL, _nl_C_name); | 
 |  | 
 |   /* This frees the data structures associated with the locale archive. | 
 |      The locales from the archive are not in the file list, so we have | 
 |      not called _nl_unload_locale on them above.  */ | 
 |   _nl_archive_subfreeres (); | 
 | } |