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