1 /* $XTermId: parser.c,v 1.25 2021/01/19 23:54:28 tom Exp $ */
2
3 /*
4 Copyright 2011-2018,2021 by Thomas E. Dickey
5 Copyright (c) 2001 by Juliusz Chroboczek
6
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be included in
15 all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 THE SOFTWARE.
24 */
25
26 #include <luit.h>
27
28 #include <parser.h>
29 #include <sys.h>
30 #include <trace.h>
31
32 #ifdef HAVE_LANGINFO_CODESET
33 #include <langinfo.h>
34 #endif
35
36 static char keyword[MAX_KEYWORD_LENGTH];
37
38 static void
skipEndOfLine(FILE * f,int c)39 skipEndOfLine(FILE *f, int c)
40 {
41 if (c == 0)
42 c = getc(f);
43
44 for (;;)
45 if (c <= 0 || c == '\n')
46 return;
47 else
48 c = getc(f);
49 }
50
51 static int
drainWhitespace(FILE * f,int c)52 drainWhitespace(FILE *f, int c)
53 {
54 if (c == 0)
55 c = getc(f);
56
57 while (c > 0 && (c == '#' || c == ' ' || c == '\t')) {
58 if (c == '#') {
59 skipEndOfLine(f, c);
60 c = '\n';
61 break;
62 }
63 c = getc(f);
64 }
65
66 return (c > 0) ? c : 0;
67 }
68
69 static int
getString(FILE * f,int string_end,int * c_return)70 getString(FILE *f, int string_end, int *c_return)
71 {
72 int i = 0;
73 int c;
74
75 c = getc(f);
76 while (c > 0) {
77 if (c == string_end)
78 break;
79 if (c == '\\') {
80 c = getc(f);
81 if (c == '\n')
82 continue;
83 }
84 keyword[i++] = (char) c;
85 if (i >= MAX_KEYWORD_LENGTH)
86 return TOK_ERROR;
87 c = getc(f);
88 }
89
90 if (c <= 0)
91 return TOK_ERROR;
92 keyword[i] = '\0';
93 *c_return = c;
94 return TOK_KEYWORD;
95 }
96
97 static int
getToken(FILE * f,int c,int parse_assignments,int * c_return)98 getToken(FILE *f, int c, int parse_assignments, int *c_return)
99 {
100 int i;
101 c = drainWhitespace(f, c);
102
103 if (c < 0)
104 return TOK_EOF;
105 if (c == '\n') {
106 *c_return = 0;
107 return TOK_EOL;
108 }
109
110 if (parse_assignments && c == '=') {
111 *c_return = 0;
112 return TOK_EQUALS;
113 }
114
115 if (c == '\'' || c == '"')
116 return getString(f, c, c_return);
117
118 i = 0;
119 while (c > 0 && c != ' ' && c != '\t' && c != '\n') {
120 if (c == '\\') {
121 c = getc(f);
122 if (c == '\n')
123 continue;
124 }
125 keyword[i++] = (char) c;
126 if (i >= MAX_KEYWORD_LENGTH)
127 return TOK_ERROR;
128 c = getc(f);
129 if (parse_assignments && c == '=')
130 break;
131 }
132
133 *c_return = c < 0 ? 0 : c;
134 keyword[i] = '\0';
135 return TOK_KEYWORD;
136 }
137
138 /* Can parse both the old and new formats for locale.alias */
139 static int
parseTwoTokenLine(FILE * f,char * first,char * second)140 parseTwoTokenLine(FILE *f, char *first, char *second)
141 {
142 int rc = 0;
143 int c = 0;
144 int tok;
145 size_t len;
146
147 do {
148 tok = getToken(f, c, 0, &c);
149 if (tok == TOK_EOF)
150 return -1;
151 } while (tok == TOK_EOL);
152
153 if (tok != TOK_KEYWORD) {
154 rc = -2;
155 } else {
156 if ((len = strlen(keyword)) != 0 && keyword[len - 1] == ':')
157 keyword[len - 1] = '\0';
158 strcpy(first, keyword);
159
160 tok = getToken(f, c, 0, &c);
161 if (tok != TOK_KEYWORD) {
162 rc = -2;
163 } else {
164 strcpy(second, keyword);
165
166 tok = getToken(f, c, 0, &c);
167 if (tok != TOK_EOL) {
168 rc = -2;
169 }
170 }
171 }
172
173 return rc;
174 }
175
176 /*
177 * Check if the result from locale.alias has the encoding specified.
178 */
179 static int
has_encoding(const char * locale)180 has_encoding(const char *locale)
181 {
182 int result = 0;
183
184 if (locale != 0 && *locale != 0) {
185 char *dot = strchr(locale, '.');
186 result = (dot != 0
187 && dot != locale
188 && dot[1] != 0
189 && strchr(dot + 1, '.') == 0);
190 }
191 return result;
192 }
193
194 char *
resolveLocale(const char * locale)195 resolveLocale(const char *locale)
196 {
197 FILE *f;
198 char first[MAX_KEYWORD_LENGTH];
199 char second[MAX_KEYWORD_LENGTH];
200 char *resolved = NULL;
201 int rc;
202 int found = 0;
203
204 TRACE(("resolveLocale(%s)\n", NonNull(locale)));
205 if (locale == NULL)
206 ExitFailure();
207
208 TRACE(("...looking in %s\n", NonNull(locale_alias)));
209 if (locale_alias == NULL)
210 ExitFailure();
211
212 f = fopen(locale_alias, "r");
213
214 if (f != NULL) {
215 do {
216 rc = parseTwoTokenLine(f, first, second);
217 if (rc < -1)
218 break;
219 if (!strcmp(first, locale)) {
220 resolved = strmalloc(second);
221 found = 1;
222 break;
223 }
224 } while (rc >= 0);
225
226 if (!found) {
227 if (resolved == NULL) {
228 TRACE(("...not found in %s\n", NonNull(locale_alias)));
229 resolved = strmalloc(locale);
230 }
231 }
232
233 fclose(f);
234 }
235
236 /*
237 * If we did not find the data in the locale.alias file (or as happens with
238 * some, the right column does not appear to specify a valid locale), see
239 * if we can get a better result from the system's locale tables.
240 */
241 if (!found || !has_encoding(resolved)) {
242 #ifdef HAVE_LANGINFO_CODESET
243 char *improved;
244 if (!ignore_locale
245 && strcmp(locale, "C")
246 && strcmp(locale, "POSIX")
247 && strcmp(locale, "US-ASCII")
248 && (improved = nl_langinfo(CODESET)) != 0) {
249 TRACE(("...nl_langinfo ->%s\n", NonNull(improved)));
250 free(resolved);
251 resolved = strmalloc(improved);
252 } else
253 #endif
254 if (f == 0 && (fopen(locale_alias, "r") == 0)) {
255 perror(locale_alias);
256 }
257 }
258
259 TRACE(("...resolveLocale ->%s\n", NonNull(resolved)));
260 return resolved;
261 }
262