1 /* $NetBSD: setlocale.c,v 1.36 2002/01/22 17:08:02 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.36 2002/01/22 17:08:02 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 <limits.h> 53 #include <ctype.h> 54 #define __SETLOCALE_SOURCE__ 55 #include <locale.h> 56 #include <paths.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 #ifdef WITH_RUNE 62 #include "rune.h" 63 #include "rune_local.h" 64 #else 65 #include "ctypeio.h" 66 #endif 67 68 /* 69 * Category names for getenv() 70 */ 71 static const char *const categories[_LC_LAST] = { 72 "LC_ALL", 73 "LC_COLLATE", 74 "LC_CTYPE", 75 "LC_MONETARY", 76 "LC_NUMERIC", 77 "LC_TIME", 78 "LC_MESSAGES" 79 }; 80 81 /* 82 * Current locales for each category 83 */ 84 static char current_categories[_LC_LAST][32] = { 85 "C", 86 "C", 87 "C", 88 "C", 89 "C", 90 "C", 91 "C" 92 }; 93 94 /* 95 * The locales we are going to try and load 96 */ 97 static char new_categories[_LC_LAST][32]; 98 99 /* 100 * Backup area to back out changes on failure 101 */ 102 static char saved_categories[_LC_LAST][32]; 103 104 static char current_locale_string[_LC_LAST * 33]; 105 char *_PathLocale; 106 107 static char *currentlocale __P((void)); 108 static char *loadlocale __P((int)); 109 110 char * 111 __setlocale(category, locale) 112 int category; 113 const char *locale; 114 { 115 int i, loadlocale_success; 116 size_t len; 117 char *env, *r; 118 119 if (issetugid() || 120 (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE")))) 121 _PathLocale = _PATH_LOCALE; 122 123 if (category < 0 || category >= _LC_LAST) 124 return (NULL); 125 126 if (!locale) 127 return (category ? 128 current_categories[category] : currentlocale()); 129 130 /* 131 * Default to the current locale for everything. 132 */ 133 for (i = 1; i < _LC_LAST; ++i) 134 (void)strlcpy(new_categories[i], current_categories[i], 135 sizeof(new_categories[i])); 136 137 /* 138 * Now go fill up new_categories from the locale argument 139 */ 140 if (!*locale) { 141 env = getenv(categories[category]); 142 143 if (!env || !*env) 144 env = getenv(categories[0]); 145 146 if (!env || !*env) 147 env = getenv("LANG"); 148 149 if (!env || !*env || strchr(env, '/')) 150 env = "C"; 151 152 (void)strlcpy(new_categories[category], env, 153 sizeof(new_categories[category])); 154 if (!category) { 155 for (i = 1; i < _LC_LAST; ++i) { 156 env = getenv(categories[i]); 157 if (!env || !*env || strchr(env, '/')) 158 env = new_categories[0]; 159 (void)strlcpy(new_categories[i], env, 160 sizeof(new_categories[i])); 161 } 162 } 163 } else if (category) { 164 (void)strlcpy(new_categories[category], locale, 165 sizeof(new_categories[category])); 166 } else { 167 if ((r = strchr(locale, '/')) == 0) { 168 for (i = 1; i < _LC_LAST; ++i) { 169 (void)strlcpy(new_categories[i], locale, 170 sizeof(new_categories[i])); 171 } 172 } else { 173 for (i = 1; r[1] == '/'; ++r) 174 ; 175 if (!r[1]) 176 return (NULL); /* Hmm, just slashes... */ 177 do { 178 len = r - locale > sizeof(new_categories[i]) - 1 179 ? sizeof(new_categories[i]) - 1 180 : r - locale; 181 (void)strncpy(new_categories[i++], locale, len); 182 new_categories[i++][len] = 0; 183 locale = r; 184 while (*locale == '/') 185 ++locale; 186 while (*++r && *r != '/'); 187 } while (*locale); 188 while (i < _LC_LAST) 189 (void)strlcpy(new_categories[i], 190 new_categories[i - 1], 191 sizeof(new_categories[i])); 192 } 193 } 194 195 if (category) 196 return (loadlocale(category)); 197 198 loadlocale_success = 0; 199 for (i = 1; i < _LC_LAST; ++i) { 200 (void)strlcpy(saved_categories[i], current_categories[i], 201 sizeof(saved_categories[i])); 202 if (loadlocale(i) != NULL) 203 loadlocale_success = 1; 204 } 205 206 /* 207 * If all categories failed, return NULL; we don't need to back 208 * changes off, since none happened. 209 */ 210 if (!loadlocale_success) 211 return NULL; 212 213 return (currentlocale()); 214 } 215 216 static char * 217 currentlocale() 218 { 219 int i; 220 221 (void)strlcpy(current_locale_string, current_categories[1], 222 sizeof(current_locale_string)); 223 224 for (i = 2; i < _LC_LAST; ++i) 225 if (strcmp(current_categories[1], current_categories[i])) { 226 (void)snprintf(current_locale_string, 227 sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s", 228 current_categories[1], current_categories[2], 229 current_categories[3], current_categories[4], 230 current_categories[5], current_categories[6]); 231 break; 232 } 233 return (current_locale_string); 234 } 235 236 static char * 237 loadlocale(category) 238 int category; 239 { 240 char name[PATH_MAX]; 241 242 if (strcmp(new_categories[category], current_categories[category]) == 0) 243 return (current_categories[category]); 244 245 if (!strcmp(new_categories[category], "C") || 246 !strcmp(new_categories[category], "POSIX")) { 247 248 switch (category) { 249 case LC_CTYPE: 250 #ifdef WITH_RUNE 251 (void)_xpg4_setrunelocale("C"); 252 (void)__runetable_to_netbsd_ctype("C"); 253 #else 254 if (_ctype_ != _C_ctype_) { 255 /* LINTED const castaway */ 256 free((void *)_ctype_); 257 _ctype_ = _C_ctype_; 258 } 259 if (_toupper_tab_ != _C_toupper_) { 260 /* LINTED const castaway */ 261 free((void *)_toupper_tab_); 262 _toupper_tab_ = _C_toupper_; 263 } 264 if (_tolower_tab_ != _C_tolower_) { 265 /* LINTED const castaway */ 266 free((void *)_tolower_tab_); 267 _tolower_tab_ = _C_tolower_; 268 } 269 #endif 270 } 271 272 (void)strlcpy(current_categories[category], 273 new_categories[category], 274 sizeof(current_categories[category])); 275 return current_categories[category]; 276 } 277 278 /* 279 * Some day we will actually look at this file. 280 */ 281 (void)snprintf(name, sizeof(name), "%s/%s/%s", 282 _PathLocale, new_categories[category], categories[category]); 283 284 switch (category) { 285 case LC_CTYPE: 286 #ifdef WITH_RUNE 287 if (_xpg4_setrunelocale(new_categories[category])) 288 return NULL; 289 if (__runetable_to_netbsd_ctype(new_categories[category])) { 290 /* very unfortunate, but need to go to "C" locale */ 291 (void)_xpg4_setrunelocale("C"); 292 (void)__runetable_to_netbsd_ctype("C"); 293 return NULL; 294 } 295 #else 296 if (!__loadctype(name)) 297 return NULL; 298 #endif 299 300 (void)strlcpy(current_categories[category], 301 new_categories[category], 302 sizeof(current_categories[category])); 303 return current_categories[category]; 304 305 case LC_COLLATE: 306 case LC_MESSAGES: 307 case LC_MONETARY: 308 case LC_NUMERIC: 309 case LC_TIME: 310 return NULL; 311 } 312 313 return NULL; 314 } 315