1/* nls.c -- skeletal internationalization code. */
2
3/* Copyright (C) 1996 Free Software Foundation, Inc.
4
5 This file is part of the GNU Readline Library, a library for
6 reading lines of text with interactive input and history editing.
7
8 The GNU Readline Library is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2, or
11 (at your option) any later version.
12
13 The GNU Readline Library is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 The GNU General Public License is often shipped with GNU software, and
19 is generally kept in a file called COPYING or LICENSE. If you do not
20 have a copy of the license, write to the Free Software Foundation,
21 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22#define READLINE_LIBRARY
23
24#if defined (HAVE_CONFIG_H)
25# include "config_readline.h"
26#endif
27
28#include <sys/types.h>
29
30#include <stdio.h>
31
32#if defined (HAVE_UNISTD_H)
33# include <unistd.h>
34#endif /* HAVE_UNISTD_H */
35
36#if defined (HAVE_STDLIB_H)
37# include <stdlib.h>
38#else
39# include "ansi_stdlib.h"
40#endif /* HAVE_STDLIB_H */
41
42#if defined (HAVE_LOCALE_H)
43# include <locale.h>
44#endif
45
46#include <ctype.h>
47
48#include "rldefs.h"
49#include "readline.h"
50#include "rlshell.h"
51#include "rlprivate.h"
52
53#if !defined (HAVE_SETLOCALE)
54/* A list of legal values for the LANG or LC_CTYPE environment variables.
55 If a locale name in this list is the value for the LC_ALL, LC_CTYPE,
56 or LANG environment variable (using the first of those with a value),
57 readline eight-bit mode is enabled. */
58static char *legal_lang_values[] =
59{
60 "iso88591",
61 "iso88592",
62 "iso88593",
63 "iso88594",
64 "iso88595",
65 "iso88596",
66 "iso88597",
67 "iso88598",
68 "iso88599",
69 "iso885910",
70 "koi8r",
71 0
72};
73
74static char *normalize_codeset PARAMS((char *));
75static char *find_codeset PARAMS((char *, size_t *));
76#endif /* !HAVE_SETLOCALE */
77
78static char *_rl_get_locale_var PARAMS((const char *));
79
80static char *
81_rl_get_locale_var (v)
82 const char *v;
83{
84 char *lspec;
85
86 lspec = sh_get_env_value ("LC_ALL");
87 if (lspec == 0 || *lspec == 0)
88 lspec = sh_get_env_value (v);
89 if (lspec == 0 || *lspec == 0)
90 lspec = sh_get_env_value ("LANG");
91
92 return lspec;
93}
94
95/* Check for LC_ALL, LC_CTYPE, and LANG and use the first with a value
96 to decide the defaults for 8-bit character input and output. Returns
97 1 if we set eight-bit mode. */
98int
99_rl_init_eightbit ()
100{
101/* If we have setlocale(3), just check the current LC_CTYPE category
102 value, and go into eight-bit mode if it's not C or POSIX. */
103#if defined (HAVE_SETLOCALE)
104 const char *lspec;
105 char *t;
106
107 /* Set the LC_CTYPE locale category from environment variables. */
108 lspec = _rl_get_locale_var ("LC_CTYPE");
109 /* Since _rl_get_locale_var queries the right environment variables,
110 we query the current locale settings with setlocale(), and, if
111 that doesn't return anything, we set lspec to the empty string to
112 force the subsequent call to setlocale() to define the `native'
113 environment. */
114 if (lspec == 0 || *lspec == 0)
115 lspec = setlocale (LC_CTYPE, (char *)NULL);
116 if (lspec == 0)
117 lspec = "";
118 t = setlocale (LC_CTYPE, lspec);
119
120 if (t && *t && (t[0] != 'C' || t[1]) && (STREQ (t, "POSIX") == 0))
121 {
122 _rl_meta_flag = 1;
123 _rl_convert_meta_chars_to_ascii = 0;
124 _rl_output_meta_chars = 1;
125 return (1);
126 }
127 else
128 return (0);
129
130#else /* !HAVE_SETLOCALE */
131 const char *lspec;
132 char *t;
133 int i;
134
135 /* We don't have setlocale. Finesse it. Check the environment for the
136 appropriate variables and set eight-bit mode if they have the right
137 values. */
138 lspec = _rl_get_locale_var ("LC_CTYPE");
139
140 if (lspec == 0 || (t = normalize_codeset (lspec)) == 0)
141 return (0);
142 for (i = 0; t && legal_lang_values[i]; i++)
143 if (STREQ (t, legal_lang_values[i]))
144 {
145 _rl_meta_flag = 1;
146 _rl_convert_meta_chars_to_ascii = 0;
147 _rl_output_meta_chars = 1;
148 break;
149 }
150 free (t);
151 return (legal_lang_values[i] ? 1 : 0);
152
153#endif /* !HAVE_SETLOCALE */
154}
155
156#if !defined (HAVE_SETLOCALE)
157static char *
158normalize_codeset (codeset)
159 char *codeset;
160{
161 size_t namelen, i;
162 int len, all_digits;
163 char *wp, *retval;
164
165 codeset = find_codeset (codeset, &namelen);
166
167 if (codeset == 0)
168 return (codeset);
169
170 all_digits = 1;
171 for (len = 0, i = 0; i < namelen; i++)
172 {
173 if (ISALNUM ((unsigned char)codeset[i]))
174 {
175 len++;
176 all_digits &= _rl_digit_p (codeset[i]);
177 }
178 }
179
180 retval = (char *)malloc ((all_digits ? 3 : 0) + len + 1);
181 if (retval == 0)
182 return ((char *)0);
183
184 wp = retval;
185 /* Add `iso' to beginning of an all-digit codeset */
186 if (all_digits)
187 {
188 *wp++ = 'i';
189 *wp++ = 's';
190 *wp++ = 'o';
191 }
192
193 for (i = 0; i < namelen; i++)
194 if (ISALPHA ((unsigned char)codeset[i]))
195 *wp++ = _rl_to_lower (codeset[i]);
196 else if (_rl_digit_p (codeset[i]))
197 *wp++ = codeset[i];
198 *wp = '\0';
199
200 return retval;
201}
202
203/* Isolate codeset portion of locale specification. */
204static char *
205find_codeset (name, lenp)
206 char *name;
207 size_t *lenp;
208{
209 char *cp, *language, *result;
210
211 cp = language = name;
212 result = (char *)0;
213
214 while (*cp && *cp != '_' && *cp != '@' && *cp != '+' && *cp != ',')
215 cp++;
216
217 /* This does not make sense: language has to be specified. As
218 an exception we allow the variable to contain only the codeset
219 name. Perhaps there are funny codeset names. */
220 if (language == cp)
221 {
222 *lenp = strlen (language);
223 result = language;
224 }
225 else
226 {
227 /* Next is the territory. */
228 if (*cp == '_')
229 do
230 ++cp;
231 while (*cp && *cp != '.' && *cp != '@' && *cp != '+' && *cp != ',' && *cp != '_');
232
233 /* Now, finally, is the codeset. */
234 result = cp;
235 if (*cp == '.')
236 do
237 ++cp;
238 while (*cp && *cp != '@');
239
240 if (cp - result > 2)
241 {
242 result++;
243 *lenp = cp - result;
244 }
245 else
246 {
247 *lenp = strlen (language);
248 result = language;
249 }
250 }
251
252 return result;
253}
254#endif /* !HAVE_SETLOCALE */
255