setlocale.c revision 1.54 1 1.54 ginsbach /* $NetBSD: setlocale.c,v 1.54 2008/06/12 20:33:23 ginsbach Exp $ */
2 1.10 kleink
3 1.1 cgd /*
4 1.53 ginsbach * Copyright (c) 1991, 1993, 2008
5 1.5 jtc * The Regents of the University of California. All rights reserved.
6 1.5 jtc *
7 1.5 jtc * This code is derived from software contributed to Berkeley by
8 1.5 jtc * Paul Borman at Krystal Technologies.
9 1.1 cgd *
10 1.1 cgd * Redistribution and use in source and binary forms, with or without
11 1.1 cgd * modification, are permitted provided that the following conditions
12 1.1 cgd * are met:
13 1.1 cgd * 1. Redistributions of source code must retain the above copyright
14 1.1 cgd * notice, this list of conditions and the following disclaimer.
15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 cgd * notice, this list of conditions and the following disclaimer in the
17 1.1 cgd * documentation and/or other materials provided with the distribution.
18 1.43 agc * 3. Neither the name of the University nor the names of its contributors
19 1.1 cgd * may be used to endorse or promote products derived from this software
20 1.1 cgd * without specific prior written permission.
21 1.1 cgd *
22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 1.1 cgd * SUCH DAMAGE.
33 1.1 cgd */
34 1.1 cgd
35 1.12 christos #include <sys/cdefs.h>
36 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint)
37 1.10 kleink #if 0
38 1.5 jtc static char sccsid[] = "@(#)setlocale.c 8.1 (Berkeley) 7/4/93";
39 1.10 kleink #else
40 1.54 ginsbach __RCSID("$NetBSD: setlocale.c,v 1.54 2008/06/12 20:33:23 ginsbach Exp $");
41 1.10 kleink #endif
42 1.1 cgd #endif /* LIBC_SCCS and not lint */
43 1.1 cgd
44 1.11 kleink #define _CTYPE_PRIVATE
45 1.11 kleink
46 1.14 kleink #include "namespace.h"
47 1.5 jtc #include <sys/localedef.h>
48 1.37 yamt #include <sys/types.h>
49 1.37 yamt #include <sys/stat.h>
50 1.37 yamt #include <assert.h>
51 1.35 kleink #include <limits.h>
52 1.11 kleink #include <ctype.h>
53 1.18 tshiozak #define __SETLOCALE_SOURCE__
54 1.1 cgd #include <locale.h>
55 1.11 kleink #include <paths.h>
56 1.6 jtc #include <stdio.h>
57 1.5 jtc #include <stdlib.h>
58 1.1 cgd #include <string.h>
59 1.21 veego #include <unistd.h>
60 1.32 itojun #ifdef WITH_RUNE
61 1.24 itojun #include "rune.h"
62 1.24 itojun #include "rune_local.h"
63 1.32 itojun #else
64 1.32 itojun #include "ctypeio.h"
65 1.32 itojun #endif
66 1.53 ginsbach #include "lcmessages.h"
67 1.53 ginsbach #include "lcmonetary.h"
68 1.53 ginsbach #include "lcnumeric.h"
69 1.53 ginsbach #include "lctime.h"
70 1.1 cgd
71 1.47 tshiozak #ifdef CITRUS
72 1.44 tshiozak #include <citrus/citrus_namespace.h>
73 1.44 tshiozak #include <citrus/citrus_region.h>
74 1.44 tshiozak #include <citrus/citrus_lookup.h>
75 1.46 tshiozak #include <citrus/citrus_bcs.h>
76 1.47 tshiozak #else
77 1.47 tshiozak #include <locale/aliasname_local.h>
78 1.47 tshiozak #define _lookup_alias(p, a, b, s, c) __unaliasname((p), (a), (b), (s))
79 1.47 tshiozak #define _bcs_strcasecmp(a, b) strcasecmp((a), (b))
80 1.47 tshiozak #endif
81 1.44 tshiozak
82 1.46 tshiozak #define _LOCALE_SYM_FORCE "/force"
83 1.44 tshiozak
84 1.5 jtc /*
85 1.5 jtc * Category names for getenv()
86 1.5 jtc */
87 1.13 mycroft static const char *const categories[_LC_LAST] = {
88 1.5 jtc "LC_ALL",
89 1.5 jtc "LC_COLLATE",
90 1.5 jtc "LC_CTYPE",
91 1.5 jtc "LC_MONETARY",
92 1.5 jtc "LC_NUMERIC",
93 1.5 jtc "LC_TIME",
94 1.5 jtc "LC_MESSAGES"
95 1.5 jtc };
96 1.1 cgd
97 1.1 cgd /*
98 1.5 jtc * Current locales for each category
99 1.5 jtc */
100 1.5 jtc static char current_categories[_LC_LAST][32] = {
101 1.5 jtc "C",
102 1.5 jtc "C",
103 1.5 jtc "C",
104 1.5 jtc "C",
105 1.5 jtc "C",
106 1.5 jtc "C",
107 1.5 jtc "C"
108 1.5 jtc };
109 1.19 kleink
110 1.5 jtc /*
111 1.5 jtc * The locales we are going to try and load
112 1.1 cgd */
113 1.5 jtc static char new_categories[_LC_LAST][32];
114 1.5 jtc
115 1.5 jtc static char current_locale_string[_LC_LAST * 33];
116 1.5 jtc
117 1.34 itojun static char *currentlocale __P((void));
118 1.46 tshiozak static void revert_to_default __P((int));
119 1.46 tshiozak static int force_locale_enable __P((int));
120 1.46 tshiozak static int load_locale_sub __P((int, const char *, int));
121 1.34 itojun static char *loadlocale __P((int));
122 1.37 yamt static const char *__get_locale_env __P((int));
123 1.24 itojun
124 1.24 itojun char *
125 1.24 itojun __setlocale(category, locale)
126 1.24 itojun int category;
127 1.24 itojun const char *locale;
128 1.24 itojun {
129 1.27 jdolecek int i, loadlocale_success;
130 1.16 christos size_t len;
131 1.37 yamt const char *env, *r;
132 1.5 jtc
133 1.20 tshiozak if (issetugid() ||
134 1.54 ginsbach ((!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE"))) ||
135 1.54 ginsbach !*_PathLocale))
136 1.49 yamt _PathLocale = _PATH_LOCALE;
137 1.5 jtc
138 1.5 jtc if (category < 0 || category >= _LC_LAST)
139 1.1 cgd return (NULL);
140 1.5 jtc
141 1.5 jtc if (!locale)
142 1.5 jtc return (category ?
143 1.5 jtc current_categories[category] : currentlocale());
144 1.5 jtc
145 1.5 jtc /*
146 1.5 jtc * Default to the current locale for everything.
147 1.5 jtc */
148 1.5 jtc for (i = 1; i < _LC_LAST; ++i)
149 1.23 itojun (void)strlcpy(new_categories[i], current_categories[i],
150 1.23 itojun sizeof(new_categories[i]));
151 1.5 jtc
152 1.5 jtc /*
153 1.5 jtc * Now go fill up new_categories from the locale argument
154 1.5 jtc */
155 1.5 jtc if (!*locale) {
156 1.37 yamt if (category == LC_ALL) {
157 1.5 jtc for (i = 1; i < _LC_LAST; ++i) {
158 1.37 yamt env = __get_locale_env(i);
159 1.23 itojun (void)strlcpy(new_categories[i], env,
160 1.23 itojun sizeof(new_categories[i]));
161 1.5 jtc }
162 1.5 jtc }
163 1.37 yamt else {
164 1.37 yamt env = __get_locale_env(category);
165 1.37 yamt (void)strlcpy(new_categories[category], env,
166 1.37 yamt sizeof(new_categories[category]));
167 1.37 yamt }
168 1.8 mrg } else if (category) {
169 1.23 itojun (void)strlcpy(new_categories[category], locale,
170 1.23 itojun sizeof(new_categories[category]));
171 1.5 jtc } else {
172 1.5 jtc if ((r = strchr(locale, '/')) == 0) {
173 1.5 jtc for (i = 1; i < _LC_LAST; ++i) {
174 1.23 itojun (void)strlcpy(new_categories[i], locale,
175 1.23 itojun sizeof(new_categories[i]));
176 1.5 jtc }
177 1.5 jtc } else {
178 1.42 enami for (i = 1;;) {
179 1.42 enami _DIAGASSERT(*r == '/' || *r == 0);
180 1.42 enami _DIAGASSERT(*locale != 0);
181 1.42 enami if (*locale == '/')
182 1.42 enami return (NULL); /* invalid format. */
183 1.40 tshiozak len = r - locale;
184 1.39 itojun if (len + 1 > sizeof(new_categories[i]))
185 1.39 itojun return (NULL); /* too long */
186 1.39 itojun (void)memcpy(new_categories[i], locale, len);
187 1.39 itojun new_categories[i][len] = '\0';
188 1.42 enami if (*r == 0)
189 1.42 enami break;
190 1.42 enami _DIAGASSERT(*r == '/');
191 1.42 enami if (*(locale = ++r) == 0)
192 1.42 enami /* slash followed by NUL */
193 1.42 enami return (NULL);
194 1.42 enami /* skip until NUL or '/' */
195 1.42 enami while (*r && *r != '/')
196 1.42 enami r++;
197 1.42 enami if (++i == _LC_LAST)
198 1.42 enami return (NULL); /* too many slashes. */
199 1.38 tshiozak }
200 1.42 enami if (i + 1 != _LC_LAST)
201 1.42 enami return (NULL); /* too few slashes. */
202 1.5 jtc }
203 1.5 jtc }
204 1.5 jtc
205 1.5 jtc if (category)
206 1.5 jtc return (loadlocale(category));
207 1.5 jtc
208 1.27 jdolecek loadlocale_success = 0;
209 1.22 itojun for (i = 1; i < _LC_LAST; ++i) {
210 1.27 jdolecek if (loadlocale(i) != NULL)
211 1.27 jdolecek loadlocale_success = 1;
212 1.22 itojun }
213 1.27 jdolecek
214 1.27 jdolecek /*
215 1.27 jdolecek * If all categories failed, return NULL; we don't need to back
216 1.27 jdolecek * changes off, since none happened.
217 1.27 jdolecek */
218 1.27 jdolecek if (!loadlocale_success)
219 1.27 jdolecek return NULL;
220 1.27 jdolecek
221 1.9 kleink return (currentlocale());
222 1.5 jtc }
223 1.5 jtc
224 1.5 jtc static char *
225 1.5 jtc currentlocale()
226 1.5 jtc {
227 1.5 jtc int i;
228 1.5 jtc
229 1.23 itojun (void)strlcpy(current_locale_string, current_categories[1],
230 1.23 itojun sizeof(current_locale_string));
231 1.5 jtc
232 1.5 jtc for (i = 2; i < _LC_LAST; ++i)
233 1.5 jtc if (strcmp(current_categories[1], current_categories[i])) {
234 1.5 jtc (void)snprintf(current_locale_string,
235 1.36 yamt sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s",
236 1.5 jtc current_categories[1], current_categories[2],
237 1.5 jtc current_categories[3], current_categories[4],
238 1.36 yamt current_categories[5], current_categories[6]);
239 1.5 jtc break;
240 1.5 jtc }
241 1.5 jtc return (current_locale_string);
242 1.5 jtc }
243 1.5 jtc
244 1.46 tshiozak static void
245 1.46 tshiozak revert_to_default(category)
246 1.46 tshiozak int category;
247 1.46 tshiozak {
248 1.46 tshiozak switch (category) {
249 1.46 tshiozak case LC_CTYPE:
250 1.46 tshiozak #ifdef WITH_RUNE
251 1.46 tshiozak (void)_xpg4_setrunelocale("C");
252 1.46 tshiozak #else
253 1.46 tshiozak if (_ctype_ != _C_ctype_) {
254 1.46 tshiozak /* LINTED const castaway */
255 1.46 tshiozak free((void *)_ctype_);
256 1.46 tshiozak _ctype_ = _C_ctype_;
257 1.46 tshiozak }
258 1.46 tshiozak if (_toupper_tab_ != _C_toupper_) {
259 1.46 tshiozak /* LINTED const castaway */
260 1.46 tshiozak free((void *)_toupper_tab_);
261 1.46 tshiozak _toupper_tab_ = _C_toupper_;
262 1.46 tshiozak }
263 1.46 tshiozak if (_tolower_tab_ != _C_tolower_) {
264 1.46 tshiozak /* LINTED const castaway */
265 1.46 tshiozak free((void *)_tolower_tab_);
266 1.46 tshiozak _tolower_tab_ = _C_tolower_;
267 1.46 tshiozak }
268 1.46 tshiozak #endif
269 1.46 tshiozak break;
270 1.51 manu case LC_TIME:
271 1.51 manu if (_CurrentTimeLocale != &_DefaultTimeLocale) {
272 1.53 ginsbach free(__UNCONST(_CurrentTimeLocale));
273 1.51 manu _CurrentTimeLocale = &_DefaultTimeLocale;
274 1.51 manu }
275 1.51 manu break;
276 1.46 tshiozak case LC_MESSAGES:
277 1.53 ginsbach if (_CurrentMessagesLocale != &_DefaultMessagesLocale) {
278 1.53 ginsbach free(__UNCONST(_CurrentMessagesLocale));
279 1.53 ginsbach _CurrentMessagesLocale = &_DefaultMessagesLocale;
280 1.53 ginsbach }
281 1.53 ginsbach break;
282 1.46 tshiozak case LC_COLLATE:
283 1.53 ginsbach break;
284 1.46 tshiozak case LC_MONETARY:
285 1.53 ginsbach if (_CurrentMonetaryLocale != &_DefaultMonetaryLocale) {
286 1.53 ginsbach free(__UNCONST(_CurrentMonetaryLocale));
287 1.53 ginsbach _CurrentMonetaryLocale = &_DefaultMonetaryLocale;
288 1.53 ginsbach }
289 1.53 ginsbach break;
290 1.46 tshiozak case LC_NUMERIC:
291 1.53 ginsbach if (_CurrentNumericLocale != &_DefaultNumericLocale) {
292 1.53 ginsbach free(__UNCONST(_CurrentNumericLocale));
293 1.53 ginsbach _CurrentNumericLocale = &_DefaultNumericLocale;
294 1.53 ginsbach }
295 1.46 tshiozak break;
296 1.46 tshiozak }
297 1.46 tshiozak }
298 1.46 tshiozak
299 1.44 tshiozak static int
300 1.46 tshiozak force_locale_enable(category)
301 1.46 tshiozak int category;
302 1.46 tshiozak {
303 1.46 tshiozak revert_to_default(category);
304 1.46 tshiozak
305 1.46 tshiozak return 0;
306 1.46 tshiozak }
307 1.46 tshiozak
308 1.46 tshiozak static int
309 1.46 tshiozak load_locale_sub(category, locname, isspecial)
310 1.44 tshiozak int category;
311 1.44 tshiozak const char *locname;
312 1.46 tshiozak int isspecial;
313 1.44 tshiozak {
314 1.44 tshiozak char name[PATH_MAX];
315 1.54 ginsbach int len;
316 1.44 tshiozak
317 1.46 tshiozak /* check for the default locales */
318 1.46 tshiozak if (!strcmp(new_categories[category], "C") ||
319 1.46 tshiozak !strcmp(new_categories[category], "POSIX")) {
320 1.46 tshiozak revert_to_default(category);
321 1.46 tshiozak return 0;
322 1.46 tshiozak }
323 1.46 tshiozak
324 1.46 tshiozak /* check whether special symbol */
325 1.46 tshiozak if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0)
326 1.46 tshiozak return force_locale_enable(category);
327 1.46 tshiozak
328 1.44 tshiozak /* sanity check */
329 1.44 tshiozak if (strchr(locname, '/') != NULL)
330 1.44 tshiozak return -1;
331 1.44 tshiozak
332 1.54 ginsbach len = snprintf(name, sizeof(name), "%s/%s/%s",
333 1.44 tshiozak _PathLocale, locname, categories[category]);
334 1.54 ginsbach if (len < 0 || len >= sizeof(name))
335 1.54 ginsbach return -1;
336 1.44 tshiozak
337 1.44 tshiozak switch (category) {
338 1.44 tshiozak case LC_CTYPE:
339 1.44 tshiozak #ifdef WITH_RUNE
340 1.44 tshiozak if (_xpg4_setrunelocale(__UNCONST(locname)))
341 1.44 tshiozak return -1;
342 1.44 tshiozak #else
343 1.44 tshiozak if (!__loadctype(name))
344 1.44 tshiozak return -1;
345 1.44 tshiozak #endif
346 1.44 tshiozak break;
347 1.44 tshiozak
348 1.44 tshiozak case LC_MESSAGES:
349 1.54 ginsbach len += snprintf(name + len, sizeof(name) - len, "/%s",
350 1.54 ginsbach categories[category]);
351 1.54 ginsbach if (len >= sizeof(name))
352 1.54 ginsbach return -1;
353 1.54 ginsbach if (!__loadmessages(name))
354 1.54 ginsbach #ifdef notyet
355 1.54 ginsbach return -1;
356 1.54 ginsbach #else
357 1.44 tshiozak /*
358 1.44 tshiozak * XXX we don't have LC_MESSAGES support yet,
359 1.44 tshiozak * but catopen may use the value of LC_MESSAGES category.
360 1.44 tshiozak * so return successfully if locale directory is present.
361 1.44 tshiozak */
362 1.44 tshiozak /* local */
363 1.44 tshiozak {
364 1.44 tshiozak struct stat st;
365 1.54 ginsbach
366 1.54 ginsbach (void)snprintf(name, sizeof(name), "%s/%s",
367 1.54 ginsbach _PathLocale, locname);
368 1.44 tshiozak if (stat(name, &st) < 0)
369 1.44 tshiozak return -1;
370 1.44 tshiozak if (!S_ISDIR(st.st_mode))
371 1.44 tshiozak return -1;
372 1.44 tshiozak }
373 1.53 ginsbach #endif
374 1.44 tshiozak break;
375 1.44 tshiozak
376 1.51 manu case LC_TIME:
377 1.51 manu if (!__loadtime(name))
378 1.51 manu return -1;
379 1.51 manu break;
380 1.44 tshiozak case LC_COLLATE:
381 1.53 ginsbach return -1;
382 1.44 tshiozak case LC_MONETARY:
383 1.53 ginsbach if (!__loadmonetary(name))
384 1.53 ginsbach return -1;
385 1.53 ginsbach break;
386 1.44 tshiozak case LC_NUMERIC:
387 1.53 ginsbach if (!__loadnumeric(name))
388 1.53 ginsbach return -1;
389 1.53 ginsbach break;
390 1.44 tshiozak }
391 1.44 tshiozak
392 1.44 tshiozak return 0;
393 1.44 tshiozak }
394 1.44 tshiozak
395 1.15 kleink static char *
396 1.5 jtc loadlocale(category)
397 1.5 jtc int category;
398 1.5 jtc {
399 1.44 tshiozak char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX];
400 1.44 tshiozak const char *alias;
401 1.5 jtc
402 1.37 yamt _DIAGASSERT(0 < category && category < _LC_LAST);
403 1.37 yamt
404 1.11 kleink if (strcmp(new_categories[category], current_categories[category]) == 0)
405 1.5 jtc return (current_categories[category]);
406 1.5 jtc
407 1.44 tshiozak /* (1) non-aliased file */
408 1.46 tshiozak if (!load_locale_sub(category, new_categories[category], 0))
409 1.44 tshiozak goto success;
410 1.44 tshiozak
411 1.44 tshiozak /* (2) lookup locname/catname type alias */
412 1.44 tshiozak (void)snprintf(aliaspath, sizeof(aliaspath),
413 1.44 tshiozak "%s/" _LOCALE_ALIAS_NAME, _PathLocale);
414 1.44 tshiozak (void)snprintf(loccat, sizeof(loccat), "%s/%s",
415 1.44 tshiozak new_categories[category], categories[category]);
416 1.44 tshiozak alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf),
417 1.44 tshiozak _LOOKUP_CASE_SENSITIVE);
418 1.46 tshiozak if (!load_locale_sub(category, alias, 1))
419 1.44 tshiozak goto success;
420 1.44 tshiozak
421 1.44 tshiozak /* (3) lookup locname type alias */
422 1.44 tshiozak alias = _lookup_alias(aliaspath, new_categories[category],
423 1.44 tshiozak buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE);
424 1.46 tshiozak if (!load_locale_sub(category, alias, 1))
425 1.44 tshiozak goto success;
426 1.11 kleink
427 1.44 tshiozak return NULL;
428 1.11 kleink
429 1.44 tshiozak success:
430 1.37 yamt (void)strlcpy(current_categories[category],
431 1.37 yamt new_categories[category],
432 1.37 yamt sizeof(current_categories[category]));
433 1.37 yamt return current_categories[category];
434 1.37 yamt }
435 1.37 yamt
436 1.37 yamt static const char *
437 1.37 yamt __get_locale_env(category)
438 1.37 yamt int category;
439 1.37 yamt {
440 1.37 yamt const char *env;
441 1.37 yamt
442 1.37 yamt _DIAGASSERT(category != LC_ALL);
443 1.37 yamt
444 1.37 yamt /* 1. check LC_ALL. */
445 1.37 yamt env = getenv(categories[0]);
446 1.37 yamt
447 1.37 yamt /* 2. check LC_* */
448 1.37 yamt if (!env || !*env)
449 1.37 yamt env = getenv(categories[category]);
450 1.37 yamt
451 1.37 yamt /* 3. check LANG */
452 1.37 yamt if (!env || !*env)
453 1.37 yamt env = getenv("LANG");
454 1.37 yamt
455 1.37 yamt /* 4. if none is set, fall to "C" */
456 1.37 yamt if (!env || !*env || strchr(env, '/'))
457 1.37 yamt env = "C";
458 1.37 yamt
459 1.37 yamt return env;
460 1.1 cgd }
461