diff options
Diffstat (limited to 'locale/setlocale.c')
-rw-r--r-- | locale/setlocale.c | 395 |
1 files changed, 377 insertions, 18 deletions
diff --git a/locale/setlocale.c b/locale/setlocale.c index 784ccb1272..79d22ab98d 100644 --- a/locale/setlocale.c +++ b/locale/setlocale.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1992 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1995 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 @@ -16,30 +16,389 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <ansidecl.h> -#include <localeinfo.h> #include <errno.h> -#include <locale.h> #include <string.h> +#include <stdlib.h> +#include <locale.h> +#include <langinfo.h> +#include "localeinfo.h" + +/* For each category declare two external variables (with weak references): + extern const struct locale_data *_nl_current_CATEGORY; + This points to the current locale's in-core data for CATEGORY. + extern const struct locale_data _nl_C_CATEGORY; + This contains the built-in "C"/"POSIX" locale's data for CATEGORY. + Both are weak references; if &_nl_current_CATEGORY is zero, + then nothing is using the locale data. */ +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ +extern const struct locale_data *_nl_current_##category; \ +extern const struct locale_data _nl_C_##category; \ +weak_symbol (_nl_current_##category) weak_symbol (_nl_C_##category) +#include "categories.def" +#undef DEFINE_CATEGORY + +/* Array indexed by category of pointers to _nl_current_CATEGORY slots. + Elements are zero for categories whose data is never used. */ +const struct locale_data * *const _nl_current[] = +{ +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ + [category] = &_nl_current_##category, +#include "categories.def" +#undef DEFINE_CATEGORY +}; + +/* Array indexed by category of pointers to _nl_C_CATEGORY slots. + Elements are zero for categories whose data is never used. */ +const struct locale_data *const _nl_C[] = +{ +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ + [category] = &_nl_C_##category, +#include "categories.def" +#undef DEFINE_CATEGORY +}; + + +/* Define an array of category names (also the environment variable names), + indexed by integral category. */ +const char *const _nl_category_names[] = + { +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ + [category] = category_name, +#include "categories.def" +#undef DEFINE_CATEGORY + }; +/* An array of their lengths, for convenience. */ +const size_t _nl_category_name_sizes[] = + { +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ + [category] = sizeof (category_name) - 1, +#include "categories.def" +#undef DEFINE_CATEGORY + }; -/* Switch to the locale called NAME in CATEGORY. - Return a string describing the locale. This string can - be used as the NAME argument in a later call. - If NAME is NULL, don't switch locales, but return the current one. - If NAME is "", switch to a locale based on the environment variables, - as per POSIX. Return NULL on error. */ +/* 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, b, c, d) \ +extern void postload (void); +#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. */ +void (*const _nl_category_postload[]) (void) = + { +#define DEFINE_CATEGORY(category, category_name, items, postload, b, c, d) \ + [category] = postload, +#include "categories.def" +#undef DEFINE_CATEGORY + }; + + +const char _nl_C_name[] = "C"; + +/* Name of current locale for each individual category. + Each is malloc'd unless it is nl_C_name. */ +const char *_nl_current_names[] = + { +#define DEFINE_CATEGORY(category, category_name, items, a, b, c, d) \ + _nl_C_name, +#include "categories.def" +#undef DEFINE_CATEGORY + }; + +/* Composite LC_ALL name for current locale. + This is malloc'd unless it's _nl_C_name. */ +char *_nl_current_composite_name = (char *) _nl_C_name; + + +/* Switch to the locale called NAME in CATEGORY. Return a string + describing the locale. This string can be used as the NAME argument in + a later call. If NAME is NULL, don't switch locales, but return the + current one. If NAME is "", switch to a locale based on the environment + variables, as per POSIX. Return NULL on error. */ + char * -DEFUN(setlocale, (category, name), int category AND CONST char *name) +setlocale (int category, const char *name) { - /* Braindead implementation until I finish the fancy one. */ + /* Return a malloc'd copy of STRING. */ + char *copy (const char *string) + { + size_t len = strlen (string) + 1; + char *new = malloc (len); + return new ? memcpy (new, string, len) : NULL; + } + + /* Construct a new composite name. */ + char *new_composite_name (int category, char *newnames[LC_ALL]) + { + size_t lens[LC_ALL], cumlen = 0; + int i; + char *new, *p; + int same = 1; + + for (i = 0; i < LC_ALL; ++i) + { + char *name = (category == LC_ALL ? newnames[i] : + category == i ? newnames[0] : + (char *) _nl_current_names[i]); + lens[i] = strlen (name); + cumlen += _nl_category_name_sizes[i] + 1 + lens[i] + 1; + if (i > 0 && same && strcmp (name, newnames[0])) + same = 0; + } + + if (same) + { + /* All the categories use the same name. */ + new = malloc (lens[0] + 1); + if (! new) + { + if (!strcmp (newnames[0], "C") || !strcmp (newnames[0], "POSIX")) + return (char *) _nl_C_name; + return NULL; + } + memcpy (new, newnames[0], lens[0] + 1); + return new; + } + + new = malloc (cumlen); + if (! new) + return NULL; + p = new; + for (i = 0; i < LC_ALL; ++i) + { + /* Add "CATEGORY=NAME;" to the string. */ + char *name = (category == LC_ALL ? newnames[i] : + category == i ? newnames[0] : + (char *) _nl_current_names[i]); + memcpy (p, _nl_category_names[i], _nl_category_name_sizes[i]); + p += _nl_category_name_sizes[i]; + *p++ = '='; + memcpy (p, name, lens[i]); + p += lens[i]; + *p++ = ';'; + } + p[-1] = '\0'; /* Clobber the last ';'. */ + return new; + } + /* Put COMPOSITE in _nl_current_composite_name and free the old value. */ + void setcomposite (char *composite) + { + char *old = _nl_current_composite_name; + _nl_current_composite_name = composite; + if (old != _nl_C_name) + free (old); + } + /* Put NAME in _nl_current_names and free the old value. */ + void setname (int category, const char *name) + { + const char *oldname = _nl_current_names[category]; + _nl_current_names[category] = name; + if (oldname != _nl_C_name) + free ((char *) oldname); + } + /* Put DATA in *_nl_current[CATEGORY] and free the old value. */ + void setdata (int category, struct locale_data *data) + { + if (_nl_current[category]) + { + const struct locale_data *olddata = *_nl_current[category]; + *_nl_current[category] = data; + if (_nl_category_postload[category]) + (*_nl_category_postload[category]) (); + if (olddata != _nl_C[category]) + _nl_free_locale ((struct locale_data *) olddata); + } + } + + const char *current_name; + char *composite; + + if (category < 0 || category > LC_ALL) + { + errno = EINVAL; + return NULL; + } + + if (category == LC_ALL) + current_name = _nl_current_composite_name; + else + current_name = _nl_current_names[category]; + + if (name == NULL) + /* Return the name of the current locale. */ + return (char *) current_name; + + if (name == current_name) + /* Changing to the same thing. */ + return (char *) current_name; + + if (category == LC_ALL) + { + const size_t len = strlen (name) + 1; + char *newnames[LC_ALL]; + char *p; + struct locale_data *newdata[LC_ALL]; + + /* Set all name pointers to the argument name. */ + for (category = 0; category < LC_ALL; ++category) + newnames[category] = (char *) name; + + p = strchr (name, ';'); + if (p) + { + /* This is a composite name. Make a local copy and split it up. */ + int i; + char *n = alloca (len); + memcpy (n, name, len); + + while (p = strchr (n, '=')) + { + for (i = 0; i < LC_ALL; ++i) + if (_nl_category_name_sizes[i] == p - n && + !memcmp (_nl_category_names[i], n, p - n)) + break; + if (i == LC_ALL) + { + /* Bogus category name. */ + errno = EINVAL; + return NULL; + } + if (i < LC_ALL) + { + /* Found the category this clause sets. */ + char *end = strchr (++p, ';'); + newnames[i] = p; + if (end) + { + /* Examine the next clause. */ + *end = '\0'; + n = end + 1; + } + else + /* This was the last clause. We are done. */ + break; + } + } + + for (i = 0; i < LC_ALL; ++i) + if (newnames[i] == name) + /* The composite name did not specify all categories. */ + return NULL; + } + + /* Load the new data for each category. */ + while (category-- > 0) + /* Only actually load the data if anything will use it. */ + if (_nl_current[category]) + { + newdata[category] = _nl_load_locale (category, + &newnames[category]); + if (newdata[category]) + newnames[category] = copy (newnames[category]); + if (! newdata[category] || ! newnames[category]) + { + if (!strcmp (newnames[category], "C") || + !strcmp (newnames[category], "POSIX")) + { + /* Loading from a file failed, but this is a request + for the default locale. Use the built-in data. */ + if (! newdata[category]) + newdata[category] + = (struct locale_data *) _nl_C[category]; + newnames[category] = (char *) _nl_C_name; + } + else + { + /* Loading this part of the locale failed. + Abort the composite load. */ + abort_composite: + while (++category < LC_ALL) + { + if (_nl_current[category]) + _nl_free_locale (newdata[category]); + if (newnames[category] != _nl_C_name) + free (newnames[category]); + } + return NULL; + } + } + } + else + { + /* The data is never used; just change the name. */ + newnames[category] = copy (newnames[category]); + if (! newnames[category]) + { + if (!strcmp (newnames[category], "C") || + !strcmp (newnames[category], "POSIX")) + newnames[category] = (char *) _nl_C_name; + else + { + while (++category < LC_ALL) + if (newnames[category] != _nl_C_name) + free (newnames[category]); + } + } + } + + composite = new_composite_name (LC_ALL, newnames); + if (! composite) + { + category = -1; + goto abort_composite; + } + + /* Now we have loaded all the new data. Put it in place. */ + for (; category < LC_ALL; ++category) + { + setdata (category, newdata[category]); + setname (category, newnames[category]); + } + setcomposite (composite); + + return composite; + } + else + { + char *newname = copy (name); + if (! newname) + { + if (!strcmp (name, "C") || !strcmp (name, "POSIX")) + newname = (char *) _nl_C_name; + else + return NULL; + } + + composite = new_composite_name (category, &newname); + if (! composite) + { + if (newname != _nl_C_name) + free (newname); + return NULL; + } - if (name == NULL || name[0] == '\0') - return (char *) "C"; + /* Only actually load the data if anything will use it. */ + if (_nl_current[category]) + { + struct locale_data *newdata = _nl_load_locale (category, + (char **) &name); + if (! newdata) + { + if (!strcmp (name, "C") || !strcmp (name, "POSIX")) + newdata = (struct locale_data *) _nl_C[category]; + else + return NULL; + } + setdata (category, newdata); + } - if (!strcmp(name, "C") || !strcmp(name, "POSIX")) - return (char *) name; + setname (category, newname); + setcomposite (composite); - errno = EINVAL; - return NULL; + return newname; + } } |