1 /** @file 2 Worker functions for the setlocale function. 3 4 Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials are licensed and made available under 6 the terms and conditions of the BSD License that accompanies this distribution. 7 The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license. 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 * Copyright (c) 1991, 1993 14 * The Regents of the University of California. All rights reserved. 15 * 16 * This code is derived from software contributed to Berkeley by 17 * Paul Borman at Krystal Technologies. 18 * 19 * Redistribution and use in source and binary forms, with or without 20 * modification, are permitted provided that the following conditions 21 * are met: 22 * 1. Redistributions of source code must retain the above copyright 23 * notice, this list of conditions and the following disclaimer. 24 * 2. Redistributions in binary form must reproduce the above copyright 25 * notice, this list of conditions and the following disclaimer in the 26 * documentation and/or other materials provided with the distribution. 27 * 3. Neither the name of the University nor the names of its contributors 28 * may be used to endorse or promote products derived from this software 29 * without specific prior written permission. 30 * 31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41 * SUCH DAMAGE. 42 43 setlocale.c 8.1 (Berkeley) 7/4/93 44 * NetBSD: setlocale.c,v 1.50 2006/02/16 19:19:49 tnozaki Exp 45 **/ 46 #include <LibConfig.h> 47 #include <sys/EfiCdefs.h> 48 49 #if defined(_MSC_VER) 50 // Disable warnings about assignment within conditional expressions. 51 #pragma warning ( disable : 4706 ) 52 #endif 53 54 #define _CTYPE_PRIVATE 55 56 #include "namespace.h" 57 #include <sys/localedef.h> 58 #include <sys/types.h> 59 #include <sys/stat.h> 60 #include <assert.h> 61 #include <limits.h> 62 #include <ctype.h> 63 #define __SETLOCALE_SOURCE__ 64 #include <locale.h> 65 #include <paths.h> 66 #include <stdio.h> 67 #include <stdlib.h> 68 #include <string.h> 69 #include <unistd.h> 70 #include "rune.h" 71 #ifdef WITH_RUNE 72 #include "rune_local.h" 73 #else 74 #include "ctypeio.h" 75 #endif 76 77 #ifdef CITRUS 78 #include <citrus/citrus_namespace.h> 79 #include <citrus/citrus_region.h> 80 #include <citrus/citrus_lookup.h> 81 #include <citrus/citrus_bcs.h> 82 #else 83 #include "aliasname_local.h" 84 #define _lookup_alias(p, a, b, s, c) __unaliasname((p), (a), (b), (s)) 85 #define _bcs_strcasecmp(a, b) strcasecmp((a), (b)) 86 #endif 87 88 #define _LOCALE_SYM_FORCE "/force" 89 90 #ifndef WITH_RUNE 91 const char *_PathLocale = NULL; 92 #endif 93 94 /** Category names for getenv(). **/ 95 static const char *const categories[_LC_LAST] = { 96 "LC_ALL", 97 "LC_COLLATE", 98 "LC_CTYPE", 99 "LC_MONETARY", 100 "LC_NUMERIC", 101 "LC_TIME", 102 "LC_MESSAGES" 103 }; 104 105 /** Current locales for each category. **/ 106 static char current_categories[_LC_LAST][32] = { 107 "C", 108 "C", 109 "C", 110 "C", 111 "C", 112 "C", 113 "C" 114 }; 115 116 /* 117 * The locales we are going to try and load 118 */ 119 static char new_categories[_LC_LAST][32]; 120 121 static char current_locale_string[_LC_LAST * 33]; 122 123 static char *currentlocale(void); 124 static void revert_to_default(int); 125 static int force_locale_enable(int); 126 static int load_locale_sub(int, const char *, int); 127 static char *loadlocale(int); 128 static const char *__get_locale_env(int); 129 130 char * 131 __setlocale(int category, const char *locale) 132 { 133 int i, loadlocale_success; 134 size_t len; 135 const char *env, *r; 136 137 //if (issetugid() || 138 // (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE")))) 139 // _PathLocale = _PATH_LOCALE; 140 141 if (category < 0 || category >= _LC_LAST) 142 return (NULL); 143 144 if (!locale) 145 return (category ? 146 current_categories[category] : currentlocale()); 147 148 /* 149 * Default to the current locale for everything. 150 */ 151 for (i = 1; i < _LC_LAST; ++i) 152 (void)strncpyX(new_categories[i], current_categories[i], 153 sizeof(new_categories[i])); 154 155 /* 156 * Now go fill up new_categories from the locale argument 157 */ 158 if (!*locale) { 159 if (category == LC_ALL) { 160 for (i = 1; i < _LC_LAST; ++i) { 161 env = __get_locale_env(i); 162 (void)strncpyX(new_categories[i], env, 163 sizeof(new_categories[i])); 164 } 165 } 166 else { 167 env = __get_locale_env(category); 168 (void)strncpyX(new_categories[category], env, 169 sizeof(new_categories[category])); 170 } 171 } else if (category) { 172 (void)strncpyX(new_categories[category], locale, 173 sizeof(new_categories[category])); 174 } else { 175 if ((r = strchr(locale, '/')) == 0) { 176 for (i = 1; i < _LC_LAST; ++i) { 177 (void)strncpyX(new_categories[i], locale, 178 sizeof(new_categories[i])); 179 } 180 } else { 181 for (i = 1;;) { 182 _DIAGASSERT(*r == '/' || *r == 0); 183 _DIAGASSERT(*locale != 0); 184 if (*locale == '/') 185 return (NULL); /* invalid format. */ 186 len = r - locale; 187 if (len + 1 > sizeof(new_categories[i])) 188 return (NULL); /* too long */ 189 (void)memcpy(new_categories[i], locale, len); 190 new_categories[i][len] = '\0'; 191 if (*r == 0) 192 break; 193 _DIAGASSERT(*r == '/'); 194 if (*(locale = ++r) == 0) 195 /* slash followed by NUL */ 196 return (NULL); 197 /* skip until NUL or '/' */ 198 while (*r && *r != '/') 199 r++; 200 if (++i == _LC_LAST) 201 return (NULL); /* too many slashes. */ 202 } 203 if (i + 1 != _LC_LAST) 204 return (NULL); /* too few slashes. */ 205 } 206 } 207 208 if (category) 209 return (loadlocale(category)); 210 211 loadlocale_success = 0; 212 for (i = 1; i < _LC_LAST; ++i) { 213 if (loadlocale(i) != NULL) 214 loadlocale_success = 1; 215 } 216 217 /* 218 * If all categories failed, return NULL; we don't need to back 219 * changes off, since none happened. 220 */ 221 if (!loadlocale_success) 222 return NULL; 223 224 return (currentlocale()); 225 } 226 227 static char * 228 currentlocale() 229 { 230 int i; 231 232 (void)strncpyX(current_locale_string, current_categories[1], 233 sizeof(current_locale_string)); 234 235 for (i = 2; i < _LC_LAST; ++i) 236 if (strcmp(current_categories[1], current_categories[i])) { 237 (void)snprintf(current_locale_string, 238 sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s", 239 current_categories[1], current_categories[2], 240 current_categories[3], current_categories[4], 241 current_categories[5], current_categories[6]); 242 break; 243 } 244 return (current_locale_string); 245 } 246 247 static void 248 revert_to_default(int category) 249 { 250 switch (category) { 251 case LC_CTYPE: 252 #ifdef WITH_RUNE 253 (void)_xpg4_setrunelocale("C"); 254 (void)__runetable_to_netbsd_ctype("C"); 255 #else 256 if (_cClass != _C_CharClassTable) { 257 /* LINTED const castaway */ 258 free((void *)_cClass); 259 _cClass = _C_CharClassTable; 260 } 261 if (_uConvT != _C_ToUpperTable) { 262 /* LINTED const castaway */ 263 free((void *)_uConvT); 264 _uConvT = _C_ToUpperTable; 265 } 266 if (_lConvT != _C_ToLowerTable) { 267 /* LINTED const castaway */ 268 free((void *)_lConvT); 269 _lConvT = _C_ToLowerTable; 270 } 271 #endif 272 break; 273 case LC_MESSAGES: 274 case LC_COLLATE: 275 case LC_MONETARY: 276 case LC_NUMERIC: 277 case LC_TIME: 278 break; 279 } 280 } 281 282 static int 283 force_locale_enable(int category) 284 { 285 revert_to_default(category); 286 287 return 0; 288 } 289 290 static int 291 load_locale_sub( 292 int category, 293 const char *locname, 294 int isspecial 295 ) 296 { 297 char name[PATH_MAX]; 298 299 /* check for the default locales */ 300 if (!strcmp(new_categories[category], "C") || 301 !strcmp(new_categories[category], "POSIX")) { 302 revert_to_default(category); 303 return 0; 304 } 305 306 /* check whether special symbol */ 307 if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0) 308 return force_locale_enable(category); 309 310 /* sanity check */ 311 if (strchr(locname, '/') != NULL) 312 return -1; 313 314 (void)snprintf(name, sizeof(name), "%s/%s/%s", 315 _PathLocale, locname, categories[category]); 316 317 switch (category) { 318 case LC_CTYPE: 319 #ifdef WITH_RUNE 320 if (_xpg4_setrunelocale(__UNCONST(locname))) 321 return -1; 322 if (__runetable_to_netbsd_ctype(locname)) { 323 /* very unfortunate, but need to go to "C" locale */ 324 revert_to_default(category); 325 return -1; 326 } 327 #else 328 if (!__loadctype(name)) 329 return -1; 330 #endif 331 break; 332 333 case LC_MESSAGES: 334 /* 335 * XXX we don't have LC_MESSAGES support yet, 336 * but catopen may use the value of LC_MESSAGES category. 337 * so return successfully if locale directory is present. 338 */ 339 (void)snprintf(name, sizeof(name), "%s/%s", 340 _PathLocale, locname); 341 /* local */ 342 { 343 struct stat st; 344 if (stat(name, &st) < 0) 345 return -1; 346 if (!S_ISDIR(st.st_mode)) 347 return -1; 348 } 349 break; 350 351 case LC_COLLATE: 352 case LC_MONETARY: 353 case LC_NUMERIC: 354 case LC_TIME: 355 return -1; 356 } 357 358 return 0; 359 } 360 361 static char * 362 loadlocale(int category) 363 { 364 //char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX]; 365 //const char *alias; 366 367 _DIAGASSERT(0 < category && category < _LC_LAST); 368 369 if (strcmp(new_categories[category], current_categories[category]) == 0) 370 return (current_categories[category]); 371 372 /* (1) non-aliased file */ 373 if (!load_locale_sub(category, new_categories[category], 0)) 374 goto success; 375 376 ///* (2) lookup locname/catname type alias */ 377 //(void)snprintf(aliaspath, sizeof(aliaspath), 378 // "%s/" _LOCALE_ALIAS_NAME, _PathLocale); 379 //(void)snprintf(loccat, sizeof(loccat), "%s/%s", 380 // new_categories[category], categories[category]); 381 //alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf), 382 // _LOOKUP_CASE_SENSITIVE); 383 //if (!load_locale_sub(category, alias, 1)) 384 // goto success; 385 386 ///* (3) lookup locname type alias */ 387 //alias = _lookup_alias(aliaspath, new_categories[category], 388 // buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE); 389 //if (!load_locale_sub(category, alias, 1)) 390 // goto success; 391 392 return NULL; 393 394 success: 395 (void)strncpyX(current_categories[category], 396 new_categories[category], 397 sizeof(current_categories[category])); 398 return current_categories[category]; 399 } 400 401 static const char * 402 __get_locale_env(int category) 403 { 404 const char *env; 405 406 //_DIAGASSERT(category != LC_ALL); 407 408 ///* 1. check LC_ALL. */ 409 //env = getenv(categories[0]); 410 411 ///* 2. check LC_* */ 412 //if (!env || !*env) 413 // env = getenv(categories[category]); 414 415 ///* 3. check LANG */ 416 //if (!env || !*env) 417 // env = getenv("LANG"); 418 419 ///* 4. if none is set, fall to "C" */ 420 //if (!env || !*env || strchr(env, '/')) 421 env = "C"; 422 423 return env; 424 } 425