1 /* $NetBSD: setlocale.c,v 1.42 2002/08/07 04:42:42 enami 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.42 2002/08/07 04:42:42 enami 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 static char current_locale_string[_LC_LAST * 33]; 103 char *_PathLocale; 104 105 static char *currentlocale __P((void)); 106 static char *loadlocale __P((int)); 107 static const char *__get_locale_env __P((int)); 108 109 char * 110 __setlocale(category, locale) 111 int category; 112 const char *locale; 113 { 114 int i, loadlocale_success; 115 size_t len; 116 const char *env, *r; 117 118 if (issetugid() || 119 (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE")))) 120 _PathLocale = _PATH_LOCALE; 121 122 if (category < 0 || category >= _LC_LAST) 123 return (NULL); 124 125 if (!locale) 126 return (category ? 127 current_categories[category] : currentlocale()); 128 129 /* 130 * Default to the current locale for everything. 131 */ 132 for (i = 1; i < _LC_LAST; ++i) 133 (void)strlcpy(new_categories[i], current_categories[i], 134 sizeof(new_categories[i])); 135 136 /* 137 * Now go fill up new_categories from the locale argument 138 */ 139 if (!*locale) { 140 if (category == LC_ALL) { 141 for (i = 1; i < _LC_LAST; ++i) { 142 env = __get_locale_env(i); 143 (void)strlcpy(new_categories[i], env, 144 sizeof(new_categories[i])); 145 } 146 } 147 else { 148 env = __get_locale_env(category); 149 (void)strlcpy(new_categories[category], env, 150 sizeof(new_categories[category])); 151 } 152 } else if (category) { 153 (void)strlcpy(new_categories[category], locale, 154 sizeof(new_categories[category])); 155 } else { 156 if ((r = strchr(locale, '/')) == 0) { 157 for (i = 1; i < _LC_LAST; ++i) { 158 (void)strlcpy(new_categories[i], locale, 159 sizeof(new_categories[i])); 160 } 161 } else { 162 for (i = 1;;) { 163 _DIAGASSERT(*r == '/' || *r == 0); 164 _DIAGASSERT(*locale != 0); 165 if (*locale == '/') 166 return (NULL); /* invalid format. */ 167 len = r - locale; 168 if (len + 1 > sizeof(new_categories[i])) 169 return (NULL); /* too long */ 170 (void)memcpy(new_categories[i], locale, len); 171 new_categories[i][len] = '\0'; 172 if (*r == 0) 173 break; 174 _DIAGASSERT(*r == '/'); 175 if (*(locale = ++r) == 0) 176 /* slash followed by NUL */ 177 return (NULL); 178 /* skip until NUL or '/' */ 179 while (*r && *r != '/') 180 r++; 181 if (++i == _LC_LAST) 182 return (NULL); /* too many slashes. */ 183 } 184 if (i + 1 != _LC_LAST) 185 return (NULL); /* too few slashes. */ 186 } 187 } 188 189 if (category) 190 return (loadlocale(category)); 191 192 loadlocale_success = 0; 193 for (i = 1; i < _LC_LAST; ++i) { 194 if (loadlocale(i) != NULL) 195 loadlocale_success = 1; 196 } 197 198 /* 199 * If all categories failed, return NULL; we don't need to back 200 * changes off, since none happened. 201 */ 202 if (!loadlocale_success) 203 return NULL; 204 205 return (currentlocale()); 206 } 207 208 static char * 209 currentlocale() 210 { 211 int i; 212 213 (void)strlcpy(current_locale_string, current_categories[1], 214 sizeof(current_locale_string)); 215 216 for (i = 2; i < _LC_LAST; ++i) 217 if (strcmp(current_categories[1], current_categories[i])) { 218 (void)snprintf(current_locale_string, 219 sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s", 220 current_categories[1], current_categories[2], 221 current_categories[3], current_categories[4], 222 current_categories[5], current_categories[6]); 223 break; 224 } 225 return (current_locale_string); 226 } 227 228 static char * 229 loadlocale(category) 230 int category; 231 { 232 char name[PATH_MAX]; 233 234 _DIAGASSERT(0 < category && category < _LC_LAST); 235 236 if (strcmp(new_categories[category], current_categories[category]) == 0) 237 return (current_categories[category]); 238 239 if (!strcmp(new_categories[category], "C") || 240 !strcmp(new_categories[category], "POSIX")) { 241 242 switch (category) { 243 case LC_CTYPE: 244 #ifdef WITH_RUNE 245 (void)_xpg4_setrunelocale("C"); 246 (void)__runetable_to_netbsd_ctype("C"); 247 #else 248 if (_ctype_ != _C_ctype_) { 249 /* LINTED const castaway */ 250 free((void *)_ctype_); 251 _ctype_ = _C_ctype_; 252 } 253 if (_toupper_tab_ != _C_toupper_) { 254 /* LINTED const castaway */ 255 free((void *)_toupper_tab_); 256 _toupper_tab_ = _C_toupper_; 257 } 258 if (_tolower_tab_ != _C_tolower_) { 259 /* LINTED const castaway */ 260 free((void *)_tolower_tab_); 261 _tolower_tab_ = _C_tolower_; 262 } 263 #endif 264 } 265 266 (void)strlcpy(current_categories[category], 267 new_categories[category], 268 sizeof(current_categories[category])); 269 return current_categories[category]; 270 } 271 272 (void)snprintf(name, sizeof(name), "%s/%s/%s", 273 _PathLocale, new_categories[category], categories[category]); 274 275 switch (category) { 276 case LC_CTYPE: 277 #ifdef WITH_RUNE 278 if (_xpg4_setrunelocale(new_categories[category])) 279 return NULL; 280 if (__runetable_to_netbsd_ctype(new_categories[category])) { 281 /* very unfortunate, but need to go to "C" locale */ 282 (void)_xpg4_setrunelocale("C"); 283 (void)__runetable_to_netbsd_ctype("C"); 284 return NULL; 285 } 286 #else 287 if (!__loadctype(name)) 288 return NULL; 289 #endif 290 break; 291 292 case LC_MESSAGES: 293 /* 294 * XXX we don't have LC_MESSAGES support yet, 295 * but catopen may use the value of LC_MESSAGES category. 296 * so return successfully if locale directory is present. 297 */ 298 (void)snprintf(name, sizeof(name), "%s/%s", 299 _PathLocale, new_categories[category]); 300 /* local */ 301 { 302 struct stat st; 303 if (stat(name, &st) < 0) 304 return NULL; 305 if (!S_ISDIR(st.st_mode)) 306 return NULL; 307 } 308 break; 309 310 case LC_COLLATE: 311 case LC_MONETARY: 312 case LC_NUMERIC: 313 case LC_TIME: 314 return NULL; 315 } 316 317 (void)strlcpy(current_categories[category], 318 new_categories[category], 319 sizeof(current_categories[category])); 320 return current_categories[category]; 321 } 322 323 static const char * 324 __get_locale_env(category) 325 int category; 326 { 327 const char *env; 328 329 _DIAGASSERT(category != LC_ALL); 330 331 /* 1. check LC_ALL. */ 332 env = getenv(categories[0]); 333 334 /* 2. check LC_* */ 335 if (!env || !*env) 336 env = getenv(categories[category]); 337 338 /* 3. check LANG */ 339 if (!env || !*env) 340 env = getenv("LANG"); 341 342 /* 4. if none is set, fall to "C" */ 343 if (!env || !*env || strchr(env, '/')) 344 env = "C"; 345 346 return env; 347 } 348