]> git.ipfire.org Git - thirdparty/bash.git/blame - locale.c
Bash-5.1 patch 12: fix race condition with child processes and resetting trapped...
[thirdparty/bash.git] / locale.c
CommitLineData
ccc6cda3
JA
1/* locale.c - Miscellaneous internationalization functions. */
2
8868edaf 3/* Copyright (C) 1996-2009,2012,2016,2020 Free Software Foundation, Inc.
ccc6cda3
JA
4
5 This file is part of GNU Bash, the Bourne Again SHell.
6
3185942a
JA
7 Bash is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
ccc6cda3 11
3185942a
JA
12 Bash is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
ccc6cda3 16
3185942a
JA
17 You should have received a copy of the GNU General Public License
18 along with Bash. If not, see <http://www.gnu.org/licenses/>.
19*/
ccc6cda3
JA
20
21#include "config.h"
22
23#include "bashtypes.h"
24
25#if defined (HAVE_UNISTD_H)
26# include <unistd.h>
27#endif
28
ac50fbac
CR
29#if HAVE_LANGINFO_CODESET
30# include <langinfo.h>
31#endif
32
ccc6cda3
JA
33#include "bashintl.h"
34#include "bashansi.h"
35#include <stdio.h>
f73dda09 36#include "chartypes.h"
3185942a 37#include <errno.h>
ccc6cda3
JA
38
39#include "shell.h"
7117c2d2
JA
40#include "input.h" /* For bash_input */
41
3185942a
JA
42#ifndef errno
43extern int errno;
44#endif
45
d233b485 46int locale_utf8locale;
ac50fbac 47int locale_mb_cur_max; /* value of MB_CUR_MAX for current locale (LC_CTYPE) */
8868edaf 48int locale_shiftstates = 0;
ac50fbac 49
7117c2d2 50extern int dump_translatable_strings, dump_po_strings;
ccc6cda3
JA
51
52/* The current locale when the program begins */
53static char *default_locale;
54
55/* The current domain for textdomain(3). */
56static char *default_domain;
57static char *default_dir;
58
59/* tracks the value of LC_ALL; used to override values for other locale
60 categories */
61static char *lc_all;
62
b80f6443
JA
63/* tracks the value of LC_ALL; used to provide defaults for locale
64 categories */
65static char *lang;
66
67/* Called to reset all of the locale variables to their appropriate values
68 if (and only if) LC_ALL has not been assigned a value. */
8868edaf 69static int reset_locale_vars PARAMS((void));
b80f6443 70
8868edaf
CR
71static void locale_setblanks PARAMS((void));
72static int locale_isutf8 PARAMS((char *));
b80f6443 73
ccc6cda3
JA
74/* Set the value of default_locale and make the current locale the
75 system default locale. This should be called very early in main(). */
76void
77set_default_locale ()
78{
79#if defined (HAVE_SETLOCALE)
80 default_locale = setlocale (LC_ALL, "");
81 if (default_locale)
82 default_locale = savestring (default_locale);
8868edaf
CR
83#else
84 default_locale = savestring ("C");
ccc6cda3 85#endif /* HAVE_SETLOCALE */
b80f6443
JA
86 bindtextdomain (PACKAGE, LOCALEDIR);
87 textdomain (PACKAGE);
ac50fbac
CR
88
89 locale_mb_cur_max = MB_CUR_MAX;
d233b485 90 locale_utf8locale = locale_isutf8 (default_locale);
8868edaf 91#if defined (HANDLE_MULTIBYTE)
d233b485 92 locale_shiftstates = mblen ((char *)NULL, 0);
8868edaf
CR
93#else
94 local_shiftstates = 0;
95#endif
ccc6cda3
JA
96}
97
95732b49
JA
98/* Set default values for LC_CTYPE, LC_COLLATE, LC_MESSAGES, LC_NUMERIC and
99 LC_TIME if they are not specified in the environment, but LC_ALL is. This
ccc6cda3
JA
100 should be called from main() after parsing the environment. */
101void
102set_default_locale_vars ()
103{
104 char *val;
105
106#if defined (HAVE_SETLOCALE)
b80f6443
JA
107
108# if defined (LC_CTYPE)
ccc6cda3
JA
109 val = get_string_value ("LC_CTYPE");
110 if (val == 0 && lc_all && *lc_all)
b80f6443
JA
111 {
112 setlocale (LC_CTYPE, lc_all);
113 locale_setblanks ();
ac50fbac 114 locale_mb_cur_max = MB_CUR_MAX;
d233b485 115 locale_utf8locale = locale_isutf8 (lc_all);
8868edaf
CR
116
117# if defined (HANDLE_MULTIBYTE)
d233b485 118 locale_shiftstates = mblen ((char *)NULL, 0);
8868edaf
CR
119# else
120 local_shiftstates = 0;
121# endif
122
ac50fbac 123 u32reset ();
b80f6443
JA
124 }
125# endif
ccc6cda3
JA
126
127# if defined (LC_COLLATE)
128 val = get_string_value ("LC_COLLATE");
129 if (val == 0 && lc_all && *lc_all)
130 setlocale (LC_COLLATE, lc_all);
131# endif /* LC_COLLATE */
132
133# if defined (LC_MESSAGES)
134 val = get_string_value ("LC_MESSAGES");
135 if (val == 0 && lc_all && *lc_all)
136 setlocale (LC_MESSAGES, lc_all);
137# endif /* LC_MESSAGES */
138
bb70624e
JA
139# if defined (LC_NUMERIC)
140 val = get_string_value ("LC_NUMERIC");
141 if (val == 0 && lc_all && *lc_all)
142 setlocale (LC_NUMERIC, lc_all);
143# endif /* LC_NUMERIC */
144
95732b49
JA
145# if defined (LC_TIME)
146 val = get_string_value ("LC_TIME");
147 if (val == 0 && lc_all && *lc_all)
148 setlocale (LC_TIME, lc_all);
149# endif /* LC_TIME */
150
ccc6cda3
JA
151#endif /* HAVE_SETLOCALE */
152
153 val = get_string_value ("TEXTDOMAIN");
154 if (val && *val)
155 {
156 FREE (default_domain);
157 default_domain = savestring (val);
ac50fbac
CR
158 if (default_dir && *default_dir)
159 bindtextdomain (default_domain, default_dir);
ccc6cda3
JA
160 }
161
162 val = get_string_value ("TEXTDOMAINDIR");
163 if (val && *val)
164 {
165 FREE (default_dir);
166 default_dir = savestring (val);
b80f6443
JA
167 if (default_domain && *default_domain)
168 bindtextdomain (default_domain, default_dir);
ccc6cda3
JA
169 }
170}
171
172/* Set one of the locale categories (specified by VAR) to VALUE. Returns 1
173 if successful, 0 otherwise. */
174int
175set_locale_var (var, value)
176 char *var, *value;
177{
b80f6443 178 int r;
3185942a 179 char *x;
b80f6443 180
3185942a
JA
181 x = "";
182 errno = 0;
ccc6cda3
JA
183 if (var[0] == 'T' && var[10] == 0) /* TEXTDOMAIN */
184 {
185 FREE (default_domain);
186 default_domain = value ? savestring (value) : (char *)NULL;
ac50fbac
CR
187 if (default_dir && *default_dir)
188 bindtextdomain (default_domain, default_dir);
ccc6cda3
JA
189 return (1);
190 }
191 else if (var[0] == 'T') /* TEXTDOMAINDIR */
192 {
193 FREE (default_dir);
194 default_dir = value ? savestring (value) : (char *)NULL;
b80f6443
JA
195 if (default_domain && *default_domain)
196 bindtextdomain (default_domain, default_dir);
ccc6cda3
JA
197 return (1);
198 }
199
200 /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */
201
202 else if (var[3] == 'A') /* LC_ALL */
203 {
204 FREE (lc_all);
e8ce775d
JA
205 if (value)
206 lc_all = savestring (value);
e8ce775d
JA
207 else
208 {
f73dda09 209 lc_all = (char *)xmalloc (1);
e8ce775d
JA
210 lc_all[0] = '\0';
211 }
ccc6cda3 212#if defined (HAVE_SETLOCALE)
3185942a
JA
213 r = *lc_all ? ((x = setlocale (LC_ALL, lc_all)) != 0) : reset_locale_vars ();
214 if (x == 0)
17345e5a
JA
215 {
216 if (errno == 0)
217 internal_warning(_("setlocale: LC_ALL: cannot change locale (%s)"), lc_all);
218 else
219 internal_warning(_("setlocale: LC_ALL: cannot change locale (%s): %s"), lc_all, strerror (errno));
220 }
b80f6443 221 locale_setblanks ();
ac50fbac 222 locale_mb_cur_max = MB_CUR_MAX;
d233b485
CR
223 /* if LC_ALL == "", reset_locale_vars has already called this */
224 if (*lc_all && x)
225 locale_utf8locale = locale_isutf8 (lc_all);
8868edaf 226# if defined (HANDLE_MULTIBYTE)
d233b485 227 locale_shiftstates = mblen ((char *)NULL, 0);
8868edaf
CR
228# else
229 local_shiftstates = 0;
230# endif
ac50fbac 231 u32reset ();
b80f6443 232 return r;
ccc6cda3
JA
233#else
234 return (1);
235#endif
236 }
237
238#if defined (HAVE_SETLOCALE)
239 else if (var[3] == 'C' && var[4] == 'T') /* LC_CTYPE */
240 {
b80f6443 241# if defined (LC_CTYPE)
ccc6cda3 242 if (lc_all == 0 || *lc_all == '\0')
b80f6443 243 {
3185942a 244 x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
b80f6443 245 locale_setblanks ();
ac50fbac 246 locale_mb_cur_max = MB_CUR_MAX;
d233b485
CR
247 /* if setlocale() returns NULL, the locale is not changed */
248 if (x)
249 locale_utf8locale = locale_isutf8 (x);
8868edaf 250#if defined (HANDLE_MULTIBYTE)
d233b485 251 locale_shiftstates = mblen ((char *)NULL, 0);
8868edaf
CR
252#else
253 local_shiftstates = 0;
254#endif
ac50fbac 255 u32reset ();
b80f6443
JA
256 }
257# endif
ccc6cda3
JA
258 }
259 else if (var[3] == 'C' && var[4] == 'O') /* LC_COLLATE */
260 {
261# if defined (LC_COLLATE)
262 if (lc_all == 0 || *lc_all == '\0')
3185942a 263 x = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
ccc6cda3
JA
264# endif /* LC_COLLATE */
265 }
266 else if (var[3] == 'M' && var[4] == 'E') /* LC_MESSAGES */
267 {
268# if defined (LC_MESSAGES)
269 if (lc_all == 0 || *lc_all == '\0')
3185942a 270 x = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
ccc6cda3
JA
271# endif /* LC_MESSAGES */
272 }
f73dda09 273 else if (var[3] == 'N' && var[4] == 'U') /* LC_NUMERIC */
bb70624e
JA
274 {
275# if defined (LC_NUMERIC)
276 if (lc_all == 0 || *lc_all == '\0')
3185942a 277 x = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
bb70624e
JA
278# endif /* LC_NUMERIC */
279 }
95732b49
JA
280 else if (var[3] == 'T' && var[4] == 'I') /* LC_TIME */
281 {
282# if defined (LC_TIME)
283 if (lc_all == 0 || *lc_all == '\0')
3185942a 284 x = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
95732b49
JA
285# endif /* LC_TIME */
286 }
ccc6cda3 287#endif /* HAVE_SETLOCALE */
95732b49 288
3185942a 289 if (x == 0)
17345e5a
JA
290 {
291 if (errno == 0)
292 internal_warning(_("setlocale: %s: cannot change locale (%s)"), var, get_locale_var (var));
293 else
294 internal_warning(_("setlocale: %s: cannot change locale (%s): %s"), var, get_locale_var (var), strerror (errno));
295 }
ccc6cda3 296
3185942a 297 return (x != 0);
ccc6cda3
JA
298}
299
b80f6443
JA
300/* Called when LANG is assigned a value. Tracks value in `lang'. Calls
301 reset_locale_vars() to reset any default values if LC_ALL is unset or
302 null. */
ccc6cda3
JA
303int
304set_lang (var, value)
305 char *var, *value;
306{
b80f6443
JA
307 FREE (lang);
308 if (value)
309 lang = savestring (value);
310 else
311 {
312 lang = (char *)xmalloc (1);
313 lang[0] = '\0';
314 }
ac50fbac 315
b80f6443 316 return ((lc_all == 0 || *lc_all == 0) ? reset_locale_vars () : 0);
ccc6cda3
JA
317}
318
0628567a
JA
319/* Set default values for LANG and LC_ALL. Default values for all other
320 locale-related variables depend on these. */
321void
322set_default_lang ()
323{
324 char *v;
325
326 v = get_string_value ("LC_ALL");
327 set_locale_var ("LC_ALL", v);
328
329 v = get_string_value ("LANG");
330 set_lang ("LANG", v);
331}
332
b80f6443
JA
333/* Get the value of one of the locale variables (LC_MESSAGES, LC_CTYPE).
334 The precedence is as POSIX.2 specifies: LC_ALL has precedence over
335 the specific locale variables, and LANG, if set, is used as the default. */
ccc6cda3
JA
336char *
337get_locale_var (var)
338 char *var;
339{
340 char *locale;
341
342 locale = lc_all;
343
b80f6443 344 if (locale == 0 || *locale == 0)
a0c0a00f 345 locale = get_string_value (var); /* XXX - no mem leak */
b80f6443
JA
346 if (locale == 0 || *locale == 0)
347 locale = lang;
348 if (locale == 0 || *locale == 0)
3185942a 349#if 0
0628567a 350 locale = default_locale; /* system-dependent; not really portable. should it be "C"? */
3185942a
JA
351#else
352 locale = "";
353#endif
ccc6cda3
JA
354 return (locale);
355}
356
b80f6443
JA
357/* Called to reset all of the locale variables to their appropriate values
358 if (and only if) LC_ALL has not been assigned a value. DO NOT CALL THIS
359 IF LC_ALL HAS BEEN ASSIGNED A VALUE. */
360static int
361reset_locale_vars ()
362{
d233b485 363 char *t, *x;
b80f6443 364#if defined (HAVE_SETLOCALE)
0628567a
JA
365 if (lang == 0 || *lang == '\0')
366 maybe_make_export_env (); /* trust that this will change environment for setlocale */
367 if (setlocale (LC_ALL, lang ? lang : "") == 0)
b80f6443
JA
368 return 0;
369
d233b485 370 x = 0;
b80f6443 371# if defined (LC_CTYPE)
d233b485 372 x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
b80f6443
JA
373# endif
374# if defined (LC_COLLATE)
3185942a 375 t = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
b80f6443
JA
376# endif
377# if defined (LC_MESSAGES)
3185942a 378 t = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
b80f6443
JA
379# endif
380# if defined (LC_NUMERIC)
3185942a 381 t = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
b80f6443 382# endif
95732b49 383# if defined (LC_TIME)
3185942a 384 t = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
95732b49 385# endif
b80f6443
JA
386
387 locale_setblanks ();
ac50fbac 388 locale_mb_cur_max = MB_CUR_MAX;
d233b485
CR
389 if (x)
390 locale_utf8locale = locale_isutf8 (x);
8868edaf 391# if defined (HANDLE_MULTIBYTE)
d233b485 392 locale_shiftstates = mblen ((char *)NULL, 0);
8868edaf
CR
393# else
394 local_shiftstates = 0;
395# endif
ac50fbac 396 u32reset ();
b80f6443
JA
397#endif
398 return 1;
399}
400
ccc6cda3
JA
401/* Translate the contents of STRING, a $"..." quoted string, according
402 to the current locale. In the `C' or `POSIX' locale, or if gettext()
403 is not available, the passed string is returned unchanged. The
404 length of the translated string is returned in LENP, if non-null. */
405char *
406localetrans (string, len, lenp)
407 char *string;
408 int len, *lenp;
409{
410 char *locale, *t;
ccc6cda3
JA
411 char *translated;
412 int tlen;
ccc6cda3
JA
413
414 /* Don't try to translate null strings. */
415 if (string == 0 || *string == 0)
416 {
417 if (lenp)
28ef6c31 418 *lenp = 0;
ccc6cda3
JA
419 return ((char *)NULL);
420 }
421
ccc6cda3
JA
422 locale = get_locale_var ("LC_MESSAGES");
423
424 /* If we don't have setlocale() or the current locale is `C' or `POSIX',
425 just return the string. If we don't have gettext(), there's no use
426 doing anything else. */
ccc6cda3
JA
427 if (locale == 0 || locale[0] == '\0' ||
428 (locale[0] == 'C' && locale[1] == '\0') || STREQ (locale, "POSIX"))
ccc6cda3 429 {
f73dda09 430 t = (char *)xmalloc (len + 1);
ccc6cda3
JA
431 strcpy (t, string);
432 if (lenp)
433 *lenp = len;
434 return (t);
435 }
436
ccc6cda3 437 /* Now try to translate it. */
b80f6443
JA
438 if (default_domain && *default_domain)
439 translated = dgettext (default_domain, string);
440 else
441 translated = string;
442
ccc6cda3
JA
443 if (translated == string) /* gettext returns its argument if untranslatable */
444 {
f73dda09 445 t = (char *)xmalloc (len + 1);
ccc6cda3
JA
446 strcpy (t, string);
447 if (lenp)
448 *lenp = len;
449 }
450 else
451 {
ccc6cda3 452 tlen = strlen (translated);
f73dda09 453 t = (char *)xmalloc (tlen + 1);
ccc6cda3
JA
454 strcpy (t, translated);
455 if (lenp)
456 *lenp = tlen;
457 }
458 return (t);
ccc6cda3 459}
7117c2d2
JA
460
461/* Change a bash string into a string suitable for inclusion in a `po' file.
462 This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */
463char *
464mk_msgstr (string, foundnlp)
465 char *string;
466 int *foundnlp;
467{
468 register int c, len;
469 char *result, *r, *s;
470
471 for (len = 0, s = string; s && *s; s++)
472 {
473 len++;
474 if (*s == '"' || *s == '\\')
475 len++;
476 else if (*s == '\n')
477 len += 5;
478 }
479
480 r = result = (char *)xmalloc (len + 3);
481 *r++ = '"';
482
483 for (s = string; s && (c = *s); s++)
484 {
485 if (c == '\n') /* <NL> -> \n"<NL>" */
486 {
487 *r++ = '\\';
488 *r++ = 'n';
489 *r++ = '"';
490 *r++ = '\n';
491 *r++ = '"';
492 if (foundnlp)
493 *foundnlp = 1;
494 continue;
495 }
496 if (c == '"' || c == '\\')
497 *r++ = '\\';
498 *r++ = c;
499 }
500
501 *r++ = '"';
502 *r++ = '\0';
503
504 return result;
505}
506
507/* $"..." -- Translate the portion of STRING between START and END
508 according to current locale using gettext (if available) and return
509 the result. The caller will take care of leaving the quotes intact.
510 The string will be left without the leading `$' by the caller.
511 If translation is performed, the translated string will be double-quoted
512 by the caller. The length of the translated string is returned in LENP,
513 if non-null. */
514char *
515localeexpand (string, start, end, lineno, lenp)
516 char *string;
517 int start, end, lineno, *lenp;
518{
519 int len, tlen, foundnl;
520 char *temp, *t, *t2;
521
522 temp = (char *)xmalloc (end - start + 1);
523 for (tlen = 0, len = start; len < end; )
524 temp[tlen++] = string[len++];
525 temp[tlen] = '\0';
526
527 /* If we're just dumping translatable strings, don't do anything with the
b80f6443
JA
528 string itself, but if we're dumping in `po' file format, convert it into
529 a form more palatable to gettext(3) and friends by quoting `"' and `\'
530 with backslashes and converting <NL> into `\n"<NL>"'. If we find a
531 newline in TEMP, we first output a `msgid ""' line and then the
532 translated string; otherwise we output the `msgid' and translated
533 string all on one line. */
7117c2d2
JA
534 if (dump_translatable_strings)
535 {
536 if (dump_po_strings)
537 {
538 foundnl = 0;
539 t = mk_msgstr (temp, &foundnl);
540 t2 = foundnl ? "\"\"\n" : "";
541
542 printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
543 yy_input_name (), lineno, t2, t);
544 free (t);
545 }
546 else
547 printf ("\"%s\"\n", temp);
548
549 if (lenp)
550 *lenp = tlen;
551 return (temp);
552 }
553 else if (*temp)
554 {
555 t = localetrans (temp, tlen, &len);
556 free (temp);
557 if (lenp)
558 *lenp = len;
559 return (t);
560 }
561 else
562 {
563 if (lenp)
564 *lenp = 0;
565 return (temp);
566 }
567}
b80f6443
JA
568
569/* Set every character in the <blank> character class to be a shell break
570 character for the lexical analyzer when the locale changes. */
571static void
572locale_setblanks ()
573{
574 int x;
575
576 for (x = 0; x < sh_syntabsiz; x++)
577 {
a0c0a00f 578 if (isblank ((unsigned char)x))
0628567a 579 sh_syntaxtab[x] |= CSHBRK|CBLANK;
b80f6443 580 else if (member (x, shell_break_chars))
0628567a
JA
581 {
582 sh_syntaxtab[x] |= CSHBRK;
583 sh_syntaxtab[x] &= ~CBLANK;
584 }
b80f6443 585 else
0628567a 586 sh_syntaxtab[x] &= ~(CSHBRK|CBLANK);
b80f6443
JA
587 }
588}
ac50fbac 589
d233b485
CR
590/* Parse a locale specification
591 language[_territory][.codeset][@modifier][+special][,[sponsor][_revision]]
592 and return TRUE if the codeset is UTF-8 or utf8 */
ac50fbac
CR
593static int
594locale_isutf8 (lspec)
595 char *lspec;
596{
d233b485 597 char *cp, *encoding;
ac50fbac
CR
598
599#if HAVE_LANGINFO_CODESET
600 cp = nl_langinfo (CODESET);
601 return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
d233b485
CR
602#elif HAVE_LOCALE_CHARSET
603 cp = locale_charset ();
604 return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8"));
ac50fbac
CR
605#else
606 /* Take a shot */
d233b485
CR
607 for (cp = lspec; *cp && *cp != '@' && *cp != '+' && *cp != ','; cp++)
608 {
609 if (*cp == '.')
610 {
611 for (encoding = ++cp; *cp && *cp != '@' && *cp != '+' && *cp != ','; cp++)
612 ;
613 /* The encoding (codeset) is the substring between encoding and cp */
614 if ((cp - encoding == 5 && STREQN (encoding, "UTF-8", 5)) ||
615 (cp - encoding == 4 && STREQN (encoding, "utf8", 4)))
616 return 1;
617 else
618 return 0;
619 }
620 }
621 return 0;
ac50fbac
CR
622#endif
623}
d233b485
CR
624
625#if defined (HAVE_LOCALECONV)
626int
627locale_decpoint ()
628{
629 struct lconv *lv;
630
631 lv = localeconv ();
632 return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.';
633}
634#else
635# undef locale_decpoint
636int
637locale_decpoint ()
638{
639 return '.';
640}
641#endif