1*2b949d04SAndroid Build Coastguard Worker /*
2*2b949d04SAndroid Build Coastguard Worker * Copyright © 2013 Ran Benita <[email protected]>
3*2b949d04SAndroid Build Coastguard Worker *
4*2b949d04SAndroid Build Coastguard Worker * Permission is hereby granted, free of charge, to any person obtaining a
5*2b949d04SAndroid Build Coastguard Worker * copy of this software and associated documentation files (the "Software"),
6*2b949d04SAndroid Build Coastguard Worker * to deal in the Software without restriction, including without limitation
7*2b949d04SAndroid Build Coastguard Worker * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8*2b949d04SAndroid Build Coastguard Worker * and/or sell copies of the Software, and to permit persons to whom the
9*2b949d04SAndroid Build Coastguard Worker * Software is furnished to do so, subject to the following conditions:
10*2b949d04SAndroid Build Coastguard Worker *
11*2b949d04SAndroid Build Coastguard Worker * The above copyright notice and this permission notice (including the next
12*2b949d04SAndroid Build Coastguard Worker * paragraph) shall be included in all copies or substantial portions of the
13*2b949d04SAndroid Build Coastguard Worker * Software.
14*2b949d04SAndroid Build Coastguard Worker *
15*2b949d04SAndroid Build Coastguard Worker * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16*2b949d04SAndroid Build Coastguard Worker * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17*2b949d04SAndroid Build Coastguard Worker * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18*2b949d04SAndroid Build Coastguard Worker * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19*2b949d04SAndroid Build Coastguard Worker * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20*2b949d04SAndroid Build Coastguard Worker * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21*2b949d04SAndroid Build Coastguard Worker * DEALINGS IN THE SOFTWARE.
22*2b949d04SAndroid Build Coastguard Worker */
23*2b949d04SAndroid Build Coastguard Worker
24*2b949d04SAndroid Build Coastguard Worker /******************************************************************
25*2b949d04SAndroid Build Coastguard Worker
26*2b949d04SAndroid Build Coastguard Worker Copyright 1992 by Oki Technosystems Laboratory, Inc.
27*2b949d04SAndroid Build Coastguard Worker Copyright 1992 by Fuji Xerox Co., Ltd.
28*2b949d04SAndroid Build Coastguard Worker
29*2b949d04SAndroid Build Coastguard Worker Permission to use, copy, modify, distribute, and sell this software
30*2b949d04SAndroid Build Coastguard Worker and its documentation for any purpose is hereby granted without fee,
31*2b949d04SAndroid Build Coastguard Worker provided that the above copyright notice appear in all copies and
32*2b949d04SAndroid Build Coastguard Worker that both that copyright notice and this permission notice appear
33*2b949d04SAndroid Build Coastguard Worker in supporting documentation, and that the name of Oki Technosystems
34*2b949d04SAndroid Build Coastguard Worker Laboratory and Fuji Xerox not be used in advertising or publicity
35*2b949d04SAndroid Build Coastguard Worker pertaining to distribution of the software without specific, written
36*2b949d04SAndroid Build Coastguard Worker prior permission.
37*2b949d04SAndroid Build Coastguard Worker Oki Technosystems Laboratory and Fuji Xerox make no representations
38*2b949d04SAndroid Build Coastguard Worker about the suitability of this software for any purpose. It is provided
39*2b949d04SAndroid Build Coastguard Worker "as is" without express or implied warranty.
40*2b949d04SAndroid Build Coastguard Worker
41*2b949d04SAndroid Build Coastguard Worker OKI TECHNOSYSTEMS LABORATORY AND FUJI XEROX DISCLAIM ALL WARRANTIES
42*2b949d04SAndroid Build Coastguard Worker WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
43*2b949d04SAndroid Build Coastguard Worker MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL OKI TECHNOSYSTEMS
44*2b949d04SAndroid Build Coastguard Worker LABORATORY AND FUJI XEROX BE LIABLE FOR ANY SPECIAL, INDIRECT OR
45*2b949d04SAndroid Build Coastguard Worker CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
46*2b949d04SAndroid Build Coastguard Worker OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
47*2b949d04SAndroid Build Coastguard Worker OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
48*2b949d04SAndroid Build Coastguard Worker OR PERFORMANCE OF THIS SOFTWARE.
49*2b949d04SAndroid Build Coastguard Worker
50*2b949d04SAndroid Build Coastguard Worker Author: Yasuhiro Kawai Oki Technosystems Laboratory
51*2b949d04SAndroid Build Coastguard Worker Author: Kazunori Nishihara Fuji Xerox
52*2b949d04SAndroid Build Coastguard Worker
53*2b949d04SAndroid Build Coastguard Worker ******************************************************************/
54*2b949d04SAndroid Build Coastguard Worker
55*2b949d04SAndroid Build Coastguard Worker #include "config.h"
56*2b949d04SAndroid Build Coastguard Worker
57*2b949d04SAndroid Build Coastguard Worker #include <errno.h>
58*2b949d04SAndroid Build Coastguard Worker
59*2b949d04SAndroid Build Coastguard Worker #include "utils.h"
60*2b949d04SAndroid Build Coastguard Worker #include "scanner-utils.h"
61*2b949d04SAndroid Build Coastguard Worker #include "table.h"
62*2b949d04SAndroid Build Coastguard Worker #include "paths.h"
63*2b949d04SAndroid Build Coastguard Worker #include "utf8.h"
64*2b949d04SAndroid Build Coastguard Worker #include "parser.h"
65*2b949d04SAndroid Build Coastguard Worker
66*2b949d04SAndroid Build Coastguard Worker #define MAX_LHS_LEN 10
67*2b949d04SAndroid Build Coastguard Worker #define MAX_INCLUDE_DEPTH 5
68*2b949d04SAndroid Build Coastguard Worker
69*2b949d04SAndroid Build Coastguard Worker /*
70*2b949d04SAndroid Build Coastguard Worker * Grammar adapted from libX11/modules/im/ximcp/imLcPrs.c.
71*2b949d04SAndroid Build Coastguard Worker * See also the XCompose(5) manpage.
72*2b949d04SAndroid Build Coastguard Worker *
73*2b949d04SAndroid Build Coastguard Worker * FILE ::= { [PRODUCTION] [COMMENT] "\n" | INCLUDE }
74*2b949d04SAndroid Build Coastguard Worker * INCLUDE ::= "include" '"' INCLUDE_STRING '"'
75*2b949d04SAndroid Build Coastguard Worker * PRODUCTION ::= LHS ":" RHS [ COMMENT ]
76*2b949d04SAndroid Build Coastguard Worker * COMMENT ::= "#" {<any character except null or newline>}
77*2b949d04SAndroid Build Coastguard Worker * LHS ::= EVENT { EVENT }
78*2b949d04SAndroid Build Coastguard Worker * EVENT ::= [MODIFIER_LIST] "<" keysym ">"
79*2b949d04SAndroid Build Coastguard Worker * MODIFIER_LIST ::= (["!"] {MODIFIER} ) | "None"
80*2b949d04SAndroid Build Coastguard Worker * MODIFIER ::= ["~"] MODIFIER_NAME
81*2b949d04SAndroid Build Coastguard Worker * MODIFIER_NAME ::= ("Ctrl"|"Lock"|"Caps"|"Shift"|"Alt"|"Meta")
82*2b949d04SAndroid Build Coastguard Worker * RHS ::= ( STRING | keysym | STRING keysym )
83*2b949d04SAndroid Build Coastguard Worker * STRING ::= '"' { CHAR } '"'
84*2b949d04SAndroid Build Coastguard Worker * CHAR ::= GRAPHIC_CHAR | ESCAPED_CHAR
85*2b949d04SAndroid Build Coastguard Worker * GRAPHIC_CHAR ::= locale (codeset) dependent code
86*2b949d04SAndroid Build Coastguard Worker * ESCAPED_CHAR ::= ('\\' | '\"' | OCTAL | HEX )
87*2b949d04SAndroid Build Coastguard Worker * OCTAL ::= '\' OCTAL_CHAR [OCTAL_CHAR [OCTAL_CHAR]]
88*2b949d04SAndroid Build Coastguard Worker * OCTAL_CHAR ::= (0|1|2|3|4|5|6|7)
89*2b949d04SAndroid Build Coastguard Worker * HEX ::= '\' (x|X) HEX_CHAR [HEX_CHAR]]
90*2b949d04SAndroid Build Coastguard Worker * HEX_CHAR ::= (0|1|2|3|4|5|6|7|8|9|A|B|C|D|E|F|a|b|c|d|e|f)
91*2b949d04SAndroid Build Coastguard Worker *
92*2b949d04SAndroid Build Coastguard Worker * INCLUDE_STRING is a filesystem path, with the following %-expansions:
93*2b949d04SAndroid Build Coastguard Worker * %% - '%'.
94*2b949d04SAndroid Build Coastguard Worker * %H - The user's home directory (the $HOME environment variable).
95*2b949d04SAndroid Build Coastguard Worker * %L - The name of the locale specific Compose file (e.g.,
96*2b949d04SAndroid Build Coastguard Worker * "/usr/share/X11/locale/<localename>/Compose").
97*2b949d04SAndroid Build Coastguard Worker * %S - The name of the system directory for Compose files (e.g.,
98*2b949d04SAndroid Build Coastguard Worker * "/usr/share/X11/locale").
99*2b949d04SAndroid Build Coastguard Worker */
100*2b949d04SAndroid Build Coastguard Worker
101*2b949d04SAndroid Build Coastguard Worker enum rules_token {
102*2b949d04SAndroid Build Coastguard Worker TOK_END_OF_FILE = 0,
103*2b949d04SAndroid Build Coastguard Worker TOK_END_OF_LINE,
104*2b949d04SAndroid Build Coastguard Worker TOK_INCLUDE,
105*2b949d04SAndroid Build Coastguard Worker TOK_INCLUDE_STRING,
106*2b949d04SAndroid Build Coastguard Worker TOK_LHS_KEYSYM,
107*2b949d04SAndroid Build Coastguard Worker TOK_COLON,
108*2b949d04SAndroid Build Coastguard Worker TOK_BANG,
109*2b949d04SAndroid Build Coastguard Worker TOK_TILDE,
110*2b949d04SAndroid Build Coastguard Worker TOK_STRING,
111*2b949d04SAndroid Build Coastguard Worker TOK_IDENT,
112*2b949d04SAndroid Build Coastguard Worker TOK_ERROR
113*2b949d04SAndroid Build Coastguard Worker };
114*2b949d04SAndroid Build Coastguard Worker
115*2b949d04SAndroid Build Coastguard Worker /* Values returned with some tokens, like yylval. */
116*2b949d04SAndroid Build Coastguard Worker union lvalue {
117*2b949d04SAndroid Build Coastguard Worker struct {
118*2b949d04SAndroid Build Coastguard Worker /* Still \0-terminated. */
119*2b949d04SAndroid Build Coastguard Worker const char *str;
120*2b949d04SAndroid Build Coastguard Worker size_t len;
121*2b949d04SAndroid Build Coastguard Worker } string;
122*2b949d04SAndroid Build Coastguard Worker };
123*2b949d04SAndroid Build Coastguard Worker
124*2b949d04SAndroid Build Coastguard Worker static enum rules_token
lex(struct scanner * s,union lvalue * val)125*2b949d04SAndroid Build Coastguard Worker lex(struct scanner *s, union lvalue *val)
126*2b949d04SAndroid Build Coastguard Worker {
127*2b949d04SAndroid Build Coastguard Worker skip_more_whitespace_and_comments:
128*2b949d04SAndroid Build Coastguard Worker /* Skip spaces. */
129*2b949d04SAndroid Build Coastguard Worker while (is_space(peek(s)))
130*2b949d04SAndroid Build Coastguard Worker if (next(s) == '\n')
131*2b949d04SAndroid Build Coastguard Worker return TOK_END_OF_LINE;
132*2b949d04SAndroid Build Coastguard Worker
133*2b949d04SAndroid Build Coastguard Worker /* Skip comments. */
134*2b949d04SAndroid Build Coastguard Worker if (chr(s, '#')) {
135*2b949d04SAndroid Build Coastguard Worker skip_to_eol(s);
136*2b949d04SAndroid Build Coastguard Worker goto skip_more_whitespace_and_comments;
137*2b949d04SAndroid Build Coastguard Worker }
138*2b949d04SAndroid Build Coastguard Worker
139*2b949d04SAndroid Build Coastguard Worker /* See if we're done. */
140*2b949d04SAndroid Build Coastguard Worker if (eof(s)) return TOK_END_OF_FILE;
141*2b949d04SAndroid Build Coastguard Worker
142*2b949d04SAndroid Build Coastguard Worker /* New token. */
143*2b949d04SAndroid Build Coastguard Worker s->token_line = s->line;
144*2b949d04SAndroid Build Coastguard Worker s->token_column = s->column;
145*2b949d04SAndroid Build Coastguard Worker s->buf_pos = 0;
146*2b949d04SAndroid Build Coastguard Worker
147*2b949d04SAndroid Build Coastguard Worker /* LHS Keysym. */
148*2b949d04SAndroid Build Coastguard Worker if (chr(s, '<')) {
149*2b949d04SAndroid Build Coastguard Worker while (peek(s) != '>' && !eol(s) && !eof(s))
150*2b949d04SAndroid Build Coastguard Worker buf_append(s, next(s));
151*2b949d04SAndroid Build Coastguard Worker if (!chr(s, '>')) {
152*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unterminated keysym literal");
153*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
154*2b949d04SAndroid Build Coastguard Worker }
155*2b949d04SAndroid Build Coastguard Worker if (!buf_append(s, '\0')) {
156*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "keysym literal is too long");
157*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
158*2b949d04SAndroid Build Coastguard Worker }
159*2b949d04SAndroid Build Coastguard Worker val->string.str = s->buf;
160*2b949d04SAndroid Build Coastguard Worker val->string.len = s->buf_pos;
161*2b949d04SAndroid Build Coastguard Worker return TOK_LHS_KEYSYM;
162*2b949d04SAndroid Build Coastguard Worker }
163*2b949d04SAndroid Build Coastguard Worker
164*2b949d04SAndroid Build Coastguard Worker /* Colon. */
165*2b949d04SAndroid Build Coastguard Worker if (chr(s, ':'))
166*2b949d04SAndroid Build Coastguard Worker return TOK_COLON;
167*2b949d04SAndroid Build Coastguard Worker if (chr(s, '!'))
168*2b949d04SAndroid Build Coastguard Worker return TOK_BANG;
169*2b949d04SAndroid Build Coastguard Worker if (chr(s, '~'))
170*2b949d04SAndroid Build Coastguard Worker return TOK_TILDE;
171*2b949d04SAndroid Build Coastguard Worker
172*2b949d04SAndroid Build Coastguard Worker /* String literal. */
173*2b949d04SAndroid Build Coastguard Worker if (chr(s, '\"')) {
174*2b949d04SAndroid Build Coastguard Worker while (!eof(s) && !eol(s) && peek(s) != '\"') {
175*2b949d04SAndroid Build Coastguard Worker if (chr(s, '\\')) {
176*2b949d04SAndroid Build Coastguard Worker uint8_t o;
177*2b949d04SAndroid Build Coastguard Worker if (chr(s, '\\')) {
178*2b949d04SAndroid Build Coastguard Worker buf_append(s, '\\');
179*2b949d04SAndroid Build Coastguard Worker }
180*2b949d04SAndroid Build Coastguard Worker else if (chr(s, '"')) {
181*2b949d04SAndroid Build Coastguard Worker buf_append(s, '"');
182*2b949d04SAndroid Build Coastguard Worker }
183*2b949d04SAndroid Build Coastguard Worker else if (chr(s, 'x') || chr(s, 'X')) {
184*2b949d04SAndroid Build Coastguard Worker if (hex(s, &o))
185*2b949d04SAndroid Build Coastguard Worker buf_append(s, (char) o);
186*2b949d04SAndroid Build Coastguard Worker else
187*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "illegal hexadecimal escape sequence in string literal");
188*2b949d04SAndroid Build Coastguard Worker }
189*2b949d04SAndroid Build Coastguard Worker else if (oct(s, &o)) {
190*2b949d04SAndroid Build Coastguard Worker buf_append(s, (char) o);
191*2b949d04SAndroid Build Coastguard Worker }
192*2b949d04SAndroid Build Coastguard Worker else {
193*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "unknown escape sequence (%c) in string literal", peek(s));
194*2b949d04SAndroid Build Coastguard Worker /* Ignore. */
195*2b949d04SAndroid Build Coastguard Worker }
196*2b949d04SAndroid Build Coastguard Worker } else {
197*2b949d04SAndroid Build Coastguard Worker buf_append(s, next(s));
198*2b949d04SAndroid Build Coastguard Worker }
199*2b949d04SAndroid Build Coastguard Worker }
200*2b949d04SAndroid Build Coastguard Worker if (!chr(s, '\"')) {
201*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unterminated string literal");
202*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
203*2b949d04SAndroid Build Coastguard Worker }
204*2b949d04SAndroid Build Coastguard Worker if (!buf_append(s, '\0')) {
205*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "string literal is too long");
206*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
207*2b949d04SAndroid Build Coastguard Worker }
208*2b949d04SAndroid Build Coastguard Worker if (!is_valid_utf8(s->buf, s->buf_pos - 1)) {
209*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "string literal is not a valid UTF-8 string");
210*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
211*2b949d04SAndroid Build Coastguard Worker }
212*2b949d04SAndroid Build Coastguard Worker val->string.str = s->buf;
213*2b949d04SAndroid Build Coastguard Worker val->string.len = s->buf_pos;
214*2b949d04SAndroid Build Coastguard Worker return TOK_STRING;
215*2b949d04SAndroid Build Coastguard Worker }
216*2b949d04SAndroid Build Coastguard Worker
217*2b949d04SAndroid Build Coastguard Worker /* Identifier or include. */
218*2b949d04SAndroid Build Coastguard Worker if (is_alpha(peek(s)) || peek(s) == '_') {
219*2b949d04SAndroid Build Coastguard Worker s->buf_pos = 0;
220*2b949d04SAndroid Build Coastguard Worker while (is_alnum(peek(s)) || peek(s) == '_')
221*2b949d04SAndroid Build Coastguard Worker buf_append(s, next(s));
222*2b949d04SAndroid Build Coastguard Worker if (!buf_append(s, '\0')) {
223*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "identifier is too long");
224*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
225*2b949d04SAndroid Build Coastguard Worker }
226*2b949d04SAndroid Build Coastguard Worker
227*2b949d04SAndroid Build Coastguard Worker if (streq(s->buf, "include"))
228*2b949d04SAndroid Build Coastguard Worker return TOK_INCLUDE;
229*2b949d04SAndroid Build Coastguard Worker
230*2b949d04SAndroid Build Coastguard Worker val->string.str = s->buf;
231*2b949d04SAndroid Build Coastguard Worker val->string.len = s->buf_pos;
232*2b949d04SAndroid Build Coastguard Worker return TOK_IDENT;
233*2b949d04SAndroid Build Coastguard Worker }
234*2b949d04SAndroid Build Coastguard Worker
235*2b949d04SAndroid Build Coastguard Worker /* Discard rest of line. */
236*2b949d04SAndroid Build Coastguard Worker skip_to_eol(s);
237*2b949d04SAndroid Build Coastguard Worker
238*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unrecognized token");
239*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
240*2b949d04SAndroid Build Coastguard Worker }
241*2b949d04SAndroid Build Coastguard Worker
242*2b949d04SAndroid Build Coastguard Worker static enum rules_token
lex_include_string(struct scanner * s,struct xkb_compose_table * table,union lvalue * val_out)243*2b949d04SAndroid Build Coastguard Worker lex_include_string(struct scanner *s, struct xkb_compose_table *table,
244*2b949d04SAndroid Build Coastguard Worker union lvalue *val_out)
245*2b949d04SAndroid Build Coastguard Worker {
246*2b949d04SAndroid Build Coastguard Worker while (is_space(peek(s)))
247*2b949d04SAndroid Build Coastguard Worker if (next(s) == '\n')
248*2b949d04SAndroid Build Coastguard Worker return TOK_END_OF_LINE;
249*2b949d04SAndroid Build Coastguard Worker
250*2b949d04SAndroid Build Coastguard Worker s->token_line = s->line;
251*2b949d04SAndroid Build Coastguard Worker s->token_column = s->column;
252*2b949d04SAndroid Build Coastguard Worker s->buf_pos = 0;
253*2b949d04SAndroid Build Coastguard Worker
254*2b949d04SAndroid Build Coastguard Worker if (!chr(s, '\"')) {
255*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "include statement must be followed by a path");
256*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
257*2b949d04SAndroid Build Coastguard Worker }
258*2b949d04SAndroid Build Coastguard Worker
259*2b949d04SAndroid Build Coastguard Worker while (!eof(s) && !eol(s) && peek(s) != '\"') {
260*2b949d04SAndroid Build Coastguard Worker if (chr(s, '%')) {
261*2b949d04SAndroid Build Coastguard Worker if (chr(s, '%')) {
262*2b949d04SAndroid Build Coastguard Worker buf_append(s, '%');
263*2b949d04SAndroid Build Coastguard Worker }
264*2b949d04SAndroid Build Coastguard Worker else if (chr(s, 'H')) {
265*2b949d04SAndroid Build Coastguard Worker const char *home = secure_getenv("HOME");
266*2b949d04SAndroid Build Coastguard Worker if (!home) {
267*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "%%H was used in an include statement, but the HOME environment variable is not set");
268*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
269*2b949d04SAndroid Build Coastguard Worker }
270*2b949d04SAndroid Build Coastguard Worker if (!buf_appends(s, home)) {
271*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "include path after expanding %%H is too long");
272*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
273*2b949d04SAndroid Build Coastguard Worker }
274*2b949d04SAndroid Build Coastguard Worker }
275*2b949d04SAndroid Build Coastguard Worker else if (chr(s, 'L')) {
276*2b949d04SAndroid Build Coastguard Worker char *path = get_locale_compose_file_path(table->locale);
277*2b949d04SAndroid Build Coastguard Worker if (!path) {
278*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "failed to expand %%L to the locale Compose file");
279*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
280*2b949d04SAndroid Build Coastguard Worker }
281*2b949d04SAndroid Build Coastguard Worker if (!buf_appends(s, path)) {
282*2b949d04SAndroid Build Coastguard Worker free(path);
283*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "include path after expanding %%L is too long");
284*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
285*2b949d04SAndroid Build Coastguard Worker }
286*2b949d04SAndroid Build Coastguard Worker free(path);
287*2b949d04SAndroid Build Coastguard Worker }
288*2b949d04SAndroid Build Coastguard Worker else if (chr(s, 'S')) {
289*2b949d04SAndroid Build Coastguard Worker const char *xlocaledir = get_xlocaledir_path();
290*2b949d04SAndroid Build Coastguard Worker if (!buf_appends(s, xlocaledir)) {
291*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "include path after expanding %%S is too long");
292*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
293*2b949d04SAndroid Build Coastguard Worker }
294*2b949d04SAndroid Build Coastguard Worker }
295*2b949d04SAndroid Build Coastguard Worker else {
296*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unknown %% format (%c) in include statement", peek(s));
297*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
298*2b949d04SAndroid Build Coastguard Worker }
299*2b949d04SAndroid Build Coastguard Worker } else {
300*2b949d04SAndroid Build Coastguard Worker buf_append(s, next(s));
301*2b949d04SAndroid Build Coastguard Worker }
302*2b949d04SAndroid Build Coastguard Worker }
303*2b949d04SAndroid Build Coastguard Worker if (!chr(s, '\"')) {
304*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unterminated include statement");
305*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
306*2b949d04SAndroid Build Coastguard Worker }
307*2b949d04SAndroid Build Coastguard Worker if (!buf_append(s, '\0')) {
308*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "include path is too long");
309*2b949d04SAndroid Build Coastguard Worker return TOK_ERROR;
310*2b949d04SAndroid Build Coastguard Worker }
311*2b949d04SAndroid Build Coastguard Worker val_out->string.str = s->buf;
312*2b949d04SAndroid Build Coastguard Worker val_out->string.len = s->buf_pos;
313*2b949d04SAndroid Build Coastguard Worker return TOK_INCLUDE_STRING;
314*2b949d04SAndroid Build Coastguard Worker }
315*2b949d04SAndroid Build Coastguard Worker
316*2b949d04SAndroid Build Coastguard Worker struct production {
317*2b949d04SAndroid Build Coastguard Worker xkb_keysym_t lhs[MAX_LHS_LEN];
318*2b949d04SAndroid Build Coastguard Worker unsigned int len;
319*2b949d04SAndroid Build Coastguard Worker xkb_keysym_t keysym;
320*2b949d04SAndroid Build Coastguard Worker char string[256];
321*2b949d04SAndroid Build Coastguard Worker /* At least one of these is true. */
322*2b949d04SAndroid Build Coastguard Worker bool has_keysym;
323*2b949d04SAndroid Build Coastguard Worker bool has_string;
324*2b949d04SAndroid Build Coastguard Worker
325*2b949d04SAndroid Build Coastguard Worker /* The matching is as follows: (active_mods & modmask) == mods. */
326*2b949d04SAndroid Build Coastguard Worker xkb_mod_mask_t modmask;
327*2b949d04SAndroid Build Coastguard Worker xkb_mod_mask_t mods;
328*2b949d04SAndroid Build Coastguard Worker };
329*2b949d04SAndroid Build Coastguard Worker
330*2b949d04SAndroid Build Coastguard Worker static void
add_production(struct xkb_compose_table * table,struct scanner * s,const struct production * production)331*2b949d04SAndroid Build Coastguard Worker add_production(struct xkb_compose_table *table, struct scanner *s,
332*2b949d04SAndroid Build Coastguard Worker const struct production *production)
333*2b949d04SAndroid Build Coastguard Worker {
334*2b949d04SAndroid Build Coastguard Worker unsigned lhs_pos = 0;
335*2b949d04SAndroid Build Coastguard Worker uint16_t curr = darray_size(table->nodes) == 1 ? 0 : 1;
336*2b949d04SAndroid Build Coastguard Worker uint16_t *pptr = NULL;
337*2b949d04SAndroid Build Coastguard Worker struct compose_node *node = NULL;
338*2b949d04SAndroid Build Coastguard Worker
339*2b949d04SAndroid Build Coastguard Worker /* Warn before potentially going over the limit, discard silently after. */
340*2b949d04SAndroid Build Coastguard Worker if (darray_size(table->nodes) + production->len + MAX_LHS_LEN > MAX_COMPOSE_NODES)
341*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "too many sequences for one Compose file; will ignore further lines");
342*2b949d04SAndroid Build Coastguard Worker if (darray_size(table->nodes) + production->len >= MAX_COMPOSE_NODES)
343*2b949d04SAndroid Build Coastguard Worker return;
344*2b949d04SAndroid Build Coastguard Worker
345*2b949d04SAndroid Build Coastguard Worker /*
346*2b949d04SAndroid Build Coastguard Worker * Insert the sequence to the ternary search tree, creating new nodes as
347*2b949d04SAndroid Build Coastguard Worker * needed.
348*2b949d04SAndroid Build Coastguard Worker *
349*2b949d04SAndroid Build Coastguard Worker * TODO: We insert in the order given, this means some inputs can create
350*2b949d04SAndroid Build Coastguard Worker * long O(n) chains, which results in total O(n^2) parsing time. We should
351*2b949d04SAndroid Build Coastguard Worker * ensure the tree is reasonably balanced somehow.
352*2b949d04SAndroid Build Coastguard Worker */
353*2b949d04SAndroid Build Coastguard Worker while (true) {
354*2b949d04SAndroid Build Coastguard Worker const xkb_keysym_t keysym = production->lhs[lhs_pos];
355*2b949d04SAndroid Build Coastguard Worker const bool last = lhs_pos + 1 == production->len;
356*2b949d04SAndroid Build Coastguard Worker
357*2b949d04SAndroid Build Coastguard Worker if (curr == 0) {
358*2b949d04SAndroid Build Coastguard Worker /*
359*2b949d04SAndroid Build Coastguard Worker * Create a new node and update the parent pointer to it.
360*2b949d04SAndroid Build Coastguard Worker * Update the pointer first because the append invalidates it.
361*2b949d04SAndroid Build Coastguard Worker */
362*2b949d04SAndroid Build Coastguard Worker struct compose_node new = {
363*2b949d04SAndroid Build Coastguard Worker .keysym = keysym,
364*2b949d04SAndroid Build Coastguard Worker .lokid = 0,
365*2b949d04SAndroid Build Coastguard Worker .hikid = 0,
366*2b949d04SAndroid Build Coastguard Worker .internal = {
367*2b949d04SAndroid Build Coastguard Worker .eqkid = 0,
368*2b949d04SAndroid Build Coastguard Worker .is_leaf = false,
369*2b949d04SAndroid Build Coastguard Worker },
370*2b949d04SAndroid Build Coastguard Worker };
371*2b949d04SAndroid Build Coastguard Worker curr = darray_size(table->nodes);
372*2b949d04SAndroid Build Coastguard Worker if (pptr != NULL) {
373*2b949d04SAndroid Build Coastguard Worker *pptr = curr;
374*2b949d04SAndroid Build Coastguard Worker pptr = NULL;
375*2b949d04SAndroid Build Coastguard Worker }
376*2b949d04SAndroid Build Coastguard Worker darray_append(table->nodes, new);
377*2b949d04SAndroid Build Coastguard Worker }
378*2b949d04SAndroid Build Coastguard Worker
379*2b949d04SAndroid Build Coastguard Worker node = &darray_item(table->nodes, curr);
380*2b949d04SAndroid Build Coastguard Worker
381*2b949d04SAndroid Build Coastguard Worker if (keysym < node->keysym) {
382*2b949d04SAndroid Build Coastguard Worker pptr = &node->lokid;
383*2b949d04SAndroid Build Coastguard Worker curr = node->lokid;
384*2b949d04SAndroid Build Coastguard Worker } else if (keysym > node->keysym) {
385*2b949d04SAndroid Build Coastguard Worker pptr = &node->hikid;
386*2b949d04SAndroid Build Coastguard Worker curr = node->hikid;
387*2b949d04SAndroid Build Coastguard Worker } else if (!last) {
388*2b949d04SAndroid Build Coastguard Worker if (node->is_leaf) {
389*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "a sequence already exists which is a prefix of this sequence; overriding");
390*2b949d04SAndroid Build Coastguard Worker node->internal.eqkid = node->lokid = node->hikid = 0;
391*2b949d04SAndroid Build Coastguard Worker node->internal.is_leaf = false;
392*2b949d04SAndroid Build Coastguard Worker }
393*2b949d04SAndroid Build Coastguard Worker lhs_pos++;
394*2b949d04SAndroid Build Coastguard Worker pptr = &node->internal.eqkid;
395*2b949d04SAndroid Build Coastguard Worker curr = node->internal.eqkid;
396*2b949d04SAndroid Build Coastguard Worker } else {
397*2b949d04SAndroid Build Coastguard Worker if (node->is_leaf) {
398*2b949d04SAndroid Build Coastguard Worker bool same_string =
399*2b949d04SAndroid Build Coastguard Worker (node->leaf.utf8 == 0 && !production->has_string) ||
400*2b949d04SAndroid Build Coastguard Worker (
401*2b949d04SAndroid Build Coastguard Worker node->leaf.utf8 != 0 && production->has_string &&
402*2b949d04SAndroid Build Coastguard Worker streq(&darray_item(table->utf8, node->leaf.utf8),
403*2b949d04SAndroid Build Coastguard Worker production->string)
404*2b949d04SAndroid Build Coastguard Worker );
405*2b949d04SAndroid Build Coastguard Worker bool same_keysym =
406*2b949d04SAndroid Build Coastguard Worker (node->leaf.keysym == XKB_KEY_NoSymbol && !production->has_keysym) ||
407*2b949d04SAndroid Build Coastguard Worker (
408*2b949d04SAndroid Build Coastguard Worker node->leaf.keysym != XKB_KEY_NoSymbol && production->has_keysym &&
409*2b949d04SAndroid Build Coastguard Worker node->leaf.keysym == production->keysym
410*2b949d04SAndroid Build Coastguard Worker );
411*2b949d04SAndroid Build Coastguard Worker if (same_string && same_keysym) {
412*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "this compose sequence is a duplicate of another; skipping line");
413*2b949d04SAndroid Build Coastguard Worker return;
414*2b949d04SAndroid Build Coastguard Worker } else {
415*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "this compose sequence already exists; overriding");
416*2b949d04SAndroid Build Coastguard Worker }
417*2b949d04SAndroid Build Coastguard Worker } else if (node->internal.eqkid != 0) {
418*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "this compose sequence is a prefix of another; skipping line");
419*2b949d04SAndroid Build Coastguard Worker return;
420*2b949d04SAndroid Build Coastguard Worker }
421*2b949d04SAndroid Build Coastguard Worker node->is_leaf = true;
422*2b949d04SAndroid Build Coastguard Worker if (production->has_string) {
423*2b949d04SAndroid Build Coastguard Worker node->leaf.utf8 = darray_size(table->utf8);
424*2b949d04SAndroid Build Coastguard Worker darray_append_items(table->utf8, production->string,
425*2b949d04SAndroid Build Coastguard Worker strlen(production->string) + 1);
426*2b949d04SAndroid Build Coastguard Worker }
427*2b949d04SAndroid Build Coastguard Worker if (production->has_keysym) {
428*2b949d04SAndroid Build Coastguard Worker node->leaf.keysym = production->keysym;
429*2b949d04SAndroid Build Coastguard Worker }
430*2b949d04SAndroid Build Coastguard Worker return;
431*2b949d04SAndroid Build Coastguard Worker }
432*2b949d04SAndroid Build Coastguard Worker }
433*2b949d04SAndroid Build Coastguard Worker }
434*2b949d04SAndroid Build Coastguard Worker
435*2b949d04SAndroid Build Coastguard Worker /* Should match resolve_modifier(). */
436*2b949d04SAndroid Build Coastguard Worker #define ALL_MODS_MASK ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3))
437*2b949d04SAndroid Build Coastguard Worker
438*2b949d04SAndroid Build Coastguard Worker static xkb_mod_index_t
resolve_modifier(const char * name)439*2b949d04SAndroid Build Coastguard Worker resolve_modifier(const char *name)
440*2b949d04SAndroid Build Coastguard Worker {
441*2b949d04SAndroid Build Coastguard Worker static const struct {
442*2b949d04SAndroid Build Coastguard Worker const char *name;
443*2b949d04SAndroid Build Coastguard Worker xkb_mod_index_t mod;
444*2b949d04SAndroid Build Coastguard Worker } mods[] = {
445*2b949d04SAndroid Build Coastguard Worker { "Shift", 0 },
446*2b949d04SAndroid Build Coastguard Worker { "Ctrl", 2 },
447*2b949d04SAndroid Build Coastguard Worker { "Alt", 3 },
448*2b949d04SAndroid Build Coastguard Worker { "Meta", 3 },
449*2b949d04SAndroid Build Coastguard Worker { "Lock", 1 },
450*2b949d04SAndroid Build Coastguard Worker { "Caps", 1 },
451*2b949d04SAndroid Build Coastguard Worker };
452*2b949d04SAndroid Build Coastguard Worker
453*2b949d04SAndroid Build Coastguard Worker for (unsigned i = 0; i < ARRAY_SIZE(mods); i++)
454*2b949d04SAndroid Build Coastguard Worker if (streq(name, mods[i].name))
455*2b949d04SAndroid Build Coastguard Worker return mods[i].mod;
456*2b949d04SAndroid Build Coastguard Worker
457*2b949d04SAndroid Build Coastguard Worker return XKB_MOD_INVALID;
458*2b949d04SAndroid Build Coastguard Worker }
459*2b949d04SAndroid Build Coastguard Worker
460*2b949d04SAndroid Build Coastguard Worker static bool
461*2b949d04SAndroid Build Coastguard Worker parse(struct xkb_compose_table *table, struct scanner *s,
462*2b949d04SAndroid Build Coastguard Worker unsigned include_depth);
463*2b949d04SAndroid Build Coastguard Worker
464*2b949d04SAndroid Build Coastguard Worker static bool
do_include(struct xkb_compose_table * table,struct scanner * s,const char * path,unsigned include_depth)465*2b949d04SAndroid Build Coastguard Worker do_include(struct xkb_compose_table *table, struct scanner *s,
466*2b949d04SAndroid Build Coastguard Worker const char *path, unsigned include_depth)
467*2b949d04SAndroid Build Coastguard Worker {
468*2b949d04SAndroid Build Coastguard Worker FILE *file;
469*2b949d04SAndroid Build Coastguard Worker bool ok;
470*2b949d04SAndroid Build Coastguard Worker char *string;
471*2b949d04SAndroid Build Coastguard Worker size_t size;
472*2b949d04SAndroid Build Coastguard Worker struct scanner new_s;
473*2b949d04SAndroid Build Coastguard Worker
474*2b949d04SAndroid Build Coastguard Worker if (include_depth >= MAX_INCLUDE_DEPTH) {
475*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
476*2b949d04SAndroid Build Coastguard Worker MAX_INCLUDE_DEPTH);
477*2b949d04SAndroid Build Coastguard Worker return false;
478*2b949d04SAndroid Build Coastguard Worker }
479*2b949d04SAndroid Build Coastguard Worker
480*2b949d04SAndroid Build Coastguard Worker file = fopen(path, "rb");
481*2b949d04SAndroid Build Coastguard Worker if (!file) {
482*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "failed to open included Compose file \"%s\": %s",
483*2b949d04SAndroid Build Coastguard Worker path, strerror(errno));
484*2b949d04SAndroid Build Coastguard Worker return false;
485*2b949d04SAndroid Build Coastguard Worker }
486*2b949d04SAndroid Build Coastguard Worker
487*2b949d04SAndroid Build Coastguard Worker ok = map_file(file, &string, &size);
488*2b949d04SAndroid Build Coastguard Worker if (!ok) {
489*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "failed to read included Compose file \"%s\": %s",
490*2b949d04SAndroid Build Coastguard Worker path, strerror(errno));
491*2b949d04SAndroid Build Coastguard Worker goto err_file;
492*2b949d04SAndroid Build Coastguard Worker }
493*2b949d04SAndroid Build Coastguard Worker
494*2b949d04SAndroid Build Coastguard Worker scanner_init(&new_s, table->ctx, string, size, path, s->priv);
495*2b949d04SAndroid Build Coastguard Worker
496*2b949d04SAndroid Build Coastguard Worker ok = parse(table, &new_s, include_depth + 1);
497*2b949d04SAndroid Build Coastguard Worker if (!ok)
498*2b949d04SAndroid Build Coastguard Worker goto err_unmap;
499*2b949d04SAndroid Build Coastguard Worker
500*2b949d04SAndroid Build Coastguard Worker err_unmap:
501*2b949d04SAndroid Build Coastguard Worker unmap_file(string, size);
502*2b949d04SAndroid Build Coastguard Worker err_file:
503*2b949d04SAndroid Build Coastguard Worker fclose(file);
504*2b949d04SAndroid Build Coastguard Worker return ok;
505*2b949d04SAndroid Build Coastguard Worker }
506*2b949d04SAndroid Build Coastguard Worker
507*2b949d04SAndroid Build Coastguard Worker static bool
parse(struct xkb_compose_table * table,struct scanner * s,unsigned include_depth)508*2b949d04SAndroid Build Coastguard Worker parse(struct xkb_compose_table *table, struct scanner *s,
509*2b949d04SAndroid Build Coastguard Worker unsigned include_depth)
510*2b949d04SAndroid Build Coastguard Worker {
511*2b949d04SAndroid Build Coastguard Worker enum rules_token tok;
512*2b949d04SAndroid Build Coastguard Worker union lvalue val;
513*2b949d04SAndroid Build Coastguard Worker xkb_keysym_t keysym;
514*2b949d04SAndroid Build Coastguard Worker struct production production;
515*2b949d04SAndroid Build Coastguard Worker enum { MAX_ERRORS = 10 };
516*2b949d04SAndroid Build Coastguard Worker int num_errors = 0;
517*2b949d04SAndroid Build Coastguard Worker
518*2b949d04SAndroid Build Coastguard Worker initial:
519*2b949d04SAndroid Build Coastguard Worker production.len = 0;
520*2b949d04SAndroid Build Coastguard Worker production.has_keysym = false;
521*2b949d04SAndroid Build Coastguard Worker production.has_string = false;
522*2b949d04SAndroid Build Coastguard Worker production.mods = 0;
523*2b949d04SAndroid Build Coastguard Worker production.modmask = 0;
524*2b949d04SAndroid Build Coastguard Worker
525*2b949d04SAndroid Build Coastguard Worker /* fallthrough */
526*2b949d04SAndroid Build Coastguard Worker
527*2b949d04SAndroid Build Coastguard Worker initial_eol:
528*2b949d04SAndroid Build Coastguard Worker switch (tok = lex(s, &val)) {
529*2b949d04SAndroid Build Coastguard Worker case TOK_END_OF_LINE:
530*2b949d04SAndroid Build Coastguard Worker goto initial_eol;
531*2b949d04SAndroid Build Coastguard Worker case TOK_END_OF_FILE:
532*2b949d04SAndroid Build Coastguard Worker goto finished;
533*2b949d04SAndroid Build Coastguard Worker case TOK_INCLUDE:
534*2b949d04SAndroid Build Coastguard Worker goto include;
535*2b949d04SAndroid Build Coastguard Worker default:
536*2b949d04SAndroid Build Coastguard Worker goto lhs_tok;
537*2b949d04SAndroid Build Coastguard Worker }
538*2b949d04SAndroid Build Coastguard Worker
539*2b949d04SAndroid Build Coastguard Worker include:
540*2b949d04SAndroid Build Coastguard Worker switch (tok = lex_include_string(s, table, &val)) {
541*2b949d04SAndroid Build Coastguard Worker case TOK_INCLUDE_STRING:
542*2b949d04SAndroid Build Coastguard Worker goto include_eol;
543*2b949d04SAndroid Build Coastguard Worker default:
544*2b949d04SAndroid Build Coastguard Worker goto unexpected;
545*2b949d04SAndroid Build Coastguard Worker }
546*2b949d04SAndroid Build Coastguard Worker
547*2b949d04SAndroid Build Coastguard Worker include_eol:
548*2b949d04SAndroid Build Coastguard Worker switch (tok = lex(s, &val)) {
549*2b949d04SAndroid Build Coastguard Worker case TOK_END_OF_LINE:
550*2b949d04SAndroid Build Coastguard Worker if (!do_include(table, s, val.string.str, include_depth))
551*2b949d04SAndroid Build Coastguard Worker goto fail;
552*2b949d04SAndroid Build Coastguard Worker goto initial;
553*2b949d04SAndroid Build Coastguard Worker default:
554*2b949d04SAndroid Build Coastguard Worker goto unexpected;
555*2b949d04SAndroid Build Coastguard Worker }
556*2b949d04SAndroid Build Coastguard Worker
557*2b949d04SAndroid Build Coastguard Worker lhs:
558*2b949d04SAndroid Build Coastguard Worker tok = lex(s, &val);
559*2b949d04SAndroid Build Coastguard Worker lhs_tok:
560*2b949d04SAndroid Build Coastguard Worker switch (tok) {
561*2b949d04SAndroid Build Coastguard Worker case TOK_COLON:
562*2b949d04SAndroid Build Coastguard Worker if (production.len <= 0) {
563*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "expected at least one keysym on left-hand side; skipping line");
564*2b949d04SAndroid Build Coastguard Worker goto skip;
565*2b949d04SAndroid Build Coastguard Worker }
566*2b949d04SAndroid Build Coastguard Worker goto rhs;
567*2b949d04SAndroid Build Coastguard Worker case TOK_IDENT:
568*2b949d04SAndroid Build Coastguard Worker if (streq(val.string.str, "None")) {
569*2b949d04SAndroid Build Coastguard Worker production.mods = 0;
570*2b949d04SAndroid Build Coastguard Worker production.modmask = ALL_MODS_MASK;
571*2b949d04SAndroid Build Coastguard Worker goto lhs_keysym;
572*2b949d04SAndroid Build Coastguard Worker }
573*2b949d04SAndroid Build Coastguard Worker goto lhs_mod_list_tok;
574*2b949d04SAndroid Build Coastguard Worker case TOK_TILDE:
575*2b949d04SAndroid Build Coastguard Worker goto lhs_mod_list_tok;
576*2b949d04SAndroid Build Coastguard Worker case TOK_BANG:
577*2b949d04SAndroid Build Coastguard Worker production.modmask = ALL_MODS_MASK;
578*2b949d04SAndroid Build Coastguard Worker goto lhs_mod_list;
579*2b949d04SAndroid Build Coastguard Worker default:
580*2b949d04SAndroid Build Coastguard Worker goto lhs_keysym_tok;
581*2b949d04SAndroid Build Coastguard Worker }
582*2b949d04SAndroid Build Coastguard Worker
583*2b949d04SAndroid Build Coastguard Worker lhs_keysym:
584*2b949d04SAndroid Build Coastguard Worker tok = lex(s, &val);
585*2b949d04SAndroid Build Coastguard Worker lhs_keysym_tok:
586*2b949d04SAndroid Build Coastguard Worker switch (tok) {
587*2b949d04SAndroid Build Coastguard Worker case TOK_LHS_KEYSYM:
588*2b949d04SAndroid Build Coastguard Worker keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS);
589*2b949d04SAndroid Build Coastguard Worker if (keysym == XKB_KEY_NoSymbol) {
590*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unrecognized keysym \"%s\" on left-hand side",
591*2b949d04SAndroid Build Coastguard Worker val.string.str);
592*2b949d04SAndroid Build Coastguard Worker goto error;
593*2b949d04SAndroid Build Coastguard Worker }
594*2b949d04SAndroid Build Coastguard Worker if (production.len + 1 > MAX_LHS_LEN) {
595*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "too many keysyms (%d) on left-hand side; skipping line",
596*2b949d04SAndroid Build Coastguard Worker MAX_LHS_LEN + 1);
597*2b949d04SAndroid Build Coastguard Worker goto skip;
598*2b949d04SAndroid Build Coastguard Worker }
599*2b949d04SAndroid Build Coastguard Worker production.lhs[production.len++] = keysym;
600*2b949d04SAndroid Build Coastguard Worker production.mods = 0;
601*2b949d04SAndroid Build Coastguard Worker production.modmask = 0;
602*2b949d04SAndroid Build Coastguard Worker goto lhs;
603*2b949d04SAndroid Build Coastguard Worker default:
604*2b949d04SAndroid Build Coastguard Worker goto unexpected;
605*2b949d04SAndroid Build Coastguard Worker }
606*2b949d04SAndroid Build Coastguard Worker
607*2b949d04SAndroid Build Coastguard Worker lhs_mod_list:
608*2b949d04SAndroid Build Coastguard Worker tok = lex(s, &val);
609*2b949d04SAndroid Build Coastguard Worker lhs_mod_list_tok: {
610*2b949d04SAndroid Build Coastguard Worker bool tilde = false;
611*2b949d04SAndroid Build Coastguard Worker xkb_mod_index_t mod;
612*2b949d04SAndroid Build Coastguard Worker
613*2b949d04SAndroid Build Coastguard Worker if (tok != TOK_TILDE && tok != TOK_IDENT)
614*2b949d04SAndroid Build Coastguard Worker goto lhs_keysym_tok;
615*2b949d04SAndroid Build Coastguard Worker
616*2b949d04SAndroid Build Coastguard Worker if (tok == TOK_TILDE) {
617*2b949d04SAndroid Build Coastguard Worker tilde = true;
618*2b949d04SAndroid Build Coastguard Worker tok = lex(s, &val);
619*2b949d04SAndroid Build Coastguard Worker }
620*2b949d04SAndroid Build Coastguard Worker
621*2b949d04SAndroid Build Coastguard Worker if (tok != TOK_IDENT)
622*2b949d04SAndroid Build Coastguard Worker goto unexpected;
623*2b949d04SAndroid Build Coastguard Worker
624*2b949d04SAndroid Build Coastguard Worker mod = resolve_modifier(val.string.str);
625*2b949d04SAndroid Build Coastguard Worker if (mod == XKB_MOD_INVALID) {
626*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unrecognized modifier \"%s\"",
627*2b949d04SAndroid Build Coastguard Worker val.string.str);
628*2b949d04SAndroid Build Coastguard Worker goto error;
629*2b949d04SAndroid Build Coastguard Worker }
630*2b949d04SAndroid Build Coastguard Worker
631*2b949d04SAndroid Build Coastguard Worker production.modmask |= 1 << mod;
632*2b949d04SAndroid Build Coastguard Worker if (tilde)
633*2b949d04SAndroid Build Coastguard Worker production.mods &= ~(1 << mod);
634*2b949d04SAndroid Build Coastguard Worker else
635*2b949d04SAndroid Build Coastguard Worker production.mods |= 1 << mod;
636*2b949d04SAndroid Build Coastguard Worker
637*2b949d04SAndroid Build Coastguard Worker goto lhs_mod_list;
638*2b949d04SAndroid Build Coastguard Worker }
639*2b949d04SAndroid Build Coastguard Worker
640*2b949d04SAndroid Build Coastguard Worker rhs:
641*2b949d04SAndroid Build Coastguard Worker switch (tok = lex(s, &val)) {
642*2b949d04SAndroid Build Coastguard Worker case TOK_STRING:
643*2b949d04SAndroid Build Coastguard Worker if (production.has_string) {
644*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "right-hand side can have at most one string; skipping line");
645*2b949d04SAndroid Build Coastguard Worker goto skip;
646*2b949d04SAndroid Build Coastguard Worker }
647*2b949d04SAndroid Build Coastguard Worker if (val.string.len <= 0) {
648*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "right-hand side string must not be empty; skipping line");
649*2b949d04SAndroid Build Coastguard Worker goto skip;
650*2b949d04SAndroid Build Coastguard Worker }
651*2b949d04SAndroid Build Coastguard Worker if (val.string.len >= sizeof(production.string)) {
652*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "right-hand side string is too long; skipping line");
653*2b949d04SAndroid Build Coastguard Worker goto skip;
654*2b949d04SAndroid Build Coastguard Worker }
655*2b949d04SAndroid Build Coastguard Worker strcpy(production.string, val.string.str);
656*2b949d04SAndroid Build Coastguard Worker production.has_string = true;
657*2b949d04SAndroid Build Coastguard Worker goto rhs;
658*2b949d04SAndroid Build Coastguard Worker case TOK_IDENT:
659*2b949d04SAndroid Build Coastguard Worker keysym = xkb_keysym_from_name(val.string.str, XKB_KEYSYM_NO_FLAGS);
660*2b949d04SAndroid Build Coastguard Worker if (keysym == XKB_KEY_NoSymbol) {
661*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unrecognized keysym \"%s\" on right-hand side",
662*2b949d04SAndroid Build Coastguard Worker val.string.str);
663*2b949d04SAndroid Build Coastguard Worker goto error;
664*2b949d04SAndroid Build Coastguard Worker }
665*2b949d04SAndroid Build Coastguard Worker if (production.has_keysym) {
666*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "right-hand side can have at most one keysym; skipping line");
667*2b949d04SAndroid Build Coastguard Worker goto skip;
668*2b949d04SAndroid Build Coastguard Worker }
669*2b949d04SAndroid Build Coastguard Worker production.keysym = keysym;
670*2b949d04SAndroid Build Coastguard Worker production.has_keysym = true;
671*2b949d04SAndroid Build Coastguard Worker /* fallthrough */
672*2b949d04SAndroid Build Coastguard Worker case TOK_END_OF_LINE:
673*2b949d04SAndroid Build Coastguard Worker if (!production.has_string && !production.has_keysym) {
674*2b949d04SAndroid Build Coastguard Worker scanner_warn(s, "right-hand side must have at least one of string or keysym; skipping line");
675*2b949d04SAndroid Build Coastguard Worker goto skip;
676*2b949d04SAndroid Build Coastguard Worker }
677*2b949d04SAndroid Build Coastguard Worker add_production(table, s, &production);
678*2b949d04SAndroid Build Coastguard Worker goto initial;
679*2b949d04SAndroid Build Coastguard Worker default:
680*2b949d04SAndroid Build Coastguard Worker goto unexpected;
681*2b949d04SAndroid Build Coastguard Worker }
682*2b949d04SAndroid Build Coastguard Worker
683*2b949d04SAndroid Build Coastguard Worker unexpected:
684*2b949d04SAndroid Build Coastguard Worker if (tok != TOK_ERROR)
685*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "unexpected token");
686*2b949d04SAndroid Build Coastguard Worker error:
687*2b949d04SAndroid Build Coastguard Worker num_errors++;
688*2b949d04SAndroid Build Coastguard Worker if (num_errors <= MAX_ERRORS)
689*2b949d04SAndroid Build Coastguard Worker goto skip;
690*2b949d04SAndroid Build Coastguard Worker
691*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "too many errors");
692*2b949d04SAndroid Build Coastguard Worker goto fail;
693*2b949d04SAndroid Build Coastguard Worker
694*2b949d04SAndroid Build Coastguard Worker fail:
695*2b949d04SAndroid Build Coastguard Worker scanner_err(s, "failed to parse file");
696*2b949d04SAndroid Build Coastguard Worker return false;
697*2b949d04SAndroid Build Coastguard Worker
698*2b949d04SAndroid Build Coastguard Worker skip:
699*2b949d04SAndroid Build Coastguard Worker while (tok != TOK_END_OF_LINE && tok != TOK_END_OF_FILE)
700*2b949d04SAndroid Build Coastguard Worker tok = lex(s, &val);
701*2b949d04SAndroid Build Coastguard Worker goto initial;
702*2b949d04SAndroid Build Coastguard Worker
703*2b949d04SAndroid Build Coastguard Worker finished:
704*2b949d04SAndroid Build Coastguard Worker return true;
705*2b949d04SAndroid Build Coastguard Worker }
706*2b949d04SAndroid Build Coastguard Worker
707*2b949d04SAndroid Build Coastguard Worker bool
parse_string(struct xkb_compose_table * table,const char * string,size_t len,const char * file_name)708*2b949d04SAndroid Build Coastguard Worker parse_string(struct xkb_compose_table *table, const char *string, size_t len,
709*2b949d04SAndroid Build Coastguard Worker const char *file_name)
710*2b949d04SAndroid Build Coastguard Worker {
711*2b949d04SAndroid Build Coastguard Worker struct scanner s;
712*2b949d04SAndroid Build Coastguard Worker scanner_init(&s, table->ctx, string, len, file_name, NULL);
713*2b949d04SAndroid Build Coastguard Worker if (!parse(table, &s, 0))
714*2b949d04SAndroid Build Coastguard Worker return false;
715*2b949d04SAndroid Build Coastguard Worker /* Maybe the allocator can use the excess space. */
716*2b949d04SAndroid Build Coastguard Worker darray_shrink(table->nodes);
717*2b949d04SAndroid Build Coastguard Worker darray_shrink(table->utf8);
718*2b949d04SAndroid Build Coastguard Worker return true;
719*2b949d04SAndroid Build Coastguard Worker }
720*2b949d04SAndroid Build Coastguard Worker
721*2b949d04SAndroid Build Coastguard Worker bool
parse_file(struct xkb_compose_table * table,FILE * file,const char * file_name)722*2b949d04SAndroid Build Coastguard Worker parse_file(struct xkb_compose_table *table, FILE *file, const char *file_name)
723*2b949d04SAndroid Build Coastguard Worker {
724*2b949d04SAndroid Build Coastguard Worker bool ok;
725*2b949d04SAndroid Build Coastguard Worker char *string;
726*2b949d04SAndroid Build Coastguard Worker size_t size;
727*2b949d04SAndroid Build Coastguard Worker
728*2b949d04SAndroid Build Coastguard Worker ok = map_file(file, &string, &size);
729*2b949d04SAndroid Build Coastguard Worker if (!ok) {
730*2b949d04SAndroid Build Coastguard Worker log_err(table->ctx, "Couldn't read Compose file %s: %s\n",
731*2b949d04SAndroid Build Coastguard Worker file_name, strerror(errno));
732*2b949d04SAndroid Build Coastguard Worker return false;
733*2b949d04SAndroid Build Coastguard Worker }
734*2b949d04SAndroid Build Coastguard Worker
735*2b949d04SAndroid Build Coastguard Worker ok = parse_string(table, string, size, file_name);
736*2b949d04SAndroid Build Coastguard Worker unmap_file(string, size);
737*2b949d04SAndroid Build Coastguard Worker return ok;
738*2b949d04SAndroid Build Coastguard Worker }
739