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