diff options
Diffstat (limited to 'REORG.TODO/locale/setlocale.c')
-rw-r--r-- | REORG.TODO/locale/setlocale.c | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/REORG.TODO/locale/setlocale.c b/REORG.TODO/locale/setlocale.c new file mode 100644 index 0000000000..19acc4b2c7 --- /dev/null +++ b/REORG.TODO/locale/setlocale.c @@ -0,0 +1,534 @@ +/* Copyright (C) 1991-2017 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 (); +} |