xref: /aosp_15_r20/external/libxkbcommon/src/compose/parser.c (revision 2b949d0487e80d67f1fda82db69e101e761f8064)
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