1 /* $NetBSD: setlocale.c,v 1.37 2002/02/13 07:59:45 yamt Exp $ */ 2 3 /* 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 #if defined(LIBC_SCCS) && !defined(lint) 41 #if 0 42 static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; 43 #else 44 __RCSID("$NetBSD: setlocale.c,v 1.37 2002/02/13 07:59:45 yamt Exp $"); 45 #endif 46 #endif /* LIBC_SCCS and not lint */ 47 48 #define _CTYPE_PRIVATE 49 50 #include "namespace.h" 51 #include <sys/localedef.h> 52 #include <sys/types.h> 53 #include <sys/stat.h> 54 #include <assert.h> 55 #include <limits.h> 56 #include <ctype.h> 57 #define __SETLOCALE_SOURCE__ 58 #include <locale.h> 59 #include <paths.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #ifdef WITH_RUNE 65 #include "rune.h" 66 #include "rune_local.h" 67 #else 68 #include "ctypeio.h" 69 #endif 70 71 /* 72 * Category names for getenv() 73 */ 74 static const char *const categories[_LC_LAST] = { 75 "LC_ALL", 76 "LC_COLLATE", 77 "LC_CTYPE", 78 "LC_MONETARY", 79 "LC_NUMERIC", 80 "LC_TIME", 81 "LC_MESSAGES" 82 }; 83 84 /* 85 * Current locales for each category 86 */ 87 static char current_categories[_LC_LAST][32] = { 88 "C", 89 "C", 90 "C", 91 "C", 92 "C", 93 "C", 94 "C" 95 }; 96 97 /* 98 * The locales we are going to try and load 99 */ 100 static char new_categories[_LC_LAST][32]; 101 102 /* 103 * Backup area to back out changes on failure 104 */ 105 static char saved_categories[_LC_LAST][32]; 106 107 static char current_locale_string[_LC_LAST * 33]; 108 char *_PathLocale; 109 110 static char *currentlocale __P((void)); 111 static char *loadlocale __P((int)); 112 static const char *__get_locale_env __P((int)); 113 114 char * 115 __setlocale(category, locale) 116 int category; 117 const char *locale; 118 { 119 int i, loadlocale_success; 120 size_t len; 121 const char *env, *r; 122 123 if (issetugid() || 124 (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE")))) 125 _PathLocale = _PATH_LOCALE; 126 127 if (category < 0 || category >= _LC_LAST) 128 return (NULL); 129 130 if (!locale) 131 return (category ? 132 current_categories[category] : currentlocale()); 133 134 /* 135 * Default to the current locale for everything. 136 */ 137 for (i = 1; i < _LC_LAST; ++i) 138 (void)strlcpy(new_categories[i], current_categories[i], 139 sizeof(new_categories[i])); 140 141 /* 142 * Now go fill up new_categories from the locale argument 143 */ 144 if (!*locale) { 145 if (category == LC_ALL) { 146 for (i = 1; i < _LC_LAST; ++i) { 147 env = __get_locale_env(i); 148 (void)strlcpy(new_categories[i], env, 149 sizeof(new_categories[i])); 150 } 151 } 152 else { 153 env = __get_locale_env(category); 154 (void)strlcpy(new_categories[category], env, 155 sizeof(new_categories[category])); 156 } 157 } else if (category) { 158 (void)strlcpy(new_categories[category], locale, 159 sizeof(new_categories[category])); 160 } else { 161 if ((r = strchr(locale, '/')) == 0) { 162 for (i = 1; i < _LC_LAST; ++i) { 163 (void)strlcpy(new_categories[i], locale, 164 sizeof(new_categories[i])); 165 } 166 } else { 167 for (i = 1; r[1] == '/'; ++r) 168 ; 169 if (!r[1]) 170 return (NULL); /* Hmm, just slashes... */ 171 do { 172 len = r - locale > sizeof(new_categories[i]) - 1 173 ? sizeof(new_categories[i]) - 1 174 : r - locale; 175 (void)strncpy(new_categories[i++], locale, len); 176 new_categories[i++][len] = 0; 177 locale = r; 178 while (*locale == '/') 179 ++locale; 180 while (*++r && *r != '/'); 181 } while (*locale); 182 while (i < _LC_LAST) 183 (void)strlcpy(new_categories[i], 184 new_categories[i - 1], 185 sizeof(new_categories[i])); 186 } 187 } 188 189 if (category) 190 return (loadlocale(category)); 191 192 loadlocale_success = 0; 193 for (i = 1; i < _LC_LAST; ++i) { 194 (void)strlcpy(saved_categories[i], current_categories[i], 195 sizeof(saved_categories[i])); 196 if (loadlocale(i) != NULL) 197 loadlocale_success = 1; 198 } 199 200 /* 201 * If all categories failed, return NULL; we don't need to back 202 * changes off, since none happened. 203 */ 204 if (!loadlocale_success) 205 return NULL; 206 207 return (currentlocale()); 208 } 209 210 static char * 211 currentlocale() 212 { 213 int i; 214 215 (void)strlcpy(current_locale_string, current_categories[1], 216 sizeof(current_locale_string)); 217 218 for (i = 2; i < _LC_LAST; ++i) 219 if (strcmp(current_categories[1], current_categories[i])) { 220 (void)snprintf(current_locale_string, 221 sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s", 222 current_categories[1], current_categories[2], 223 current_categories[3], current_categories[4], 224 current_categories[5], current_categories[6]); 225 break; 226 } 227 return (current_locale_string); 228 } 229 230 static char * 231 loadlocale(category) 232 int category; 233 { 234 char name[PATH_MAX]; 235 236 _DIAGASSERT(0 < category && category < _LC_LAST); 237 238 if (strcmp(new_categories[category], current_categories[category]) == 0) 239 return (current_categories[category]); 240 241 if (!strcmp(new_categories[category], "C") || 242 !strcmp(new_categories[category], "POSIX")) { 243 244 switch (category) { 245 case LC_CTYPE: 246 #ifdef WITH_RUNE 247 (void)_xpg4_setrunelocale("C"); 248 (void)__runetable_to_netbsd_ctype("C"); 249 #else 250 if (_ctype_ != _C_ctype_) { 251 /* LINTED const castaway */ 252 free((void *)_ctype_); 253 _ctype_ = _C_ctype_; 254 } 255 if (_toupper_tab_ != _C_toupper_) { 256 /* LINTED const castaway */ 257 free((void *)_toupper_tab_); 258 _toupper_tab_ = _C_toupper_; 259 } 260 if (_tolower_tab_ != _C_tolower_) { 261 /* LINTED const castaway */ 262 free((void *)_tolower_tab_); 263 _tolower_tab_ = _C_tolower_; 264 } 265 #endif 266 } 267 268 (void)strlcpy(current_categories[category], 269 new_categories[category], 270 sizeof(current_categories[category])); 271 return current_categories[category]; 272 } 273 274 (void)snprintf(name, sizeof(name), "%s/%s/%s", 275 _PathLocale, new_categories[category], categories[category]); 276 277 switch (category) { 278 case LC_CTYPE: 279 #ifdef WITH_RUNE 280 if (_xpg4_setrunelocale(new_categories[category])) 281 return NULL; 282 if (__runetable_to_netbsd_ctype(new_categories[category])) { 283 /* very unfortunate, but need to go to "C" locale */ 284 (void)_xpg4_setrunelocale("C"); 285 (void)__runetable_to_netbsd_ctype("C"); 286 return NULL; 287 } 288 #else 289 if (!__loadctype(name)) 290 return NULL; 291 #endif 292 break; 293 294 case LC_MESSAGES: 295 /* 296 * XXX we don't have LC_MESSAGES support yet, 297 * but catopen may use the value of LC_MESSAGES category. 298 * so return successfully if locale directory is present. 299 */ 300 (void)snprintf(name, sizeof(name), "%s/%s", 301 _PathLocale, new_categories[category]); 302 /* local */ 303 { 304 struct stat st; 305 if (stat(name, &st) < 0) 306 return NULL; 307 if (!S_ISDIR(st.st_mode)) 308 return NULL; 309 } 310 break; 311 312 case LC_COLLATE: 313 case LC_MONETARY: 314 case LC_NUMERIC: 315 case LC_TIME: 316 return NULL; 317 } 318 319 (void)strlcpy(current_categories[category], 320 new_categories[category], 321 sizeof(current_categories[category])); 322 return current_categories[category]; 323 } 324 325 static const char * 326 __get_locale_env(category) 327 int category; 328 { 329 const char *env; 330 331 _DIAGASSERT(category != LC_ALL); 332 333 /* 1. check LC_ALL. */ 334 env = getenv(categories[0]); 335 336 /* 2. check LC_* */ 337 if (!env || !*env) 338 env = getenv(categories[category]); 339 340 /* 3. check LANG */ 341 if (!env || !*env) 342 env = getenv("LANG"); 343 344 /* 4. if none is set, fall to "C" */ 345 if (!env || !*env || strchr(env, '/')) 346 env = "C"; 347 348 return env; 349 } 350