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