1 /* $NetBSD: src/lib/libc/locale/setlocale.c,v 1.47 2004/07/21 20:27:46 tshiozak Exp $ */
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Paul Borman at Krystal Technologies.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #define _CTYPE_PRIVATE
37 #include <sys/types.h>
38 #include <sys/localedef.h>
51 #include "rune_local.h"
53 #include "../citrus/citrus_namespace.h"
54 #include "../citrus/citrus_region.h"
55 #include "../citrus/citrus_lookup.h"
56 #include "../citrus/citrus_bcs.h"
58 #define _LOCALE_ALIAS_NAME "locale.alias"
59 #define _LOCALE_SYM_FORCE "/force"
61 static char *currentlocale(void);
62 static void revert_to_default(int);
63 static int force_locale_enable(int);
64 static int load_locale_sub(int, const char *, int);
65 static char *loadlocale(int);
66 static const char *__get_locale_env(int);
68 static void revert_collate(void);
69 static int load_ctype(const char *);
70 static void revert_ctype(void);
71 static int load_messages(const char *);
75 int (*load_function)(const char *);
76 void (*revert_function)(void);
78 { "LC_ALL", NULL, NULL },
79 { "LC_COLLATE", __collate_load_tables, revert_collate },
80 { "LC_CTYPE", load_ctype, revert_ctype },
81 { "LC_MONETARY", NULL, NULL },
82 { "LC_NUMERIC", NULL, NULL },
83 { "LC_TIME", NULL, NULL },
84 { "LC_MESSAGES", load_messages, NULL }
88 * Current locales for each category
90 static char current_categories[_LC_LAST][32] = {
101 * The locales we are going to try and load
103 static char new_categories[_LC_LAST][32];
105 static char current_locale_string[_LC_LAST * 33];
106 const char *_PathLocale;
109 load_ctype(const char *locale)
111 if (_xpg4_setrunelocale(locale))
113 if (__runetable_to_netbsd_ctype(locale)) {
114 /* very unfortunate, but need to go to "C" locale */
125 _xpg4_setrunelocale("C");
126 __runetable_to_netbsd_ctype("C");
132 __collate_load_tables("C");
136 load_messages(const char *locale)
142 * XXX we don't have LC_MESSAGES support yet,
143 * but catopen may use the value of LC_MESSAGES category.
144 * so return successfully if locale directory is present.
146 snprintf(name, sizeof(name), "%s/%s", _PathLocale, locale);
148 if (stat(name, &st) < 0)
150 if (!S_ISDIR(st.st_mode))
156 setlocale(int category, const char *locale)
158 int i, loadlocale_success;
162 __mb_len_max_runtime = 32;
165 (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE"))))
166 _PathLocale = _PATH_LOCALE;
168 if (category < 0 || category >= (int)__arysize(categories))
173 current_categories[category] : currentlocale());
176 * Default to the current locale for everything.
178 for (i = 1; i < _LC_LAST; ++i)
179 strlcpy(new_categories[i], current_categories[i],
180 sizeof(new_categories[i]));
183 * Now go fill up new_categories from the locale argument
185 if (*locale == '\0') {
186 if (category == LC_ALL) {
187 for (i = 1; i < _LC_LAST; ++i) {
188 env = __get_locale_env(i);
189 strlcpy(new_categories[i], env,
190 sizeof(new_categories[i]));
194 env = __get_locale_env(category);
195 strlcpy(new_categories[category], env,
196 sizeof(new_categories[category]));
198 } else if (category) {
199 strlcpy(new_categories[category], locale,
200 sizeof(new_categories[category]));
202 if ((r = strchr(locale, '/')) == NULL) {
203 for (i = 1; i < _LC_LAST; ++i) {
204 strlcpy(new_categories[i], locale,
205 sizeof(new_categories[i]));
209 _DIAGASSERT(*r == '/' || *r == 0);
210 _DIAGASSERT(*locale != 0);
212 return(NULL); /* invalid format. */
214 if (len + 1 > sizeof(new_categories[i]))
215 return(NULL); /* too long */
216 memcpy(new_categories[i], locale, len);
217 new_categories[i][len] = '\0';
220 _DIAGASSERT(*r == '/');
221 if (*(locale = ++r) == 0)
222 /* slash followed by NUL */
224 /* skip until NUL or '/' */
225 while (*r && *r != '/')
228 return(NULL); /* too many slashes. */
230 if (i + 1 != _LC_LAST)
231 return(NULL); /* too few slashes. */
236 return(loadlocale(category));
238 loadlocale_success = 0;
239 for (i = 1; i < _LC_LAST; ++i) {
240 if (loadlocale(i) != NULL)
241 loadlocale_success = 1;
245 * If all categories failed, return NULL; we don't need to back
246 * changes off, since none happened.
248 if (!loadlocale_success)
251 return(currentlocale());
259 strlcpy(current_locale_string, current_categories[1],
260 sizeof(current_locale_string));
262 for (i = 2; i < _LC_LAST; ++i)
263 if (strcmp(current_categories[1], current_categories[i])) {
264 snprintf(current_locale_string,
265 sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s",
266 current_categories[1], current_categories[2],
267 current_categories[3], current_categories[4],
268 current_categories[5], current_categories[6]);
271 return(current_locale_string);
275 revert_to_default(int category)
277 _DIAGASSERT(category >= 0 && category < _LC_LAST);
279 if (categories[category].revert_function != NULL)
280 categories[category].revert_function();
284 force_locale_enable(int category)
286 revert_to_default(category);
292 load_locale_sub(int category, const char *locname, int isspecial)
296 /* check for the default locales */
297 if (!strcmp(new_categories[category], "C") ||
298 !strcmp(new_categories[category], "POSIX")) {
299 revert_to_default(category);
303 /* check whether special symbol */
304 if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0)
305 return(force_locale_enable(category));
308 if (strchr(locname, '/') != NULL)
311 snprintf(name, sizeof(name), "%s/%s/%s", _PathLocale, locname,
312 categories[category].name);
314 if (category > 0 && category < (int)__arysize(categories) &&
315 categories[category].load_function != NULL)
316 return(categories[category].load_function(locname));
322 loadlocale(int category)
324 char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX];
327 _DIAGASSERT(0 < category && category < __arysize(categories));
329 if (strcmp(new_categories[category], current_categories[category]) == 0)
330 return(current_categories[category]);
332 /* (1) non-aliased file */
333 if (!load_locale_sub(category, new_categories[category], 0))
336 /* (2) lookup locname/catname type alias */
337 snprintf(aliaspath, sizeof(aliaspath), "%s/" _LOCALE_ALIAS_NAME,
339 snprintf(loccat, sizeof(loccat), "%s/%s", new_categories[category],
340 categories[category].name);
341 alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf),
342 _LOOKUP_CASE_SENSITIVE);
343 if (!load_locale_sub(category, alias, 1))
346 /* (3) lookup locname type alias */
347 alias = _lookup_alias(aliaspath, new_categories[category],
348 buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE);
349 if (!load_locale_sub(category, alias, 1))
355 strlcpy(current_categories[category], new_categories[category],
356 sizeof(current_categories[category]));
357 return(current_categories[category]);
361 __get_locale_env(int category)
365 _DIAGASSERT(category != LC_ALL);
367 /* 1. check LC_ALL. */
368 env = getenv(categories[0].name);
371 if (env == NULL || *env == '\0')
372 env = getenv(categories[category].name);
375 if (env == NULL || *env == '\0')
376 env = getenv("LANG");
378 /* 4. if none is set, fall to "C" */
379 if (env == NULL || *env == '\0' || strchr(env, '/'))