Print this page
2964 need POSIX 2008 locale object support
Reviewed by: Robert Mustacchi <rm@joyent.com>

*** 1,6 **** --- 1,7 ---- /* + * Copyright 2014 Garrett D'Amore <garrett@damore.org> * Copyright 2010 Nexenta Systems, Inc. All rights reserved. * Copyright (c) 1996 - 2002 FreeBSD Project * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. *
*** 39,236 **** #include <limits.h> #include <locale.h> #include <stdlib.h> #include <string.h> #include <unistd.h> - #include <alloca.h> #include <stdio.h> #include "collate.h" ! #include "lmonetary.h" /* for __monetary_load_locale() */ ! #include "lnumeric.h" /* for __numeric_load_locale() */ ! #include "lmessages.h" /* for __messages_load_locale() */ #include "setlocale.h" - #include "ldpart.h" - #include "timelocal.h" /* for __time_load_locale() */ #include "../i18n/_loc_path.h" /* - * Category names for getenv() Note that this was modified - * for Solaris. See <iso/locale_iso.h>. - */ - #define NUM_CATS 7 - static char *categories[7] = { - "LC_CTYPE", - "LC_NUMERIC", - "LC_TIME", - "LC_COLLATE", - "LC_MONETARY", - "LC_MESSAGES", - "LC_ALL", - }; - - /* - * Current locales for each category - */ - static char current_categories[NUM_CATS][ENCODING_LEN + 1] = { - "C", - "C", - "C", - "C", - "C", - "C", - "C", - }; - - /* * Path to locale storage directory. See ../i18n/_loc_path.h */ char *_PathLocale = _DFLT_LOC_PATH; ! /* ! * The locales we are going to try and load ! */ ! static char new_categories[NUM_CATS][ENCODING_LEN + 1]; ! static char saved_categories[NUM_CATS][ENCODING_LEN + 1]; ! static char current_locale_string[NUM_CATS * (ENCODING_LEN + 1 + 1)]; - static char *currentlocale(void); - static char *loadlocale(int); - static const char *__get_locale_env(int); - char * ! setlocale(int category, const char *locale) { ! int i, j, saverr; ! const char *env, *r; ! if (category < 0 || category >= NUM_CATS) { errno = EINVAL; return (NULL); } ! if (locale == NULL) ! return (category != LC_ALL ? ! current_categories[category] : currentlocale()); ! /* ! * Default to the current locale for everything. ! */ ! for (i = 0; i < NUM_CATS; ++i) ! (void) strcpy(new_categories[i], current_categories[i]); ! ! /* ! * Now go fill up new_categories from the locale argument ! */ ! if (!*locale) { ! if (category == LC_ALL) { ! for (i = 0; i < NUM_CATS; ++i) { ! if (i == LC_ALL) ! continue; ! env = __get_locale_env(i); ! if (strlen(env) > ENCODING_LEN) { ! errno = EINVAL; return (NULL); - } - (void) strcpy(new_categories[i], env); - } - } else { - env = __get_locale_env(category); - if (strlen(env) > ENCODING_LEN) { - errno = EINVAL; - return (NULL); - } - (void) strcpy(new_categories[category], env); - } - } else if (category != LC_ALL) { - if (strlen(locale) > ENCODING_LEN) { - errno = EINVAL; - return (NULL); - } - (void) strcpy(new_categories[category], locale); - } else { - if ((r = strchr(locale, '/')) == NULL) { - if (strlen(locale) > ENCODING_LEN) { - errno = EINVAL; - return (NULL); - } - for (i = 0; i < NUM_CATS; ++i) - (void) strcpy(new_categories[i], locale); - } else { - char *buf; - char *save; ! buf = alloca(strlen(locale) + 1); ! (void) strcpy(buf, locale); ! save = NULL; ! r = strtok_r(buf, "/", &save); ! for (i = 0; i < NUM_CATS; i++) { ! if (i == LC_ALL) ! continue; ! if (r == NULL) { ! /* ! * Composite Locale is inadequately ! * specified! (Or with empty fields.) ! * The old code would fill fields ! * out from the last one, but I think ! * this is suboptimal. ! */ ! errno = EINVAL; return (NULL); } - (void) strlcpy(new_categories[i], r, - ENCODING_LEN); - r = strtok_r(NULL, "/", &save); - } - if (r != NULL) { - /* - * Too many components - we had left over - * data in the LC_ALL. It is malformed. - */ - errno = EINVAL; - return (NULL); - } - } - } ! if (category != LC_ALL) ! return (loadlocale(category)); ! for (i = 0; i < NUM_CATS; ++i) { ! (void) strcpy(saved_categories[i], current_categories[i]); ! if (i == LC_ALL) ! continue; ! if (loadlocale(i) == NULL) { ! saverr = errno; ! for (j = 0; j < i; j++) { ! (void) strcpy(new_categories[j], ! saved_categories[j]); ! if (i == LC_ALL) ! continue; ! if (loadlocale(j) == NULL) { ! (void) strcpy(new_categories[j], "C"); ! (void) loadlocale(j); ! } ! } ! errno = saverr; ! return (NULL); ! } ! } ! return (currentlocale()); } static char * ! currentlocale(void) { - int i; int composite = 0; /* Look to see if any category is different */ ! for (i = 1; i < NUM_CATS; ++i) { ! if (i == LC_ALL) ! continue; ! if (strcmp(current_categories[0], current_categories[i])) { composite = 1; break; } } --- 40,115 ---- #include <limits.h> #include <locale.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include "collate.h" ! #include "lnumeric.h" /* for struct lc_numeric */ ! #include "lctype.h" /* for struct lc_ctype */ #include "setlocale.h" #include "../i18n/_loc_path.h" + #include "localeimpl.h" + #include "../i18n/_locale.h" /* * Path to locale storage directory. See ../i18n/_loc_path.h */ char *_PathLocale = _DFLT_LOC_PATH; ! static char *current_locale(locale_t, int); ! static void install_legacy(locale_t, int); char * ! setlocale(int category, const char *locname) { ! locale_t oldloc; ! locale_t newloc; ! int mask; ! if (category < 0 || category > LC_ALL) { errno = EINVAL; return (NULL); } ! if (locname == NULL) ! return (current_locale(__global_locale, category)); ! if ((oldloc = duplocale(__global_locale)) == NULL) return (NULL); ! mask = (category == LC_ALL ? LC_ALL_MASK : (1 << category)); ! newloc = newlocale(mask, locname, oldloc); ! if (newloc == NULL) { ! freelocale(oldloc); return (NULL); } ! oldloc = __global_locale; ! __global_locale = newloc; ! install_legacy(newloc, mask); ! ! freelocale(oldloc); ! return (current_locale(newloc, category)); } static char * ! current_locale(locale_t loc, int cat) { int composite = 0; + static char buf[LC_ALL * (ENCODING_LEN + 1 + 1)]; + if (cat != LC_ALL) { + return (loc->locdata[cat]->l_lname); + } + /* Look to see if any category is different */ ! for (int i = 1; i < LC_ALL; ++i) { ! if (strcmp(loc->locdata[0]->l_lname, ! loc->locdata[i]->l_lname) != 0) { composite = 1; break; } }
*** 238,329 **** /* * Note ordering of these follows the numeric order, * if the order is changed, then setlocale() will need * to be changed as well. */ ! (void) snprintf(current_locale_string, ! sizeof (current_locale_string), "%s/%s/%s/%s/%s/%s", ! current_categories[LC_CTYPE], ! current_categories[LC_NUMERIC], ! current_categories[LC_TIME], ! current_categories[LC_COLLATE], ! current_categories[LC_MONETARY], ! current_categories[LC_MESSAGES]); ! } else { ! (void) strlcpy(current_locale_string, current_categories[0], ! sizeof (current_locale_string)); } ! return (current_locale_string); } ! static char * ! loadlocale(int category) { ! char *new = new_categories[category]; ! char *old = current_categories[category]; ! int (*func)(const char *); ! ! if ((new[0] == '.' && ! (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || ! strchr(new, '/') != NULL) { ! errno = EINVAL; ! return (NULL); } ! switch (category) { ! case LC_CTYPE: ! func = __wrap_setrunelocale; ! break; ! case LC_COLLATE: ! func = _collate_load_tables; ! break; ! case LC_TIME: ! func = __time_load_locale; ! break; ! case LC_NUMERIC: ! func = __numeric_load_locale; ! break; ! case LC_MONETARY: ! func = __monetary_load_locale; ! break; ! case LC_MESSAGES: ! func = __messages_load_locale; ! break; ! default: ! errno = EINVAL; ! return (NULL); } ! if (strcmp(new, old) == 0) ! return (old); ! if (func(new) != _LDP_ERROR) { ! (void) strcpy(old, new); ! return (old); } ! return (NULL); ! } ! ! static const char * ! __get_locale_env(int category) ! { ! const char *env; ! ! /* 1. check LC_ALL. */ ! env = getenv(categories[LC_ALL]); ! ! /* 2. check LC_* */ ! if (env == NULL || !*env) ! env = getenv(categories[category]); ! ! /* 3. check LANG */ ! if (env == NULL || !*env) ! env = getenv("LANG"); ! ! /* 4. if none is set, fall to "C" */ ! if (env == NULL || !*env) ! env = "C"; ! ! return (env); } --- 117,181 ---- /* * Note ordering of these follows the numeric order, * if the order is changed, then setlocale() will need * to be changed as well. */ ! (void) snprintf(buf, sizeof (buf), "%s/%s/%s/%s/%s/%s", ! loc->locdata[LC_CTYPE]->l_lname, ! loc->locdata[LC_NUMERIC]->l_lname, ! loc->locdata[LC_TIME]->l_lname, ! loc->locdata[LC_COLLATE]->l_lname, ! loc->locdata[LC_MONETARY]->l_lname, ! loc->locdata[LC_MESSAGES]->l_lname); ! return (buf); } ! ! return (loc->locdata[0]->l_lname); } ! static void ! install_legacy(locale_t loc, int mask) { ! /* ! * Update the legacy fixed variables that may be baked into ! * legacy programs. This is really unfortunate, but we can't ! * solve for them otherwise. Note that such legacy programs ! * are only going to see the global locale settings, and cannot ! * benefit from uselocale(). ! */ ! if (mask & LC_NUMERIC_MASK) { ! struct lc_numeric *lnum; ! lnum = loc->locdata[LC_NUMERIC]->l_data[0]; ! _numeric[0] = *lnum->decimal_point; ! _numeric[1] = *lnum->thousands_sep; } ! if (mask & LC_CTYPE_MASK) { ! struct lc_ctype *lct; ! lct = loc->locdata[LC_CTYPE]->l_data[0]; ! for (int i = 0; i < _CACHED_RUNES; i++) { ! /* ctype can only encode the lower 8 bits. */ ! __ctype[i+1] = lct->lc_ctype_mask[i] & 0xff; ! __ctype_mask[i] = lct->lc_ctype_mask[i]; } ! /* The bottom half is the toupper/lower array */ ! for (int i = 0; i < _CACHED_RUNES; i++) { ! int u, l; ! __ctype[258 + i] = i; ! u = lct->lc_trans_upper[i]; ! l = lct->lc_trans_lower[i]; ! if (u && u != i) ! __ctype[258+i] = u; ! if (l && l != i) ! __ctype[258+i] = l; ! /* Don't forget these annoyances either! */ ! __trans_upper[i] = u; ! __trans_lower[i] = l; } ! /* Maximum mblen, cswidth, weird legacy */ ! __ctype[520] = lct->lc_max_mblen; ! } }