1 /* $NetBSD: src/lib/libc/locale/setlocale.c,v 1.47 2004/07/21 20:27:46 tshiozak Exp $ */
2 /* $DragonFly: src/lib/libc/locale/setlocale.c,v 1.3 2005/04/21 18:36:34 joerg Exp $ */
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
8 * This code is derived from software contributed to Berkeley by
9 * Paul Borman at Krystal Technologies.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #define _CTYPE_PRIVATE
38 #include <sys/types.h>
39 #include <sys/localedef.h>
52 #include "rune_local.h"
54 #include "../citrus/citrus_namespace.h"
55 #include "../citrus/citrus_region.h"
56 #include "../citrus/citrus_lookup.h"
57 #include "../citrus/citrus_bcs.h"
59 #define _LOCALE_ALIAS_NAME "locale.alias"
60 #define _LOCALE_SYM_FORCE "/force"
62 static char *currentlocale(void);
63 static void revert_to_default(int);
64 static int force_locale_enable(int);
65 static int load_locale_sub(int, const char *, int);
66 static char *loadlocale(int);
67 static const char *__get_locale_env(int);
69 static void revert_collate(void);
70 static int load_ctype(const char *);
71 static void revert_ctype(void);
72 static int load_messages(const char *);
76 int (*load_function
)(const char *);
77 void (*revert_function
)(void);
79 { "LC_ALL", NULL
, NULL
},
80 { "LC_COLLATE", __collate_load_tables
, revert_collate
},
81 { "LC_CTYPE", load_ctype
, revert_ctype
},
82 { "LC_MONETARY", NULL
, NULL
},
83 { "LC_NUMERIC", NULL
, NULL
},
84 { "LC_TIME", NULL
, NULL
},
85 { "LC_MESSAGES", load_messages
, NULL
}
89 * Current locales for each category
91 static char current_categories
[_LC_LAST
][32] = {
102 * The locales we are going to try and load
104 static char new_categories
[_LC_LAST
][32];
106 static char current_locale_string
[_LC_LAST
* 33];
107 const char *_PathLocale
;
110 load_ctype(const char *locale
)
112 if (_xpg4_setrunelocale(locale
))
114 if (__runetable_to_netbsd_ctype(locale
)) {
115 /* very unfortunate, but need to go to "C" locale */
126 _xpg4_setrunelocale("C");
127 __runetable_to_netbsd_ctype("C");
133 __collate_load_tables("C");
137 load_messages(const char *locale
)
143 * XXX we don't have LC_MESSAGES support yet,
144 * but catopen may use the value of LC_MESSAGES category.
145 * so return successfully if locale directory is present.
147 snprintf(name
, sizeof(name
), "%s/%s", _PathLocale
, locale
);
149 if (stat(name
, &st
) < 0)
151 if (!S_ISDIR(st
.st_mode
))
157 setlocale(int category
, const char *locale
)
159 int i
, loadlocale_success
;
163 __mb_len_max_runtime
= 32;
166 (!_PathLocale
&& !(_PathLocale
= getenv("PATH_LOCALE"))))
167 _PathLocale
= _PATH_LOCALE
;
169 if (category
< 0 || category
>= (int)__arysize(categories
))
174 current_categories
[category
] : currentlocale());
177 * Default to the current locale for everything.
179 for (i
= 1; i
< _LC_LAST
; ++i
)
180 strlcpy(new_categories
[i
], current_categories
[i
],
181 sizeof(new_categories
[i
]));
184 * Now go fill up new_categories from the locale argument
186 if (*locale
== '\0') {
187 if (category
== LC_ALL
) {
188 for (i
= 1; i
< _LC_LAST
; ++i
) {
189 env
= __get_locale_env(i
);
190 strlcpy(new_categories
[i
], env
,
191 sizeof(new_categories
[i
]));
195 env
= __get_locale_env(category
);
196 strlcpy(new_categories
[category
], env
,
197 sizeof(new_categories
[category
]));
199 } else if (category
) {
200 strlcpy(new_categories
[category
], locale
,
201 sizeof(new_categories
[category
]));
203 if ((r
= strchr(locale
, '/')) == 0) {
204 for (i
= 1; i
< _LC_LAST
; ++i
) {
205 strlcpy(new_categories
[i
], locale
,
206 sizeof(new_categories
[i
]));
210 _DIAGASSERT(*r
== '/' || *r
== 0);
211 _DIAGASSERT(*locale
!= 0);
213 return(NULL
); /* invalid format. */
215 if (len
+ 1 > sizeof(new_categories
[i
]))
216 return(NULL
); /* too long */
217 memcpy(new_categories
[i
], locale
, len
);
218 new_categories
[i
][len
] = '\0';
221 _DIAGASSERT(*r
== '/');
222 if (*(locale
= ++r
) == 0)
223 /* slash followed by NUL */
225 /* skip until NUL or '/' */
226 while (*r
&& *r
!= '/')
229 return(NULL
); /* too many slashes. */
231 if (i
+ 1 != _LC_LAST
)
232 return(NULL
); /* too few slashes. */
237 return(loadlocale(category
));
239 loadlocale_success
= 0;
240 for (i
= 1; i
< _LC_LAST
; ++i
) {
241 if (loadlocale(i
) != NULL
)
242 loadlocale_success
= 1;
246 * If all categories failed, return NULL; we don't need to back
247 * changes off, since none happened.
249 if (!loadlocale_success
)
252 return(currentlocale());
260 strlcpy(current_locale_string
, current_categories
[1],
261 sizeof(current_locale_string
));
263 for (i
= 2; i
< _LC_LAST
; ++i
)
264 if (strcmp(current_categories
[1], current_categories
[i
])) {
265 snprintf(current_locale_string
,
266 sizeof(current_locale_string
), "%s/%s/%s/%s/%s/%s",
267 current_categories
[1], current_categories
[2],
268 current_categories
[3], current_categories
[4],
269 current_categories
[5], current_categories
[6]);
272 return(current_locale_string
);
276 revert_to_default(int category
)
278 _DIAGASSERT(category
>= 0 && category
< _LC_LAST
);
280 if (categories
[category
].revert_function
!= NULL
)
281 categories
[category
].revert_function();
285 force_locale_enable(int category
)
287 revert_to_default(category
);
293 load_locale_sub(int category
, const char *locname
, int isspecial
)
297 /* check for the default locales */
298 if (!strcmp(new_categories
[category
], "C") ||
299 !strcmp(new_categories
[category
], "POSIX")) {
300 revert_to_default(category
);
304 /* check whether special symbol */
305 if (isspecial
&& _bcs_strcasecmp(locname
, _LOCALE_SYM_FORCE
) == 0)
306 return(force_locale_enable(category
));
309 if (strchr(locname
, '/') != NULL
)
312 snprintf(name
, sizeof(name
), "%s/%s/%s", _PathLocale
, locname
,
313 categories
[category
].name
);
315 if (category
> 0 && category
< (int)__arysize(categories
) &&
316 categories
[category
].load_function
!= NULL
)
317 return(categories
[category
].load_function(locname
));
323 loadlocale(int category
)
325 char aliaspath
[PATH_MAX
], loccat
[PATH_MAX
], buf
[PATH_MAX
];
328 _DIAGASSERT(0 < category
&& category
< __arysize(categories
));
330 if (strcmp(new_categories
[category
], current_categories
[category
]) == 0)
331 return(current_categories
[category
]);
333 /* (1) non-aliased file */
334 if (!load_locale_sub(category
, new_categories
[category
], 0))
337 /* (2) lookup locname/catname type alias */
338 snprintf(aliaspath
, sizeof(aliaspath
), "%s/" _LOCALE_ALIAS_NAME
,
340 snprintf(loccat
, sizeof(loccat
), "%s/%s", new_categories
[category
],
341 categories
[category
].name
);
342 alias
= _lookup_alias(aliaspath
, loccat
, buf
, sizeof(buf
),
343 _LOOKUP_CASE_SENSITIVE
);
344 if (!load_locale_sub(category
, alias
, 1))
347 /* (3) lookup locname type alias */
348 alias
= _lookup_alias(aliaspath
, new_categories
[category
],
349 buf
, sizeof(buf
), _LOOKUP_CASE_SENSITIVE
);
350 if (!load_locale_sub(category
, alias
, 1))
356 strlcpy(current_categories
[category
], new_categories
[category
],
357 sizeof(current_categories
[category
]));
358 return(current_categories
[category
]);
362 __get_locale_env(int category
)
366 _DIAGASSERT(category
!= LC_ALL
);
368 /* 1. check LC_ALL. */
369 env
= getenv(categories
[0].name
);
372 if (env
== NULL
|| *env
== '\0')
373 env
= getenv(categories
[category
].name
);
376 if (env
== NULL
|| *env
== '\0')
377 env
= getenv("LANG");
379 /* 4. if none is set, fall to "C" */
380 if (env
== NULL
|| *env
== '\0' || strchr(env
, '/'))