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 */ 37 38 #include "lint.h" 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <errno.h> 42 #include <limits.h> 43 #include <locale.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <alloca.h> 48 #include <stdio.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 /* 59 * Category names for getenv() Note that this was modified 60 * for Solaris. See <iso/locale_iso.h>. 61 */ 62 #define NUM_CATS 7 63 static char *categories[7] = { 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 if (i == LC_ALL) 130 continue; 131 env = __get_locale_env(i); 132 if (strlen(env) > ENCODING_LEN) { 133 errno = EINVAL; 134 return (NULL); 135 } 136 (void) strcpy(new_categories[i], env); 137 } 138 } else { 139 env = __get_locale_env(category); 140 if (strlen(env) > ENCODING_LEN) { 141 errno = EINVAL; 142 return (NULL); 143 } 144 (void) strcpy(new_categories[category], env); 145 } 146 } else if (category != LC_ALL) { 147 if (strlen(locale) > ENCODING_LEN) { 148 errno = EINVAL; 149 return (NULL); 150 } 151 (void) strcpy(new_categories[category], locale); 152 } else { 153 if ((r = strchr(locale, '/')) == NULL) { 154 if (strlen(locale) > ENCODING_LEN) { 155 errno = EINVAL; 156 return (NULL); 157 } 158 for (i = 0; i < NUM_CATS; ++i) 159 (void) strcpy(new_categories[i], locale); 160 } else { 161 char *buf; 162 char *save; 163 164 buf = alloca(strlen(locale) + 1); 165 (void) strcpy(buf, locale); 166 167 save = NULL; 168 r = strtok_r(buf, "/", &save); 169 for (i = 0; i < NUM_CATS; i++) { 170 if (i == LC_ALL) 171 continue; 172 if (r == NULL) { 173 /* 174 * Composite Locale is inadequately 175 * specified! (Or with empty fields.) 176 * The old code would fill fields 177 * out from the last one, but I think 178 * this is suboptimal. 179 */ 180 errno = EINVAL; 181 return (NULL); 182 } 183 (void) strlcpy(new_categories[i], r, 184 ENCODING_LEN); 185 r = strtok_r(NULL, "/", &save); 186 } 187 if (r != NULL) { 188 /* 189 * Too many components - we had left over 190 * data in the LC_ALL. It is malformed. 191 */ 192 errno = EINVAL; 193 return (NULL); 194 } 195 } 196 } 197 198 if (category != LC_ALL) 199 return (loadlocale(category)); 200 201 for (i = 0; i < NUM_CATS; ++i) { 202 (void) strcpy(saved_categories[i], current_categories[i]); 203 if (i == LC_ALL) 204 continue; 205 if (loadlocale(i) == NULL) { 206 saverr = errno; 207 for (j = 0; j < i; j++) { 208 (void) strcpy(new_categories[j], 209 saved_categories[j]); 210 if (i == LC_ALL) 211 continue; 212 if (loadlocale(j) == NULL) { 213 (void) strcpy(new_categories[j], "C"); 214 (void) loadlocale(j); 215 } 216 } 217 errno = saverr; 218 return (NULL); 219 } 220 } 221 return (currentlocale()); 222 } 223 224 static char * 225 currentlocale(void) 226 { 227 int i; 228 int composite = 0; 229 230 /* Look to see if any category is different */ 231 for (i = 1; i < NUM_CATS; ++i) { 232 if (i == LC_ALL) 233 continue; 234 if (strcmp(current_categories[0], current_categories[i])) { 235 composite = 1; 236 break; 237 } 238 } 239 240 if (composite) { 241 /* 242 * Note ordering of these follows the numeric order, 243 * if the order is changed, then setlocale() will need 244 * to be changed as well. 245 */ 246 (void) snprintf(current_locale_string, 247 sizeof (current_locale_string), 248 "%s/%s/%s/%s/%s/%s", 249 current_categories[LC_CTYPE], 250 current_categories[LC_NUMERIC], 251 current_categories[LC_TIME], 252 current_categories[LC_COLLATE], 253 current_categories[LC_MONETARY], 254 current_categories[LC_MESSAGES]); 255 } else { 256 (void) strlcpy(current_locale_string, current_categories[0], 257 sizeof (current_locale_string)); 258 } 259 return (current_locale_string); 260 } 261 262 static char * 263 loadlocale(int category) 264 { 265 char *new = new_categories[category]; 266 char *old = current_categories[category]; 267 int (*func)(const char *); 268 269 if ((new[0] == '.' && 270 (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 271 strchr(new, '/') != NULL) { 272 errno = EINVAL; 273 return (NULL); 274 } 275 276 switch (category) { 277 case LC_CTYPE: 278 func = __wrap_setrunelocale; 279 break; 280 case LC_COLLATE: 281 func = __collate_load_tables; 282 break; 283 case LC_TIME: 284 func = __time_load_locale; 285 break; 286 case LC_NUMERIC: 287 func = __numeric_load_locale; 288 break; 289 case LC_MONETARY: 290 func = __monetary_load_locale; 291 break; 292 case LC_MESSAGES: 293 func = __messages_load_locale; 294 break; 295 default: 296 errno = EINVAL; 297 return (NULL); 298 } 299 300 if (strcmp(new, old) == 0) 301 return (old); 302 303 if (func(new) != _LDP_ERROR) { 304 (void) strcpy(old, new); 305 return (old); 306 } 307 308 return (NULL); 309 } 310 311 static const char * 312 __get_locale_env(int category) 313 { 314 const char *env; 315 316 /* 1. check LC_ALL. */ 317 env = getenv(categories[LC_ALL]); 318 319 /* 2. check LC_* */ 320 if (env == NULL || !*env) 321 env = getenv(categories[category]); 322 323 /* 3. check LANG */ 324 if (env == NULL || !*env) 325 env = getenv("LANG"); 326 327 /* 4. if none is set, fall to "C" */ 328 if (env == NULL || !*env) 329 env = "C"; 330 331 return (env); 332 } 333