improve of cmpl.
[bush.git] / src / locale.c
blobe3d8ba6a1bfb16250fb542c4e3212ba62ab23e3d
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/>.
21 #include "config.h"
23 #include "bushtypes.h"
25 #if defined (HAVE_UNISTD_H)
26 # include <unistd.h>
27 #endif
29 #if HAVE_LANGINFO_CODESET
30 # include <langinfo.h>
31 #endif
33 #include "bushintl.h"
34 #include "bushansi.h"
35 #include <stdio.h>
36 #include "chartypes.h"
37 #include <errno.h>
39 #include "shell.h"
40 #include "input/input.h" /* For bush_input */
42 #ifndef errno
43 extern int errno;
44 #endif
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
60 categories */
61 static char *lc_all;
63 /* tracks the value of LC_ALL; used to provide defaults for locale
64 categories */
65 static char *lang;
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(). */
76 void
77 set_default_locale ()
79 #if defined (HAVE_SETLOCALE)
80 default_locale = setlocale (LC_ALL, "");
81 if (default_locale)
82 default_locale = savestring (default_locale);
83 #else
84 default_locale = savestring ("C");
85 #endif /* HAVE_SETLOCALE */
86 bindtextdomain (PACKAGE, LOCALEDIR);
87 textdomain (PACKAGE);
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);
93 #else
94 local_shiftstates = 0;
95 #endif
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. */
101 void
102 set_default_locale_vars ()
104 char *val;
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);
113 locale_setblanks ();
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);
119 # else
120 local_shiftstates = 0;
121 # endif
123 u32reset ();
125 # endif
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");
154 if (val && *val)
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");
163 if (val && *val)
165 FREE (default_dir);
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)
176 char *var, *value;
178 int r;
179 char *x;
181 x = "";
182 errno = 0;
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);
189 return (1);
191 else if (var[0] == 'T') /* TEXTDOMAINDIR */
193 FREE (default_dir);
194 default_dir = value ? savestring (value) : (char *)NULL;
195 if (default_domain && *default_domain)
196 bindtextdomain (default_domain, default_dir);
197 return (1);
200 /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */
202 else if (var[3] == 'A') /* LC_ALL */
204 FREE (lc_all);
205 if (value)
206 lc_all = savestring (value);
207 else
209 lc_all = (char *)xmalloc (1);
210 lc_all[0] = '\0';
212 #if defined (HAVE_SETLOCALE)
213 r = *lc_all ? ((x = setlocale (LC_ALL, lc_all)) != 0) : reset_locale_vars ();
214 if (x == 0)
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));
221 locale_setblanks ();
222 locale_mb_cur_max = MB_CUR_MAX;
223 /* if LC_ALL == "", reset_locale_vars has already called this */
224 if (*lc_all && x)
225 locale_utf8locale = locale_isutf8 (lc_all);
226 # if defined (HANDLE_MULTIBYTE)
227 locale_shiftstates = mblen ((char *)NULL, 0);
228 # else
229 local_shiftstates = 0;
230 # endif
231 u32reset ();
232 return r;
233 #else
234 return (1);
235 #endif
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"));
245 locale_setblanks ();
246 locale_mb_cur_max = MB_CUR_MAX;
247 /* if setlocale() returns NULL, the locale is not changed */
248 if (x)
249 locale_utf8locale = locale_isutf8 (x);
250 #if defined (HANDLE_MULTIBYTE)
251 locale_shiftstates = mblen ((char *)NULL, 0);
252 #else
253 local_shiftstates = 0;
254 #endif
255 u32reset ();
257 # endif
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 */
289 if (x == 0)
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));
297 return (x != 0);
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. */
304 set_lang (var, value)
305 char *var, *value;
307 FREE (lang);
308 if (value)
309 lang = savestring (value);
310 else
312 lang = (char *)xmalloc (1);
313 lang[0] = '\0';
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. */
321 void
322 set_default_lang ()
324 char *v;
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. */
336 char *
337 get_locale_var (var)
338 char *var;
340 char *locale;
342 locale = lc_all;
344 if (locale == 0 || *locale == 0)
345 locale = get_string_value (var); /* XXX - no mem leak */
346 if (locale == 0 || *locale == 0)
347 locale = lang;
348 if (locale == 0 || *locale == 0)
349 #if 0
350 locale = default_locale; /* system-dependent; not really portable. should it be "C"? */
351 #else
352 locale = "";
353 #endif
354 return (locale);
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. */
360 static int
361 reset_locale_vars ()
363 char *t, *x;
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)
368 return 0;
370 x = 0;
371 # if defined (LC_CTYPE)
372 x = setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
373 # endif
374 # if defined (LC_COLLATE)
375 t = setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
376 # endif
377 # if defined (LC_MESSAGES)
378 t = setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
379 # endif
380 # if defined (LC_NUMERIC)
381 t = setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
382 # endif
383 # if defined (LC_TIME)
384 t = setlocale (LC_TIME, get_locale_var ("LC_TIME"));
385 # endif
387 locale_setblanks ();
388 locale_mb_cur_max = MB_CUR_MAX;
389 if (x)
390 locale_utf8locale = locale_isutf8 (x);
391 # if defined (HANDLE_MULTIBYTE)
392 locale_shiftstates = mblen ((char *)NULL, 0);
393 # else
394 local_shiftstates = 0;
395 # endif
396 u32reset ();
397 #endif
398 return 1;
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. */
405 char *
406 localetrans (string, len, lenp)
407 char *string;
408 int len, *lenp;
410 char *locale, *t;
411 char *translated;
412 int tlen;
414 /* Don't try to translate null strings. */
415 if (string == 0 || *string == 0)
417 if (lenp)
418 *lenp = 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);
431 strcpy (t, string);
432 if (lenp)
433 *lenp = len;
434 return (t);
437 /* Now try to translate it. */
438 if (default_domain && *default_domain)
439 translated = dgettext (default_domain, string);
440 else
441 translated = string;
443 if (translated == string) /* gettext returns its argument if untranslatable */
445 t = (char *)xmalloc (len + 1);
446 strcpy (t, string);
447 if (lenp)
448 *lenp = len;
450 else
452 tlen = strlen (translated);
453 t = (char *)xmalloc (tlen + 1);
454 strcpy (t, translated);
455 if (lenp)
456 *lenp = tlen;
458 return (t);
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". */
463 char *
464 mk_msgstr (string, foundnlp)
465 char *string;
466 int *foundnlp;
468 register int c, len;
469 char *result, *r, *s;
471 for (len = 0, s = string; s && *s; s++)
473 len++;
474 if (*s == '"' || *s == '\\')
475 len++;
476 else if (*s == '\n')
477 len += 5;
480 r = result = (char *)xmalloc (len + 3);
481 *r++ = '"';
483 for (s = string; s && (c = *s); s++)
485 if (c == '\n') /* <NL> -> \n"<NL>" */
487 *r++ = '\\';
488 *r++ = 'n';
489 *r++ = '"';
490 *r++ = '\n';
491 *r++ = '"';
492 if (foundnlp)
493 *foundnlp = 1;
494 continue;
496 if (c == '"' || c == '\\')
497 *r++ = '\\';
498 *r++ = c;
501 *r++ = '"';
502 *r++ = '\0';
504 return result;
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. */
514 char *
515 localeexpand (string, start, end, lineno, lenp)
516 char *string;
517 int start, end, lineno, *lenp;
519 int len, tlen, foundnl;
520 char *temp, *t, *t2;
522 temp = (char *)xmalloc (end - start + 1);
523 for (tlen = 0, len = start; len < end; )
524 temp[tlen++] = string[len++];
525 temp[tlen] = '\0';
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)
536 if (dump_po_strings)
538 foundnl = 0;
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);
544 free (t);
546 else
547 printf ("\"%s\"\n", temp);
549 if (lenp)
550 *lenp = tlen;
551 return (temp);
553 else if (*temp)
555 t = localetrans (temp, tlen, &len);
556 free (temp);
557 if (lenp)
558 *lenp = len;
559 return (t);
561 else
563 if (lenp)
564 *lenp = 0;
565 return (temp);
569 /* Set every character in the <blank> character class to be a shell break
570 character for the lexical analyzer when the locale changes. */
571 static void
572 locale_setblanks ()
574 int x;
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;
585 else
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 */
593 static int
594 locale_isutf8 (lspec)
595 char *lspec;
597 char *cp, *encoding;
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"));
605 #else
606 /* Take a shot */
607 for (cp = lspec; *cp && *cp != '@' && *cp != '+' && *cp != ','; cp++)
609 if (*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)))
616 return 1;
617 else
618 return 0;
621 return 0;
622 #endif
625 #if defined (HAVE_LOCALECONV)
627 locale_decpoint ()
629 struct lconv *lv;
631 lv = localeconv ();
632 return (lv && lv->decimal_point && lv->decimal_point[0]) ? lv->decimal_point[0] : '.';
634 #else
635 # undef locale_decpoint
637 locale_decpoint ()
639 return '.';
641 #endif