setlocale.c revision 101193
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 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38#if defined(LIBC_SCCS) && !defined(lint) 39static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93"; 40#endif /* LIBC_SCCS and not lint */ 41#include <sys/cdefs.h> 42__FBSDID("$FreeBSD: head/lib/libc/locale/setlocale.c 101193 2002-08-02 01:04:49Z ache $"); 43 44#include <sys/types.h> 45#include <sys/stat.h> 46#include <limits.h> 47#include <locale.h> 48#include <rune.h> 49#include <stdlib.h> 50#include <string.h> 51#include <unistd.h> 52#include "collate.h" 53#include "lmonetary.h" /* for __monetary_load_locale() */ 54#include "lnumeric.h" /* for __numeric_load_locale() */ 55#include "lmessages.h" /* for __messages_load_locale() */ 56#include "setlocale.h" 57#include "../stdtime/timelocal.h" /* for __time_load_locale() */ 58 59/* 60 * Category names for getenv() 61 */ 62static char *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 * The locales we are going to try and load 87 */ 88static char new_categories[_LC_LAST][ENCODING_LEN + 1]; 89static char saved_categories[_LC_LAST][ENCODING_LEN + 1]; 90 91static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)]; 92 93static char *currentlocale(void); 94static char *loadlocale(int); 95 96char * 97setlocale(category, locale) 98 int category; 99 const char *locale; 100{ 101 int i, j, len; 102 char *env, *r; 103 104 if (category < LC_ALL || category >= _LC_LAST) 105 return (NULL); 106 107 if (!locale) 108 return (category != LC_ALL ? 109 current_categories[category] : currentlocale()); 110 111 /* 112 * Default to the current locale for everything. 113 */ 114 for (i = 1; i < _LC_LAST; ++i) 115 (void)strcpy(new_categories[i], current_categories[i]); 116 117 /* 118 * Now go fill up new_categories from the locale argument 119 */ 120 if (!*locale) { 121 env = getenv("LC_ALL"); 122 123 if (category != LC_ALL && (!env || !*env)) 124 env = getenv(categories[category]); 125 126 if (!env || !*env) 127 env = getenv("LANG"); 128 129 if (!env || !*env || strchr(env, '/')) 130 env = "C"; 131 132 (void)strlcpy(new_categories[category], env, ENCODING_LEN + 1); 133 if (category == LC_ALL) { 134 for (i = 1; i < _LC_LAST; ++i) { 135 if (!(env = getenv(categories[i])) || !*env) 136 env = new_categories[LC_ALL]; 137 (void)strlcpy(new_categories[i], env, ENCODING_LEN + 1); 138 } 139 } 140 } else if (category != LC_ALL) 141 (void)strlcpy(new_categories[category], locale, ENCODING_LEN + 1); 142 else { 143 if ((r = strchr(locale, '/')) == NULL) { 144 for (i = 1; i < _LC_LAST; ++i) 145 (void)strlcpy(new_categories[i], locale, ENCODING_LEN + 1); 146 } else { 147 for (i = 1; r[1] == '/'; ++r); 148 if (!r[1]) 149 return (NULL); /* Hmm, just slashes... */ 150 do { 151 if (i == _LC_LAST) 152 return(NULL); /* Too many slashes... */ 153 len = r - locale > ENCODING_LEN ? ENCODING_LEN : r - locale; 154 (void)strlcpy(new_categories[i], locale, len + 1); 155 i++; 156 locale = r; 157 while (*locale == '/') 158 ++locale; 159 while (*++r && *r != '/'); 160 } while (*locale); 161 while (i < _LC_LAST) { 162 (void)strcpy(new_categories[i], 163 new_categories[i-1]); 164 i++; 165 } 166 } 167 } 168 169 if (category != LC_ALL) 170 return (loadlocale(category)); 171 172 for (i = 1; i < _LC_LAST; ++i) { 173 (void)strcpy(saved_categories[i], current_categories[i]); 174 if (loadlocale(i) == NULL) { 175 for (j = 1; j < i; j++) { 176 (void)strcpy(new_categories[j], 177 saved_categories[j]); 178 /* XXX can fail too */ 179 (void)loadlocale(j); 180 } 181 return (NULL); 182 } 183 } 184 return (currentlocale()); 185} 186 187static char * 188currentlocale() 189{ 190 int i; 191 192 (void)strcpy(current_locale_string, current_categories[1]); 193 194 for (i = 2; i < _LC_LAST; ++i) 195 if (strcmp(current_categories[1], current_categories[i])) { 196 for (i = 2; i < _LC_LAST; ++i) { 197 (void) strcat(current_locale_string, "/"); 198 (void) strcat(current_locale_string, current_categories[i]); 199 } 200 break; 201 } 202 return (current_locale_string); 203} 204 205static char * 206loadlocale(category) 207 int category; 208{ 209 char *ret; 210 char *new = new_categories[category]; 211 char *old = current_categories[category]; 212 213 if (_PathLocale == NULL) { 214 char *p = getenv("PATH_LOCALE"); 215 216 if (p != NULL 217#ifndef __NETBSD_SYSCALLS 218 && !issetugid() 219#endif 220 ) { 221 if (strlen(p) + 1/*"/"*/ + ENCODING_LEN + 222 1/*"/"*/ + CATEGORY_LEN >= PATH_MAX) 223 return (NULL); 224 _PathLocale = strdup(p); 225 if (_PathLocale == NULL) 226 return (NULL); 227 } else 228 _PathLocale = _PATH_LOCALE; 229 } 230 231 if (strcmp(new, old) == 0) 232 return (old); 233 234 if (category == LC_CTYPE) { 235 ret = setrunelocale(new) ? NULL : new; 236 if (!ret) 237 (void)setrunelocale(old); 238 else 239 (void)strcpy(old, new); 240 return (ret); 241 } 242 243#define LOAD_CATEGORY(CAT, FUNC) \ 244 if (category == CAT) { \ 245 ret = (FUNC(new) < 0) ? NULL : new; \ 246 if (!ret) \ 247 (void)FUNC(old); \ 248 else \ 249 (void)strcpy(old, new); \ 250 return (ret); \ 251 } 252 253 LOAD_CATEGORY(LC_COLLATE, __collate_load_tables); 254 LOAD_CATEGORY(LC_TIME, __time_load_locale); 255 LOAD_CATEGORY(LC_NUMERIC, __numeric_load_locale); 256 LOAD_CATEGORY(LC_MONETARY, __monetary_load_locale); 257 LOAD_CATEGORY(LC_MESSAGES, __messages_load_locale); 258 259 /* Just in case...*/ 260 return (NULL); 261} 262 263