Commit | Line | Data |
---|---|---|
d60d9f65 SS |
1 | /* nls.c -- skeletal internationalization code. */ |
2 | ||
cb41b9e7 | 3 | /* Copyright (C) 1996-2017 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 | ||
775e241e TT |
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 | |
775e241e TT |
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", | |
cb41b9e7 | 77 | "utf8", |
d60d9f65 SS |
78 | 0 |
79 | }; | |
80 | ||
9255ee31 | 81 | static char *normalize_codeset PARAMS((char *)); |
5836a818 | 82 | #endif /* !HAVE_SETLOCALE */ |
4a11f206 | 83 | |
775e241e TT |
84 | static char *find_codeset PARAMS((char *, size_t *)); |
85 | ||
5bdf8622 DJ |
86 | static char *_rl_get_locale_var PARAMS((const char *)); |
87 | ||
88 | static char * | |
cb41b9e7 | 89 | _rl_get_locale_var (const char *v) |
5bdf8622 DJ |
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 | } | |
775e241e TT |
101 | |
102 | static int | |
cb41b9e7 | 103 | utf8locale (char *lspec) |
775e241e TT |
104 | { |
105 | char *cp; | |
106 | size_t len; | |
107 | ||
108 | #if HAVE_LANGINFO_CODESET | |
109 | cp = nl_langinfo (CODESET); | |
110 | return (STREQ (cp, "UTF-8") || STREQ (cp, "utf8")); | |
111 | #else | |
112 | cp = find_codeset (lspec, &len); | |
113 | ||
114 | if (cp == 0 || len < 4 || len > 5) | |
115 | return 0; | |
116 | return ((len == 5) ? strncmp (cp, "UTF-8", len) == 0 : strncmp (cp, "utf8", 4) == 0); | |
117 | #endif | |
118 | } | |
119 | ||
cb41b9e7 TT |
120 | /* Query the right environment variables and call setlocale() to initialize |
121 | the C library locale settings. */ | |
122 | char * | |
123 | _rl_init_locale (void) | |
d60d9f65 | 124 | { |
cb41b9e7 | 125 | char *ret, *lspec; |
d60d9f65 SS |
126 | |
127 | /* Set the LC_CTYPE locale category from environment variables. */ | |
5bdf8622 DJ |
128 | lspec = _rl_get_locale_var ("LC_CTYPE"); |
129 | /* Since _rl_get_locale_var queries the right environment variables, | |
130 | we query the current locale settings with setlocale(), and, if | |
131 | that doesn't return anything, we set lspec to the empty string to | |
132 | force the subsequent call to setlocale() to define the `native' | |
133 | environment. */ | |
134 | if (lspec == 0 || *lspec == 0) | |
135 | lspec = setlocale (LC_CTYPE, (char *)NULL); | |
136 | if (lspec == 0) | |
137 | lspec = ""; | |
cb41b9e7 TT |
138 | ret = setlocale (LC_CTYPE, lspec); /* ok, since it does not change locale */ |
139 | ||
140 | _rl_utf8locale = (ret && *ret) ? utf8locale (ret) : 0; | |
141 | ||
142 | return ret; | |
143 | } | |
144 | ||
145 | /* Check for LC_ALL, LC_CTYPE, and LANG and use the first with a value | |
146 | to decide the defaults for 8-bit character input and output. Returns | |
147 | 1 if we set eight-bit mode. */ | |
148 | int | |
149 | _rl_init_eightbit (void) | |
150 | { | |
151 | /* If we have setlocale(3), just check the current LC_CTYPE category | |
152 | value, and go into eight-bit mode if it's not C or POSIX. */ | |
153 | #if defined (HAVE_SETLOCALE) | |
154 | char *lspec, *t; | |
5bdf8622 | 155 | |
cb41b9e7 | 156 | t = _rl_init_locale (); /* returns static pointer */ |
775e241e | 157 | |
d60d9f65 SS |
158 | if (t && *t && (t[0] != 'C' || t[1]) && (STREQ (t, "POSIX") == 0)) |
159 | { | |
160 | _rl_meta_flag = 1; | |
161 | _rl_convert_meta_chars_to_ascii = 0; | |
162 | _rl_output_meta_chars = 1; | |
163 | return (1); | |
164 | } | |
165 | else | |
166 | return (0); | |
167 | ||
168 | #else /* !HAVE_SETLOCALE */ | |
169 | char *lspec, *t; | |
170 | int i; | |
171 | ||
172 | /* We don't have setlocale. Finesse it. Check the environment for the | |
173 | appropriate variables and set eight-bit mode if they have the right | |
174 | values. */ | |
5bdf8622 DJ |
175 | lspec = _rl_get_locale_var ("LC_CTYPE"); |
176 | ||
d60d9f65 SS |
177 | if (lspec == 0 || (t = normalize_codeset (lspec)) == 0) |
178 | return (0); | |
179 | for (i = 0; t && legal_lang_values[i]; i++) | |
180 | if (STREQ (t, legal_lang_values[i])) | |
181 | { | |
182 | _rl_meta_flag = 1; | |
183 | _rl_convert_meta_chars_to_ascii = 0; | |
184 | _rl_output_meta_chars = 1; | |
185 | break; | |
186 | } | |
cb41b9e7 TT |
187 | |
188 | _rl_utf8locale = *t ? STREQ (t, "utf8") : 0; | |
189 | ||
cc88a640 | 190 | xfree (t); |
d60d9f65 | 191 | return (legal_lang_values[i] ? 1 : 0); |
d60d9f65 SS |
192 | #endif /* !HAVE_SETLOCALE */ |
193 | } | |
194 | ||
195 | #if !defined (HAVE_SETLOCALE) | |
196 | static char * | |
cb41b9e7 | 197 | normalize_codeset (char *codeset) |
d60d9f65 SS |
198 | { |
199 | size_t namelen, i; | |
200 | int len, all_digits; | |
201 | char *wp, *retval; | |
202 | ||
203 | codeset = find_codeset (codeset, &namelen); | |
204 | ||
205 | if (codeset == 0) | |
206 | return (codeset); | |
207 | ||
208 | all_digits = 1; | |
209 | for (len = 0, i = 0; i < namelen; i++) | |
210 | { | |
9255ee31 | 211 | if (ISALNUM ((unsigned char)codeset[i])) |
d60d9f65 SS |
212 | { |
213 | len++; | |
9255ee31 | 214 | all_digits &= _rl_digit_p (codeset[i]); |
d60d9f65 SS |
215 | } |
216 | } | |
217 | ||
218 | retval = (char *)malloc ((all_digits ? 3 : 0) + len + 1); | |
219 | if (retval == 0) | |
220 | return ((char *)0); | |
221 | ||
222 | wp = retval; | |
223 | /* Add `iso' to beginning of an all-digit codeset */ | |
224 | if (all_digits) | |
225 | { | |
226 | *wp++ = 'i'; | |
227 | *wp++ = 's'; | |
228 | *wp++ = 'o'; | |
229 | } | |
230 | ||
231 | for (i = 0; i < namelen; i++) | |
9255ee31 EZ |
232 | if (ISALPHA ((unsigned char)codeset[i])) |
233 | *wp++ = _rl_to_lower (codeset[i]); | |
234 | else if (_rl_digit_p (codeset[i])) | |
d60d9f65 SS |
235 | *wp++ = codeset[i]; |
236 | *wp = '\0'; | |
237 | ||
238 | return retval; | |
239 | } | |
775e241e | 240 | #endif /* !HAVE_SETLOCALE */ |
d60d9f65 SS |
241 | |
242 | /* Isolate codeset portion of locale specification. */ | |
243 | static char * | |
cb41b9e7 | 244 | find_codeset (char *name, size_t *lenp) |
d60d9f65 SS |
245 | { |
246 | char *cp, *language, *result; | |
247 | ||
248 | cp = language = name; | |
249 | result = (char *)0; | |
250 | ||
251 | while (*cp && *cp != '_' && *cp != '@' && *cp != '+' && *cp != ',') | |
252 | cp++; | |
253 | ||
254 | /* This does not make sense: language has to be specified. As | |
255 | an exception we allow the variable to contain only the codeset | |
256 | name. Perhaps there are funny codeset names. */ | |
257 | if (language == cp) | |
258 | { | |
259 | *lenp = strlen (language); | |
260 | result = language; | |
261 | } | |
262 | else | |
263 | { | |
264 | /* Next is the territory. */ | |
265 | if (*cp == '_') | |
266 | do | |
267 | ++cp; | |
268 | while (*cp && *cp != '.' && *cp != '@' && *cp != '+' && *cp != ',' && *cp != '_'); | |
269 | ||
270 | /* Now, finally, is the codeset. */ | |
271 | result = cp; | |
272 | if (*cp == '.') | |
273 | do | |
274 | ++cp; | |
275 | while (*cp && *cp != '@'); | |
276 | ||
277 | if (cp - result > 2) | |
278 | { | |
279 | result++; | |
280 | *lenp = cp - result; | |
281 | } | |
282 | else | |
283 | { | |
284 | *lenp = strlen (language); | |
285 | result = language; | |
286 | } | |
287 | } | |
288 | ||
289 | return result; | |
290 | } |