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