setlocale.c revision 1.52.6.1 1 1.52.6.1 yamt /* $NetBSD: setlocale.c,v 1.52.6.1 2008/05/18 12:30:17 yamt Exp $ */
2 1.10 kleink
3 1.1 cgd /*
4 1.52.6.1 yamt * 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.52.6.1 yamt __RCSID("$NetBSD: setlocale.c,v 1.52.6.1 2008/05/18 12:30:17 yamt 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.52.6.1 yamt #include "lcmessages.h"
67 1.52.6.1 yamt #include "lcmonetary.h"
68 1.52.6.1 yamt #include "lcnumeric.h"
69 1.52.6.1 yamt #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.24 itojun (!_PathLocale && !(_PathLocale = getenv("PATH_LOCALE"))))
135 1.49 yamt _PathLocale = _PATH_LOCALE;
136 1.5 jtc
137 1.5 jtc if (category < 0 || category >= _LC_LAST)
138 1.1 cgd return (NULL);
139 1.5 jtc
140 1.5 jtc if (!locale)
141 1.5 jtc return (category ?
142 1.5 jtc current_categories[category] : currentlocale());
143 1.5 jtc
144 1.5 jtc /*
145 1.5 jtc * Default to the current locale for everything.
146 1.5 jtc */
147 1.5 jtc for (i = 1; i < _LC_LAST; ++i)
148 1.23 itojun (void)strlcpy(new_categories[i], current_categories[i],
149 1.23 itojun sizeof(new_categories[i]));
150 1.5 jtc
151 1.5 jtc /*
152 1.5 jtc * Now go fill up new_categories from the locale argument
153 1.5 jtc */
154 1.5 jtc if (!*locale) {
155 1.37 yamt if (category == LC_ALL) {
156 1.5 jtc for (i = 1; i < _LC_LAST; ++i) {
157 1.37 yamt env = __get_locale_env(i);
158 1.23 itojun (void)strlcpy(new_categories[i], env,
159 1.23 itojun sizeof(new_categories[i]));
160 1.5 jtc }
161 1.5 jtc }
162 1.37 yamt else {
163 1.37 yamt env = __get_locale_env(category);
164 1.37 yamt (void)strlcpy(new_categories[category], env,
165 1.37 yamt sizeof(new_categories[category]));
166 1.37 yamt }
167 1.8 mrg } else if (category) {
168 1.23 itojun (void)strlcpy(new_categories[category], locale,
169 1.23 itojun sizeof(new_categories[category]));
170 1.5 jtc } else {
171 1.5 jtc if ((r = strchr(locale, '/')) == 0) {
172 1.5 jtc for (i = 1; i < _LC_LAST; ++i) {
173 1.23 itojun (void)strlcpy(new_categories[i], locale,
174 1.23 itojun sizeof(new_categories[i]));
175 1.5 jtc }
176 1.5 jtc } else {
177 1.42 enami for (i = 1;;) {
178 1.42 enami _DIAGASSERT(*r == '/' || *r == 0);
179 1.42 enami _DIAGASSERT(*locale != 0);
180 1.42 enami if (*locale == '/')
181 1.42 enami return (NULL); /* invalid format. */
182 1.40 tshiozak len = r - locale;
183 1.39 itojun if (len + 1 > sizeof(new_categories[i]))
184 1.39 itojun return (NULL); /* too long */
185 1.39 itojun (void)memcpy(new_categories[i], locale, len);
186 1.39 itojun new_categories[i][len] = '\0';
187 1.42 enami if (*r == 0)
188 1.42 enami break;
189 1.42 enami _DIAGASSERT(*r == '/');
190 1.42 enami if (*(locale = ++r) == 0)
191 1.42 enami /* slash followed by NUL */
192 1.42 enami return (NULL);
193 1.42 enami /* skip until NUL or '/' */
194 1.42 enami while (*r && *r != '/')
195 1.42 enami r++;
196 1.42 enami if (++i == _LC_LAST)
197 1.42 enami return (NULL); /* too many slashes. */
198 1.38 tshiozak }
199 1.42 enami if (i + 1 != _LC_LAST)
200 1.42 enami return (NULL); /* too few slashes. */
201 1.5 jtc }
202 1.5 jtc }
203 1.5 jtc
204 1.5 jtc if (category)
205 1.5 jtc return (loadlocale(category));
206 1.5 jtc
207 1.27 jdolecek loadlocale_success = 0;
208 1.22 itojun for (i = 1; i < _LC_LAST; ++i) {
209 1.27 jdolecek if (loadlocale(i) != NULL)
210 1.27 jdolecek loadlocale_success = 1;
211 1.22 itojun }
212 1.27 jdolecek
213 1.27 jdolecek /*
214 1.27 jdolecek * If all categories failed, return NULL; we don't need to back
215 1.27 jdolecek * changes off, since none happened.
216 1.27 jdolecek */
217 1.27 jdolecek if (!loadlocale_success)
218 1.27 jdolecek return NULL;
219 1.27 jdolecek
220 1.9 kleink return (currentlocale());
221 1.5 jtc }
222 1.5 jtc
223 1.5 jtc static char *
224 1.5 jtc currentlocale()
225 1.5 jtc {
226 1.5 jtc int i;
227 1.5 jtc
228 1.23 itojun (void)strlcpy(current_locale_string, current_categories[1],
229 1.23 itojun sizeof(current_locale_string));
230 1.5 jtc
231 1.5 jtc for (i = 2; i < _LC_LAST; ++i)
232 1.5 jtc if (strcmp(current_categories[1], current_categories[i])) {
233 1.5 jtc (void)snprintf(current_locale_string,
234 1.36 yamt sizeof(current_locale_string), "%s/%s/%s/%s/%s/%s",
235 1.5 jtc current_categories[1], current_categories[2],
236 1.5 jtc current_categories[3], current_categories[4],
237 1.36 yamt current_categories[5], current_categories[6]);
238 1.5 jtc break;
239 1.5 jtc }
240 1.5 jtc return (current_locale_string);
241 1.5 jtc }
242 1.5 jtc
243 1.46 tshiozak static void
244 1.46 tshiozak revert_to_default(category)
245 1.46 tshiozak int category;
246 1.46 tshiozak {
247 1.46 tshiozak switch (category) {
248 1.46 tshiozak case LC_CTYPE:
249 1.46 tshiozak #ifdef WITH_RUNE
250 1.46 tshiozak (void)_xpg4_setrunelocale("C");
251 1.46 tshiozak #else
252 1.46 tshiozak if (_ctype_ != _C_ctype_) {
253 1.46 tshiozak /* LINTED const castaway */
254 1.46 tshiozak free((void *)_ctype_);
255 1.46 tshiozak _ctype_ = _C_ctype_;
256 1.46 tshiozak }
257 1.46 tshiozak if (_toupper_tab_ != _C_toupper_) {
258 1.46 tshiozak /* LINTED const castaway */
259 1.46 tshiozak free((void *)_toupper_tab_);
260 1.46 tshiozak _toupper_tab_ = _C_toupper_;
261 1.46 tshiozak }
262 1.46 tshiozak if (_tolower_tab_ != _C_tolower_) {
263 1.46 tshiozak /* LINTED const castaway */
264 1.46 tshiozak free((void *)_tolower_tab_);
265 1.46 tshiozak _tolower_tab_ = _C_tolower_;
266 1.46 tshiozak }
267 1.46 tshiozak #endif
268 1.46 tshiozak break;
269 1.51 manu case LC_TIME:
270 1.51 manu if (_CurrentTimeLocale != &_DefaultTimeLocale) {
271 1.52.6.1 yamt free(__UNCONST(_CurrentTimeLocale));
272 1.51 manu _CurrentTimeLocale = &_DefaultTimeLocale;
273 1.51 manu }
274 1.51 manu break;
275 1.46 tshiozak case LC_MESSAGES:
276 1.52.6.1 yamt if (_CurrentMessagesLocale != &_DefaultMessagesLocale) {
277 1.52.6.1 yamt free(__UNCONST(_CurrentMessagesLocale));
278 1.52.6.1 yamt _CurrentMessagesLocale = &_DefaultMessagesLocale;
279 1.52.6.1 yamt }
280 1.52.6.1 yamt break;
281 1.46 tshiozak case LC_COLLATE:
282 1.52.6.1 yamt break;
283 1.46 tshiozak case LC_MONETARY:
284 1.52.6.1 yamt if (_CurrentMonetaryLocale != &_DefaultMonetaryLocale) {
285 1.52.6.1 yamt free(__UNCONST(_CurrentMonetaryLocale));
286 1.52.6.1 yamt _CurrentMonetaryLocale = &_DefaultMonetaryLocale;
287 1.52.6.1 yamt }
288 1.52.6.1 yamt break;
289 1.46 tshiozak case LC_NUMERIC:
290 1.52.6.1 yamt if (_CurrentNumericLocale != &_DefaultNumericLocale) {
291 1.52.6.1 yamt free(__UNCONST(_CurrentNumericLocale));
292 1.52.6.1 yamt _CurrentNumericLocale = &_DefaultNumericLocale;
293 1.52.6.1 yamt }
294 1.46 tshiozak break;
295 1.46 tshiozak }
296 1.46 tshiozak }
297 1.46 tshiozak
298 1.44 tshiozak static int
299 1.46 tshiozak force_locale_enable(category)
300 1.46 tshiozak int category;
301 1.46 tshiozak {
302 1.46 tshiozak revert_to_default(category);
303 1.46 tshiozak
304 1.46 tshiozak return 0;
305 1.46 tshiozak }
306 1.46 tshiozak
307 1.46 tshiozak static int
308 1.46 tshiozak load_locale_sub(category, locname, isspecial)
309 1.44 tshiozak int category;
310 1.44 tshiozak const char *locname;
311 1.46 tshiozak int isspecial;
312 1.44 tshiozak {
313 1.44 tshiozak char name[PATH_MAX];
314 1.44 tshiozak
315 1.46 tshiozak /* check for the default locales */
316 1.46 tshiozak if (!strcmp(new_categories[category], "C") ||
317 1.46 tshiozak !strcmp(new_categories[category], "POSIX")) {
318 1.46 tshiozak revert_to_default(category);
319 1.46 tshiozak return 0;
320 1.46 tshiozak }
321 1.46 tshiozak
322 1.46 tshiozak /* check whether special symbol */
323 1.46 tshiozak if (isspecial && _bcs_strcasecmp(locname, _LOCALE_SYM_FORCE) == 0)
324 1.46 tshiozak return force_locale_enable(category);
325 1.46 tshiozak
326 1.44 tshiozak /* sanity check */
327 1.44 tshiozak if (strchr(locname, '/') != NULL)
328 1.44 tshiozak return -1;
329 1.44 tshiozak
330 1.44 tshiozak (void)snprintf(name, sizeof(name), "%s/%s/%s",
331 1.44 tshiozak _PathLocale, locname, categories[category]);
332 1.44 tshiozak
333 1.44 tshiozak switch (category) {
334 1.44 tshiozak case LC_CTYPE:
335 1.44 tshiozak #ifdef WITH_RUNE
336 1.44 tshiozak if (_xpg4_setrunelocale(__UNCONST(locname)))
337 1.44 tshiozak return -1;
338 1.44 tshiozak #else
339 1.44 tshiozak if (!__loadctype(name))
340 1.44 tshiozak return -1;
341 1.44 tshiozak #endif
342 1.44 tshiozak break;
343 1.44 tshiozak
344 1.44 tshiozak case LC_MESSAGES:
345 1.52.6.1 yamt #ifdef OLD_NO_LC_MESSAGES
346 1.44 tshiozak /*
347 1.44 tshiozak * XXX we don't have LC_MESSAGES support yet,
348 1.44 tshiozak * but catopen may use the value of LC_MESSAGES category.
349 1.44 tshiozak * so return successfully if locale directory is present.
350 1.44 tshiozak */
351 1.44 tshiozak (void)snprintf(name, sizeof(name), "%s/%s",
352 1.44 tshiozak _PathLocale, locname);
353 1.44 tshiozak /* local */
354 1.44 tshiozak {
355 1.44 tshiozak struct stat st;
356 1.44 tshiozak if (stat(name, &st) < 0)
357 1.44 tshiozak return -1;
358 1.44 tshiozak if (!S_ISDIR(st.st_mode))
359 1.44 tshiozak return -1;
360 1.44 tshiozak }
361 1.52.6.1 yamt #else
362 1.52.6.1 yamt if (!__loadmessages(name))
363 1.52.6.1 yamt return -1;
364 1.52.6.1 yamt #endif
365 1.44 tshiozak break;
366 1.44 tshiozak
367 1.51 manu case LC_TIME:
368 1.51 manu if (!__loadtime(name))
369 1.51 manu return -1;
370 1.51 manu break;
371 1.44 tshiozak case LC_COLLATE:
372 1.52.6.1 yamt return -1;
373 1.44 tshiozak case LC_MONETARY:
374 1.52.6.1 yamt if (!__loadmonetary(name))
375 1.52.6.1 yamt return -1;
376 1.52.6.1 yamt break;
377 1.44 tshiozak case LC_NUMERIC:
378 1.52.6.1 yamt if (!__loadnumeric(name))
379 1.52.6.1 yamt return -1;
380 1.52.6.1 yamt break;
381 1.44 tshiozak }
382 1.44 tshiozak
383 1.44 tshiozak return 0;
384 1.44 tshiozak }
385 1.44 tshiozak
386 1.15 kleink static char *
387 1.5 jtc loadlocale(category)
388 1.5 jtc int category;
389 1.5 jtc {
390 1.44 tshiozak char aliaspath[PATH_MAX], loccat[PATH_MAX], buf[PATH_MAX];
391 1.44 tshiozak const char *alias;
392 1.5 jtc
393 1.37 yamt _DIAGASSERT(0 < category && category < _LC_LAST);
394 1.37 yamt
395 1.11 kleink if (strcmp(new_categories[category], current_categories[category]) == 0)
396 1.5 jtc return (current_categories[category]);
397 1.5 jtc
398 1.44 tshiozak /* (1) non-aliased file */
399 1.46 tshiozak if (!load_locale_sub(category, new_categories[category], 0))
400 1.44 tshiozak goto success;
401 1.44 tshiozak
402 1.44 tshiozak /* (2) lookup locname/catname type alias */
403 1.44 tshiozak (void)snprintf(aliaspath, sizeof(aliaspath),
404 1.44 tshiozak "%s/" _LOCALE_ALIAS_NAME, _PathLocale);
405 1.44 tshiozak (void)snprintf(loccat, sizeof(loccat), "%s/%s",
406 1.44 tshiozak new_categories[category], categories[category]);
407 1.44 tshiozak alias = _lookup_alias(aliaspath, loccat, buf, sizeof(buf),
408 1.44 tshiozak _LOOKUP_CASE_SENSITIVE);
409 1.46 tshiozak if (!load_locale_sub(category, alias, 1))
410 1.44 tshiozak goto success;
411 1.44 tshiozak
412 1.44 tshiozak /* (3) lookup locname type alias */
413 1.44 tshiozak alias = _lookup_alias(aliaspath, new_categories[category],
414 1.44 tshiozak buf, sizeof(buf), _LOOKUP_CASE_SENSITIVE);
415 1.46 tshiozak if (!load_locale_sub(category, alias, 1))
416 1.44 tshiozak goto success;
417 1.11 kleink
418 1.44 tshiozak return NULL;
419 1.11 kleink
420 1.44 tshiozak success:
421 1.37 yamt (void)strlcpy(current_categories[category],
422 1.37 yamt new_categories[category],
423 1.37 yamt sizeof(current_categories[category]));
424 1.37 yamt return current_categories[category];
425 1.37 yamt }
426 1.37 yamt
427 1.37 yamt static const char *
428 1.37 yamt __get_locale_env(category)
429 1.37 yamt int category;
430 1.37 yamt {
431 1.37 yamt const char *env;
432 1.37 yamt
433 1.37 yamt _DIAGASSERT(category != LC_ALL);
434 1.37 yamt
435 1.37 yamt /* 1. check LC_ALL. */
436 1.37 yamt env = getenv(categories[0]);
437 1.37 yamt
438 1.37 yamt /* 2. check LC_* */
439 1.37 yamt if (!env || !*env)
440 1.37 yamt env = getenv(categories[category]);
441 1.37 yamt
442 1.37 yamt /* 3. check LANG */
443 1.37 yamt if (!env || !*env)
444 1.37 yamt env = getenv("LANG");
445 1.37 yamt
446 1.37 yamt /* 4. if none is set, fall to "C" */
447 1.37 yamt if (!env || !*env || strchr(env, '/'))
448 1.37 yamt env = "C";
449 1.37 yamt
450 1.37 yamt return env;
451 1.1 cgd }
452