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#if defined(LIBC_SCCS) && !defined(lint) 35static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; 36#endif /* LIBC_SCCS and not lint */ 37#include <sys/cdefs.h> 38__FBSDID("$FreeBSD: src/lib/libc/locale/setlocale.c,v 1.51 2007/01/09 00:28:00 imp Exp $"); 39 40#include "xlocale_private.h" 41 42#include <sys/types.h> 43#include <sys/stat.h> 44#include <errno.h> 45#include <limits.h> 46#include <locale.h> 47#include <paths.h> /* for _PATH_LOCALE */ 48#include <stdlib.h> 49#include <string.h> 50#include <unistd.h> 51#include "collate.h" 52#include "lmonetary.h" /* for __monetary_load_locale() */ 53#include "lnumeric.h" /* for __numeric_load_locale() */ 54#include "lmessages.h" /* for __messages_load_locale() */ 55#include "setlocale.h" 56#include "ldpart.h" 57#include "timelocal.h" /* for __time_load_locale() */ 58 59/* 60 * Category names for getenv() 61 */ 62static const char * const categories[_LC_LAST] = { 63 "LC_ALL", 64 "LC_COLLATE", 65 "LC_CTYPE", 66 "LC_MONETARY", 67 "LC_NUMERIC", 68 "LC_TIME", 69 "LC_MESSAGES", 70}; 71 72/* 73 * Current locales for each category 74 */ 75static char current_categories[_LC_LAST][ENCODING_LEN + 1] = { 76 "C", 77 "C", 78 "C", 79 "C", 80 "C", 81 "C", 82 "C", 83}; 84 85/* 86 * Path to locale storage directory 87 */ 88char *_PathLocale; 89 90/* 91 * The locales we are going to try and load 92 */ 93static char new_categories[_LC_LAST][ENCODING_LEN + 1]; 94static char saved_categories[_LC_LAST][ENCODING_LEN + 1]; 95 96static char *currentlocale(void); 97static char *loadlocale(int); 98__private_extern__ const char *__get_locale_env(int); 99 100#define UNLOCK_AND_RETURN(x) {XL_UNLOCK(&__global_locale); return (x);} 101 102char * 103setlocale(category, locale) 104 int category; 105 const char *locale; 106{ 107 int i, j, len, saverr, save__numeric_fp_cvt; 108 const char *env, *r; 109 locale_t save__lc_numeric_loc; 110 111 if (category < LC_ALL || category >= _LC_LAST) { 112 errno = EINVAL; 113 return (NULL); 114 } 115 116 if (locale == NULL) 117 return (category != LC_ALL ? 118 current_categories[category] : currentlocale()); 119 120 XL_LOCK(&__global_locale); 121 /* 122 * Default to the current locale for everything. 123 */ 124 for (i = 1; i < _LC_LAST; ++i) 125 (void)strcpy(new_categories[i], current_categories[i]); 126 127 /* 128 * Now go fill up new_categories from the locale argument 129 */ 130 if (!*locale) { 131 if (category == LC_ALL) { 132 for (i = 1; i < _LC_LAST; ++i) { 133 env = __get_locale_env(i); 134 if (strlen(env) > ENCODING_LEN) { 135 errno = EINVAL; 136 UNLOCK_AND_RETURN (NULL); 137 } 138 (void)strcpy(new_categories[i], env); 139 } 140 } else { 141 env = __get_locale_env(category); 142 if (strlen(env) > ENCODING_LEN) { 143 errno = EINVAL; 144 UNLOCK_AND_RETURN (NULL); 145 } 146 (void)strcpy(new_categories[category], env); 147 } 148 } else if (category != LC_ALL) { 149 if (strlen(locale) > ENCODING_LEN) { 150 errno = EINVAL; 151 UNLOCK_AND_RETURN (NULL); 152 } 153 (void)strcpy(new_categories[category], locale); 154 } else { 155 if ((r = strchr(locale, '/')) == NULL) { 156 if (strlen(locale) > ENCODING_LEN) { 157 errno = EINVAL; 158 UNLOCK_AND_RETURN (NULL); 159 } 160 for (i = 1; i < _LC_LAST; ++i) 161 (void)strcpy(new_categories[i], locale); 162 } else { 163 for (i = 1; r[1] == '/'; ++r) 164 ; 165 if (!r[1]) { 166 errno = EINVAL; 167 UNLOCK_AND_RETURN (NULL); /* Hmm, just slashes... */ 168 } 169 do { 170 if (i == _LC_LAST) 171 break; /* Too many slashes... */ 172 if ((len = r - locale) > ENCODING_LEN) { 173 errno = EINVAL; 174 UNLOCK_AND_RETURN (NULL); 175 } 176 (void)strlcpy(new_categories[i], locale, 177 len + 1); 178 i++; 179 while (*r == '/') 180 r++; 181 locale = r; 182 while (*r && *r != '/') 183 r++; 184 } while (*locale); 185 while (i < _LC_LAST) { 186 (void)strcpy(new_categories[i], 187 new_categories[i-1]); 188 i++; 189 } 190 } 191 } 192 193 if (category != LC_ALL) 194 UNLOCK_AND_RETURN (loadlocale(category)); 195 196 save__numeric_fp_cvt = __global_locale.__numeric_fp_cvt; 197 save__lc_numeric_loc = __global_locale.__lc_numeric_loc; 198 XL_RETAIN(save__lc_numeric_loc); 199 for (i = 1; i < _LC_LAST; ++i) { 200 (void)strcpy(saved_categories[i], current_categories[i]); 201 if (loadlocale(i) == NULL) { 202 saverr = errno; 203 for (j = 1; j < i; j++) { 204 (void)strcpy(new_categories[j], 205 saved_categories[j]); 206 if (loadlocale(j) == NULL) { 207 (void)strcpy(new_categories[j], "C"); 208 (void)loadlocale(j); 209 } 210 } 211 __global_locale.__numeric_fp_cvt = save__numeric_fp_cvt; 212 __global_locale.__lc_numeric_loc = save__lc_numeric_loc; 213 XL_RELEASE(save__lc_numeric_loc); 214 errno = saverr; 215 UNLOCK_AND_RETURN (NULL); 216 } 217 } 218 XL_RELEASE(save__lc_numeric_loc); 219 UNLOCK_AND_RETURN (currentlocale()); 220} 221 222static char * 223currentlocale() 224{ 225 int i; 226 227 size_t bufsiz = _LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1); 228 static char *current_locale_string = NULL; 229 230 if (current_locale_string == NULL) { 231 current_locale_string = malloc(bufsiz); 232 if (current_locale_string == NULL) { 233 return NULL; 234 } 235 } 236 237 (void)strlcpy(current_locale_string, current_categories[1], bufsiz); 238 239 for (i = 2; i < _LC_LAST; ++i) 240 if (strcmp(current_categories[1], current_categories[i])) { 241 for (i = 2; i < _LC_LAST; ++i) { 242 (void)strcat(current_locale_string, "/"); 243 (void)strcat(current_locale_string, 244 current_categories[i]); 245 } 246 break; 247 } 248 return (current_locale_string); 249} 250 251static char * 252loadlocale(category) 253 int category; 254{ 255 char *new = new_categories[category]; 256 char *old = current_categories[category]; 257 int (*func)(const char *, locale_t); 258 int saved_errno; 259 260 if ((new[0] == '.' && 261 (new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) || 262 strchr(new, '/') != NULL) { 263 errno = EINVAL; 264 return (NULL); 265 } 266 267 saved_errno = errno; 268 errno = __detect_path_locale(); 269 if (errno != 0) 270 return (NULL); 271 errno = saved_errno; 272 273 switch (category) { 274 case LC_CTYPE: 275 func = __wrap_setrunelocale; 276 break; 277 case LC_COLLATE: 278 func = __collate_load_tables; 279 break; 280 case LC_TIME: 281 func = __time_load_locale; 282 break; 283 case LC_NUMERIC: 284 func = __numeric_load_locale; 285 break; 286 case LC_MONETARY: 287 func = __monetary_load_locale; 288 break; 289 case LC_MESSAGES: 290 func = __messages_load_locale; 291 break; 292 default: 293 errno = EINVAL; 294 return (NULL); 295 } 296 297 if (strcmp(new, old) == 0) 298 return (old); 299 300 if (func(new, &__global_locale) != _LDP_ERROR) { 301 (void)strcpy(old, new); 302 switch (category) { 303 case LC_CTYPE: 304 if (__global_locale.__numeric_fp_cvt == LC_NUMERIC_FP_SAME_LOCALE) 305 __global_locale.__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; 306 break; 307 case LC_NUMERIC: 308 __global_locale.__numeric_fp_cvt = LC_NUMERIC_FP_UNINITIALIZED; 309 XL_RELEASE(__global_locale.__lc_numeric_loc); 310 __global_locale.__lc_numeric_loc = NULL; 311 break; 312 } 313 return (old); 314 } 315 316 return (NULL); 317} 318 319__private_extern__ const char * 320__get_locale_env(category) 321 int category; 322{ 323 const char *env; 324 325 /* 1. check LC_ALL. */ 326 env = getenv(categories[0]); 327 328 /* 2. check LC_* */ 329 if (env == NULL || !*env) 330 env = getenv(categories[category]); 331 332 /* 3. check LANG */ 333 if (env == NULL || !*env) 334 env = getenv("LANG"); 335 336 /* 4. if none is set, fall to "C" */ 337 if (env == NULL || !*env) 338 env = "C"; 339 340 return (env); 341} 342 343/* 344 * Detect locale storage location and store its value to _PathLocale variable 345 */ 346__private_extern__ int 347__detect_path_locale(void) 348{ 349 if (_PathLocale == NULL) { 350 char *p = getenv("PATH_LOCALE"); 351 352 if (p != NULL && !issetugid()) { 353 if (strlen(p) + 1/*"/"*/ + ENCODING_LEN + 354 1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) 355 return (ENAMETOOLONG); 356 _PathLocale = strdup(p); 357 if (_PathLocale == NULL) 358 return (errno == 0 ? ENOMEM : errno); 359 } else 360 _PathLocale = _PATH_LOCALE; 361 } 362 return (0); 363} 364 365