1 /* locale.c - Miscellaneous internationalization functions. */
3 /* Copyright (C) 1996-2009,2012,2016,2020 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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.
12 Bush 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.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
23 #include "bushtypes.h"
25 #if defined (HAVE_UNISTD_H)
29 #if HAVE_LANGINFO_CODESET
30 # include <langinfo.h>
36 #include "chartypes.h"
40 #include "input/input.h" /* For bush_input */
46 int locale_utf8locale
;
47 int locale_mb_cur_max
; /* value of MB_CUR_MAX for current locale (LC_CTYPE) */
48 int locale_shiftstates
= 0;
50 extern int dump_translatable_strings
, dump_po_strings
;
52 /* The current locale when the program begins */
53 static char *default_locale
;
55 /* The current domain for textdomain(3). */
56 static char *default_domain
;
57 static char *default_dir
;
59 /* tracks the value of LC_ALL; used to override values for other locale
63 /* tracks the value of LC_ALL; used to provide defaults for locale
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. */
69 static int reset_locale_vars
PARAMS((void));
71 static void locale_setblanks
PARAMS((void));
72 static int locale_isutf8
PARAMS((char *));
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(). */
79 #if defined (HAVE_SETLOCALE)
80 default_locale
= setlocale (LC_ALL
, "");
82 default_locale
= savestring (default_locale
);
84 default_locale
= savestring ("C");
85 #endif /* HAVE_SETLOCALE */
86 bindtextdomain (PACKAGE
, LOCALEDIR
);
89 locale_mb_cur_max
= MB_CUR_MAX
;
90 locale_utf8locale
= locale_isutf8 (default_locale
);
91 #if defined (HANDLE_MULTIBYTE)
92 locale_shiftstates
= mblen ((char *)NULL
, 0);
94 local_shiftstates
= 0;
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
100 should be called from main() after parsing the environment. */
102 set_default_locale_vars ()
106 #if defined (HAVE_SETLOCALE)
108 # if defined (LC_CTYPE)
109 val
= get_string_value ("LC_CTYPE");
110 if (val
== 0 && lc_all
&& *lc_all
)
112 setlocale (LC_CTYPE
, lc_all
);
114 locale_mb_cur_max
= MB_CUR_MAX
;
115 locale_utf8locale
= locale_isutf8 (lc_all
);
117 # if defined (HANDLE_MULTIBYTE)
118 locale_shiftstates
= mblen ((char *)NULL
, 0);
120 local_shiftstates
= 0;
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 */
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 */
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 */
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 */
151 #endif /* HAVE_SETLOCALE */
153 val
= get_string_value ("TEXTDOMAIN");
156 FREE (default_domain
);
157 default_domain
= savestring (val
);
158 if (default_dir
&& *default_dir
)
159 bindtextdomain (default_domain
, default_dir
);
162 val
= get_string_value ("TEXTDOMAINDIR");
166 default_dir
= savestring (val
);
167 if (default_domain
&& *default_domain
)
168 bindtextdomain (default_domain
, default_dir
);
172 /* Set one of the locale categories (specified by VAR) to VALUE. Returns 1
173 if successful, 0 otherwise. */
175 set_locale_var (var
, value
)
183 if (var
[0] == 'T' && var
[10] == 0) /* TEXTDOMAIN */
185 FREE (default_domain
);
186 default_domain
= value
? savestring (value
) : (char *)NULL
;
187 if (default_dir
&& *default_dir
)
188 bindtextdomain (default_domain
, default_dir
);
191 else if (var
[0] == 'T') /* TEXTDOMAINDIR */
194 default_dir
= value
? savestring (value
) : (char *)NULL
;
195 if (default_domain
&& *default_domain
)
196 bindtextdomain (default_domain
, default_dir
);
200 /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */
202 else if (var
[3] == 'A') /* LC_ALL */
206 lc_all
= savestring (value
);
209 lc_all
= (char *)xmalloc (1);
212 #if defined (HAVE_SETLOCALE)
213 r
= *lc_all
? ((x
= setlocale (LC_ALL
, lc_all
)) != 0) : reset_locale_vars ();
217 internal_warning(_("setlocale: LC_ALL: cannot change locale (%s)"), lc_all
);
219 internal_warning(_("setlocale: LC_ALL: cannot change locale (%s): %s"), lc_all
, strerror (errno
));
222 locale_mb_cur_max
= MB_CUR_MAX
;
223 /* if LC_ALL == "", reset_locale_vars has already called this */
225 locale_utf8locale
= locale_isutf8 (lc_all
);
226 # if defined (HANDLE_MULTIBYTE)
227 locale_shiftstates
= mblen ((char *)NULL
, 0);
229 local_shiftstates
= 0;
238 #if defined (HAVE_SETLOCALE)
239 else if (var
[3] == 'C' && var
[4] == 'T') /* LC_CTYPE */
241 # if defined (LC_CTYPE)
242 if (lc_all
== 0 || *lc_all
== '\0')
244 x
= setlocale (LC_CTYPE
, get_locale_var ("LC_CTYPE"));
246 locale_mb_cur_max
= MB_CUR_MAX
;
247 /* if setlocale() returns NULL, the locale is not changed */
249 locale_utf8locale
= locale_isutf8 (x
);
250 #if defined (HANDLE_MULTIBYTE)
251 locale_shiftstates
= mblen ((char *)NULL
, 0);
253 local_shiftstates
= 0;
259 else if (var
[3] == 'C' && var
[4] == 'O') /* LC_COLLATE */
261 # if defined (LC_COLLATE)
262 if (lc_all
== 0 || *lc_all
== '\0')
263 x
= setlocale (LC_COLLATE
, get_locale_var ("LC_COLLATE"));
264 # endif /* LC_COLLATE */
266 else if (var
[3] == 'M' && var
[4] == 'E') /* LC_MESSAGES */
268 # if defined (LC_MESSAGES)
269 if (lc_all
== 0 || *lc_all
== '\0')
270 x
= setlocale (LC_MESSAGES
, get_locale_var ("LC_MESSAGES"));
271 # endif /* LC_MESSAGES */
273 else if (var
[3] == 'N' && var
[4] == 'U') /* LC_NUMERIC */
275 # if defined (LC_NUMERIC)
276 if (lc_all
== 0 || *lc_all
== '\0')
277 x
= setlocale (LC_NUMERIC
, get_locale_var ("LC_NUMERIC"));
278 # endif /* LC_NUMERIC */
280 else if (var
[3] == 'T' && var
[4] == 'I') /* LC_TIME */
282 # if defined (LC_TIME)
283 if (lc_all
== 0 || *lc_all
== '\0')
284 x
= setlocale (LC_TIME
, get_locale_var ("LC_TIME"));
285 # endif /* LC_TIME */
287 #endif /* HAVE_SETLOCALE */
292 internal_warning(_("setlocale: %s: cannot change locale (%s)"), var
, get_locale_var (var
));
294 internal_warning(_("setlocale: %s: cannot change locale (%s): %s"), var
, get_locale_var (var
), strerror (errno
));
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
304 set_lang (var
, value
)
309 lang
= savestring (value
);
312 lang
= (char *)xmalloc (1);
316 return ((lc_all
== 0 || *lc_all
== 0) ? reset_locale_vars () : 0);
319 /* Set default values for LANG and LC_ALL. Default values for all other
320 locale-related variables depend on these. */
326 v
= get_string_value ("LC_ALL");
327 set_locale_var ("LC_ALL", v
);
329 v
= get_string_value ("LANG");
330 set_lang ("LANG", v
);
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. */
344 if (locale
== 0 || *locale
== 0)
345 locale
= get_string_value (var
); /* XXX - no mem leak */
346 if (locale
== 0 || *locale
== 0)
348 if (locale
== 0 || *locale
== 0)
350 locale
= default_locale
; /* system-dependent; not really portable. should it be "C"? */
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. */
364 #if defined (HAVE_SETLOCALE)
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)
371 # if defined (LC_CTYPE)
372 x
= setlocale (LC_CTYPE
, get_locale_var ("LC_CTYPE"));
374 # if defined (LC_COLLATE)
375 t
= setlocale (LC_COLLATE
, get_locale_var ("LC_COLLATE"));
377 # if defined (LC_MESSAGES)
378 t
= setlocale (LC_MESSAGES
, get_locale_var ("LC_MESSAGES"));
380 # if defined (LC_NUMERIC)
381 t
= setlocale (LC_NUMERIC
, get_locale_var ("LC_NUMERIC"));
383 # if defined (LC_TIME)
384 t
= setlocale (LC_TIME
, get_locale_var ("LC_TIME"));
388 locale_mb_cur_max
= MB_CUR_MAX
;
390 locale_utf8locale
= locale_isutf8 (x
);
391 # if defined (HANDLE_MULTIBYTE)
392 locale_shiftstates
= mblen ((char *)NULL
, 0);
394 local_shiftstates
= 0;
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. */
406 localetrans (string
, len
, lenp
)
414 /* Don't try to translate null strings. */
415 if (string
== 0 || *string
== 0)
419 return ((char *)NULL
);
422 locale
= get_locale_var ("LC_MESSAGES");
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. */
427 if (locale
== 0 || locale
[0] == '\0' ||
428 (locale
[0] == 'C' && locale
[1] == '\0') || STREQ (locale
, "POSIX"))
430 t
= (char *)xmalloc (len
+ 1);
437 /* Now try to translate it. */
438 if (default_domain
&& *default_domain
)
439 translated
= dgettext (default_domain
, string
);
443 if (translated
== string
) /* gettext returns its argument if untranslatable */
445 t
= (char *)xmalloc (len
+ 1);
452 tlen
= strlen (translated
);
453 t
= (char *)xmalloc (tlen
+ 1);
454 strcpy (t
, translated
);
461 /* Change a bush string into a string suitable for inclusion in a `po' file.
462 This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */
464 mk_msgstr (string
, foundnlp
)
469 char *result
, *r
, *s
;
471 for (len
= 0, s
= string
; s
&& *s
; s
++)
474 if (*s
== '"' || *s
== '\\')
480 r
= result
= (char *)xmalloc (len
+ 3);
483 for (s
= string
; s
&& (c
= *s
); s
++)
485 if (c
== '\n') /* <NL> -> \n"<NL>" */
496 if (c
== '"' || c
== '\\')
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,
515 localeexpand (string
, start
, end
, lineno
, lenp
)
517 int start
, end
, lineno
, *lenp
;
519 int len
, tlen
, foundnl
;
522 temp
= (char *)xmalloc (end
- start
+ 1);
523 for (tlen
= 0, len
= start
; len
< end
; )
524 temp
[tlen
++] = string
[len
++];
527 /* If we're just dumping translatable strings, don't do anything with the
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. */
534 if (dump_translatable_strings
)
539 t
= mk_msgstr (temp
, &foundnl
);
540 t2
= foundnl
? "\"\"\n" : "";
542 printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
543 yy_input_name (), lineno
, t2
, t
);
547 printf ("\"%s\"\n", temp
);
555 t
= localetrans (temp
, tlen
, &len
);
569 /* Set every character in the <blank> character class to be a shell break
570 character for the lexical analyzer when the locale changes. */
576 for (x
= 0; x
< sh_syntabsiz
; x
++)
578 if (isblank ((unsigned char)x
))
579 sh_syntaxtab
[x
] |= CSHBRK
|CBLANK
;
580 else if (member (x
, shell_break_chars
))
582 sh_syntaxtab
[x
] |= CSHBRK
;
583 sh_syntaxtab
[x
] &= ~CBLANK
;
586 sh_syntaxtab
[x
] &= ~(CSHBRK
|CBLANK
);
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 */
594 locale_isutf8 (lspec
)
599 #if HAVE_LANGINFO_CODESET
600 cp
= nl_langinfo (CODESET
);
601 return (STREQ (cp
, "UTF-8") || STREQ (cp
, "utf8"));
602 #elif HAVE_LOCALE_CHARSET
603 cp
= locale_charset ();
604 return (STREQ (cp
, "UTF-8") || STREQ (cp
, "utf8"));
607 for (cp
= lspec
; *cp
&& *cp
!= '@' && *cp
!= '+' && *cp
!= ','; cp
++)
611 for (encoding
= ++cp
; *cp
&& *cp
!= '@' && *cp
!= '+' && *cp
!= ','; cp
++)
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)))
625 #if defined (HAVE_LOCALECONV)
632 return (lv
&& lv
->decimal_point
&& lv
->decimal_point
[0]) ? lv
->decimal_point
[0] : '.';
635 # undef locale_decpoint