1 /* 2 * Copyright (c) 1996 - 2002 FreeBSD Project 3 * Copyright (c) 1991, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Paul Borman at Krystal Technologies. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 /* 35 * Copyright 2010 Nexenta Systems, Inc. All rights reserved. 36 * Use is subject to license terms. 37 */ 38 39 #include "lint.h" 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 #include <errno.h> 43 #include <limits.h> 44 #include <locale.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <unistd.h> 48 #include <alloca.h> 49 #include "collate.h" 50 #include "lmonetary.h" /* for __monetary_load_locale() */ 51 #include "lnumeric.h" /* for __numeric_load_locale() */ 52 #include "lmessages.h" /* for __messages_load_locale() */ 53 #include "setlocale.h" 54 #include "ldpart.h" 55 #include "timelocal.h" /* for __time_load_locale() */ 56 #include "../i18n/_loc_path.h" 57 58 #define NUM_CATS 7 59 /* 60 * Category names for getenv() Note that this was modified 61 * for Solaris. See <iso/locale_iso.h>. 62 */ 63 static char *categories[NUM_CATS] = { 64 "LC_CTYPE", 65 "LC_NUMERIC", 66 "LC_TIME", 67 "LC_COLLATE", 68 "LC_MONETARY", 69 "LC_MESSAGES", 70 "LC_ALL", 71 }; 72 73 /* 74 * Current locales for each category 75 */ 76 static char current_categories[NUM_CATS][ENCODING_LEN + 1] = { 77 "C", 78 "C", 79 "C", 80 "C", 81 "C", 82 "C", 83 "C", 84 }; 85 86 /* 87 * Path to locale storage directory. See ../i18n/_loc_path.h 88 */ 89 char *_PathLocale = _DFLT_LOC_PATH; 90 91 /* 92 * The locales we are going to try and load 93 */ 94 static char new_categories[NUM_CATS][ENCODING_LEN + 1]; 95 static char saved_categories[NUM_CATS][ENCODING_LEN + 1]; 96 static char current_locale_string[NUM_CATS * (ENCODING_LEN + 1 + 1)]; 97 98 static char *currentlocale(void); 99 static char *loadlocale(int); 100 static const char *__get_locale_env(int); 101 102 char * 103 setlocale(int category, const char *locale) 104 { 105 int i, j, saverr; 106 const char *env, *r; 107 108 if (category < 0 || category >= NUM_CATS) { 109 errno = EINVAL; 110 return (NULL); 111 } 112 113 if (locale == NULL) 114 return (category != LC_ALL ? 115 current_categories[category] : currentlocale()); 116 117 /* 118 * Default to the current locale for everything. 119 */ 120 for (i = 0; i < NUM_CATS; ++i) 121 (void) strcpy(new_categories[i], current_categories[i]); 122 123 /* 124 * Now go fill up new_categories from the locale argument 125 */ 126 if (!*locale) { 127 if (category == LC_ALL) { 128 for (i = 0; i < NUM_CATS; ++i) { 129 env = __get_locale_env(i); 130 if (strlen(env) > ENCODING_LEN) { 131 errno = EINVAL; 132 return (NULL); 133 } 134 (void) strcpy(new_categories[i], env); 135 } 136 } else { 137 env = __get_locale_env(category); 138 if (strlen(env) > ENCODING_LEN) { 139 errno = EINVAL; 140 return (NULL); 141 } 142 (void) strcpy(new_categories[category], env); 143 } 144 } else if (category != LC_ALL) { 145 if (strlen(locale) > ENCODING_LEN) { 146 errno = EINVAL; 147 return (NULL); 148 } 149 (void) strcpy(new_categories[category], locale); 150 } else { 151 if ((r = strchr(locale, '/')) == NULL) { 152 if (strlen(locale) > ENCODING_LEN) { 153 errno = EINVAL; 154 return (NULL); 155 } 156 for (i = 1; i < NUM_CATS; ++i) 157 (void) strcpy(new_categories[i], locale); 158 } else { 159 char *buf; 160 char *save; 161 162 buf = alloca(strlen(locale) + 1); 163 164 for (i = 0, save = NULL; i <= LC_ALL; i++) { 165 r = strtok_r(buf, "/", &save); 166 if (r == NULL) { 167 if (i == LC_ALL) { 168 /* Good! Fully specified! */ 169 break; 170 } 171 /* 172 * Composite Locale is inadequately 173 * specified! (Or with empty fields.) 174 * The old code would fill fields 175 * out from the last one, but I think 176 * this is suboptimal. 177 */ 178 errno = EINVAL; 179 return (NULL); 180 } 181 if (i == LC_ALL) { 182 /* Too many components */ 183 errno = EINVAL; 184 return (NULL); 185 } 186 (void) strlcpy(new_categories[i], r, 187 ENCODING_LEN); 188 buf = NULL; /* for strtok's benefit */ 189 } 190 } 191 } 192 193 if (category != LC_ALL) 194 return (loadlocale(category)); 195 196 for (i = 0; i < LC_ALL; ++i) { 197 (void) strcpy(saved_categories[i], current_categories[i]); 198 if (loadlocale(i) == NULL) { 199 saverr = errno; 200 for (j = 1; j < i; j++) { 201 (void) strcpy(new_categories[j], 202 saved_categories[j]); 203 if (loadlocale(j) == NULL) { 204 (void) strcpy(new_categories[j], "C"); 205 (void) loadlocale(j); 206 } 207 } 208 errno = saverr; 209 return (NULL); 210 } 211 } 212 return (currentlocale()); 213 } 214 215 static char * 216 currentlocale(void) 217 { 218 int i; 219 220 (void) strcpy(current_locale_string, current_categories[0]); 221 222 for (i = 1; i < LC_ALL; ++i) 223 if (strcmp(current_categories[1], current_categories[i])) { 224 for (i = 1; i < LC_ALL; ++i) { 225 (void) strcat(current_locale_string, "/"); 226 (void) strcat(current_locale_string, 227 current_categories[i]); 228 } 229 break; 230 } 231 return (current_locale_string); 232 } 233 234 static char * 235 loadlocale(int category) 236 { 237 char *new = new_categories[category]; 238 char *old = current_categories[category]; 239 int (*func)(const char *); 240 241 if ((new[0] == '.' && 242 (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 243 strchr(new, '/') != NULL) { 244 errno = EINVAL; 245 return (NULL); 246 } 247 248 switch (category) { 249 case LC_CTYPE: 250 func = __wrap_setrunelocale; 251 break; 252 case LC_COLLATE: 253 func = __collate_load_tables; 254 break; 255 case LC_TIME: 256 func = __time_load_locale; 257 break; 258 case LC_NUMERIC: 259 func = __numeric_load_locale; 260 break; 261 case LC_MONETARY: 262 func = __monetary_load_locale; 263 break; 264 case LC_MESSAGES: 265 func = __messages_load_locale; 266 break; 267 default: 268 errno = EINVAL; 269 return (NULL); 270 } 271 272 if (strcmp(new, old) == 0) 273 return (old); 274 275 if (func(new) != _LDP_ERROR) { 276 (void) strcpy(old, new); 277 return (old); 278 } 279 280 return (NULL); 281 } 282 283 static const char * 284 __get_locale_env(int category) 285 { 286 const char *env; 287 288 /* 1. check LC_ALL. */ 289 env = getenv(categories[0]); 290 291 /* 2. check LC_* */ 292 if (env == NULL || !*env) 293 env = getenv(categories[category]); 294 295 /* 3. check LANG */ 296 if (env == NULL || !*env) 297 env = getenv("LANG"); 298 299 /* 4. if none is set, fall to "C" */ 300 if (env == NULL || !*env) 301 env = "C"; 302 303 return (env); 304 } 305