]>
Commit | Line | Data |
---|---|---|
72c74375 | 1 | /* Copyright (C) 1991, 92, 95-99, 2000 Free Software Foundation, Inc. |
c84142e8 | 2 | This file is part of the GNU C Library. |
28f540f4 | 3 | |
c84142e8 UD |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public License as | |
6 | published by the Free Software Foundation; either version 2 of the | |
7 | 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 | |
12 | Library General Public License for more details. | |
28f540f4 | 13 | |
c84142e8 UD |
14 | You should have received a copy of the GNU Library General Public |
15 | License along with the GNU C Library; see the file COPYING.LIB. If not, | |
16 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
17 | Boston, MA 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 | ||
30 | /* For each category declare two external variables (with weak references): | |
31 | extern const struct locale_data *_nl_current_CATEGORY; | |
32 | This points to the current locale's in-core data for CATEGORY. | |
33 | extern const struct locale_data _nl_C_CATEGORY; | |
34 | This contains the built-in "C"/"POSIX" locale's data for CATEGORY. | |
35 | Both are weak references; if &_nl_current_CATEGORY is zero, | |
36 | then nothing is using the locale data. */ | |
4b10dd6c | 37 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
c84142e8 | 38 | extern struct locale_data *_nl_current_##category; \ |
23f0c99c | 39 | extern struct locale_data _nl_C_##category; |
933e73fa RM |
40 | #include "categories.def" |
41 | #undef DEFINE_CATEGORY | |
42 | ||
4b10dd6c UD |
43 | /* Array indexed by category of pointers to _nl_current_CATEGORY slots. |
44 | Elements are zero for categories whose data is never used. */ | |
c4563d2d | 45 | struct locale_data * *const _nl_current[] = |
7a12c6bb | 46 | { |
4b10dd6c | 47 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
7a12c6bb | 48 | [category] = &_nl_current_##category, |
933e73fa RM |
49 | #include "categories.def" |
50 | #undef DEFINE_CATEGORY | |
c131718c UD |
51 | /* We need this additional element to simplify the code. It must |
52 | simply be != NULL. */ | |
53 | [LC_ALL] = (struct locale_data **) ~0ul | |
7a12c6bb | 54 | }; |
933e73fa RM |
55 | |
56 | /* Array indexed by category of pointers to _nl_C_CATEGORY slots. | |
57 | Elements are zero for categories whose data is never used. */ | |
c84142e8 | 58 | struct locale_data *const _nl_C[] = |
036cc82f | 59 | { |
4b10dd6c | 60 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
036cc82f | 61 | [category] = &_nl_C_##category, |
933e73fa RM |
62 | #include "categories.def" |
63 | #undef DEFINE_CATEGORY | |
036cc82f | 64 | }; |
933e73fa RM |
65 | |
66 | ||
67 | /* Define an array of category names (also the environment variable names), | |
4b10dd6c UD |
68 | indexed by integral category. */ |
69 | const char *const _nl_category_names[] = | |
933e73fa | 70 | { |
4b10dd6c | 71 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
933e73fa RM |
72 | [category] = category_name, |
73 | #include "categories.def" | |
74 | #undef DEFINE_CATEGORY | |
7a12c6bb | 75 | [LC_ALL] = "LC_ALL" |
933e73fa RM |
76 | }; |
77 | /* An array of their lengths, for convenience. */ | |
78 | const size_t _nl_category_name_sizes[] = | |
79 | { | |
4b10dd6c | 80 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
933e73fa RM |
81 | [category] = sizeof (category_name) - 1, |
82 | #include "categories.def" | |
83 | #undef DEFINE_CATEGORY | |
7a12c6bb | 84 | [LC_ALL] = sizeof ("LC_ALL") - 1 |
933e73fa | 85 | }; |
28f540f4 RM |
86 | |
87 | ||
933e73fa RM |
88 | /* Declare the postload functions used below. */ |
89 | #undef NO_POSTLOAD | |
90 | #define NO_POSTLOAD _nl_postload_ctype /* Harmless thing known to exist. */ | |
4b10dd6c | 91 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ |
933e73fa RM |
92 | extern void postload (void); |
93 | #include "categories.def" | |
94 | #undef DEFINE_CATEGORY | |
95 | #undef NO_POSTLOAD | |
96 | ||
97 | /* Define an array indexed by category of postload functions to call after | |
98 | loading and installing that category's data. */ | |
a5113b14 | 99 | static void (*const _nl_category_postload[]) (void) = |
933e73fa | 100 | { |
4b10dd6c | 101 | #define DEFINE_CATEGORY(category, category_name, items, postload) \ |
933e73fa RM |
102 | [category] = postload, |
103 | #include "categories.def" | |
104 | #undef DEFINE_CATEGORY | |
105 | }; | |
106 | ||
107 | ||
933e73fa RM |
108 | /* Name of current locale for each individual category. |
109 | Each is malloc'd unless it is nl_C_name. */ | |
7a12c6bb | 110 | static const char *_nl_current_names[] = |
933e73fa | 111 | { |
4b10dd6c | 112 | #define DEFINE_CATEGORY(category, category_name, items, a) \ |
7a12c6bb | 113 | [category] = _nl_C_name, |
933e73fa RM |
114 | #include "categories.def" |
115 | #undef DEFINE_CATEGORY | |
7a12c6bb | 116 | [LC_ALL] = _nl_C_name /* For LC_ALL. */ |
933e73fa RM |
117 | }; |
118 | ||
933e73fa | 119 | |
a5113b14 | 120 | /* Lock for protecting global data. */ |
c4029823 | 121 | __libc_lock_define_initialized (, __libc_setlocale_lock) |
a5113b14 | 122 | |
111bb972 UD |
123 | /* Defined in loadmsgcat.c. */ |
124 | extern int _nl_msg_cat_cntr; | |
125 | ||
933e73fa | 126 | |
7a12c6bb RM |
127 | /* Use this when we come along an error. */ |
128 | #define ERROR_RETURN \ | |
129 | do { \ | |
c4029823 | 130 | __set_errno (EINVAL); \ |
7a12c6bb RM |
131 | return NULL; \ |
132 | } while (0) | |
933e73fa | 133 | |
7a12c6bb | 134 | |
7a12c6bb RM |
135 | /* Construct a new composite name. */ |
136 | static inline char * | |
4b10dd6c | 137 | new_composite_name (int category, const char *newnames[__LC_LAST]) |
7a12c6bb | 138 | { |
e918a7fe | 139 | size_t last_len = 0; |
7a12c6bb RM |
140 | size_t cumlen = 0; |
141 | int i; | |
142 | char *new, *p; | |
143 | int same = 1; | |
144 | ||
4b10dd6c UD |
145 | for (i = 0; i < __LC_LAST; ++i) |
146 | if (i != LC_ALL) | |
147 | { | |
148 | const char *name = (category == LC_ALL ? newnames[i] : | |
149 | category == i ? newnames[0] : | |
150 | _nl_current_names[i]); | |
151 | last_len = strlen (name); | |
152 | cumlen += _nl_category_name_sizes[i] + 1 + last_len + 1; | |
153 | if (i > 0 && same && strcmp (name, newnames[0]) != 0) | |
154 | same = 0; | |
155 | } | |
7a12c6bb RM |
156 | |
157 | if (same) | |
933e73fa | 158 | { |
7a12c6bb | 159 | /* All the categories use the same name. */ |
72c74375 UD |
160 | if (strcmp (newnames[0], _nl_C_name) == 0 |
161 | || strcmp (newnames[0], _nl_POSIX_name) == 0) | |
7a12c6bb RM |
162 | return (char *) _nl_C_name; |
163 | ||
164 | new = malloc (last_len + 1); | |
7a12c6bb | 165 | |
036cc82f | 166 | return new == NULL ? NULL : memcpy (new, newnames[0], last_len + 1); |
933e73fa | 167 | } |
7a12c6bb RM |
168 | |
169 | new = malloc (cumlen); | |
170 | if (new == NULL) | |
171 | return NULL; | |
172 | p = new; | |
4b10dd6c UD |
173 | for (i = 0; i < __LC_LAST; ++i) |
174 | if (i != LC_ALL) | |
175 | { | |
176 | /* Add "CATEGORY=NAME;" to the string. */ | |
177 | const char *name = (category == LC_ALL ? newnames[i] : | |
178 | category == i ? newnames[0] : | |
179 | _nl_current_names[i]); | |
180 | p = __stpcpy (p, _nl_category_names[i]); | |
181 | *p++ = '='; | |
182 | p = __stpcpy (p, name); | |
183 | *p++ = ';'; | |
184 | } | |
7a12c6bb RM |
185 | p[-1] = '\0'; /* Clobber the last ';'. */ |
186 | return new; | |
187 | } | |
933e73fa | 188 | |
933e73fa | 189 | |
7a12c6bb RM |
190 | /* Put NAME in _nl_current_names. */ |
191 | static inline void | |
192 | setname (int category, const char *name) | |
193 | { | |
762a2918 UD |
194 | if (_nl_current_names[category] == name) |
195 | return; | |
196 | ||
a2b08ee5 UD |
197 | if (category == LC_ALL && _nl_current_names[category] != _nl_C_name) |
198 | free ((char *) _nl_current_names[category]); | |
7a12c6bb RM |
199 | |
200 | _nl_current_names[category] = name; | |
201 | } | |
202 | ||
203 | ||
204 | /* Put DATA in *_nl_current[CATEGORY]. */ | |
205 | static inline void | |
c84142e8 | 206 | setdata (int category, struct locale_data *data) |
7a12c6bb RM |
207 | { |
208 | if (_nl_current[category] != NULL) | |
933e73fa | 209 | { |
7a12c6bb RM |
210 | *_nl_current[category] = data; |
211 | if (_nl_category_postload[category]) | |
212 | (*_nl_category_postload[category]) (); | |
933e73fa | 213 | } |
7a12c6bb | 214 | } |
933e73fa | 215 | |
933e73fa | 216 | |
7a12c6bb RM |
217 | char * |
218 | setlocale (int category, const char *locale) | |
219 | { | |
7a12c6bb RM |
220 | char *locale_path; |
221 | size_t locale_path_len; | |
d68171ed | 222 | const char *locpath_var; |
7a12c6bb | 223 | char *composite; |
933e73fa | 224 | |
7a12c6bb | 225 | /* Sanity check for CATEGORY argument. */ |
323fb88d UD |
226 | if (__builtin_expect (category, 0) < 0 |
227 | || __builtin_expect (category, 0) >= __LC_LAST) | |
7a12c6bb RM |
228 | ERROR_RETURN; |
229 | ||
230 | /* Does user want name of current locale? */ | |
231 | if (locale == NULL) | |
232 | return (char *) _nl_current_names[category]; | |
233 | ||
234 | if (strcmp (locale, _nl_current_names[category]) == 0) | |
933e73fa | 235 | /* Changing to the same thing. */ |
7a12c6bb RM |
236 | return (char *) _nl_current_names[category]; |
237 | ||
238 | /* We perhaps really have to load some data. So we determine the | |
a5113b14 UD |
239 | path in which to look for the data now. The environment variable |
240 | `LOCPATH' must only be used when the binary has no SUID or SGID | |
7a12c6bb RM |
241 | bit set. */ |
242 | locale_path = NULL; | |
243 | locale_path_len = 0; | |
244 | ||
74955460 | 245 | locpath_var = getenv ("LOCPATH"); |
d68171ed UD |
246 | if (locpath_var != NULL && locpath_var[0] != '\0') |
247 | if (__argz_create_sep (locpath_var, ':', | |
248 | &locale_path, &locale_path_len) != 0) | |
249 | return NULL; | |
933e73fa | 250 | |
d8337213 | 251 | if (__argz_add_sep (&locale_path, &locale_path_len, LOCALEDIR, ':') != 0) |
7a12c6bb | 252 | return NULL; |
db2286f6 | 253 | |
933e73fa RM |
254 | if (category == LC_ALL) |
255 | { | |
7a12c6bb RM |
256 | /* The user wants to set all categories. The desired locales |
257 | for the individual categories can be selected by using a | |
258 | composite locale name. This is a semi-colon separated list | |
259 | of entries of the form `CATEGORY=VALUE'. */ | |
4b10dd6c UD |
260 | const char *newnames[__LC_LAST]; |
261 | struct locale_data *newdata[__LC_LAST]; | |
933e73fa RM |
262 | |
263 | /* Set all name pointers to the argument name. */ | |
4b10dd6c UD |
264 | for (category = 0; category < __LC_LAST; ++category) |
265 | if (category != LC_ALL) | |
266 | newnames[category] = (char *) locale; | |
db2286f6 | 267 | |
323fb88d | 268 | if (__builtin_expect (strchr (locale, ';') != NULL, 0)) |
933e73fa | 269 | { |
7a12c6bb RM |
270 | /* This is a composite name. Make a copy and split it up. */ |
271 | char *np = strdupa (locale); | |
272 | char *cp; | |
273 | int cnt; | |
933e73fa | 274 | |
7a12c6bb | 275 | while ((cp = strchr (np, '=')) != NULL) |
933e73fa | 276 | { |
4b10dd6c UD |
277 | for (cnt = 0; cnt < __LC_LAST; ++cnt) |
278 | if (cnt != LC_ALL | |
279 | && (size_t) (cp - np) == _nl_category_name_sizes[cnt] | |
7a12c6bb | 280 | && memcmp (np, _nl_category_names[cnt], cp - np) == 0) |
933e73fa | 281 | break; |
7a12c6bb | 282 | |
4b10dd6c | 283 | if (cnt == __LC_LAST) |
7a12c6bb RM |
284 | /* Bogus category name. */ |
285 | ERROR_RETURN; | |
286 | ||
287 | /* Found the category this clause sets. */ | |
288 | newnames[cnt] = ++cp; | |
289 | cp = strchr (cp, ';'); | |
290 | if (cp != NULL) | |
933e73fa | 291 | { |
7a12c6bb RM |
292 | /* Examine the next clause. */ |
293 | *cp = '\0'; | |
294 | np = cp + 1; | |
933e73fa | 295 | } |
7a12c6bb RM |
296 | else |
297 | /* This was the last clause. We are done. */ | |
298 | break; | |
933e73fa RM |
299 | } |
300 | ||
4b10dd6c UD |
301 | for (cnt = 0; cnt < __LC_LAST; ++cnt) |
302 | if (cnt != LC_ALL && newnames[cnt] == locale) | |
933e73fa | 303 | /* The composite name did not specify all categories. */ |
7a12c6bb | 304 | ERROR_RETURN; |
933e73fa | 305 | } |
19bc17a9 | 306 | |
a5113b14 | 307 | /* Protect global data. */ |
c4029823 | 308 | __libc_lock_lock (__libc_setlocale_lock); |
a5113b14 | 309 | |
933e73fa RM |
310 | /* Load the new data for each category. */ |
311 | while (category-- > 0) | |
ef5d6645 UD |
312 | if (category != LC_ALL) |
313 | { | |
ef5d6645 UD |
314 | newdata[category] = _nl_find_locale (locale_path, locale_path_len, |
315 | category, | |
316 | &newnames[category]); | |
1fb05e3d | 317 | |
ef5d6645 UD |
318 | if (newdata[category] == NULL) |
319 | break; | |
1fb05e3d | 320 | |
ef5d6645 UD |
321 | /* We must not simply free a global locale since we have no |
322 | control over the usage. So we mark it as un-deletable. */ | |
323 | if (newdata[category]->usage_count != UNDELETABLE) | |
324 | newdata[category]->usage_count = UNDELETABLE; | |
325 | } | |
933e73fa | 326 | |
7a12c6bb | 327 | /* Create new composite name. */ |
d4c5cf80 UD |
328 | composite = (category >= 0 |
329 | ? NULL : new_composite_name (LC_ALL, newnames)); | |
330 | if (composite != NULL) | |
933e73fa | 331 | { |
a5113b14 | 332 | /* Now we have loaded all the new data. Put it in place. */ |
4b10dd6c UD |
333 | for (category = 0; category < __LC_LAST; ++category) |
334 | if (category != LC_ALL) | |
335 | { | |
336 | setdata (category, newdata[category]); | |
337 | setname (category, newnames[category]); | |
338 | } | |
a5113b14 | 339 | setname (LC_ALL, composite); |
111bb972 UD |
340 | |
341 | /* We successfully loaded a new locale. Let the message catalog | |
342 | functions know about this. */ | |
343 | ++_nl_msg_cat_cntr; | |
933e73fa | 344 | } |
a5113b14 UD |
345 | |
346 | /* Critical section left. */ | |
c4029823 | 347 | __libc_lock_unlock (__libc_setlocale_lock); |
933e73fa | 348 | |
714a562f UD |
349 | /* Free the resources (the locale path variable. */ |
350 | free (locale_path); | |
351 | ||
933e73fa RM |
352 | return composite; |
353 | } | |
354 | else | |
355 | { | |
c84142e8 | 356 | struct locale_data *newdata = NULL; |
650425ce | 357 | const char *newname[1] = { locale }; |
7a12c6bb | 358 | |
a5113b14 | 359 | /* Protect global data. */ |
c4029823 | 360 | __libc_lock_lock (__libc_setlocale_lock); |
a5113b14 | 361 | |
7a12c6bb | 362 | if (_nl_current[category] != NULL) |
933e73fa | 363 | { |
7a12c6bb | 364 | /* Only actually load the data if anything will use it. */ |
7a12c6bb | 365 | newdata = _nl_find_locale (locale_path, locale_path_len, category, |
650425ce | 366 | &newname[0]); |
7a12c6bb | 367 | if (newdata == NULL) |
a5113b14 | 368 | goto abort_single; |
c84142e8 UD |
369 | |
370 | /* We must not simply free a global locale since we have no | |
a2b08ee5 UD |
371 | control over the usage. So we mark it as un-deletable. |
372 | ||
3081378b | 373 | Note: do not remove the `if', it's necessary to copy with |
a2b08ee5 | 374 | the builtin locale data. */ |
9756dfe1 UD |
375 | if (newdata->usage_count != UNDELETABLE) |
376 | newdata->usage_count = UNDELETABLE; | |
933e73fa RM |
377 | } |
378 | ||
7a12c6bb | 379 | /* Create new composite name. */ |
650425ce | 380 | composite = new_composite_name (category, newname); |
7a12c6bb | 381 | if (composite == NULL) |
933e73fa | 382 | { |
845dcb57 | 383 | /* Say that we don't have any data loaded. */ |
a5113b14 | 384 | abort_single: |
650425ce | 385 | newname[0] = NULL; |
933e73fa | 386 | } |
a5113b14 UD |
387 | else |
388 | { | |
389 | if (_nl_current[category] != NULL) | |
390 | setdata (category, newdata); | |
28f540f4 | 391 | |
650425ce | 392 | setname (category, newname[0]); |
a5113b14 | 393 | setname (LC_ALL, composite); |
111bb972 UD |
394 | |
395 | /* We successfully loaded a new locale. Let the message catalog | |
396 | functions know about this. */ | |
397 | ++_nl_msg_cat_cntr; | |
a5113b14 | 398 | } |
28f540f4 | 399 | |
a5113b14 | 400 | /* Critical section left. */ |
c4029823 | 401 | __libc_lock_unlock (__libc_setlocale_lock); |
28f540f4 | 402 | |
714a562f UD |
403 | /* Free the resources (the locale path variable. */ |
404 | free (locale_path); | |
405 | ||
650425ce | 406 | return (char *) newname[0]; |
933e73fa | 407 | } |
28f540f4 | 408 | } |
f84ad0b1 UD |
409 | |
410 | ||
411 | static void __attribute__ ((unused)) | |
412 | free_mem (void) | |
413 | { | |
414 | int category; | |
415 | ||
4b10dd6c UD |
416 | for (category = 0; category < __LC_LAST; ++category) |
417 | if (category != LC_ALL) | |
418 | { | |
419 | struct locale_data *here = *_nl_current[category]; | |
f84ad0b1 | 420 | |
4b10dd6c UD |
421 | /* If this category is already "C" don't do anything. */ |
422 | if (here == _nl_C[category]) | |
423 | continue; | |
74454183 | 424 | |
4b10dd6c UD |
425 | /* We have to be prepared that sometime later me still might |
426 | need the locale information. */ | |
427 | setdata (category, _nl_C[category]); | |
428 | setname (category, _nl_C_name); | |
f84ad0b1 | 429 | |
4b10dd6c UD |
430 | _nl_unload_locale (here); |
431 | } | |
f84ad0b1 UD |
432 | |
433 | setname (LC_ALL, _nl_C_name); | |
434 | } | |
435 | text_set_element (__libc_subfreeres, free_mem); |