/* 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 modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public 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 #include #include #include #include #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; \ /* XXX The linker is broken so we cannot do the weak symbols right just now. */ /* 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 }; /* 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 * setlocale (int category, const char *name) { /* 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, '=')) != NULL) { 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 = 0; 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; } /* 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); } setname (category, newname); setcomposite (composite); return newname; } }