/* Copyright (C) 1991-2014 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 <bits/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]; /* 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. */ char *np = strdupa (locale); 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); /* 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 (the locale path variable). */ free (locale_path); 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 (); }