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