14297a3b0SGarrett D'Amore /* 24297a3b0SGarrett D'Amore * Copyright (c) 1996 - 2002 FreeBSD Project 34297a3b0SGarrett D'Amore * Copyright (c) 1991, 1993 44297a3b0SGarrett D'Amore * The Regents of the University of California. All rights reserved. 54297a3b0SGarrett D'Amore * 64297a3b0SGarrett D'Amore * This code is derived from software contributed to Berkeley by 74297a3b0SGarrett D'Amore * Paul Borman at Krystal Technologies. 84297a3b0SGarrett D'Amore * 94297a3b0SGarrett D'Amore * Redistribution and use in source and binary forms, with or without 104297a3b0SGarrett D'Amore * modification, are permitted provided that the following conditions 114297a3b0SGarrett D'Amore * are met: 124297a3b0SGarrett D'Amore * 1. Redistributions of source code must retain the above copyright 134297a3b0SGarrett D'Amore * notice, this list of conditions and the following disclaimer. 144297a3b0SGarrett D'Amore * 2. Redistributions in binary form must reproduce the above copyright 154297a3b0SGarrett D'Amore * notice, this list of conditions and the following disclaimer in the 164297a3b0SGarrett D'Amore * documentation and/or other materials provided with the distribution. 174297a3b0SGarrett D'Amore * 4. Neither the name of the University nor the names of its contributors 184297a3b0SGarrett D'Amore * may be used to endorse or promote products derived from this software 194297a3b0SGarrett D'Amore * without specific prior written permission. 204297a3b0SGarrett D'Amore * 214297a3b0SGarrett D'Amore * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 224297a3b0SGarrett D'Amore * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 234297a3b0SGarrett D'Amore * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 244297a3b0SGarrett D'Amore * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 254297a3b0SGarrett D'Amore * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 264297a3b0SGarrett D'Amore * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 274297a3b0SGarrett D'Amore * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 284297a3b0SGarrett D'Amore * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 294297a3b0SGarrett D'Amore * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 304297a3b0SGarrett D'Amore * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 314297a3b0SGarrett D'Amore * SUCH DAMAGE. 324297a3b0SGarrett D'Amore */ 334297a3b0SGarrett D'Amore 344297a3b0SGarrett D'Amore /* 354297a3b0SGarrett D'Amore * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 364297a3b0SGarrett D'Amore */ 374297a3b0SGarrett D'Amore 384297a3b0SGarrett D'Amore #include "lint.h" 394297a3b0SGarrett D'Amore #include <sys/types.h> 404297a3b0SGarrett D'Amore #include <sys/stat.h> 414297a3b0SGarrett D'Amore #include <errno.h> 424297a3b0SGarrett D'Amore #include <limits.h> 434297a3b0SGarrett D'Amore #include <locale.h> 444297a3b0SGarrett D'Amore #include <stdlib.h> 454297a3b0SGarrett D'Amore #include <string.h> 464297a3b0SGarrett D'Amore #include <unistd.h> 474297a3b0SGarrett D'Amore #include <alloca.h> 48*5f5fdac7SGarrett D'Amore #include <stdio.h> 494297a3b0SGarrett D'Amore #include "collate.h" 504297a3b0SGarrett D'Amore #include "lmonetary.h" /* for __monetary_load_locale() */ 514297a3b0SGarrett D'Amore #include "lnumeric.h" /* for __numeric_load_locale() */ 524297a3b0SGarrett D'Amore #include "lmessages.h" /* for __messages_load_locale() */ 534297a3b0SGarrett D'Amore #include "setlocale.h" 544297a3b0SGarrett D'Amore #include "ldpart.h" 554297a3b0SGarrett D'Amore #include "timelocal.h" /* for __time_load_locale() */ 564297a3b0SGarrett D'Amore #include "../i18n/_loc_path.h" 574297a3b0SGarrett D'Amore 584297a3b0SGarrett D'Amore /* 594297a3b0SGarrett D'Amore * Category names for getenv() Note that this was modified 604297a3b0SGarrett D'Amore * for Solaris. See <iso/locale_iso.h>. 614297a3b0SGarrett D'Amore */ 62*5f5fdac7SGarrett D'Amore #define NUM_CATS 7 63*5f5fdac7SGarrett D'Amore static char *categories[7] = { 644297a3b0SGarrett D'Amore "LC_CTYPE", 654297a3b0SGarrett D'Amore "LC_NUMERIC", 664297a3b0SGarrett D'Amore "LC_TIME", 674297a3b0SGarrett D'Amore "LC_COLLATE", 684297a3b0SGarrett D'Amore "LC_MONETARY", 694297a3b0SGarrett D'Amore "LC_MESSAGES", 704297a3b0SGarrett D'Amore "LC_ALL", 714297a3b0SGarrett D'Amore }; 724297a3b0SGarrett D'Amore 734297a3b0SGarrett D'Amore /* 744297a3b0SGarrett D'Amore * Current locales for each category 754297a3b0SGarrett D'Amore */ 764297a3b0SGarrett D'Amore static char current_categories[NUM_CATS][ENCODING_LEN + 1] = { 774297a3b0SGarrett D'Amore "C", 784297a3b0SGarrett D'Amore "C", 794297a3b0SGarrett D'Amore "C", 804297a3b0SGarrett D'Amore "C", 814297a3b0SGarrett D'Amore "C", 824297a3b0SGarrett D'Amore "C", 834297a3b0SGarrett D'Amore "C", 844297a3b0SGarrett D'Amore }; 854297a3b0SGarrett D'Amore 864297a3b0SGarrett D'Amore /* 874297a3b0SGarrett D'Amore * Path to locale storage directory. See ../i18n/_loc_path.h 884297a3b0SGarrett D'Amore */ 894297a3b0SGarrett D'Amore char *_PathLocale = _DFLT_LOC_PATH; 904297a3b0SGarrett D'Amore 914297a3b0SGarrett D'Amore /* 924297a3b0SGarrett D'Amore * The locales we are going to try and load 934297a3b0SGarrett D'Amore */ 944297a3b0SGarrett D'Amore static char new_categories[NUM_CATS][ENCODING_LEN + 1]; 954297a3b0SGarrett D'Amore static char saved_categories[NUM_CATS][ENCODING_LEN + 1]; 964297a3b0SGarrett D'Amore static char current_locale_string[NUM_CATS * (ENCODING_LEN + 1 + 1)]; 974297a3b0SGarrett D'Amore 984297a3b0SGarrett D'Amore static char *currentlocale(void); 994297a3b0SGarrett D'Amore static char *loadlocale(int); 1004297a3b0SGarrett D'Amore static const char *__get_locale_env(int); 1014297a3b0SGarrett D'Amore 1024297a3b0SGarrett D'Amore char * 1034297a3b0SGarrett D'Amore setlocale(int category, const char *locale) 1044297a3b0SGarrett D'Amore { 1054297a3b0SGarrett D'Amore int i, j, saverr; 1064297a3b0SGarrett D'Amore const char *env, *r; 1074297a3b0SGarrett D'Amore 1084297a3b0SGarrett D'Amore if (category < 0 || category >= NUM_CATS) { 1094297a3b0SGarrett D'Amore errno = EINVAL; 1104297a3b0SGarrett D'Amore return (NULL); 1114297a3b0SGarrett D'Amore } 1124297a3b0SGarrett D'Amore 1134297a3b0SGarrett D'Amore if (locale == NULL) 1144297a3b0SGarrett D'Amore return (category != LC_ALL ? 1154297a3b0SGarrett D'Amore current_categories[category] : currentlocale()); 1164297a3b0SGarrett D'Amore 1174297a3b0SGarrett D'Amore /* 1184297a3b0SGarrett D'Amore * Default to the current locale for everything. 1194297a3b0SGarrett D'Amore */ 1204297a3b0SGarrett D'Amore for (i = 0; i < NUM_CATS; ++i) 1214297a3b0SGarrett D'Amore (void) strcpy(new_categories[i], current_categories[i]); 1224297a3b0SGarrett D'Amore 1234297a3b0SGarrett D'Amore /* 1244297a3b0SGarrett D'Amore * Now go fill up new_categories from the locale argument 1254297a3b0SGarrett D'Amore */ 1264297a3b0SGarrett D'Amore if (!*locale) { 1274297a3b0SGarrett D'Amore if (category == LC_ALL) { 1284297a3b0SGarrett D'Amore for (i = 0; i < NUM_CATS; ++i) { 129*5f5fdac7SGarrett D'Amore if (i == LC_ALL) 130*5f5fdac7SGarrett D'Amore continue; 1314297a3b0SGarrett D'Amore env = __get_locale_env(i); 1324297a3b0SGarrett D'Amore if (strlen(env) > ENCODING_LEN) { 1334297a3b0SGarrett D'Amore errno = EINVAL; 1344297a3b0SGarrett D'Amore return (NULL); 1354297a3b0SGarrett D'Amore } 1364297a3b0SGarrett D'Amore (void) strcpy(new_categories[i], env); 1374297a3b0SGarrett D'Amore } 1384297a3b0SGarrett D'Amore } else { 1394297a3b0SGarrett D'Amore env = __get_locale_env(category); 1404297a3b0SGarrett D'Amore if (strlen(env) > ENCODING_LEN) { 1414297a3b0SGarrett D'Amore errno = EINVAL; 1424297a3b0SGarrett D'Amore return (NULL); 1434297a3b0SGarrett D'Amore } 1444297a3b0SGarrett D'Amore (void) strcpy(new_categories[category], env); 1454297a3b0SGarrett D'Amore } 1464297a3b0SGarrett D'Amore } else if (category != LC_ALL) { 1474297a3b0SGarrett D'Amore if (strlen(locale) > ENCODING_LEN) { 1484297a3b0SGarrett D'Amore errno = EINVAL; 1494297a3b0SGarrett D'Amore return (NULL); 1504297a3b0SGarrett D'Amore } 1514297a3b0SGarrett D'Amore (void) strcpy(new_categories[category], locale); 1524297a3b0SGarrett D'Amore } else { 1534297a3b0SGarrett D'Amore if ((r = strchr(locale, '/')) == NULL) { 1544297a3b0SGarrett D'Amore if (strlen(locale) > ENCODING_LEN) { 1554297a3b0SGarrett D'Amore errno = EINVAL; 1564297a3b0SGarrett D'Amore return (NULL); 1574297a3b0SGarrett D'Amore } 158*5f5fdac7SGarrett D'Amore for (i = 0; i < NUM_CATS; ++i) 1594297a3b0SGarrett D'Amore (void) strcpy(new_categories[i], locale); 1604297a3b0SGarrett D'Amore } else { 1614297a3b0SGarrett D'Amore char *buf; 1624297a3b0SGarrett D'Amore char *save; 1634297a3b0SGarrett D'Amore 1644297a3b0SGarrett D'Amore buf = alloca(strlen(locale) + 1); 165*5f5fdac7SGarrett D'Amore (void) strcpy(buf, locale); 1664297a3b0SGarrett D'Amore 167*5f5fdac7SGarrett D'Amore save = NULL; 168*5f5fdac7SGarrett D'Amore r = strtok_r(buf, "/", &save); 169*5f5fdac7SGarrett D'Amore for (i = 0; i < NUM_CATS; i++) { 170*5f5fdac7SGarrett D'Amore if (i == LC_ALL) 171*5f5fdac7SGarrett D'Amore continue; 1724297a3b0SGarrett D'Amore if (r == NULL) { 1734297a3b0SGarrett D'Amore /* 1744297a3b0SGarrett D'Amore * Composite Locale is inadequately 1754297a3b0SGarrett D'Amore * specified! (Or with empty fields.) 1764297a3b0SGarrett D'Amore * The old code would fill fields 1774297a3b0SGarrett D'Amore * out from the last one, but I think 1784297a3b0SGarrett D'Amore * this is suboptimal. 1794297a3b0SGarrett D'Amore */ 1804297a3b0SGarrett D'Amore errno = EINVAL; 1814297a3b0SGarrett D'Amore return (NULL); 1824297a3b0SGarrett D'Amore } 1834297a3b0SGarrett D'Amore (void) strlcpy(new_categories[i], r, 1844297a3b0SGarrett D'Amore ENCODING_LEN); 185*5f5fdac7SGarrett D'Amore r = strtok_r(NULL, "/", &save); 186*5f5fdac7SGarrett D'Amore } 187*5f5fdac7SGarrett D'Amore if (r != NULL) { 188*5f5fdac7SGarrett D'Amore /* 189*5f5fdac7SGarrett D'Amore * Too many components - we had left over 190*5f5fdac7SGarrett D'Amore * data in the LC_ALL. It is malformed. 191*5f5fdac7SGarrett D'Amore */ 192*5f5fdac7SGarrett D'Amore errno = EINVAL; 193*5f5fdac7SGarrett D'Amore return (NULL); 1944297a3b0SGarrett D'Amore } 1954297a3b0SGarrett D'Amore } 1964297a3b0SGarrett D'Amore } 1974297a3b0SGarrett D'Amore 1984297a3b0SGarrett D'Amore if (category != LC_ALL) 1994297a3b0SGarrett D'Amore return (loadlocale(category)); 2004297a3b0SGarrett D'Amore 201*5f5fdac7SGarrett D'Amore for (i = 0; i < NUM_CATS; ++i) { 2024297a3b0SGarrett D'Amore (void) strcpy(saved_categories[i], current_categories[i]); 203*5f5fdac7SGarrett D'Amore if (i == LC_ALL) 204*5f5fdac7SGarrett D'Amore continue; 2054297a3b0SGarrett D'Amore if (loadlocale(i) == NULL) { 2064297a3b0SGarrett D'Amore saverr = errno; 207*5f5fdac7SGarrett D'Amore for (j = 0; j < i; j++) { 2084297a3b0SGarrett D'Amore (void) strcpy(new_categories[j], 2094297a3b0SGarrett D'Amore saved_categories[j]); 210*5f5fdac7SGarrett D'Amore if (i == LC_ALL) 211*5f5fdac7SGarrett D'Amore continue; 2124297a3b0SGarrett D'Amore if (loadlocale(j) == NULL) { 2134297a3b0SGarrett D'Amore (void) strcpy(new_categories[j], "C"); 2144297a3b0SGarrett D'Amore (void) loadlocale(j); 2154297a3b0SGarrett D'Amore } 2164297a3b0SGarrett D'Amore } 2174297a3b0SGarrett D'Amore errno = saverr; 2184297a3b0SGarrett D'Amore return (NULL); 2194297a3b0SGarrett D'Amore } 2204297a3b0SGarrett D'Amore } 2214297a3b0SGarrett D'Amore return (currentlocale()); 2224297a3b0SGarrett D'Amore } 2234297a3b0SGarrett D'Amore 2244297a3b0SGarrett D'Amore static char * 2254297a3b0SGarrett D'Amore currentlocale(void) 2264297a3b0SGarrett D'Amore { 2274297a3b0SGarrett D'Amore int i; 228*5f5fdac7SGarrett D'Amore int composite = 0; 2294297a3b0SGarrett D'Amore 230*5f5fdac7SGarrett D'Amore /* Look to see if any category is different */ 231*5f5fdac7SGarrett D'Amore for (i = 1; i < NUM_CATS; ++i) { 232*5f5fdac7SGarrett D'Amore if (i == LC_ALL) 233*5f5fdac7SGarrett D'Amore continue; 234*5f5fdac7SGarrett D'Amore if (strcmp(current_categories[0], current_categories[i])) { 235*5f5fdac7SGarrett D'Amore composite = 1; 2364297a3b0SGarrett D'Amore break; 2374297a3b0SGarrett D'Amore } 238*5f5fdac7SGarrett D'Amore } 239*5f5fdac7SGarrett D'Amore 240*5f5fdac7SGarrett D'Amore if (composite) { 241*5f5fdac7SGarrett D'Amore /* 242*5f5fdac7SGarrett D'Amore * Note ordering of these follows the numeric order, 243*5f5fdac7SGarrett D'Amore * if the order is changed, then setlocale() will need 244*5f5fdac7SGarrett D'Amore * to be changed as well. 245*5f5fdac7SGarrett D'Amore */ 246*5f5fdac7SGarrett D'Amore (void) snprintf(current_locale_string, 247*5f5fdac7SGarrett D'Amore sizeof (current_locale_string), 248*5f5fdac7SGarrett D'Amore "%s/%s/%s/%s/%s/%s", 249*5f5fdac7SGarrett D'Amore current_categories[LC_CTYPE], 250*5f5fdac7SGarrett D'Amore current_categories[LC_NUMERIC], 251*5f5fdac7SGarrett D'Amore current_categories[LC_TIME], 252*5f5fdac7SGarrett D'Amore current_categories[LC_COLLATE], 253*5f5fdac7SGarrett D'Amore current_categories[LC_MONETARY], 254*5f5fdac7SGarrett D'Amore current_categories[LC_MESSAGES]); 255*5f5fdac7SGarrett D'Amore } else { 256*5f5fdac7SGarrett D'Amore (void) strlcpy(current_locale_string, current_categories[0], 257*5f5fdac7SGarrett D'Amore sizeof (current_locale_string)); 258*5f5fdac7SGarrett D'Amore } 2594297a3b0SGarrett D'Amore return (current_locale_string); 2604297a3b0SGarrett D'Amore } 2614297a3b0SGarrett D'Amore 2624297a3b0SGarrett D'Amore static char * 2634297a3b0SGarrett D'Amore loadlocale(int category) 2644297a3b0SGarrett D'Amore { 2654297a3b0SGarrett D'Amore char *new = new_categories[category]; 2664297a3b0SGarrett D'Amore char *old = current_categories[category]; 2674297a3b0SGarrett D'Amore int (*func)(const char *); 2684297a3b0SGarrett D'Amore 2694297a3b0SGarrett D'Amore if ((new[0] == '.' && 2704297a3b0SGarrett D'Amore (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 2714297a3b0SGarrett D'Amore strchr(new, '/') != NULL) { 2724297a3b0SGarrett D'Amore errno = EINVAL; 2734297a3b0SGarrett D'Amore return (NULL); 2744297a3b0SGarrett D'Amore } 2754297a3b0SGarrett D'Amore 2764297a3b0SGarrett D'Amore switch (category) { 2774297a3b0SGarrett D'Amore case LC_CTYPE: 2784297a3b0SGarrett D'Amore func = __wrap_setrunelocale; 2794297a3b0SGarrett D'Amore break; 2804297a3b0SGarrett D'Amore case LC_COLLATE: 2814297a3b0SGarrett D'Amore func = __collate_load_tables; 2824297a3b0SGarrett D'Amore break; 2834297a3b0SGarrett D'Amore case LC_TIME: 2844297a3b0SGarrett D'Amore func = __time_load_locale; 2854297a3b0SGarrett D'Amore break; 2864297a3b0SGarrett D'Amore case LC_NUMERIC: 2874297a3b0SGarrett D'Amore func = __numeric_load_locale; 2884297a3b0SGarrett D'Amore break; 2894297a3b0SGarrett D'Amore case LC_MONETARY: 2904297a3b0SGarrett D'Amore func = __monetary_load_locale; 2914297a3b0SGarrett D'Amore break; 2924297a3b0SGarrett D'Amore case LC_MESSAGES: 2934297a3b0SGarrett D'Amore func = __messages_load_locale; 2944297a3b0SGarrett D'Amore break; 2954297a3b0SGarrett D'Amore default: 2964297a3b0SGarrett D'Amore errno = EINVAL; 2974297a3b0SGarrett D'Amore return (NULL); 2984297a3b0SGarrett D'Amore } 2994297a3b0SGarrett D'Amore 3004297a3b0SGarrett D'Amore if (strcmp(new, old) == 0) 3014297a3b0SGarrett D'Amore return (old); 3024297a3b0SGarrett D'Amore 3034297a3b0SGarrett D'Amore if (func(new) != _LDP_ERROR) { 3044297a3b0SGarrett D'Amore (void) strcpy(old, new); 3054297a3b0SGarrett D'Amore return (old); 3064297a3b0SGarrett D'Amore } 3074297a3b0SGarrett D'Amore 3084297a3b0SGarrett D'Amore return (NULL); 3094297a3b0SGarrett D'Amore } 3104297a3b0SGarrett D'Amore 3114297a3b0SGarrett D'Amore static const char * 3124297a3b0SGarrett D'Amore __get_locale_env(int category) 3134297a3b0SGarrett D'Amore { 3144297a3b0SGarrett D'Amore const char *env; 3154297a3b0SGarrett D'Amore 3164297a3b0SGarrett D'Amore /* 1. check LC_ALL. */ 317*5f5fdac7SGarrett D'Amore env = getenv(categories[LC_ALL]); 3184297a3b0SGarrett D'Amore 3194297a3b0SGarrett D'Amore /* 2. check LC_* */ 3204297a3b0SGarrett D'Amore if (env == NULL || !*env) 3214297a3b0SGarrett D'Amore env = getenv(categories[category]); 3224297a3b0SGarrett D'Amore 3234297a3b0SGarrett D'Amore /* 3. check LANG */ 3244297a3b0SGarrett D'Amore if (env == NULL || !*env) 3254297a3b0SGarrett D'Amore env = getenv("LANG"); 3264297a3b0SGarrett D'Amore 3274297a3b0SGarrett D'Amore /* 4. if none is set, fall to "C" */ 3284297a3b0SGarrett D'Amore if (env == NULL || !*env) 3294297a3b0SGarrett D'Amore env = "C"; 3304297a3b0SGarrett D'Amore 3314297a3b0SGarrett D'Amore return (env); 3324297a3b0SGarrett D'Amore } 333