1/* locale.c - Miscellaneous internationalization functions. */
2
3/* Copyright (C) 1996-2004 Free Software Foundation, Inc.
4
5   This file is part of GNU Bash, the Bourne Again SHell.
6
7   Bash is free software; you can redistribute it and/or modify it under
8   the terms of the GNU General Public License as published by the Free
9   Software Foundation; either version 2, or (at your option) any later
10   version.
11
12   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
13   WARRANTY; without even the implied warranty of MERCHANTABILITY or
14   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15   for more details.
16
17   You should have received a copy of the GNU General Public License along
18   with Bash; see the file COPYING.  If not, write to the Free Software
19   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
20
21#include "config.h"
22
23#include "bashtypes.h"
24
25#if defined (HAVE_UNISTD_H)
26#  include <unistd.h>
27#endif
28
29#include "bashintl.h"
30#include "bashansi.h"
31#include <stdio.h>
32#include "chartypes.h"
33
34#include "shell.h"
35#include "input.h"	/* For bash_input */
36
37extern int dump_translatable_strings, dump_po_strings;
38
39/* The current locale when the program begins */
40static char *default_locale;
41
42/* The current domain for textdomain(3). */
43static char *default_domain;
44static char *default_dir;
45
46/* tracks the value of LC_ALL; used to override values for other locale
47   categories */
48static char *lc_all;
49
50/* tracks the value of LC_ALL; used to provide defaults for locale
51   categories */
52static char *lang;
53
54/* Called to reset all of the locale variables to their appropriate values
55   if (and only if) LC_ALL has not been assigned a value. */
56static int reset_locale_vars __P((void));
57
58static void locale_setblanks __P((void));
59
60/* Set the value of default_locale and make the current locale the
61   system default locale.  This should be called very early in main(). */
62void
63set_default_locale ()
64{
65#if defined (HAVE_SETLOCALE)
66  default_locale = setlocale (LC_ALL, "");
67  if (default_locale)
68    default_locale = savestring (default_locale);
69#endif /* HAVE_SETLOCALE */
70  bindtextdomain (PACKAGE, LOCALEDIR);
71  textdomain (PACKAGE);
72}
73
74/* Set default values for LC_CTYPE, LC_COLLATE, LC_MESSAGES, LC_NUMERIC and
75   LC_TIME if they are not specified in the environment, but LC_ALL is.  This
76   should be called from main() after parsing the environment. */
77void
78set_default_locale_vars ()
79{
80  char *val;
81
82#if defined (HAVE_SETLOCALE)
83
84#  if defined (LC_CTYPE)
85  val = get_string_value ("LC_CTYPE");
86  if (val == 0 && lc_all && *lc_all)
87    {
88      setlocale (LC_CTYPE, lc_all);
89      locale_setblanks ();
90    }
91#  endif
92
93#  if defined (LC_COLLATE)
94  val = get_string_value ("LC_COLLATE");
95  if (val == 0 && lc_all && *lc_all)
96    setlocale (LC_COLLATE, lc_all);
97#  endif /* LC_COLLATE */
98
99#  if defined (LC_MESSAGES)
100  val = get_string_value ("LC_MESSAGES");
101  if (val == 0 && lc_all && *lc_all)
102    setlocale (LC_MESSAGES, lc_all);
103#  endif /* LC_MESSAGES */
104
105#  if defined (LC_NUMERIC)
106  val = get_string_value ("LC_NUMERIC");
107  if (val == 0 && lc_all && *lc_all)
108    setlocale (LC_NUMERIC, lc_all);
109#  endif /* LC_NUMERIC */
110
111#  if defined (LC_TIME)
112  val = get_string_value ("LC_TIME");
113  if (val == 0 && lc_all && *lc_all)
114    setlocale (LC_TIME, lc_all);
115#  endif /* LC_TIME */
116
117#endif /* HAVE_SETLOCALE */
118
119  val = get_string_value ("TEXTDOMAIN");
120  if (val && *val)
121    {
122      FREE (default_domain);
123      default_domain = savestring (val);
124#if 0
125      /* Don't want to override the shell's textdomain as the default */
126      textdomain (default_domain);
127#endif
128    }
129
130  val = get_string_value ("TEXTDOMAINDIR");
131  if (val && *val)
132    {
133      FREE (default_dir);
134      default_dir = savestring (val);
135      if (default_domain && *default_domain)
136	bindtextdomain (default_domain, default_dir);
137    }
138}
139
140/* Set one of the locale categories (specified by VAR) to VALUE.  Returns 1
141  if successful, 0 otherwise. */
142int
143set_locale_var (var, value)
144     char *var, *value;
145{
146  int r;
147
148  if (var[0] == 'T' && var[10] == 0)		/* TEXTDOMAIN */
149    {
150      FREE (default_domain);
151      default_domain = value ? savestring (value) : (char *)NULL;
152#if 0
153      /* Don't want to override the shell's textdomain as the default */
154      textdomain (default_domain);
155#endif
156      return (1);
157    }
158  else if (var[0] == 'T')			/* TEXTDOMAINDIR */
159    {
160      FREE (default_dir);
161      default_dir = value ? savestring (value) : (char *)NULL;
162      if (default_domain && *default_domain)
163	bindtextdomain (default_domain, default_dir);
164      return (1);
165    }
166
167  /* var[0] == 'L' && var[1] == 'C' && var[2] == '_' */
168
169  else if (var[3] == 'A')			/* LC_ALL */
170    {
171      FREE (lc_all);
172      if (value)
173	lc_all = savestring (value);
174      else
175	{
176	  lc_all = (char *)xmalloc (1);
177	  lc_all[0] = '\0';
178	}
179#if defined (HAVE_SETLOCALE)
180      r = *lc_all ? (setlocale (LC_ALL, lc_all) != 0) : reset_locale_vars ();
181      locale_setblanks ();
182      return r;
183#else
184      return (1);
185#endif
186    }
187
188#if defined (HAVE_SETLOCALE)
189  else if (var[3] == 'C' && var[4] == 'T')	/* LC_CTYPE */
190    {
191#  if defined (LC_CTYPE)
192      if (lc_all == 0 || *lc_all == '\0')
193	{
194	  r = (setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE")) != 0);
195	  locale_setblanks ();
196	  return r;
197	}
198#  endif
199    }
200  else if (var[3] == 'C' && var[4] == 'O')	/* LC_COLLATE */
201    {
202#  if defined (LC_COLLATE)
203      if (lc_all == 0 || *lc_all == '\0')
204	return (setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE")) != 0);
205#  endif /* LC_COLLATE */
206    }
207  else if (var[3] == 'M' && var[4] == 'E')	/* LC_MESSAGES */
208    {
209#  if defined (LC_MESSAGES)
210      if (lc_all == 0 || *lc_all == '\0')
211	return (setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES")) != 0);
212#  endif /* LC_MESSAGES */
213    }
214  else if (var[3] == 'N' && var[4] == 'U')	/* LC_NUMERIC */
215    {
216#  if defined (LC_NUMERIC)
217      if (lc_all == 0 || *lc_all == '\0')
218	return (setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC")) != 0);
219#  endif /* LC_NUMERIC */
220    }
221  else if (var[3] == 'T' && var[4] == 'I')	/* LC_TIME */
222    {
223#  if defined (LC_TIME)
224      if (lc_all == 0 || *lc_all == '\0')
225	return (setlocale (LC_TIME, get_locale_var ("LC_TIME")) != 0);
226#  endif /* LC_TIME */
227    }
228#endif /* HAVE_SETLOCALE */
229
230
231  return (0);
232}
233
234/* Called when LANG is assigned a value.  Tracks value in `lang'.  Calls
235   reset_locale_vars() to reset any default values if LC_ALL is unset or
236   null. */
237int
238set_lang (var, value)
239     char *var, *value;
240{
241  FREE (lang);
242  if (value)
243    lang = savestring (value);
244  else
245    {
246      lang = (char *)xmalloc (1);
247      lang[0] = '\0';
248    }
249
250  return ((lc_all == 0 || *lc_all == 0) ? reset_locale_vars () : 0);
251}
252
253/* Set default values for LANG and LC_ALL.  Default values for all other
254   locale-related variables depend on these. */
255void
256set_default_lang ()
257{
258  char *v;
259
260  v = get_string_value ("LC_ALL");
261  set_locale_var ("LC_ALL", v);
262
263  v = get_string_value ("LANG");
264  set_lang ("LANG", v);
265}
266
267/* Get the value of one of the locale variables (LC_MESSAGES, LC_CTYPE).
268   The precedence is as POSIX.2 specifies:  LC_ALL has precedence over
269   the specific locale variables, and LANG, if set, is used as the default. */
270char *
271get_locale_var (var)
272     char *var;
273{
274  char *locale;
275
276  locale = lc_all;
277
278  if (locale == 0 || *locale == 0)
279    locale = get_string_value (var);
280  if (locale == 0 || *locale == 0)
281    locale = lang;
282  if (locale == 0 || *locale == 0)
283    locale = default_locale;	/* system-dependent; not really portable.  should it be "C"? */
284
285  return (locale);
286}
287
288/* Called to reset all of the locale variables to their appropriate values
289   if (and only if) LC_ALL has not been assigned a value.  DO NOT CALL THIS
290   IF LC_ALL HAS BEEN ASSIGNED A VALUE. */
291static int
292reset_locale_vars ()
293{
294#if defined (HAVE_SETLOCALE)
295  if (lang == 0 || *lang == '\0')
296    maybe_make_export_env ();		/* trust that this will change environment for setlocale */
297  if (setlocale (LC_ALL, lang ? lang : "") == 0)
298    return 0;
299
300#  if defined (LC_CTYPE)
301  setlocale (LC_CTYPE, get_locale_var ("LC_CTYPE"));
302#  endif
303#  if defined (LC_COLLATE)
304  setlocale (LC_COLLATE, get_locale_var ("LC_COLLATE"));
305#  endif
306#  if defined (LC_MESSAGES)
307  setlocale (LC_MESSAGES, get_locale_var ("LC_MESSAGES"));
308#  endif
309#  if defined (LC_NUMERIC)
310  setlocale (LC_NUMERIC, get_locale_var ("LC_NUMERIC"));
311#  endif
312#  if defined (LC_TIME)
313  setlocale (LC_TIME, get_locale_var ("LC_TIME"));
314#  endif
315
316  locale_setblanks ();
317
318#endif
319  return 1;
320}
321
322/* Translate the contents of STRING, a $"..." quoted string, according
323   to the current locale.  In the `C' or `POSIX' locale, or if gettext()
324   is not available, the passed string is returned unchanged.  The
325   length of the translated string is returned in LENP, if non-null. */
326char *
327localetrans (string, len, lenp)
328     char *string;
329     int len, *lenp;
330{
331  char *locale, *t;
332  char *translated;
333  int tlen;
334
335  /* Don't try to translate null strings. */
336  if (string == 0 || *string == 0)
337    {
338      if (lenp)
339	*lenp = 0;
340      return ((char *)NULL);
341    }
342
343  locale = get_locale_var ("LC_MESSAGES");
344
345  /* If we don't have setlocale() or the current locale is `C' or `POSIX',
346     just return the string.  If we don't have gettext(), there's no use
347     doing anything else. */
348  if (locale == 0 || locale[0] == '\0' ||
349      (locale[0] == 'C' && locale[1] == '\0') || STREQ (locale, "POSIX"))
350    {
351      t = (char *)xmalloc (len + 1);
352      strcpy (t, string);
353      if (lenp)
354	*lenp = len;
355      return (t);
356    }
357
358  /* Now try to translate it. */
359  if (default_domain && *default_domain)
360    translated = dgettext (default_domain, string);
361  else
362    translated = string;
363
364  if (translated == string)	/* gettext returns its argument if untranslatable */
365    {
366      t = (char *)xmalloc (len + 1);
367      strcpy (t, string);
368      if (lenp)
369	*lenp = len;
370    }
371  else
372    {
373      tlen = strlen (translated);
374      t = (char *)xmalloc (tlen + 1);
375      strcpy (t, translated);
376      if (lenp)
377	*lenp = tlen;
378    }
379  return (t);
380}
381
382/* Change a bash string into a string suitable for inclusion in a `po' file.
383   This backslash-escapes `"' and `\' and changes newlines into \\\n"\n". */
384char *
385mk_msgstr (string, foundnlp)
386     char *string;
387     int *foundnlp;
388{
389  register int c, len;
390  char *result, *r, *s;
391
392  for (len = 0, s = string; s && *s; s++)
393    {
394      len++;
395      if (*s == '"' || *s == '\\')
396	len++;
397      else if (*s == '\n')
398	len += 5;
399    }
400
401  r = result = (char *)xmalloc (len + 3);
402  *r++ = '"';
403
404  for (s = string; s && (c = *s); s++)
405    {
406      if (c == '\n')	/* <NL> -> \n"<NL>" */
407	{
408	  *r++ = '\\';
409	  *r++ = 'n';
410	  *r++ = '"';
411	  *r++ = '\n';
412	  *r++ = '"';
413	  if (foundnlp)
414	    *foundnlp = 1;
415	  continue;
416	}
417      if (c == '"' || c == '\\')
418	*r++ = '\\';
419      *r++ = c;
420    }
421
422  *r++ = '"';
423  *r++ = '\0';
424
425  return result;
426}
427
428/* $"..." -- Translate the portion of STRING between START and END
429   according to current locale using gettext (if available) and return
430   the result.  The caller will take care of leaving the quotes intact.
431   The string will be left without the leading `$' by the caller.
432   If translation is performed, the translated string will be double-quoted
433   by the caller.  The length of the translated string is returned in LENP,
434   if non-null. */
435char *
436localeexpand (string, start, end, lineno, lenp)
437     char *string;
438     int start, end, lineno, *lenp;
439{
440  int len, tlen, foundnl;
441  char *temp, *t, *t2;
442
443  temp = (char *)xmalloc (end - start + 1);
444  for (tlen = 0, len = start; len < end; )
445    temp[tlen++] = string[len++];
446  temp[tlen] = '\0';
447
448  /* If we're just dumping translatable strings, don't do anything with the
449     string itself, but if we're dumping in `po' file format, convert it into
450     a form more palatable to gettext(3) and friends by quoting `"' and `\'
451     with backslashes and converting <NL> into `\n"<NL>"'.  If we find a
452     newline in TEMP, we first output a `msgid ""' line and then the
453     translated string; otherwise we output the `msgid' and translated
454     string all on one line. */
455  if (dump_translatable_strings)
456    {
457      if (dump_po_strings)
458	{
459	  foundnl = 0;
460	  t = mk_msgstr (temp, &foundnl);
461	  t2 = foundnl ? "\"\"\n" : "";
462
463	  printf ("#: %s:%d\nmsgid %s%s\nmsgstr \"\"\n",
464			yy_input_name (), lineno, t2, t);
465	  free (t);
466	}
467      else
468	printf ("\"%s\"\n", temp);
469
470      if (lenp)
471	*lenp = tlen;
472      return (temp);
473    }
474  else if (*temp)
475    {
476      t = localetrans (temp, tlen, &len);
477      free (temp);
478      if (lenp)
479	*lenp = len;
480      return (t);
481    }
482  else
483    {
484      if (lenp)
485	*lenp = 0;
486      return (temp);
487    }
488}
489
490/* Set every character in the <blank> character class to be a shell break
491   character for the lexical analyzer when the locale changes. */
492static void
493locale_setblanks ()
494{
495  int x;
496
497  for (x = 0; x < sh_syntabsiz; x++)
498    {
499      if (isblank (x))
500	sh_syntaxtab[x] |= CSHBRK|CBLANK;
501      else if (member (x, shell_break_chars))
502	{
503	  sh_syntaxtab[x] |= CSHBRK;
504	  sh_syntaxtab[x] &= ~CBLANK;
505	}
506      else
507	sh_syntaxtab[x] &= ~(CSHBRK|CBLANK);
508    }
509}
510