12d08521bSGarrett D'Amore /* 22d08521bSGarrett D'Amore * This file and its contents are supplied under the terms of the 32d08521bSGarrett D'Amore * Common Development and Distribution License ("CDDL"), version 1.0. 42d08521bSGarrett D'Amore * You may only use this file in accordance with the terms of version 52d08521bSGarrett D'Amore * 1.0 of the CDDL. 62d08521bSGarrett D'Amore * 72d08521bSGarrett D'Amore * A full copy of the text of the CDDL should have accompanied this 82d08521bSGarrett D'Amore * source. A copy of the CDDL is also available via the Internet at 92d08521bSGarrett D'Amore * http://www.illumos.org/license/CDDL. 102d08521bSGarrett D'Amore */ 112d08521bSGarrett D'Amore 122d08521bSGarrett D'Amore /* 132d08521bSGarrett D'Amore * Copyright 2014 Garrett D'Amore <garrett@damore.org> 142d08521bSGarrett D'Amore */ 152d08521bSGarrett D'Amore 162d08521bSGarrett D'Amore /* 172d08521bSGarrett D'Amore * This file implements the 2008 newlocale and friends handling. 182d08521bSGarrett D'Amore */ 192d08521bSGarrett D'Amore 202d08521bSGarrett D'Amore #ifndef _LCONV_C99 212d08521bSGarrett D'Amore #define _LCONV_C99 222d08521bSGarrett D'Amore #endif 232d08521bSGarrett D'Amore 242d08521bSGarrett D'Amore #include "lint.h" 252d08521bSGarrett D'Amore #include <atomic.h> 262d08521bSGarrett D'Amore #include <locale.h> 272d08521bSGarrett D'Amore #include <sys/types.h> 282d08521bSGarrett D'Amore #include <sys/mman.h> 292d08521bSGarrett D'Amore #include <errno.h> 302d08521bSGarrett D'Amore #include <string.h> 312d08521bSGarrett D'Amore #include "libc.h" 322d08521bSGarrett D'Amore #include "mtlib.h" 332d08521bSGarrett D'Amore #include "tsd.h" 342d08521bSGarrett D'Amore #include "localeimpl.h" 352d08521bSGarrett D'Amore #include "lctype.h" 362d08521bSGarrett D'Amore 372d08521bSGarrett D'Amore /* 382d08521bSGarrett D'Amore * Big Theory of Locales: 392d08521bSGarrett D'Amore * 402d08521bSGarrett D'Amore * (It is recommended that readers familiarize themselves with the POSIX 412d08521bSGarrett D'Amore * 2008 (XPG Issue 7) specifications for locales, first.) 422d08521bSGarrett D'Amore * 432d08521bSGarrett D'Amore * Historically, we had a bunch of global variables that stored locale 442d08521bSGarrett D'Amore * data. While this worked well, it limited applications to a single locale 452d08521bSGarrett D'Amore * at a time. This doesn't work well in certain server applications. 462d08521bSGarrett D'Amore * 472d08521bSGarrett D'Amore * Issue 7, X/Open introduced the concept of a locale_t object, along with 482d08521bSGarrett D'Amore * versions of functions that can take this object as a parameter, along 492d08521bSGarrett D'Amore * with functions to clone and manipulate these locale objects. The new 502d08521bSGarrett D'Amore * functions are named with a _l() suffix. 512d08521bSGarrett D'Amore * 522d08521bSGarrett D'Amore * Additionally uselocale() is introduced which can change the locale of 532d08521bSGarrett D'Amore * of a single thread. However, setlocale() can still be used to change 542d08521bSGarrett D'Amore * the global locale. 552d08521bSGarrett D'Amore * 562d08521bSGarrett D'Amore * In our implementation, we use libc's TSD to store the locale data that 572d08521bSGarrett D'Amore * was previously global. We still have global data because some applications 582d08521bSGarrett D'Amore * have had those global objects compiled into them. (Such applications will 592d08521bSGarrett D'Amore * be unable to benefit from uselocale(), btw.) The legacy routines are 602d08521bSGarrett D'Amore * reimplemented as wrappers that use the appropriate locale object by 612d08521bSGarrett D'Amore * calling uselocale(). uselocale() when passed a NULL pointer returns the 622d08521bSGarrett D'Amore * thread-specific locale object if one is present, or the global locale 632d08521bSGarrett D'Amore * object otherwise. Note that once the TSD data is set, the only way 642d08521bSGarrett D'Amore * to revert to the global locale is to pass the global locale LC_GLOBAL_LOCALE 652d08521bSGarrett D'Amore * to uselocale(). 662d08521bSGarrett D'Amore * 672d08521bSGarrett D'Amore * We are careful to minimize performance impact of multiple calls to 682d08521bSGarrett D'Amore * uselocale() or setlocale() by using a cache of locale data whenever possible. 692d08521bSGarrett D'Amore * As a consequence of this, applications that iterate over all possible 702d08521bSGarrett D'Amore * locales will burn through a lot of virtual memory, but we find such 712d08521bSGarrett D'Amore * applications rare. (locale -a might be an exception, but it is short lived.) 722d08521bSGarrett D'Amore * 732d08521bSGarrett D'Amore * Category data is never released (although enclosing locale objects might be), 742d08521bSGarrett D'Amore * in order to guarantee thread-safety. Calling freelocale() on an object 752d08521bSGarrett D'Amore * while it is in use by another thread is a programmer error (use-after-free) 762d08521bSGarrett D'Amore * and we don't bother to note it further. 772d08521bSGarrett D'Amore * 782d08521bSGarrett D'Amore * Locale objects (global locales) established by setlocale() are also 792d08521bSGarrett D'Amore * never freed (for MT safety), but we will save previous locale objects 802d08521bSGarrett D'Amore * and reuse them when we can. 812d08521bSGarrett D'Amore */ 822d08521bSGarrett D'Amore 832d08521bSGarrett D'Amore typedef struct locdata *(*loadfn_t)(const char *); 842d08521bSGarrett D'Amore 852d08521bSGarrett D'Amore static const loadfn_t loaders[LC_ALL] = { 862d08521bSGarrett D'Amore __lc_ctype_load, 872d08521bSGarrett D'Amore __lc_numeric_load, 882d08521bSGarrett D'Amore __lc_time_load, 892d08521bSGarrett D'Amore __lc_collate_load, 902d08521bSGarrett D'Amore __lc_monetary_load, 912d08521bSGarrett D'Amore __lc_messages_load, 922d08521bSGarrett D'Amore }; 932d08521bSGarrett D'Amore 942d08521bSGarrett D'Amore extern struct lc_monetary lc_monetary_posix; 952d08521bSGarrett D'Amore extern struct lc_numeric lc_numeric_posix; 962d08521bSGarrett D'Amore extern struct lc_messages lc_messages_posix; 972d08521bSGarrett D'Amore extern struct lc_time lc_time_posix; 982d08521bSGarrett D'Amore extern struct lc_ctype lc_ctype_posix; 992d08521bSGarrett D'Amore extern struct lc_collate lc_collate_posix; 100*bc09504fSGordon Ross extern struct _RuneLocale _DefaultRuneLocale; 1012d08521bSGarrett D'Amore 102732efd55SDan McDonald static struct _locale posix_locale = { 1032d08521bSGarrett D'Amore /* locdata */ 1042d08521bSGarrett D'Amore .locdata = { 1052d08521bSGarrett D'Amore &__posix_ctype_locdata, 1062d08521bSGarrett D'Amore &__posix_numeric_locdata, 1072d08521bSGarrett D'Amore &__posix_time_locdata, 1082d08521bSGarrett D'Amore &__posix_collate_locdata, 1092d08521bSGarrett D'Amore &__posix_monetary_locdata, 1102d08521bSGarrett D'Amore &__posix_messages_locdata, 1112d08521bSGarrett D'Amore }, 1122d08521bSGarrett D'Amore .locname = "C", 1132d08521bSGarrett D'Amore .ctype = &lc_ctype_posix, 1142d08521bSGarrett D'Amore .numeric = &lc_numeric_posix, 1152d08521bSGarrett D'Amore .collate = &lc_collate_posix, 1162d08521bSGarrett D'Amore .monetary = &lc_monetary_posix, 1172d08521bSGarrett D'Amore .messages = &lc_messages_posix, 1182d08521bSGarrett D'Amore .time = &lc_time_posix, 1192d08521bSGarrett D'Amore .runelocale = &_DefaultRuneLocale, 1202d08521bSGarrett D'Amore }; 1212d08521bSGarrett D'Amore 1222d08521bSGarrett D'Amore locale_t ___global_locale = &posix_locale; 1232d08521bSGarrett D'Amore 1242d08521bSGarrett D'Amore locale_t 1252d08521bSGarrett D'Amore __global_locale(void) 1262d08521bSGarrett D'Amore { 1272d08521bSGarrett D'Amore return (___global_locale); 1282d08521bSGarrett D'Amore } 1292d08521bSGarrett D'Amore 1302d08521bSGarrett D'Amore /* 1312d08521bSGarrett D'Amore * Category names for getenv() Note that this was modified 1322d08521bSGarrett D'Amore * for Solaris. See <iso/locale_iso.h>. 1332d08521bSGarrett D'Amore */ 1342d08521bSGarrett D'Amore #define NUM_CATS 7 1352d08521bSGarrett D'Amore static char *categories[7] = { 1362d08521bSGarrett D'Amore "LC_CTYPE", 1372d08521bSGarrett D'Amore "LC_NUMERIC", 1382d08521bSGarrett D'Amore "LC_TIME", 1392d08521bSGarrett D'Amore "LC_COLLATE", 1402d08521bSGarrett D'Amore "LC_MONETARY", 1412d08521bSGarrett D'Amore "LC_MESSAGES", 1422d08521bSGarrett D'Amore "LC_ALL", 1432d08521bSGarrett D'Amore }; 1442d08521bSGarrett D'Amore 1452d08521bSGarrett D'Amore /* 1462d08521bSGarrett D'Amore * Prototypes. 1472d08521bSGarrett D'Amore */ 1482d08521bSGarrett D'Amore static const char *get_locale_env(int); 1492d08521bSGarrett D'Amore static struct locdata *locdata_get(int, const const char *); 1502d08521bSGarrett D'Amore static struct locdata *locdata_get_cache(int, const char *); 1512d08521bSGarrett D'Amore static locale_t mklocname(locale_t); 1522d08521bSGarrett D'Amore 1532d08521bSGarrett D'Amore /* 1542d08521bSGarrett D'Amore * Some utility routines. 1552d08521bSGarrett D'Amore */ 1562d08521bSGarrett D'Amore 1572d08521bSGarrett D'Amore struct locdata * 1582d08521bSGarrett D'Amore __locdata_alloc(const char *name, size_t memsz) 1592d08521bSGarrett D'Amore { 1602d08521bSGarrett D'Amore struct locdata *ldata; 1612d08521bSGarrett D'Amore 1622d08521bSGarrett D'Amore if ((ldata = lmalloc(sizeof (*ldata))) == NULL) { 1632d08521bSGarrett D'Amore return (NULL); 1642d08521bSGarrett D'Amore } 1652d08521bSGarrett D'Amore if ((ldata->l_data[0] = libc_malloc(memsz)) == NULL) { 1662d08521bSGarrett D'Amore lfree(ldata, sizeof (*ldata)); 1672d08521bSGarrett D'Amore errno = ENOMEM; 1682d08521bSGarrett D'Amore return (NULL); 1692d08521bSGarrett D'Amore } 1702d08521bSGarrett D'Amore (void) strlcpy(ldata->l_lname, name, sizeof (ldata->l_lname)); 1712d08521bSGarrett D'Amore 1722d08521bSGarrett D'Amore return (ldata); 1732d08521bSGarrett D'Amore } 1742d08521bSGarrett D'Amore 1752d08521bSGarrett D'Amore /* 1762d08521bSGarrett D'Amore * Normally we never free locale data truly, but if we failed to load it 1772d08521bSGarrett D'Amore * for some reason, this routine is used to cleanup the partial mess. 1782d08521bSGarrett D'Amore */ 1792d08521bSGarrett D'Amore void 1802d08521bSGarrett D'Amore __locdata_free(struct locdata *ldata) 1812d08521bSGarrett D'Amore { 1822d08521bSGarrett D'Amore for (int i = 0; i < NLOCDATA; i++) 1832d08521bSGarrett D'Amore libc_free(ldata->l_data[i]); 1842d08521bSGarrett D'Amore if (ldata->l_map != NULL && ldata->l_map_len) 1852d08521bSGarrett D'Amore (void) munmap(ldata->l_map, ldata->l_map_len); 1862d08521bSGarrett D'Amore lfree(ldata, sizeof (*ldata)); 1872d08521bSGarrett D'Amore } 1882d08521bSGarrett D'Amore 1892d08521bSGarrett D'Amore /* 1902d08521bSGarrett D'Amore * It turns out that for performance reasons we would really like to 1912d08521bSGarrett D'Amore * cache the most recently referenced locale data to avoid wasteful 1922d08521bSGarrett D'Amore * loading from files. 1932d08521bSGarrett D'Amore */ 1942d08521bSGarrett D'Amore 1952d08521bSGarrett D'Amore static struct locdata *cache_data[LC_ALL]; 1962d08521bSGarrett D'Amore static struct locdata *cat_data[LC_ALL]; 1972d08521bSGarrett D'Amore static mutex_t cache_lock = DEFAULTMUTEX; 1982d08521bSGarrett D'Amore 1992d08521bSGarrett D'Amore /* 2002d08521bSGarrett D'Amore * Returns the cached data if the locale name is the same. If not, 2012d08521bSGarrett D'Amore * returns NULL (cache miss). The locdata is returned with a hold on 2022d08521bSGarrett D'Amore * it, taken on behalf of the caller. The caller should drop the hold 2032d08521bSGarrett D'Amore * when it is finished. 2042d08521bSGarrett D'Amore */ 2052d08521bSGarrett D'Amore static struct locdata * 2062d08521bSGarrett D'Amore locdata_get_cache(int category, const char *locname) 2072d08521bSGarrett D'Amore { 2082d08521bSGarrett D'Amore struct locdata *loc; 2092d08521bSGarrett D'Amore 2102d08521bSGarrett D'Amore if (category < 0 || category >= LC_ALL) 2112d08521bSGarrett D'Amore return (NULL); 2122d08521bSGarrett D'Amore 2132d08521bSGarrett D'Amore /* Try cache first. */ 2142d08521bSGarrett D'Amore lmutex_lock(&cache_lock); 2152d08521bSGarrett D'Amore loc = cache_data[category]; 2162d08521bSGarrett D'Amore 2172d08521bSGarrett D'Amore if ((loc != NULL) && (strcmp(loc->l_lname, locname) == 0)) { 2182d08521bSGarrett D'Amore lmutex_unlock(&cache_lock); 2192d08521bSGarrett D'Amore return (loc); 2202d08521bSGarrett D'Amore } 2212d08521bSGarrett D'Amore 2222d08521bSGarrett D'Amore /* 2232d08521bSGarrett D'Amore * Failing that try previously loaded locales (linear search) -- 2242d08521bSGarrett D'Amore * this could be optimized to a hash, but its unlikely that a single 2252d08521bSGarrett D'Amore * application will ever need to work with more than a few locales. 2262d08521bSGarrett D'Amore */ 2272d08521bSGarrett D'Amore for (loc = cat_data[category]; loc != NULL; loc = loc->l_next) { 2282d08521bSGarrett D'Amore if (strcmp(locname, loc->l_lname) == 0) { 2292d08521bSGarrett D'Amore break; 2302d08521bSGarrett D'Amore } 2312d08521bSGarrett D'Amore } 2322d08521bSGarrett D'Amore 2332d08521bSGarrett D'Amore /* 2342d08521bSGarrett D'Amore * Finally, if we still don't have one, try loading the locale 2352d08521bSGarrett D'Amore * data from the actual on-disk data. 2362d08521bSGarrett D'Amore * 2372d08521bSGarrett D'Amore * We drop the lock (libc wants to ensure no internal locks 2382d08521bSGarrett D'Amore * are held when we call other routines required to read from 2392d08521bSGarrett D'Amore * files, allocate memory, etc.) There is a small race here, 2402d08521bSGarrett D'Amore * but the consequences of the race are benign -- if multiple 2412d08521bSGarrett D'Amore * threads hit this at precisely the same point, we could 2422d08521bSGarrett D'Amore * wind up with duplicates of the locale data in the cache. 2432d08521bSGarrett D'Amore * 2442d08521bSGarrett D'Amore * This wastes the memory for an extra copy of the locale 2452d08521bSGarrett D'Amore * data, but there is no further harm beyond that. Its not 2462d08521bSGarrett D'Amore * worth the effort to recode this to something "safe" 2472d08521bSGarrett D'Amore * (which would require rescanning the list, etc.), given 2482d08521bSGarrett D'Amore * that this race will probably never actually occur. 2492d08521bSGarrett D'Amore */ 2502d08521bSGarrett D'Amore if (loc == NULL) { 2512d08521bSGarrett D'Amore lmutex_unlock(&cache_lock); 2522d08521bSGarrett D'Amore loc = (*loaders[category])(locname); 2532d08521bSGarrett D'Amore lmutex_lock(&cache_lock); 2542d08521bSGarrett D'Amore if (loc != NULL) 2552d08521bSGarrett D'Amore (void) strlcpy(loc->l_lname, locname, 2562d08521bSGarrett D'Amore sizeof (loc->l_lname)); 2572d08521bSGarrett D'Amore } 2582d08521bSGarrett D'Amore 2592d08521bSGarrett D'Amore /* 2602d08521bSGarrett D'Amore * Assuming we got one, update the cache, and stick us on the list 2612d08521bSGarrett D'Amore * of loaded locale data. We insert into the head (more recent 2622d08521bSGarrett D'Amore * use is likely to win.) 2632d08521bSGarrett D'Amore */ 2642d08521bSGarrett D'Amore if (loc != NULL) { 2652d08521bSGarrett D'Amore cache_data[category] = loc; 2662d08521bSGarrett D'Amore if (!loc->l_cached) { 2672d08521bSGarrett D'Amore loc->l_cached = 1; 2682d08521bSGarrett D'Amore loc->l_next = cat_data[category]; 2692d08521bSGarrett D'Amore cat_data[category] = loc; 2702d08521bSGarrett D'Amore } 2712d08521bSGarrett D'Amore } 2722d08521bSGarrett D'Amore 2732d08521bSGarrett D'Amore lmutex_unlock(&cache_lock); 2742d08521bSGarrett D'Amore return (loc); 2752d08521bSGarrett D'Amore } 2762d08521bSGarrett D'Amore 2772d08521bSGarrett D'Amore /* 2782d08521bSGarrett D'Amore * Routine to get the locdata for a given category and locale. 2792d08521bSGarrett D'Amore * This includes retrieving it from cache, retrieving it from 2802d08521bSGarrett D'Amore * a file, etc. 2812d08521bSGarrett D'Amore */ 2822d08521bSGarrett D'Amore static struct locdata * 2832d08521bSGarrett D'Amore locdata_get(int category, const char *locname) 2842d08521bSGarrett D'Amore { 2852d08521bSGarrett D'Amore char scratch[ENCODING_LEN + 1]; 2862d08521bSGarrett D'Amore char *slash; 2872d08521bSGarrett D'Amore int cnt; 2882d08521bSGarrett D'Amore int len; 2892d08521bSGarrett D'Amore 2902d08521bSGarrett D'Amore if (locname == NULL || *locname == 0) { 2912d08521bSGarrett D'Amore locname = get_locale_env(category); 2922d08521bSGarrett D'Amore } 2932d08521bSGarrett D'Amore 2942d08521bSGarrett D'Amore /* 2952d08521bSGarrett D'Amore * Extract the locale name for the category if it is a composite 2962d08521bSGarrett D'Amore * locale. 2972d08521bSGarrett D'Amore */ 2982d08521bSGarrett D'Amore if ((slash = strchr(locname, '/')) != NULL) { 2992d08521bSGarrett D'Amore for (cnt = category; cnt && slash != NULL; cnt--) { 3002d08521bSGarrett D'Amore locname = slash + 1; 3012d08521bSGarrett D'Amore slash = strchr(locname, '/'); 3022d08521bSGarrett D'Amore } 3032d08521bSGarrett D'Amore if (slash) { 3042d08521bSGarrett D'Amore len = slash - locname + 1; 3052d08521bSGarrett D'Amore if (len >= sizeof (scratch)) { 3062d08521bSGarrett D'Amore len = sizeof (scratch); 3072d08521bSGarrett D'Amore } 3082d08521bSGarrett D'Amore } else { 3092d08521bSGarrett D'Amore len = sizeof (scratch); 3102d08521bSGarrett D'Amore } 3112d08521bSGarrett D'Amore (void) strlcpy(scratch, locname, len); 3122d08521bSGarrett D'Amore locname = scratch; 3132d08521bSGarrett D'Amore } 3142d08521bSGarrett D'Amore 3152d08521bSGarrett D'Amore if ((strcmp(locname, "C") == 0) || (strcmp(locname, "POSIX") == 0)) 3162d08521bSGarrett D'Amore return (posix_locale.locdata[category]); 3172d08521bSGarrett D'Amore 3182d08521bSGarrett D'Amore return (locdata_get_cache(category, locname)); 3192d08521bSGarrett D'Amore } 3202d08521bSGarrett D'Amore 3212d08521bSGarrett D'Amore /* tsd destructor */ 3222d08521bSGarrett D'Amore static void 3232d08521bSGarrett D'Amore freelocptr(void *arg) 3242d08521bSGarrett D'Amore { 3252d08521bSGarrett D'Amore locale_t *locptr = arg; 3262d08521bSGarrett D'Amore if (*locptr != NULL) 3272d08521bSGarrett D'Amore freelocale(*locptr); 3282d08521bSGarrett D'Amore } 3292d08521bSGarrett D'Amore 3302d08521bSGarrett D'Amore static const char * 3312d08521bSGarrett D'Amore get_locale_env(int category) 3322d08521bSGarrett D'Amore { 3332d08521bSGarrett D'Amore const char *env; 3342d08521bSGarrett D'Amore 3352d08521bSGarrett D'Amore /* 1. check LC_ALL. */ 3362d08521bSGarrett D'Amore env = getenv(categories[LC_ALL]); 3372d08521bSGarrett D'Amore 3382d08521bSGarrett D'Amore /* 2. check LC_* */ 3392d08521bSGarrett D'Amore if (env == NULL || *env == '\0') 3402d08521bSGarrett D'Amore env = getenv(categories[category]); 3412d08521bSGarrett D'Amore 3422d08521bSGarrett D'Amore /* 3. check LANG */ 3432d08521bSGarrett D'Amore if (env == NULL || *env == '\0') 3442d08521bSGarrett D'Amore env = getenv("LANG"); 3452d08521bSGarrett D'Amore 3462d08521bSGarrett D'Amore /* 4. if none is set, fall to "C" */ 3472d08521bSGarrett D'Amore if (env == NULL || *env == '\0') 3482d08521bSGarrett D'Amore env = "C"; 3492d08521bSGarrett D'Amore 3502d08521bSGarrett D'Amore return (env); 3512d08521bSGarrett D'Amore } 3522d08521bSGarrett D'Amore 3532d08521bSGarrett D'Amore 3542d08521bSGarrett D'Amore /* 3552d08521bSGarrett D'Amore * This routine is exposed via the MB_CUR_MAX macro. Note that legacy 3562d08521bSGarrett D'Amore * code will continue to use _ctype[520], but we prefer this function as 3572d08521bSGarrett D'Amore * it is the only way to get thread-specific information. 3582d08521bSGarrett D'Amore */ 3592d08521bSGarrett D'Amore unsigned char 3602d08521bSGarrett D'Amore __mb_cur_max_l(locale_t loc) 3612d08521bSGarrett D'Amore { 3622d08521bSGarrett D'Amore return (loc->ctype->lc_max_mblen); 3632d08521bSGarrett D'Amore } 3642d08521bSGarrett D'Amore 3652d08521bSGarrett D'Amore unsigned char 3662d08521bSGarrett D'Amore __mb_cur_max(void) 3672d08521bSGarrett D'Amore { 3682d08521bSGarrett D'Amore return (__mb_cur_max_l(uselocale(NULL))); 3692d08521bSGarrett D'Amore } 3702d08521bSGarrett D'Amore 3712d08521bSGarrett D'Amore /* 3722d08521bSGarrett D'Amore * Public interfaces. 3732d08521bSGarrett D'Amore */ 3742d08521bSGarrett D'Amore 3752d08521bSGarrett D'Amore locale_t 3762d08521bSGarrett D'Amore duplocale(locale_t src) 3772d08521bSGarrett D'Amore { 3782d08521bSGarrett D'Amore locale_t loc; 3792d08521bSGarrett D'Amore int i; 3802d08521bSGarrett D'Amore 3812d08521bSGarrett D'Amore loc = lmalloc(sizeof (*loc)); 3822d08521bSGarrett D'Amore if (loc == NULL) { 3832d08521bSGarrett D'Amore return (NULL); 3842d08521bSGarrett D'Amore } 3852d08521bSGarrett D'Amore if (src == NULL) { 3862d08521bSGarrett D'Amore /* illumos extension: POSIX says LC_GLOBAL_LOCALE here */ 3872d08521bSGarrett D'Amore src = ___global_locale; 3882d08521bSGarrett D'Amore } 3892d08521bSGarrett D'Amore for (i = 0; i < LC_ALL; i++) { 3902d08521bSGarrett D'Amore loc->locdata[i] = src->locdata[i]; 3912d08521bSGarrett D'Amore loc->loaded[i] = 0; 3922d08521bSGarrett D'Amore } 3932d08521bSGarrett D'Amore loc->collate = loc->locdata[LC_COLLATE]->l_data[0]; 3942d08521bSGarrett D'Amore loc->ctype = loc->locdata[LC_CTYPE]->l_data[0]; 3952d08521bSGarrett D'Amore loc->runelocale = loc->locdata[LC_CTYPE]->l_data[1]; 3962d08521bSGarrett D'Amore loc->messages = loc->locdata[LC_MESSAGES]->l_data[0]; 3972d08521bSGarrett D'Amore loc->monetary = loc->locdata[LC_MONETARY]->l_data[0]; 3982d08521bSGarrett D'Amore loc->numeric = loc->locdata[LC_NUMERIC]->l_data[0]; 3992d08521bSGarrett D'Amore loc->time = loc->locdata[LC_TIME]->l_data[0]; 4002d08521bSGarrett D'Amore return (loc); 4012d08521bSGarrett D'Amore } 4022d08521bSGarrett D'Amore 4032d08521bSGarrett D'Amore void 4042d08521bSGarrett D'Amore freelocale(locale_t loc) 4052d08521bSGarrett D'Amore { 4062d08521bSGarrett D'Amore /* 4072d08521bSGarrett D'Amore * We take extra care never to free a saved locale created by 4082d08521bSGarrett D'Amore * setlocale(). This shouldn't be strictly necessary, but a little 4092d08521bSGarrett D'Amore * extra safety doesn't hurt here. 4102d08521bSGarrett D'Amore */ 4112d08521bSGarrett D'Amore if ((loc != NULL) && (loc != &posix_locale) && (!loc->on_list)) 4122d08521bSGarrett D'Amore lfree(loc, sizeof (*loc)); 4132d08521bSGarrett D'Amore } 4142d08521bSGarrett D'Amore 4152d08521bSGarrett D'Amore locale_t 4162d08521bSGarrett D'Amore newlocale(int catmask, const char *locname, locale_t base) 4172d08521bSGarrett D'Amore { 4182d08521bSGarrett D'Amore locale_t loc; 4192d08521bSGarrett D'Amore int i, e; 4202d08521bSGarrett D'Amore 4212d08521bSGarrett D'Amore if (catmask & ~(LC_ALL_MASK)) { 4222d08521bSGarrett D'Amore errno = EINVAL; 4232d08521bSGarrett D'Amore return (NULL); 4242d08521bSGarrett D'Amore } 4252d08521bSGarrett D'Amore 4262d08521bSGarrett D'Amore /* 4272d08521bSGarrett D'Amore * Technically passing LC_GLOBAL_LOCALE here is illegal, 4282d08521bSGarrett D'Amore * but we allow it. 4292d08521bSGarrett D'Amore */ 4302d08521bSGarrett D'Amore if (base == NULL || base == ___global_locale) { 4312d08521bSGarrett D'Amore loc = duplocale(___global_locale); 4322d08521bSGarrett D'Amore } else { 4332d08521bSGarrett D'Amore loc = duplocale(base); 4342d08521bSGarrett D'Amore } 4352d08521bSGarrett D'Amore if (loc == NULL) { 4362d08521bSGarrett D'Amore return (NULL); 4372d08521bSGarrett D'Amore } 4382d08521bSGarrett D'Amore 4392d08521bSGarrett D'Amore for (i = 0; i < LC_ALL; i++) { 4402d08521bSGarrett D'Amore struct locdata *ldata; 4412d08521bSGarrett D'Amore loc->loaded[i] = 0; 4422d08521bSGarrett D'Amore if (((1 << i) & catmask) == 0) { 4432d08521bSGarrett D'Amore /* Default to base locale if not overriding */ 4442d08521bSGarrett D'Amore continue; 4452d08521bSGarrett D'Amore } 4462d08521bSGarrett D'Amore ldata = locdata_get(i, locname); 4472d08521bSGarrett D'Amore if (ldata == NULL) { 4482d08521bSGarrett D'Amore e = errno; 4492d08521bSGarrett D'Amore freelocale(loc); 4502d08521bSGarrett D'Amore errno = e; 4512d08521bSGarrett D'Amore return (NULL); 4522d08521bSGarrett D'Amore } 4532d08521bSGarrett D'Amore loc->locdata[i] = ldata; 4542d08521bSGarrett D'Amore } 4552d08521bSGarrett D'Amore loc->collate = loc->locdata[LC_COLLATE]->l_data[0]; 4562d08521bSGarrett D'Amore loc->ctype = loc->locdata[LC_CTYPE]->l_data[0]; 4572d08521bSGarrett D'Amore loc->runelocale = loc->locdata[LC_CTYPE]->l_data[1]; 4582d08521bSGarrett D'Amore loc->messages = loc->locdata[LC_MESSAGES]->l_data[0]; 4592d08521bSGarrett D'Amore loc->monetary = loc->locdata[LC_MONETARY]->l_data[0]; 4602d08521bSGarrett D'Amore loc->numeric = loc->locdata[LC_NUMERIC]->l_data[0]; 4612d08521bSGarrett D'Amore loc->time = loc->locdata[LC_TIME]->l_data[0]; 4622d08521bSGarrett D'Amore freelocale(base); 4632d08521bSGarrett D'Amore 4642d08521bSGarrett D'Amore return (mklocname(loc)); 4652d08521bSGarrett D'Amore } 4662d08521bSGarrett D'Amore 4672d08521bSGarrett D'Amore locale_t 4682d08521bSGarrett D'Amore uselocale(locale_t loc) 4692d08521bSGarrett D'Amore { 4702d08521bSGarrett D'Amore locale_t lastloc = ___global_locale; 4712d08521bSGarrett D'Amore locale_t *locptr; 4722d08521bSGarrett D'Amore 4732d08521bSGarrett D'Amore locptr = tsdalloc(_T_SETLOCALE, sizeof (locale_t), freelocptr); 4742d08521bSGarrett D'Amore /* Should never occur */ 4752d08521bSGarrett D'Amore if (locptr == NULL) { 4762d08521bSGarrett D'Amore errno = EINVAL; 4772d08521bSGarrett D'Amore return (NULL); 4782d08521bSGarrett D'Amore } 4792d08521bSGarrett D'Amore 4802d08521bSGarrett D'Amore if (*locptr != NULL) 4812d08521bSGarrett D'Amore lastloc = *locptr; 4822d08521bSGarrett D'Amore 4832d08521bSGarrett D'Amore /* Argument loc is NULL if we are just querying. */ 4842d08521bSGarrett D'Amore if (loc != NULL) { 4852d08521bSGarrett D'Amore /* 4862d08521bSGarrett D'Amore * Set it to LC_GLOBAL_LOCAL to return to using 4872d08521bSGarrett D'Amore * the global locale (setlocale). 4882d08521bSGarrett D'Amore */ 4892d08521bSGarrett D'Amore if (loc == ___global_locale) { 4902d08521bSGarrett D'Amore *locptr = NULL; 4912d08521bSGarrett D'Amore } else { 4922d08521bSGarrett D'Amore /* No validation of the provided locale at present */ 4932d08521bSGarrett D'Amore *locptr = loc; 4942d08521bSGarrett D'Amore } 4952d08521bSGarrett D'Amore } 4962d08521bSGarrett D'Amore 4972d08521bSGarrett D'Amore /* 4982d08521bSGarrett D'Amore * The caller is responsible for freeing, of course it would be 4992d08521bSGarrett D'Amore * gross error to call freelocale() on a locale object that is still 5002d08521bSGarrett D'Amore * in use. 5012d08521bSGarrett D'Amore */ 5022d08521bSGarrett D'Amore return (lastloc); 5032d08521bSGarrett D'Amore } 5042d08521bSGarrett D'Amore 5052d08521bSGarrett D'Amore static locale_t 5062d08521bSGarrett D'Amore mklocname(locale_t loc) 5072d08521bSGarrett D'Amore { 5082d08521bSGarrett D'Amore int composite = 0; 5092d08521bSGarrett D'Amore 5102d08521bSGarrett D'Amore /* Look to see if any category is different */ 5112d08521bSGarrett D'Amore for (int i = 1; i < LC_ALL; ++i) { 5122d08521bSGarrett D'Amore if (strcmp(loc->locdata[0]->l_lname, 5132d08521bSGarrett D'Amore loc->locdata[i]->l_lname) != 0) { 5142d08521bSGarrett D'Amore composite = 1; 5152d08521bSGarrett D'Amore break; 5162d08521bSGarrett D'Amore } 5172d08521bSGarrett D'Amore } 5182d08521bSGarrett D'Amore 5192d08521bSGarrett D'Amore if (composite) { 5202d08521bSGarrett D'Amore /* 5212d08521bSGarrett D'Amore * Note ordering of these follows the numeric order, 5222d08521bSGarrett D'Amore * if the order is changed, then setlocale() will need 5232d08521bSGarrett D'Amore * to be changed as well. 5242d08521bSGarrett D'Amore */ 5252d08521bSGarrett D'Amore (void) snprintf(loc->locname, sizeof (loc->locname), 5262d08521bSGarrett D'Amore "%s/%s/%s/%s/%s/%s", 5272d08521bSGarrett D'Amore loc->locdata[LC_CTYPE]->l_lname, 5282d08521bSGarrett D'Amore loc->locdata[LC_NUMERIC]->l_lname, 5292d08521bSGarrett D'Amore loc->locdata[LC_TIME]->l_lname, 5302d08521bSGarrett D'Amore loc->locdata[LC_COLLATE]->l_lname, 5312d08521bSGarrett D'Amore loc->locdata[LC_MONETARY]->l_lname, 5322d08521bSGarrett D'Amore loc->locdata[LC_MESSAGES]->l_lname); 5332d08521bSGarrett D'Amore } else { 5342d08521bSGarrett D'Amore (void) strlcpy(loc->locname, loc->locdata[LC_CTYPE]->l_lname, 5352d08521bSGarrett D'Amore sizeof (loc->locname)); 5362d08521bSGarrett D'Amore } 5372d08521bSGarrett D'Amore return (loc); 5382d08521bSGarrett D'Amore } 539