]>
Commit | Line | Data |
---|---|---|
ab26a24a | 1 | /* Copyright (C) 1991, 92, 95-99, 2000, 2002 Free Software Foundation, Inc. |
c84142e8 | 2 | This file is part of the GNU C Library. |
28f540f4 | 3 | |
c84142e8 | 4 | The GNU C Library is free software; you can redistribute it and/or |
41bdb6e2 AJ |
5 | modify it under the terms of the GNU Lesser General Public |
6 | License as published by the Free Software Foundation; either | |
7 | version 2.1 of the License, or (at your option) any later version. | |
28f540f4 | 8 | |
c84142e8 UD |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
41bdb6e2 | 12 | Lesser General Public License for more details. |
28f540f4 | 13 | |
41bdb6e2 AJ |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; if not, write to the Free | |
16 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | |
17 | 02111-1307 USA. */ | |
28f540f4 | 18 | |
7a12c6bb RM |
19 | #include <alloca.h> |
20 | #include <argz.h> | |
28f540f4 | 21 | #include <errno.h> |
5107cf1d | 22 | #include <bits/libc-lock.h> |
933e73fa | 23 | #include <locale.h> |
7a12c6bb RM |
24 | #include <stdlib.h> |
25 | #include <string.h> | |
26 | #include <unistd.h> | |
27 | ||
933e73fa RM |
28 | #include "localeinfo.h" |
29 | ||
1a0d874e RM |
30 | #ifdef NL_CURRENT_INDIRECT |
31 | ||
32 | /* For each category declare a special external symbol | |
33 | _nl_current_CATEGORY_used with a weak reference. | |
34 | This symbol will is defined in lc-CATEGORY.c and will be linked in | |
35 | if anything uses _nl_current_CATEGORY (also defined in that module). | |
36 | Also use a weak reference for the _nl_current_CATEGORY thread variable. */ | |
37 | ||
38 | # define DEFINE_CATEGORY(category, category_name, items, a) \ | |
39 | extern char _nl_current_##category##_used; \ | |
40 | weak_extern (_nl_current_##category##_used) \ | |
41 | weak_extern (_nl_current_##category) | |
42 | # include "categories.def" | |
43 | # undef DEFINE_CATEGORY | |
44 | ||
45 | /* Now define a table of flags based on those special weak symbols' values. | |
46 | _nl_current_used[CATEGORY] will be zero if _nl_current_CATEGORY is not | |
47 | linked in. */ | |
48 | static char *const _nl_current_used[] = | |
036cc82f | 49 | { |
1a0d874e RM |
50 | # define DEFINE_CATEGORY(category, category_name, items, a) \ |
51 | [category] = &_nl_current_##category##_used, | |
52 | # include "categories.def" | |
53 | # undef DEFINE_CATEGORY | |
036cc82f | 54 | }; |
933e73fa | 55 | |
1a0d874e | 56 | # define CATEGORY_USED(category) (_nl_current_used[category] != 0) |
30c14c31 RM |
57 | |
58 | #else | |
59 | ||
60 | /* The shared library always loads all the categories, | |
61 | and the current global settings are kept in _nl_global_locale. */ | |
62 | ||
30c14c31 RM |
63 | # define CATEGORY_USED(category) (1) |
64 | ||
65 | #endif | |
66 | ||
933e73fa RM |
67 | |
68 | /* Define an array of category names (also the environment variable names), | |
4b10dd6c UD |
69 | indexed by integral category. */ |
70 | const char *const _nl_category_names[] = | |
933e73fa | 71 | { |
4b10dd6c | 72 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
933e73fa RM |
73 | [category] = category_name, |
74 | #include "categories.def" | |
75 | #undef DEFINE_CATEGORY | |
7a12c6bb | 76 | [LC_ALL] = "LC_ALL" |
933e73fa RM |
77 | }; |
78 | /* An array of their lengths, for convenience. */ | |
79 | const size_t _nl_category_name_sizes[] = | |
80 | { | |
4b10dd6c | 81 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
933e73fa RM |
82 | [category] = sizeof (category_name) - 1, |
83 | #include "categories.def" | |
84 | #undef DEFINE_CATEGORY | |
7a12c6bb | 85 | [LC_ALL] = sizeof ("LC_ALL") - 1 |
933e73fa | 86 | }; |
28f540f4 RM |
87 | |
88 | ||
933e73fa RM |
89 | /* Declare the postload functions used below. */ |
90 | #undef NO_POSTLOAD | |
91 | #define NO_POSTLOAD _nl_postload_ctype /* Harmless thing known to exist. */ | |
4b10dd6c | 92 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ |
27abf7a3 | 93 | extern void postload (void); weak_extern (postload) |
933e73fa RM |
94 | #include "categories.def" |
95 | #undef DEFINE_CATEGORY | |
96 | #undef NO_POSTLOAD | |
97 | ||
98 | /* Define an array indexed by category of postload functions to call after | |
99 | loading and installing that category's data. */ | |
a5113b14 | 100 | static void (*const _nl_category_postload[]) (void) = |
933e73fa | 101 | { |
4b10dd6c | 102 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ |
933e73fa RM |
103 | [category] = postload, |
104 | #include "categories.def" | |
105 | #undef DEFINE_CATEGORY | |
106 | }; | |
107 | ||
108 | ||
a5113b14 | 109 | /* Lock for protecting global data. */ |
ab26a24a | 110 | __libc_lock_define_initialized (, __libc_setlocale_lock attribute_hidden) |
a5113b14 | 111 | |
111bb972 UD |
112 | /* Defined in loadmsgcat.c. */ |
113 | extern int _nl_msg_cat_cntr; | |
114 | ||
933e73fa | 115 | |
7a12c6bb RM |
116 | /* Use this when we come along an error. */ |
117 | #define ERROR_RETURN \ | |
118 | do { \ | |
c4029823 | 119 | __set_errno (EINVAL); \ |
7a12c6bb RM |
120 | return NULL; \ |
121 | } while (0) | |
933e73fa | 122 | |
7a12c6bb | 123 | |
7a12c6bb RM |
124 | /* Construct a new composite name. */ |
125 | static inline char * | |
4b10dd6c | 126 | new_composite_name (int category, const char *newnames[__LC_LAST]) |
7a12c6bb | 127 | { |
e918a7fe | 128 | size_t last_len = 0; |
7a12c6bb RM |
129 | size_t cumlen = 0; |
130 | int i; | |
131 | char *new, *p; | |
132 | int same = 1; | |
133 | ||
4b10dd6c UD |
134 | for (i = 0; i < __LC_LAST; ++i) |
135 | if (i != LC_ALL) | |
136 | { | |
137 | const char *name = (category == LC_ALL ? newnames[i] : | |
138 | category == i ? newnames[0] : | |
139 | _nl_current_names[i]); | |
140 | last_len = strlen (name); | |
141 | cumlen += _nl_category_name_sizes[i] + 1 + last_len + 1; | |
142 | if (i > 0 && same && strcmp (name, newnames[0]) != 0) | |
143 | same = 0; | |
144 | } | |
7a12c6bb RM |
145 | |
146 | if (same) | |
933e73fa | 147 | { |
7a12c6bb | 148 | /* All the categories use the same name. */ |
72c74375 UD |
149 | if (strcmp (newnames[0], _nl_C_name) == 0 |
150 | || strcmp (newnames[0], _nl_POSIX_name) == 0) | |
7a12c6bb RM |
151 | return (char *) _nl_C_name; |
152 | ||
153 | new = malloc (last_len + 1); | |
7a12c6bb | 154 | |
036cc82f | 155 | return new == NULL ? NULL : memcpy (new, newnames[0], last_len + 1); |
933e73fa | 156 | } |
7a12c6bb RM |
157 | |
158 | new = malloc (cumlen); | |
159 | if (new == NULL) | |
160 | return NULL; | |
161 | p = new; | |
4b10dd6c UD |
162 | for (i = 0; i < __LC_LAST; ++i) |
163 | if (i != LC_ALL) | |
164 | { | |
165 | /* Add "CATEGORY=NAME;" to the string. */ | |
166 | const char *name = (category == LC_ALL ? newnames[i] : | |
167 | category == i ? newnames[0] : | |
168 | _nl_current_names[i]); | |
169 | p = __stpcpy (p, _nl_category_names[i]); | |
170 | *p++ = '='; | |
171 | p = __stpcpy (p, name); | |
172 | *p++ = ';'; | |
173 | } | |
7a12c6bb RM |
174 | p[-1] = '\0'; /* Clobber the last ';'. */ |
175 | return new; | |
176 | } | |
933e73fa | 177 | |
933e73fa | 178 | |
7a12c6bb RM |
179 | /* Put NAME in _nl_current_names. */ |
180 | static inline void | |
181 | setname (int category, const char *name) | |
182 | { | |
762a2918 UD |
183 | if (_nl_current_names[category] == name) |
184 | return; | |
185 | ||
411adb10 | 186 | if (_nl_current_names[category] != _nl_C_name) |
a2b08ee5 | 187 | free ((char *) _nl_current_names[category]); |
7a12c6bb RM |
188 | |
189 | _nl_current_names[category] = name; | |
190 | } | |
191 | ||
7a12c6bb RM |
192 | /* Put DATA in *_nl_current[CATEGORY]. */ |
193 | static inline void | |
c84142e8 | 194 | setdata (int category, struct locale_data *data) |
7a12c6bb | 195 | { |
30c14c31 | 196 | if (CATEGORY_USED (category)) |
933e73fa | 197 | { |
30c14c31 | 198 | _nl_global_locale.__locales[category] = data; |
7a12c6bb RM |
199 | if (_nl_category_postload[category]) |
200 | (*_nl_category_postload[category]) (); | |
933e73fa | 201 | } |
7a12c6bb | 202 | } |
933e73fa | 203 | |
7a12c6bb RM |
204 | char * |
205 | setlocale (int category, const char *locale) | |
206 | { | |
7a12c6bb RM |
207 | char *locale_path; |
208 | size_t locale_path_len; | |
d68171ed | 209 | const char *locpath_var; |
7a12c6bb | 210 | char *composite; |
933e73fa | 211 | |
7a12c6bb | 212 | /* Sanity check for CATEGORY argument. */ |
323fb88d UD |
213 | if (__builtin_expect (category, 0) < 0 |
214 | || __builtin_expect (category, 0) >= __LC_LAST) | |
7a12c6bb RM |
215 | ERROR_RETURN; |
216 | ||
217 | /* Does user want name of current locale? */ | |
218 | if (locale == NULL) | |
219 | return (char *) _nl_current_names[category]; | |
220 | ||
221 | if (strcmp (locale, _nl_current_names[category]) == 0) | |
933e73fa | 222 | /* Changing to the same thing. */ |
7a12c6bb RM |
223 | return (char *) _nl_current_names[category]; |
224 | ||
225 | /* We perhaps really have to load some data. So we determine the | |
a5113b14 UD |
226 | path in which to look for the data now. The environment variable |
227 | `LOCPATH' must only be used when the binary has no SUID or SGID | |
cb09a2cd RM |
228 | bit set. If using the default path, we tell _nl_find_locale |
229 | by passing null and it can check the canonical locale archive. */ | |
7a12c6bb RM |
230 | locale_path = NULL; |
231 | locale_path_len = 0; | |
232 | ||
74955460 | 233 | locpath_var = getenv ("LOCPATH"); |
d68171ed | 234 | if (locpath_var != NULL && locpath_var[0] != '\0') |
cb09a2cd RM |
235 | { |
236 | if (__argz_create_sep (locpath_var, ':', | |
237 | &locale_path, &locale_path_len) != 0) | |
238 | return NULL; | |
933e73fa | 239 | |
cb09a2cd RM |
240 | if (__argz_add_sep (&locale_path, &locale_path_len, |
241 | _nl_default_locale_path, ':') != 0) | |
242 | return NULL; | |
243 | } | |
db2286f6 | 244 | |
933e73fa RM |
245 | if (category == LC_ALL) |
246 | { | |
7a12c6bb RM |
247 | /* The user wants to set all categories. The desired locales |
248 | for the individual categories can be selected by using a | |
249 | composite locale name. This is a semi-colon separated list | |
250 | of entries of the form `CATEGORY=VALUE'. */ | |
4b10dd6c UD |
251 | const char *newnames[__LC_LAST]; |
252 | struct locale_data *newdata[__LC_LAST]; | |
933e73fa RM |
253 | |
254 | /* Set all name pointers to the argument name. */ | |
4b10dd6c UD |
255 | for (category = 0; category < __LC_LAST; ++category) |
256 | if (category != LC_ALL) | |
257 | newnames[category] = (char *) locale; | |
db2286f6 | 258 | |
323fb88d | 259 | if (__builtin_expect (strchr (locale, ';') != NULL, 0)) |
933e73fa | 260 | { |
7a12c6bb RM |
261 | /* This is a composite name. Make a copy and split it up. */ |
262 | char *np = strdupa (locale); | |
263 | char *cp; | |
264 | int cnt; | |
933e73fa | 265 | |
7a12c6bb | 266 | while ((cp = strchr (np, '=')) != NULL) |
933e73fa | 267 | { |
4b10dd6c UD |
268 | for (cnt = 0; cnt < __LC_LAST; ++cnt) |
269 | if (cnt != LC_ALL | |
270 | && (size_t) (cp - np) == _nl_category_name_sizes[cnt] | |
7a12c6bb | 271 | && memcmp (np, _nl_category_names[cnt], cp - np) == 0) |
933e73fa | 272 | break; |
7a12c6bb | 273 | |
4b10dd6c | 274 | if (cnt == __LC_LAST) |
7a12c6bb RM |
275 | /* Bogus category name. */ |
276 | ERROR_RETURN; | |
277 | ||
278 | /* Found the category this clause sets. */ | |
279 | newnames[cnt] = ++cp; | |
280 | cp = strchr (cp, ';'); | |
281 | if (cp != NULL) | |
933e73fa | 282 | { |
7a12c6bb RM |
283 | /* Examine the next clause. */ |
284 | *cp = '\0'; | |
285 | np = cp + 1; | |
933e73fa | 286 | } |
7a12c6bb RM |
287 | else |
288 | /* This was the last clause. We are done. */ | |
289 | break; | |
933e73fa RM |
290 | } |
291 | ||
4b10dd6c UD |
292 | for (cnt = 0; cnt < __LC_LAST; ++cnt) |
293 | if (cnt != LC_ALL && newnames[cnt] == locale) | |
933e73fa | 294 | /* The composite name did not specify all categories. */ |
7a12c6bb | 295 | ERROR_RETURN; |
933e73fa | 296 | } |
19bc17a9 | 297 | |
a5113b14 | 298 | /* Protect global data. */ |
c4029823 | 299 | __libc_lock_lock (__libc_setlocale_lock); |
a5113b14 | 300 | |
933e73fa RM |
301 | /* Load the new data for each category. */ |
302 | while (category-- > 0) | |
ef5d6645 UD |
303 | if (category != LC_ALL) |
304 | { | |
ef5d6645 UD |
305 | newdata[category] = _nl_find_locale (locale_path, locale_path_len, |
306 | category, | |
307 | &newnames[category]); | |
1fb05e3d | 308 | |
ef5d6645 | 309 | if (newdata[category] == NULL) |
9a411bf5 RM |
310 | { |
311 | #ifdef NL_CURRENT_INDIRECT | |
312 | if (newnames[category] == _nl_C_name) | |
313 | /* Null because it's the weak value of _nl_C_LC_FOO. */ | |
314 | continue; | |
315 | #endif | |
316 | break; | |
317 | } | |
1fb05e3d | 318 | |
ef5d6645 UD |
319 | /* We must not simply free a global locale since we have no |
320 | control over the usage. So we mark it as un-deletable. */ | |
321 | if (newdata[category]->usage_count != UNDELETABLE) | |
322 | newdata[category]->usage_count = UNDELETABLE; | |
411adb10 UD |
323 | |
324 | /* Make a copy of locale name. */ | |
325 | if (newnames[category] != _nl_C_name) | |
326 | { | |
327 | newnames[category] = strdup (newnames[category]); | |
328 | if (newnames[category] == NULL) | |
329 | break; | |
330 | } | |
ef5d6645 | 331 | } |
933e73fa | 332 | |
7a12c6bb | 333 | /* Create new composite name. */ |
d4c5cf80 UD |
334 | composite = (category >= 0 |
335 | ? NULL : new_composite_name (LC_ALL, newnames)); | |
336 | if (composite != NULL) | |
933e73fa | 337 | { |
a5113b14 | 338 | /* Now we have loaded all the new data. Put it in place. */ |
4b10dd6c UD |
339 | for (category = 0; category < __LC_LAST; ++category) |
340 | if (category != LC_ALL) | |
341 | { | |
342 | setdata (category, newdata[category]); | |
343 | setname (category, newnames[category]); | |
344 | } | |
a5113b14 | 345 | setname (LC_ALL, composite); |
111bb972 UD |
346 | |
347 | /* We successfully loaded a new locale. Let the message catalog | |
348 | functions know about this. */ | |
349 | ++_nl_msg_cat_cntr; | |
933e73fa | 350 | } |
411adb10 UD |
351 | else |
352 | for (++category; category < __LC_LAST; ++category) | |
353 | if (category != LC_ALL && newnames[category] != _nl_C_name) | |
354 | free ((char *) newnames[category]); | |
a5113b14 UD |
355 | |
356 | /* Critical section left. */ | |
c4029823 | 357 | __libc_lock_unlock (__libc_setlocale_lock); |
933e73fa | 358 | |
714a562f UD |
359 | /* Free the resources (the locale path variable. */ |
360 | free (locale_path); | |
361 | ||
933e73fa RM |
362 | return composite; |
363 | } | |
364 | else | |
365 | { | |
c84142e8 | 366 | struct locale_data *newdata = NULL; |
650425ce | 367 | const char *newname[1] = { locale }; |
7a12c6bb | 368 | |
a5113b14 | 369 | /* Protect global data. */ |
c4029823 | 370 | __libc_lock_lock (__libc_setlocale_lock); |
a5113b14 | 371 | |
30c14c31 | 372 | if (CATEGORY_USED (category)) |
933e73fa | 373 | { |
7a12c6bb | 374 | /* Only actually load the data if anything will use it. */ |
7a12c6bb | 375 | newdata = _nl_find_locale (locale_path, locale_path_len, category, |
650425ce | 376 | &newname[0]); |
7a12c6bb | 377 | if (newdata == NULL) |
a5113b14 | 378 | goto abort_single; |
c84142e8 UD |
379 | |
380 | /* We must not simply free a global locale since we have no | |
a2b08ee5 UD |
381 | control over the usage. So we mark it as un-deletable. |
382 | ||
3081378b | 383 | Note: do not remove the `if', it's necessary to copy with |
a2b08ee5 | 384 | the builtin locale data. */ |
9756dfe1 UD |
385 | if (newdata->usage_count != UNDELETABLE) |
386 | newdata->usage_count = UNDELETABLE; | |
933e73fa RM |
387 | } |
388 | ||
411adb10 UD |
389 | /* Make a copy of locale name. */ |
390 | if (newname[0] != _nl_C_name) | |
391 | { | |
392 | newname[0] = strdup (newname[0]); | |
393 | if (newname[0] == NULL) | |
394 | goto abort_single; | |
395 | } | |
396 | ||
7a12c6bb | 397 | /* Create new composite name. */ |
650425ce | 398 | composite = new_composite_name (category, newname); |
7a12c6bb | 399 | if (composite == NULL) |
933e73fa | 400 | { |
411adb10 UD |
401 | if (newname[0] != _nl_C_name) |
402 | free ((char *) newname[0]); | |
403 | ||
845dcb57 | 404 | /* Say that we don't have any data loaded. */ |
a5113b14 | 405 | abort_single: |
650425ce | 406 | newname[0] = NULL; |
933e73fa | 407 | } |
a5113b14 UD |
408 | else |
409 | { | |
30c14c31 | 410 | if (CATEGORY_USED (category)) |
a5113b14 | 411 | setdata (category, newdata); |
28f540f4 | 412 | |
650425ce | 413 | setname (category, newname[0]); |
a5113b14 | 414 | setname (LC_ALL, composite); |
111bb972 UD |
415 | |
416 | /* We successfully loaded a new locale. Let the message catalog | |
417 | functions know about this. */ | |
418 | ++_nl_msg_cat_cntr; | |
a5113b14 | 419 | } |
28f540f4 | 420 | |
a5113b14 | 421 | /* Critical section left. */ |
c4029823 | 422 | __libc_lock_unlock (__libc_setlocale_lock); |
28f540f4 | 423 | |
714a562f UD |
424 | /* Free the resources (the locale path variable. */ |
425 | free (locale_path); | |
426 | ||
650425ce | 427 | return (char *) newname[0]; |
933e73fa | 428 | } |
28f540f4 | 429 | } |
30c14c31 | 430 | libc_hidden_def (setlocale) |
f84ad0b1 | 431 | |
1a0d874e RM |
432 | static void |
433 | free_category (int category, | |
434 | struct locale_data *here, struct locale_data *c_data) | |
435 | { | |
436 | struct loaded_l10nfile *runp = _nl_locale_file_list[category]; | |
437 | ||
438 | /* If this category is already "C" don't do anything. */ | |
439 | if (here != c_data) | |
440 | { | |
441 | /* We have to be prepared that sometime later we still | |
442 | might need the locale information. */ | |
443 | setdata (category, c_data); | |
444 | setname (category, _nl_C_name); | |
445 | } | |
446 | ||
447 | while (runp != NULL) | |
448 | { | |
449 | struct loaded_l10nfile *curr = runp; | |
450 | struct locale_data *data = (struct locale_data *) runp->data; | |
451 | ||
452 | if (data != NULL && data != c_data) | |
453 | _nl_unload_locale (data); | |
454 | runp = runp->next; | |
455 | free ((char *) curr->filename); | |
456 | free (curr); | |
457 | } | |
458 | } | |
459 | ||
f84ad0b1 UD |
460 | static void __attribute__ ((unused)) |
461 | free_mem (void) | |
462 | { | |
1a0d874e RM |
463 | #ifdef NL_CURRENT_INDIRECT |
464 | /* We don't use the loop because we want to have individual weak | |
465 | symbol references here. */ | |
466 | # define DEFINE_CATEGORY(category, category_name, items, a) \ | |
467 | if (CATEGORY_USED (category)) \ | |
468 | { \ | |
469 | extern struct locale_data _nl_C_##category; \ | |
470 | weak_extern (_nl_C_##category) \ | |
471 | free_category (category, *_nl_current_##category, &_nl_C_##category); \ | |
472 | } | |
473 | # include "categories.def" | |
474 | # undef DEFINE_CATEGORY | |
475 | #else | |
f84ad0b1 UD |
476 | int category; |
477 | ||
4b10dd6c UD |
478 | for (category = 0; category < __LC_LAST; ++category) |
479 | if (category != LC_ALL) | |
1a0d874e RM |
480 | free_category (category, _NL_CURRENT_DATA (category), |
481 | _nl_C_locobj.__locales[category]); | |
482 | #endif | |
f84ad0b1 UD |
483 | |
484 | setname (LC_ALL, _nl_C_name); | |
a89a3dab RM |
485 | |
486 | /* This frees the data structures associated with the locale archive. | |
487 | The locales from the archive are not in the file list, so we have | |
488 | not called _nl_unload_locale on them above. */ | |
489 | _nl_archive_subfreeres (); | |
f84ad0b1 UD |
490 | } |
491 | text_set_element (__libc_subfreeres, free_mem); |