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