| /* 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]; |
| /* 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 (__builtin_expect (strchr (locale, ';') != NULL, 0)) |
| { |
| /* 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 (); |
| } |