xref: /aosp_15_r20/external/libxkbcommon/src/xkbcomp/rules.c (revision 2b949d0487e80d67f1fda82db69e101e761f8064)
1*2b949d04SAndroid Build Coastguard Worker /************************************************************
2*2b949d04SAndroid Build Coastguard Worker  * Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
3*2b949d04SAndroid Build Coastguard Worker  *
4*2b949d04SAndroid Build Coastguard Worker  * Permission to use, copy, modify, and distribute this
5*2b949d04SAndroid Build Coastguard Worker  * software and its documentation for any purpose and without
6*2b949d04SAndroid Build Coastguard Worker  * fee is hereby granted, provided that the above copyright
7*2b949d04SAndroid Build Coastguard Worker  * notice appear in all copies and that both that copyright
8*2b949d04SAndroid Build Coastguard Worker  * notice and this permission notice appear in supporting
9*2b949d04SAndroid Build Coastguard Worker  * documentation, and that the name of Silicon Graphics not be
10*2b949d04SAndroid Build Coastguard Worker  * used in advertising or publicity pertaining to distribution
11*2b949d04SAndroid Build Coastguard Worker  * of the software without specific prior written permission.
12*2b949d04SAndroid Build Coastguard Worker  * Silicon Graphics makes no representation about the suitability
13*2b949d04SAndroid Build Coastguard Worker  * of this software for any purpose. It is provided "as is"
14*2b949d04SAndroid Build Coastguard Worker  * without any express or implied warranty.
15*2b949d04SAndroid Build Coastguard Worker  *
16*2b949d04SAndroid Build Coastguard Worker  * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17*2b949d04SAndroid Build Coastguard Worker  * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18*2b949d04SAndroid Build Coastguard Worker  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19*2b949d04SAndroid Build Coastguard Worker  * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20*2b949d04SAndroid Build Coastguard Worker  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21*2b949d04SAndroid Build Coastguard Worker  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22*2b949d04SAndroid Build Coastguard Worker  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
23*2b949d04SAndroid Build Coastguard Worker  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
24*2b949d04SAndroid Build Coastguard Worker  *
25*2b949d04SAndroid Build Coastguard Worker  ********************************************************/
26*2b949d04SAndroid Build Coastguard Worker 
27*2b949d04SAndroid Build Coastguard Worker /*
28*2b949d04SAndroid Build Coastguard Worker  * Copyright © 2012 Ran Benita <[email protected]>
29*2b949d04SAndroid Build Coastguard Worker  *
30*2b949d04SAndroid Build Coastguard Worker  * Permission is hereby granted, free of charge, to any person obtaining a
31*2b949d04SAndroid Build Coastguard Worker  * copy of this software and associated documentation files (the "Software"),
32*2b949d04SAndroid Build Coastguard Worker  * to deal in the Software without restriction, including without limitation
33*2b949d04SAndroid Build Coastguard Worker  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34*2b949d04SAndroid Build Coastguard Worker  * and/or sell copies of the Software, and to permit persons to whom the
35*2b949d04SAndroid Build Coastguard Worker  * Software is furnished to do so, subject to the following conditions:
36*2b949d04SAndroid Build Coastguard Worker  *
37*2b949d04SAndroid Build Coastguard Worker  * The above copyright notice and this permission notice (including the next
38*2b949d04SAndroid Build Coastguard Worker  * paragraph) shall be included in all copies or substantial portions of the
39*2b949d04SAndroid Build Coastguard Worker  * Software.
40*2b949d04SAndroid Build Coastguard Worker  *
41*2b949d04SAndroid Build Coastguard Worker  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42*2b949d04SAndroid Build Coastguard Worker  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43*2b949d04SAndroid Build Coastguard Worker  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
44*2b949d04SAndroid Build Coastguard Worker  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45*2b949d04SAndroid Build Coastguard Worker  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46*2b949d04SAndroid Build Coastguard Worker  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47*2b949d04SAndroid Build Coastguard Worker  * DEALINGS IN THE SOFTWARE.
48*2b949d04SAndroid Build Coastguard Worker  */
49*2b949d04SAndroid Build Coastguard Worker 
50*2b949d04SAndroid Build Coastguard Worker #include "config.h"
51*2b949d04SAndroid Build Coastguard Worker 
52*2b949d04SAndroid Build Coastguard Worker #include "xkbcomp-priv.h"
53*2b949d04SAndroid Build Coastguard Worker #include "rules.h"
54*2b949d04SAndroid Build Coastguard Worker #include "include.h"
55*2b949d04SAndroid Build Coastguard Worker #include "scanner-utils.h"
56*2b949d04SAndroid Build Coastguard Worker 
57*2b949d04SAndroid Build Coastguard Worker #define MAX_INCLUDE_DEPTH 5
58*2b949d04SAndroid Build Coastguard Worker 
59*2b949d04SAndroid Build Coastguard Worker /* Scanner / Lexer */
60*2b949d04SAndroid Build Coastguard Worker 
61*2b949d04SAndroid Build Coastguard Worker /* Values returned with some tokens, like yylval. */
62*2b949d04SAndroid Build Coastguard Worker union lvalue {
63*2b949d04SAndroid Build Coastguard Worker     struct sval string;
64*2b949d04SAndroid Build Coastguard Worker };
65*2b949d04SAndroid Build Coastguard Worker 
66*2b949d04SAndroid Build Coastguard Worker enum rules_token {
67*2b949d04SAndroid Build Coastguard Worker     TOK_END_OF_FILE = 0,
68*2b949d04SAndroid Build Coastguard Worker     TOK_END_OF_LINE,
69*2b949d04SAndroid Build Coastguard Worker     TOK_IDENTIFIER,
70*2b949d04SAndroid Build Coastguard Worker     TOK_GROUP_NAME,
71*2b949d04SAndroid Build Coastguard Worker     TOK_BANG,
72*2b949d04SAndroid Build Coastguard Worker     TOK_EQUALS,
73*2b949d04SAndroid Build Coastguard Worker     TOK_STAR,
74*2b949d04SAndroid Build Coastguard Worker     TOK_INCLUDE,
75*2b949d04SAndroid Build Coastguard Worker     TOK_ERROR
76*2b949d04SAndroid Build Coastguard Worker };
77*2b949d04SAndroid Build Coastguard Worker 
78*2b949d04SAndroid Build Coastguard Worker static inline bool
is_ident(char ch)79*2b949d04SAndroid Build Coastguard Worker is_ident(char ch)
80*2b949d04SAndroid Build Coastguard Worker {
81*2b949d04SAndroid Build Coastguard Worker     return is_graph(ch) && ch != '\\';
82*2b949d04SAndroid Build Coastguard Worker }
83*2b949d04SAndroid Build Coastguard Worker 
84*2b949d04SAndroid Build Coastguard Worker static enum rules_token
lex(struct scanner * s,union lvalue * val)85*2b949d04SAndroid Build Coastguard Worker lex(struct scanner *s, union lvalue *val)
86*2b949d04SAndroid Build Coastguard Worker {
87*2b949d04SAndroid Build Coastguard Worker skip_more_whitespace_and_comments:
88*2b949d04SAndroid Build Coastguard Worker     /* Skip spaces. */
89*2b949d04SAndroid Build Coastguard Worker     while (chr(s, ' ') || chr(s, '\t') || chr(s, '\r'));
90*2b949d04SAndroid Build Coastguard Worker 
91*2b949d04SAndroid Build Coastguard Worker     /* Skip comments. */
92*2b949d04SAndroid Build Coastguard Worker     if (lit(s, "//")) {
93*2b949d04SAndroid Build Coastguard Worker         skip_to_eol(s);
94*2b949d04SAndroid Build Coastguard Worker     }
95*2b949d04SAndroid Build Coastguard Worker 
96*2b949d04SAndroid Build Coastguard Worker     /* New line. */
97*2b949d04SAndroid Build Coastguard Worker     if (eol(s)) {
98*2b949d04SAndroid Build Coastguard Worker         while (eol(s)) next(s);
99*2b949d04SAndroid Build Coastguard Worker         return TOK_END_OF_LINE;
100*2b949d04SAndroid Build Coastguard Worker     }
101*2b949d04SAndroid Build Coastguard Worker 
102*2b949d04SAndroid Build Coastguard Worker     /* Escaped line continuation. */
103*2b949d04SAndroid Build Coastguard Worker     if (chr(s, '\\')) {
104*2b949d04SAndroid Build Coastguard Worker         /* Optional \r. */
105*2b949d04SAndroid Build Coastguard Worker         chr(s, '\r');
106*2b949d04SAndroid Build Coastguard Worker         if (!eol(s)) {
107*2b949d04SAndroid Build Coastguard Worker             scanner_err(s, "illegal new line escape; must appear at end of line");
108*2b949d04SAndroid Build Coastguard Worker             return TOK_ERROR;
109*2b949d04SAndroid Build Coastguard Worker         }
110*2b949d04SAndroid Build Coastguard Worker         next(s);
111*2b949d04SAndroid Build Coastguard Worker         goto skip_more_whitespace_and_comments;
112*2b949d04SAndroid Build Coastguard Worker     }
113*2b949d04SAndroid Build Coastguard Worker 
114*2b949d04SAndroid Build Coastguard Worker     /* See if we're done. */
115*2b949d04SAndroid Build Coastguard Worker     if (eof(s)) return TOK_END_OF_FILE;
116*2b949d04SAndroid Build Coastguard Worker 
117*2b949d04SAndroid Build Coastguard Worker     /* New token. */
118*2b949d04SAndroid Build Coastguard Worker     s->token_line = s->line;
119*2b949d04SAndroid Build Coastguard Worker     s->token_column = s->column;
120*2b949d04SAndroid Build Coastguard Worker 
121*2b949d04SAndroid Build Coastguard Worker     /* Operators and punctuation. */
122*2b949d04SAndroid Build Coastguard Worker     if (chr(s, '!')) return TOK_BANG;
123*2b949d04SAndroid Build Coastguard Worker     if (chr(s, '=')) return TOK_EQUALS;
124*2b949d04SAndroid Build Coastguard Worker     if (chr(s, '*')) return TOK_STAR;
125*2b949d04SAndroid Build Coastguard Worker 
126*2b949d04SAndroid Build Coastguard Worker     /* Group name. */
127*2b949d04SAndroid Build Coastguard Worker     if (chr(s, '$')) {
128*2b949d04SAndroid Build Coastguard Worker         val->string.start = s->s + s->pos;
129*2b949d04SAndroid Build Coastguard Worker         val->string.len = 0;
130*2b949d04SAndroid Build Coastguard Worker         while (is_ident(peek(s))) {
131*2b949d04SAndroid Build Coastguard Worker             next(s);
132*2b949d04SAndroid Build Coastguard Worker             val->string.len++;
133*2b949d04SAndroid Build Coastguard Worker         }
134*2b949d04SAndroid Build Coastguard Worker         if (val->string.len == 0) {
135*2b949d04SAndroid Build Coastguard Worker             scanner_err(s, "unexpected character after \'$\'; expected name");
136*2b949d04SAndroid Build Coastguard Worker             return TOK_ERROR;
137*2b949d04SAndroid Build Coastguard Worker         }
138*2b949d04SAndroid Build Coastguard Worker         return TOK_GROUP_NAME;
139*2b949d04SAndroid Build Coastguard Worker     }
140*2b949d04SAndroid Build Coastguard Worker 
141*2b949d04SAndroid Build Coastguard Worker     /* Include statement. */
142*2b949d04SAndroid Build Coastguard Worker     if (lit(s, "include"))
143*2b949d04SAndroid Build Coastguard Worker         return TOK_INCLUDE;
144*2b949d04SAndroid Build Coastguard Worker 
145*2b949d04SAndroid Build Coastguard Worker     /* Identifier. */
146*2b949d04SAndroid Build Coastguard Worker     if (is_ident(peek(s))) {
147*2b949d04SAndroid Build Coastguard Worker         val->string.start = s->s + s->pos;
148*2b949d04SAndroid Build Coastguard Worker         val->string.len = 0;
149*2b949d04SAndroid Build Coastguard Worker         while (is_ident(peek(s))) {
150*2b949d04SAndroid Build Coastguard Worker             next(s);
151*2b949d04SAndroid Build Coastguard Worker             val->string.len++;
152*2b949d04SAndroid Build Coastguard Worker         }
153*2b949d04SAndroid Build Coastguard Worker         return TOK_IDENTIFIER;
154*2b949d04SAndroid Build Coastguard Worker     }
155*2b949d04SAndroid Build Coastguard Worker 
156*2b949d04SAndroid Build Coastguard Worker     scanner_err(s, "unrecognized token");
157*2b949d04SAndroid Build Coastguard Worker     return TOK_ERROR;
158*2b949d04SAndroid Build Coastguard Worker }
159*2b949d04SAndroid Build Coastguard Worker 
160*2b949d04SAndroid Build Coastguard Worker /***====================================================================***/
161*2b949d04SAndroid Build Coastguard Worker 
162*2b949d04SAndroid Build Coastguard Worker enum rules_mlvo {
163*2b949d04SAndroid Build Coastguard Worker     MLVO_MODEL,
164*2b949d04SAndroid Build Coastguard Worker     MLVO_LAYOUT,
165*2b949d04SAndroid Build Coastguard Worker     MLVO_VARIANT,
166*2b949d04SAndroid Build Coastguard Worker     MLVO_OPTION,
167*2b949d04SAndroid Build Coastguard Worker     _MLVO_NUM_ENTRIES
168*2b949d04SAndroid Build Coastguard Worker };
169*2b949d04SAndroid Build Coastguard Worker 
170*2b949d04SAndroid Build Coastguard Worker #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
171*2b949d04SAndroid Build Coastguard Worker 
172*2b949d04SAndroid Build Coastguard Worker static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
173*2b949d04SAndroid Build Coastguard Worker     [MLVO_MODEL] = SVAL_LIT("model"),
174*2b949d04SAndroid Build Coastguard Worker     [MLVO_LAYOUT] = SVAL_LIT("layout"),
175*2b949d04SAndroid Build Coastguard Worker     [MLVO_VARIANT] = SVAL_LIT("variant"),
176*2b949d04SAndroid Build Coastguard Worker     [MLVO_OPTION] = SVAL_LIT("option"),
177*2b949d04SAndroid Build Coastguard Worker };
178*2b949d04SAndroid Build Coastguard Worker 
179*2b949d04SAndroid Build Coastguard Worker enum rules_kccgst {
180*2b949d04SAndroid Build Coastguard Worker     KCCGST_KEYCODES,
181*2b949d04SAndroid Build Coastguard Worker     KCCGST_TYPES,
182*2b949d04SAndroid Build Coastguard Worker     KCCGST_COMPAT,
183*2b949d04SAndroid Build Coastguard Worker     KCCGST_SYMBOLS,
184*2b949d04SAndroid Build Coastguard Worker     KCCGST_GEOMETRY,
185*2b949d04SAndroid Build Coastguard Worker     _KCCGST_NUM_ENTRIES
186*2b949d04SAndroid Build Coastguard Worker };
187*2b949d04SAndroid Build Coastguard Worker 
188*2b949d04SAndroid Build Coastguard Worker static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
189*2b949d04SAndroid Build Coastguard Worker     [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
190*2b949d04SAndroid Build Coastguard Worker     [KCCGST_TYPES] = SVAL_LIT("types"),
191*2b949d04SAndroid Build Coastguard Worker     [KCCGST_COMPAT] = SVAL_LIT("compat"),
192*2b949d04SAndroid Build Coastguard Worker     [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
193*2b949d04SAndroid Build Coastguard Worker     [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
194*2b949d04SAndroid Build Coastguard Worker };
195*2b949d04SAndroid Build Coastguard Worker 
196*2b949d04SAndroid Build Coastguard Worker /* We use this to keep score whether an mlvo was matched or not; if not,
197*2b949d04SAndroid Build Coastguard Worker  * we warn the user that his preference was ignored. */
198*2b949d04SAndroid Build Coastguard Worker struct matched_sval {
199*2b949d04SAndroid Build Coastguard Worker     struct sval sval;
200*2b949d04SAndroid Build Coastguard Worker     bool matched;
201*2b949d04SAndroid Build Coastguard Worker };
202*2b949d04SAndroid Build Coastguard Worker typedef darray(struct matched_sval) darray_matched_sval;
203*2b949d04SAndroid Build Coastguard Worker 
204*2b949d04SAndroid Build Coastguard Worker /*
205*2b949d04SAndroid Build Coastguard Worker  * A broken-down version of xkb_rule_names (without the rules,
206*2b949d04SAndroid Build Coastguard Worker  * obviously).
207*2b949d04SAndroid Build Coastguard Worker  */
208*2b949d04SAndroid Build Coastguard Worker struct rule_names {
209*2b949d04SAndroid Build Coastguard Worker     struct matched_sval model;
210*2b949d04SAndroid Build Coastguard Worker     darray_matched_sval layouts;
211*2b949d04SAndroid Build Coastguard Worker     darray_matched_sval variants;
212*2b949d04SAndroid Build Coastguard Worker     darray_matched_sval options;
213*2b949d04SAndroid Build Coastguard Worker };
214*2b949d04SAndroid Build Coastguard Worker 
215*2b949d04SAndroid Build Coastguard Worker struct group {
216*2b949d04SAndroid Build Coastguard Worker     struct sval name;
217*2b949d04SAndroid Build Coastguard Worker     darray_sval elements;
218*2b949d04SAndroid Build Coastguard Worker };
219*2b949d04SAndroid Build Coastguard Worker 
220*2b949d04SAndroid Build Coastguard Worker struct mapping {
221*2b949d04SAndroid Build Coastguard Worker     int mlvo_at_pos[_MLVO_NUM_ENTRIES];
222*2b949d04SAndroid Build Coastguard Worker     unsigned int num_mlvo;
223*2b949d04SAndroid Build Coastguard Worker     unsigned int defined_mlvo_mask;
224*2b949d04SAndroid Build Coastguard Worker     xkb_layout_index_t layout_idx, variant_idx;
225*2b949d04SAndroid Build Coastguard Worker     int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
226*2b949d04SAndroid Build Coastguard Worker     unsigned int num_kccgst;
227*2b949d04SAndroid Build Coastguard Worker     unsigned int defined_kccgst_mask;
228*2b949d04SAndroid Build Coastguard Worker     bool skip;
229*2b949d04SAndroid Build Coastguard Worker };
230*2b949d04SAndroid Build Coastguard Worker 
231*2b949d04SAndroid Build Coastguard Worker enum mlvo_match_type {
232*2b949d04SAndroid Build Coastguard Worker     MLVO_MATCH_NORMAL = 0,
233*2b949d04SAndroid Build Coastguard Worker     MLVO_MATCH_WILDCARD,
234*2b949d04SAndroid Build Coastguard Worker     MLVO_MATCH_GROUP,
235*2b949d04SAndroid Build Coastguard Worker };
236*2b949d04SAndroid Build Coastguard Worker 
237*2b949d04SAndroid Build Coastguard Worker struct rule {
238*2b949d04SAndroid Build Coastguard Worker     struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
239*2b949d04SAndroid Build Coastguard Worker     enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
240*2b949d04SAndroid Build Coastguard Worker     unsigned int num_mlvo_values;
241*2b949d04SAndroid Build Coastguard Worker     struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
242*2b949d04SAndroid Build Coastguard Worker     unsigned int num_kccgst_values;
243*2b949d04SAndroid Build Coastguard Worker     bool skip;
244*2b949d04SAndroid Build Coastguard Worker };
245*2b949d04SAndroid Build Coastguard Worker 
246*2b949d04SAndroid Build Coastguard Worker /*
247*2b949d04SAndroid Build Coastguard Worker  * This is the main object used to match a given RMLVO against a rules
248*2b949d04SAndroid Build Coastguard Worker  * file and aggragate the results in a KcCGST. It goes through a simple
249*2b949d04SAndroid Build Coastguard Worker  * matching state machine, with tokens as transitions (see
250*2b949d04SAndroid Build Coastguard Worker  * matcher_match()).
251*2b949d04SAndroid Build Coastguard Worker  */
252*2b949d04SAndroid Build Coastguard Worker struct matcher {
253*2b949d04SAndroid Build Coastguard Worker     struct xkb_context *ctx;
254*2b949d04SAndroid Build Coastguard Worker     /* Input.*/
255*2b949d04SAndroid Build Coastguard Worker     struct rule_names rmlvo;
256*2b949d04SAndroid Build Coastguard Worker     union lvalue val;
257*2b949d04SAndroid Build Coastguard Worker     darray(struct group) groups;
258*2b949d04SAndroid Build Coastguard Worker     /* Current mapping. */
259*2b949d04SAndroid Build Coastguard Worker     struct mapping mapping;
260*2b949d04SAndroid Build Coastguard Worker     /* Current rule. */
261*2b949d04SAndroid Build Coastguard Worker     struct rule rule;
262*2b949d04SAndroid Build Coastguard Worker     /* Output. */
263*2b949d04SAndroid Build Coastguard Worker     darray_char kccgst[_KCCGST_NUM_ENTRIES];
264*2b949d04SAndroid Build Coastguard Worker };
265*2b949d04SAndroid Build Coastguard Worker 
266*2b949d04SAndroid Build Coastguard Worker static struct sval
strip_spaces(struct sval v)267*2b949d04SAndroid Build Coastguard Worker strip_spaces(struct sval v)
268*2b949d04SAndroid Build Coastguard Worker {
269*2b949d04SAndroid Build Coastguard Worker     while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; }
270*2b949d04SAndroid Build Coastguard Worker     while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--;
271*2b949d04SAndroid Build Coastguard Worker     return v;
272*2b949d04SAndroid Build Coastguard Worker }
273*2b949d04SAndroid Build Coastguard Worker 
274*2b949d04SAndroid Build Coastguard Worker static darray_matched_sval
split_comma_separated_mlvo(const char * s)275*2b949d04SAndroid Build Coastguard Worker split_comma_separated_mlvo(const char *s)
276*2b949d04SAndroid Build Coastguard Worker {
277*2b949d04SAndroid Build Coastguard Worker     darray_matched_sval arr = darray_new();
278*2b949d04SAndroid Build Coastguard Worker 
279*2b949d04SAndroid Build Coastguard Worker     /*
280*2b949d04SAndroid Build Coastguard Worker      * Make sure the array returned by this function always includes at
281*2b949d04SAndroid Build Coastguard Worker      * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
282*2b949d04SAndroid Build Coastguard Worker      */
283*2b949d04SAndroid Build Coastguard Worker 
284*2b949d04SAndroid Build Coastguard Worker     if (!s) {
285*2b949d04SAndroid Build Coastguard Worker         struct matched_sval val = { .sval = { NULL, 0 } };
286*2b949d04SAndroid Build Coastguard Worker         darray_append(arr, val);
287*2b949d04SAndroid Build Coastguard Worker         return arr;
288*2b949d04SAndroid Build Coastguard Worker     }
289*2b949d04SAndroid Build Coastguard Worker 
290*2b949d04SAndroid Build Coastguard Worker     while (true) {
291*2b949d04SAndroid Build Coastguard Worker         struct matched_sval val = { .sval = { s, 0 } };
292*2b949d04SAndroid Build Coastguard Worker         while (*s != '\0' && *s != ',') { s++; val.sval.len++; }
293*2b949d04SAndroid Build Coastguard Worker         val.sval = strip_spaces(val.sval);
294*2b949d04SAndroid Build Coastguard Worker         darray_append(arr, val);
295*2b949d04SAndroid Build Coastguard Worker         if (*s == '\0') break;
296*2b949d04SAndroid Build Coastguard Worker         if (*s == ',') s++;
297*2b949d04SAndroid Build Coastguard Worker     }
298*2b949d04SAndroid Build Coastguard Worker 
299*2b949d04SAndroid Build Coastguard Worker     return arr;
300*2b949d04SAndroid Build Coastguard Worker }
301*2b949d04SAndroid Build Coastguard Worker 
302*2b949d04SAndroid Build Coastguard Worker static struct matcher *
matcher_new(struct xkb_context * ctx,const struct xkb_rule_names * rmlvo)303*2b949d04SAndroid Build Coastguard Worker matcher_new(struct xkb_context *ctx,
304*2b949d04SAndroid Build Coastguard Worker             const struct xkb_rule_names *rmlvo)
305*2b949d04SAndroid Build Coastguard Worker {
306*2b949d04SAndroid Build Coastguard Worker     struct matcher *m = calloc(1, sizeof(*m));
307*2b949d04SAndroid Build Coastguard Worker     if (!m)
308*2b949d04SAndroid Build Coastguard Worker         return NULL;
309*2b949d04SAndroid Build Coastguard Worker 
310*2b949d04SAndroid Build Coastguard Worker     m->ctx = ctx;
311*2b949d04SAndroid Build Coastguard Worker     m->rmlvo.model.sval.start = rmlvo->model;
312*2b949d04SAndroid Build Coastguard Worker     m->rmlvo.model.sval.len = strlen_safe(rmlvo->model);
313*2b949d04SAndroid Build Coastguard Worker     m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout);
314*2b949d04SAndroid Build Coastguard Worker     m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant);
315*2b949d04SAndroid Build Coastguard Worker     m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options);
316*2b949d04SAndroid Build Coastguard Worker 
317*2b949d04SAndroid Build Coastguard Worker     return m;
318*2b949d04SAndroid Build Coastguard Worker }
319*2b949d04SAndroid Build Coastguard Worker 
320*2b949d04SAndroid Build Coastguard Worker static void
matcher_free(struct matcher * m)321*2b949d04SAndroid Build Coastguard Worker matcher_free(struct matcher *m)
322*2b949d04SAndroid Build Coastguard Worker {
323*2b949d04SAndroid Build Coastguard Worker     struct group *group;
324*2b949d04SAndroid Build Coastguard Worker     if (!m)
325*2b949d04SAndroid Build Coastguard Worker         return;
326*2b949d04SAndroid Build Coastguard Worker     darray_free(m->rmlvo.layouts);
327*2b949d04SAndroid Build Coastguard Worker     darray_free(m->rmlvo.variants);
328*2b949d04SAndroid Build Coastguard Worker     darray_free(m->rmlvo.options);
329*2b949d04SAndroid Build Coastguard Worker     darray_foreach(group, m->groups)
330*2b949d04SAndroid Build Coastguard Worker         darray_free(group->elements);
331*2b949d04SAndroid Build Coastguard Worker     for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++)
332*2b949d04SAndroid Build Coastguard Worker         darray_free(m->kccgst[i]);
333*2b949d04SAndroid Build Coastguard Worker     darray_free(m->groups);
334*2b949d04SAndroid Build Coastguard Worker     free(m);
335*2b949d04SAndroid Build Coastguard Worker }
336*2b949d04SAndroid Build Coastguard Worker 
337*2b949d04SAndroid Build Coastguard Worker static void
matcher_group_start_new(struct matcher * m,struct sval name)338*2b949d04SAndroid Build Coastguard Worker matcher_group_start_new(struct matcher *m, struct sval name)
339*2b949d04SAndroid Build Coastguard Worker {
340*2b949d04SAndroid Build Coastguard Worker     struct group group = { .name = name, .elements = darray_new() };
341*2b949d04SAndroid Build Coastguard Worker     darray_append(m->groups, group);
342*2b949d04SAndroid Build Coastguard Worker }
343*2b949d04SAndroid Build Coastguard Worker 
344*2b949d04SAndroid Build Coastguard Worker static void
matcher_group_add_element(struct matcher * m,struct scanner * s,struct sval element)345*2b949d04SAndroid Build Coastguard Worker matcher_group_add_element(struct matcher *m, struct scanner *s,
346*2b949d04SAndroid Build Coastguard Worker                           struct sval element)
347*2b949d04SAndroid Build Coastguard Worker {
348*2b949d04SAndroid Build Coastguard Worker     darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
349*2b949d04SAndroid Build Coastguard Worker                   element);
350*2b949d04SAndroid Build Coastguard Worker }
351*2b949d04SAndroid Build Coastguard Worker 
352*2b949d04SAndroid Build Coastguard Worker static bool
353*2b949d04SAndroid Build Coastguard Worker read_rules_file(struct xkb_context *ctx,
354*2b949d04SAndroid Build Coastguard Worker                 struct matcher *matcher,
355*2b949d04SAndroid Build Coastguard Worker                 unsigned include_depth,
356*2b949d04SAndroid Build Coastguard Worker                 FILE *file,
357*2b949d04SAndroid Build Coastguard Worker                 const char *path);
358*2b949d04SAndroid Build Coastguard Worker 
359*2b949d04SAndroid Build Coastguard Worker static void
matcher_include(struct matcher * m,struct scanner * parent_scanner,unsigned include_depth,struct sval inc)360*2b949d04SAndroid Build Coastguard Worker matcher_include(struct matcher *m, struct scanner *parent_scanner,
361*2b949d04SAndroid Build Coastguard Worker                 unsigned include_depth,
362*2b949d04SAndroid Build Coastguard Worker                 struct sval inc)
363*2b949d04SAndroid Build Coastguard Worker {
364*2b949d04SAndroid Build Coastguard Worker     struct scanner s; /* parses the !include value */
365*2b949d04SAndroid Build Coastguard Worker     FILE *file;
366*2b949d04SAndroid Build Coastguard Worker 
367*2b949d04SAndroid Build Coastguard Worker     scanner_init(&s, m->ctx, inc.start, inc.len,
368*2b949d04SAndroid Build Coastguard Worker                  parent_scanner->file_name, NULL);
369*2b949d04SAndroid Build Coastguard Worker     s.token_line = parent_scanner->token_line;
370*2b949d04SAndroid Build Coastguard Worker     s.token_column = parent_scanner->token_column;
371*2b949d04SAndroid Build Coastguard Worker     s.buf_pos = 0;
372*2b949d04SAndroid Build Coastguard Worker 
373*2b949d04SAndroid Build Coastguard Worker     if (include_depth >= MAX_INCLUDE_DEPTH) {
374*2b949d04SAndroid Build Coastguard Worker         scanner_err(&s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
375*2b949d04SAndroid Build Coastguard Worker                     MAX_INCLUDE_DEPTH);
376*2b949d04SAndroid Build Coastguard Worker         return;
377*2b949d04SAndroid Build Coastguard Worker     }
378*2b949d04SAndroid Build Coastguard Worker 
379*2b949d04SAndroid Build Coastguard Worker     while (!eof(&s) && !eol(&s)) {
380*2b949d04SAndroid Build Coastguard Worker         if (chr(&s, '%')) {
381*2b949d04SAndroid Build Coastguard Worker             if (chr(&s, '%')) {
382*2b949d04SAndroid Build Coastguard Worker                 buf_append(&s, '%');
383*2b949d04SAndroid Build Coastguard Worker             }
384*2b949d04SAndroid Build Coastguard Worker             else if (chr(&s, 'H')) {
385*2b949d04SAndroid Build Coastguard Worker                 const char *home = secure_getenv("HOME");
386*2b949d04SAndroid Build Coastguard Worker                 if (!home) {
387*2b949d04SAndroid Build Coastguard Worker                     scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set");
388*2b949d04SAndroid Build Coastguard Worker                     return;
389*2b949d04SAndroid Build Coastguard Worker                 }
390*2b949d04SAndroid Build Coastguard Worker                 if (!buf_appends(&s, home)) {
391*2b949d04SAndroid Build Coastguard Worker                     scanner_err(&s, "include path after expanding %%H is too long");
392*2b949d04SAndroid Build Coastguard Worker                     return;
393*2b949d04SAndroid Build Coastguard Worker                 }
394*2b949d04SAndroid Build Coastguard Worker             }
395*2b949d04SAndroid Build Coastguard Worker             else if (chr(&s, 'S')) {
396*2b949d04SAndroid Build Coastguard Worker                 const char *default_root = xkb_context_include_path_get_system_path(m->ctx);
397*2b949d04SAndroid Build Coastguard Worker                 if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) {
398*2b949d04SAndroid Build Coastguard Worker                     scanner_err(&s, "include path after expanding %%S is too long");
399*2b949d04SAndroid Build Coastguard Worker                     return;
400*2b949d04SAndroid Build Coastguard Worker                 }
401*2b949d04SAndroid Build Coastguard Worker             }
402*2b949d04SAndroid Build Coastguard Worker             else if (chr(&s, 'E')) {
403*2b949d04SAndroid Build Coastguard Worker                 const char *default_root = xkb_context_include_path_get_extra_path(m->ctx);
404*2b949d04SAndroid Build Coastguard Worker                 if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) {
405*2b949d04SAndroid Build Coastguard Worker                     scanner_err(&s, "include path after expanding %%E is too long");
406*2b949d04SAndroid Build Coastguard Worker                     return;
407*2b949d04SAndroid Build Coastguard Worker                 }
408*2b949d04SAndroid Build Coastguard Worker             }
409*2b949d04SAndroid Build Coastguard Worker             else {
410*2b949d04SAndroid Build Coastguard Worker                 scanner_err(&s, "unknown %% format (%c) in include statement", peek(&s));
411*2b949d04SAndroid Build Coastguard Worker                 return;
412*2b949d04SAndroid Build Coastguard Worker             }
413*2b949d04SAndroid Build Coastguard Worker         }
414*2b949d04SAndroid Build Coastguard Worker         else {
415*2b949d04SAndroid Build Coastguard Worker             buf_append(&s, next(&s));
416*2b949d04SAndroid Build Coastguard Worker         }
417*2b949d04SAndroid Build Coastguard Worker     }
418*2b949d04SAndroid Build Coastguard Worker     if (!buf_append(&s, '\0')) {
419*2b949d04SAndroid Build Coastguard Worker         scanner_err(&s, "include path is too long");
420*2b949d04SAndroid Build Coastguard Worker         return;
421*2b949d04SAndroid Build Coastguard Worker     }
422*2b949d04SAndroid Build Coastguard Worker 
423*2b949d04SAndroid Build Coastguard Worker     file = fopen(s.buf, "rb");
424*2b949d04SAndroid Build Coastguard Worker     if (file) {
425*2b949d04SAndroid Build Coastguard Worker         bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf);
426*2b949d04SAndroid Build Coastguard Worker         if (!ret)
427*2b949d04SAndroid Build Coastguard Worker             log_err(m->ctx, "No components returned from included XKB rules \"%s\"\n", s.buf);
428*2b949d04SAndroid Build Coastguard Worker         fclose(file);
429*2b949d04SAndroid Build Coastguard Worker     } else {
430*2b949d04SAndroid Build Coastguard Worker         log_err(m->ctx, "Failed to open included XKB rules \"%s\"\n", s.buf);
431*2b949d04SAndroid Build Coastguard Worker     }
432*2b949d04SAndroid Build Coastguard Worker }
433*2b949d04SAndroid Build Coastguard Worker 
434*2b949d04SAndroid Build Coastguard Worker static void
matcher_mapping_start_new(struct matcher * m)435*2b949d04SAndroid Build Coastguard Worker matcher_mapping_start_new(struct matcher *m)
436*2b949d04SAndroid Build Coastguard Worker {
437*2b949d04SAndroid Build Coastguard Worker     for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++)
438*2b949d04SAndroid Build Coastguard Worker         m->mapping.mlvo_at_pos[i] = -1;
439*2b949d04SAndroid Build Coastguard Worker     for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++)
440*2b949d04SAndroid Build Coastguard Worker         m->mapping.kccgst_at_pos[i] = -1;
441*2b949d04SAndroid Build Coastguard Worker     m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
442*2b949d04SAndroid Build Coastguard Worker     m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
443*2b949d04SAndroid Build Coastguard Worker     m->mapping.defined_mlvo_mask = 0;
444*2b949d04SAndroid Build Coastguard Worker     m->mapping.defined_kccgst_mask = 0;
445*2b949d04SAndroid Build Coastguard Worker     m->mapping.skip = false;
446*2b949d04SAndroid Build Coastguard Worker }
447*2b949d04SAndroid Build Coastguard Worker 
448*2b949d04SAndroid Build Coastguard Worker static int
extract_layout_index(const char * s,size_t max_len,xkb_layout_index_t * out)449*2b949d04SAndroid Build Coastguard Worker extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
450*2b949d04SAndroid Build Coastguard Worker {
451*2b949d04SAndroid Build Coastguard Worker     /* This function is pretty stupid, but works for now. */
452*2b949d04SAndroid Build Coastguard Worker     *out = XKB_LAYOUT_INVALID;
453*2b949d04SAndroid Build Coastguard Worker     if (max_len < 3)
454*2b949d04SAndroid Build Coastguard Worker         return -1;
455*2b949d04SAndroid Build Coastguard Worker     if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']')
456*2b949d04SAndroid Build Coastguard Worker         return -1;
457*2b949d04SAndroid Build Coastguard Worker     if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
458*2b949d04SAndroid Build Coastguard Worker         return -1;
459*2b949d04SAndroid Build Coastguard Worker     /* To zero-based index. */
460*2b949d04SAndroid Build Coastguard Worker     *out = s[1] - '0' - 1;
461*2b949d04SAndroid Build Coastguard Worker     return 3;
462*2b949d04SAndroid Build Coastguard Worker }
463*2b949d04SAndroid Build Coastguard Worker 
464*2b949d04SAndroid Build Coastguard Worker static void
matcher_mapping_set_mlvo(struct matcher * m,struct scanner * s,struct sval ident)465*2b949d04SAndroid Build Coastguard Worker matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s,
466*2b949d04SAndroid Build Coastguard Worker                          struct sval ident)
467*2b949d04SAndroid Build Coastguard Worker {
468*2b949d04SAndroid Build Coastguard Worker     enum rules_mlvo mlvo;
469*2b949d04SAndroid Build Coastguard Worker     struct sval mlvo_sval;
470*2b949d04SAndroid Build Coastguard Worker 
471*2b949d04SAndroid Build Coastguard Worker     for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
472*2b949d04SAndroid Build Coastguard Worker         mlvo_sval = rules_mlvo_svals[mlvo];
473*2b949d04SAndroid Build Coastguard Worker 
474*2b949d04SAndroid Build Coastguard Worker         if (svaleq_prefix(mlvo_sval, ident))
475*2b949d04SAndroid Build Coastguard Worker             break;
476*2b949d04SAndroid Build Coastguard Worker     }
477*2b949d04SAndroid Build Coastguard Worker 
478*2b949d04SAndroid Build Coastguard Worker     /* Not found. */
479*2b949d04SAndroid Build Coastguard Worker     if (mlvo >= _MLVO_NUM_ENTRIES) {
480*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
481*2b949d04SAndroid Build Coastguard Worker                     ident.len, ident.start);
482*2b949d04SAndroid Build Coastguard Worker         m->mapping.skip = true;
483*2b949d04SAndroid Build Coastguard Worker         return;
484*2b949d04SAndroid Build Coastguard Worker     }
485*2b949d04SAndroid Build Coastguard Worker 
486*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.defined_mlvo_mask & (1u << mlvo)) {
487*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
488*2b949d04SAndroid Build Coastguard Worker                     mlvo_sval.len, mlvo_sval.start);
489*2b949d04SAndroid Build Coastguard Worker         m->mapping.skip = true;
490*2b949d04SAndroid Build Coastguard Worker         return;
491*2b949d04SAndroid Build Coastguard Worker     }
492*2b949d04SAndroid Build Coastguard Worker 
493*2b949d04SAndroid Build Coastguard Worker     /* If there are leftovers still, it must be an index. */
494*2b949d04SAndroid Build Coastguard Worker     if (mlvo_sval.len < ident.len) {
495*2b949d04SAndroid Build Coastguard Worker         xkb_layout_index_t idx;
496*2b949d04SAndroid Build Coastguard Worker         int consumed = extract_layout_index(ident.start + mlvo_sval.len,
497*2b949d04SAndroid Build Coastguard Worker                                             ident.len - mlvo_sval.len, &idx);
498*2b949d04SAndroid Build Coastguard Worker         if ((int) (ident.len - mlvo_sval.len) != consumed) {
499*2b949d04SAndroid Build Coastguard Worker             scanner_err(s, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set",
500*2b949d04SAndroid Build Coastguard Worker                         mlvo_sval.len, mlvo_sval.start);
501*2b949d04SAndroid Build Coastguard Worker             m->mapping.skip = true;
502*2b949d04SAndroid Build Coastguard Worker             return;
503*2b949d04SAndroid Build Coastguard Worker         }
504*2b949d04SAndroid Build Coastguard Worker 
505*2b949d04SAndroid Build Coastguard Worker         if (mlvo == MLVO_LAYOUT) {
506*2b949d04SAndroid Build Coastguard Worker             m->mapping.layout_idx = idx;
507*2b949d04SAndroid Build Coastguard Worker         }
508*2b949d04SAndroid Build Coastguard Worker         else if (mlvo == MLVO_VARIANT) {
509*2b949d04SAndroid Build Coastguard Worker             m->mapping.variant_idx = idx;
510*2b949d04SAndroid Build Coastguard Worker         }
511*2b949d04SAndroid Build Coastguard Worker         else {
512*2b949d04SAndroid Build Coastguard Worker             scanner_err(s, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set",
513*2b949d04SAndroid Build Coastguard Worker                         mlvo_sval.len, mlvo_sval.start);
514*2b949d04SAndroid Build Coastguard Worker             m->mapping.skip = true;
515*2b949d04SAndroid Build Coastguard Worker             return;
516*2b949d04SAndroid Build Coastguard Worker         }
517*2b949d04SAndroid Build Coastguard Worker     }
518*2b949d04SAndroid Build Coastguard Worker 
519*2b949d04SAndroid Build Coastguard Worker     m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
520*2b949d04SAndroid Build Coastguard Worker     m->mapping.defined_mlvo_mask |= 1u << mlvo;
521*2b949d04SAndroid Build Coastguard Worker     m->mapping.num_mlvo++;
522*2b949d04SAndroid Build Coastguard Worker }
523*2b949d04SAndroid Build Coastguard Worker 
524*2b949d04SAndroid Build Coastguard Worker static void
matcher_mapping_set_kccgst(struct matcher * m,struct scanner * s,struct sval ident)525*2b949d04SAndroid Build Coastguard Worker matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident)
526*2b949d04SAndroid Build Coastguard Worker {
527*2b949d04SAndroid Build Coastguard Worker     enum rules_kccgst kccgst;
528*2b949d04SAndroid Build Coastguard Worker     struct sval kccgst_sval;
529*2b949d04SAndroid Build Coastguard Worker 
530*2b949d04SAndroid Build Coastguard Worker     for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
531*2b949d04SAndroid Build Coastguard Worker         kccgst_sval = rules_kccgst_svals[kccgst];
532*2b949d04SAndroid Build Coastguard Worker 
533*2b949d04SAndroid Build Coastguard Worker         if (svaleq(rules_kccgst_svals[kccgst], ident))
534*2b949d04SAndroid Build Coastguard Worker             break;
535*2b949d04SAndroid Build Coastguard Worker     }
536*2b949d04SAndroid Build Coastguard Worker 
537*2b949d04SAndroid Build Coastguard Worker     /* Not found. */
538*2b949d04SAndroid Build Coastguard Worker     if (kccgst >= _KCCGST_NUM_ENTRIES) {
539*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
540*2b949d04SAndroid Build Coastguard Worker                     ident.len, ident.start);
541*2b949d04SAndroid Build Coastguard Worker         m->mapping.skip = true;
542*2b949d04SAndroid Build Coastguard Worker         return;
543*2b949d04SAndroid Build Coastguard Worker     }
544*2b949d04SAndroid Build Coastguard Worker 
545*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.defined_kccgst_mask & (1u << kccgst)) {
546*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
547*2b949d04SAndroid Build Coastguard Worker                     kccgst_sval.len, kccgst_sval.start);
548*2b949d04SAndroid Build Coastguard Worker         m->mapping.skip = true;
549*2b949d04SAndroid Build Coastguard Worker         return;
550*2b949d04SAndroid Build Coastguard Worker     }
551*2b949d04SAndroid Build Coastguard Worker 
552*2b949d04SAndroid Build Coastguard Worker     m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
553*2b949d04SAndroid Build Coastguard Worker     m->mapping.defined_kccgst_mask |= 1u << kccgst;
554*2b949d04SAndroid Build Coastguard Worker     m->mapping.num_kccgst++;
555*2b949d04SAndroid Build Coastguard Worker }
556*2b949d04SAndroid Build Coastguard Worker 
557*2b949d04SAndroid Build Coastguard Worker static void
matcher_mapping_verify(struct matcher * m,struct scanner * s)558*2b949d04SAndroid Build Coastguard Worker matcher_mapping_verify(struct matcher *m, struct scanner *s)
559*2b949d04SAndroid Build Coastguard Worker {
560*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.num_mlvo == 0) {
561*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: must have at least one value on the left hand side; ignoring rule set");
562*2b949d04SAndroid Build Coastguard Worker         goto skip;
563*2b949d04SAndroid Build Coastguard Worker     }
564*2b949d04SAndroid Build Coastguard Worker 
565*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.num_kccgst == 0) {
566*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid mapping: must have at least one value on the right hand side; ignoring rule set");
567*2b949d04SAndroid Build Coastguard Worker         goto skip;
568*2b949d04SAndroid Build Coastguard Worker     }
569*2b949d04SAndroid Build Coastguard Worker 
570*2b949d04SAndroid Build Coastguard Worker     /*
571*2b949d04SAndroid Build Coastguard Worker      * This following is very stupid, but this is how it works.
572*2b949d04SAndroid Build Coastguard Worker      * See the "Notes" section in the overview above.
573*2b949d04SAndroid Build Coastguard Worker      */
574*2b949d04SAndroid Build Coastguard Worker 
575*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) {
576*2b949d04SAndroid Build Coastguard Worker         if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
577*2b949d04SAndroid Build Coastguard Worker             if (darray_size(m->rmlvo.layouts) > 1)
578*2b949d04SAndroid Build Coastguard Worker                 goto skip;
579*2b949d04SAndroid Build Coastguard Worker         }
580*2b949d04SAndroid Build Coastguard Worker         else {
581*2b949d04SAndroid Build Coastguard Worker             if (darray_size(m->rmlvo.layouts) == 1 ||
582*2b949d04SAndroid Build Coastguard Worker                 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
583*2b949d04SAndroid Build Coastguard Worker                 goto skip;
584*2b949d04SAndroid Build Coastguard Worker         }
585*2b949d04SAndroid Build Coastguard Worker     }
586*2b949d04SAndroid Build Coastguard Worker 
587*2b949d04SAndroid Build Coastguard Worker     if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) {
588*2b949d04SAndroid Build Coastguard Worker         if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
589*2b949d04SAndroid Build Coastguard Worker             if (darray_size(m->rmlvo.variants) > 1)
590*2b949d04SAndroid Build Coastguard Worker                 goto skip;
591*2b949d04SAndroid Build Coastguard Worker         }
592*2b949d04SAndroid Build Coastguard Worker         else {
593*2b949d04SAndroid Build Coastguard Worker             if (darray_size(m->rmlvo.variants) == 1 ||
594*2b949d04SAndroid Build Coastguard Worker                 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
595*2b949d04SAndroid Build Coastguard Worker                 goto skip;
596*2b949d04SAndroid Build Coastguard Worker         }
597*2b949d04SAndroid Build Coastguard Worker     }
598*2b949d04SAndroid Build Coastguard Worker 
599*2b949d04SAndroid Build Coastguard Worker     return;
600*2b949d04SAndroid Build Coastguard Worker 
601*2b949d04SAndroid Build Coastguard Worker skip:
602*2b949d04SAndroid Build Coastguard Worker     m->mapping.skip = true;
603*2b949d04SAndroid Build Coastguard Worker }
604*2b949d04SAndroid Build Coastguard Worker 
605*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_start_new(struct matcher * m)606*2b949d04SAndroid Build Coastguard Worker matcher_rule_start_new(struct matcher *m)
607*2b949d04SAndroid Build Coastguard Worker {
608*2b949d04SAndroid Build Coastguard Worker     memset(&m->rule, 0, sizeof(m->rule));
609*2b949d04SAndroid Build Coastguard Worker     m->rule.skip = m->mapping.skip;
610*2b949d04SAndroid Build Coastguard Worker }
611*2b949d04SAndroid Build Coastguard Worker 
612*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_set_mlvo_common(struct matcher * m,struct scanner * s,struct sval ident,enum mlvo_match_type match_type)613*2b949d04SAndroid Build Coastguard Worker matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s,
614*2b949d04SAndroid Build Coastguard Worker                              struct sval ident,
615*2b949d04SAndroid Build Coastguard Worker                              enum mlvo_match_type match_type)
616*2b949d04SAndroid Build Coastguard Worker {
617*2b949d04SAndroid Build Coastguard Worker     if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
618*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
619*2b949d04SAndroid Build Coastguard Worker         m->rule.skip = true;
620*2b949d04SAndroid Build Coastguard Worker         return;
621*2b949d04SAndroid Build Coastguard Worker     }
622*2b949d04SAndroid Build Coastguard Worker     m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
623*2b949d04SAndroid Build Coastguard Worker     m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
624*2b949d04SAndroid Build Coastguard Worker     m->rule.num_mlvo_values++;
625*2b949d04SAndroid Build Coastguard Worker }
626*2b949d04SAndroid Build Coastguard Worker 
627*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_set_mlvo_wildcard(struct matcher * m,struct scanner * s)628*2b949d04SAndroid Build Coastguard Worker matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s)
629*2b949d04SAndroid Build Coastguard Worker {
630*2b949d04SAndroid Build Coastguard Worker     struct sval dummy = { NULL, 0 };
631*2b949d04SAndroid Build Coastguard Worker     matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD);
632*2b949d04SAndroid Build Coastguard Worker }
633*2b949d04SAndroid Build Coastguard Worker 
634*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_set_mlvo_group(struct matcher * m,struct scanner * s,struct sval ident)635*2b949d04SAndroid Build Coastguard Worker matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s,
636*2b949d04SAndroid Build Coastguard Worker                             struct sval ident)
637*2b949d04SAndroid Build Coastguard Worker {
638*2b949d04SAndroid Build Coastguard Worker     matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP);
639*2b949d04SAndroid Build Coastguard Worker }
640*2b949d04SAndroid Build Coastguard Worker 
641*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_set_mlvo(struct matcher * m,struct scanner * s,struct sval ident)642*2b949d04SAndroid Build Coastguard Worker matcher_rule_set_mlvo(struct matcher *m, struct scanner *s,
643*2b949d04SAndroid Build Coastguard Worker                       struct sval ident)
644*2b949d04SAndroid Build Coastguard Worker {
645*2b949d04SAndroid Build Coastguard Worker     matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL);
646*2b949d04SAndroid Build Coastguard Worker }
647*2b949d04SAndroid Build Coastguard Worker 
648*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_set_kccgst(struct matcher * m,struct scanner * s,struct sval ident)649*2b949d04SAndroid Build Coastguard Worker matcher_rule_set_kccgst(struct matcher *m, struct scanner *s,
650*2b949d04SAndroid Build Coastguard Worker                         struct sval ident)
651*2b949d04SAndroid Build Coastguard Worker {
652*2b949d04SAndroid Build Coastguard Worker     if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
653*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
654*2b949d04SAndroid Build Coastguard Worker         m->rule.skip = true;
655*2b949d04SAndroid Build Coastguard Worker         return;
656*2b949d04SAndroid Build Coastguard Worker     }
657*2b949d04SAndroid Build Coastguard Worker     m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
658*2b949d04SAndroid Build Coastguard Worker     m->rule.num_kccgst_values++;
659*2b949d04SAndroid Build Coastguard Worker }
660*2b949d04SAndroid Build Coastguard Worker 
661*2b949d04SAndroid Build Coastguard Worker static bool
match_group(struct matcher * m,struct sval group_name,struct sval to)662*2b949d04SAndroid Build Coastguard Worker match_group(struct matcher *m, struct sval group_name, struct sval to)
663*2b949d04SAndroid Build Coastguard Worker {
664*2b949d04SAndroid Build Coastguard Worker     struct group *group;
665*2b949d04SAndroid Build Coastguard Worker     struct sval *element;
666*2b949d04SAndroid Build Coastguard Worker     bool found = false;
667*2b949d04SAndroid Build Coastguard Worker 
668*2b949d04SAndroid Build Coastguard Worker     darray_foreach(group, m->groups) {
669*2b949d04SAndroid Build Coastguard Worker         if (svaleq(group->name, group_name)) {
670*2b949d04SAndroid Build Coastguard Worker             found = true;
671*2b949d04SAndroid Build Coastguard Worker             break;
672*2b949d04SAndroid Build Coastguard Worker         }
673*2b949d04SAndroid Build Coastguard Worker     }
674*2b949d04SAndroid Build Coastguard Worker 
675*2b949d04SAndroid Build Coastguard Worker     if (!found) {
676*2b949d04SAndroid Build Coastguard Worker         /*
677*2b949d04SAndroid Build Coastguard Worker          * rules/evdev intentionally uses some undeclared group names
678*2b949d04SAndroid Build Coastguard Worker          * in rules (e.g. commented group definitions which may be
679*2b949d04SAndroid Build Coastguard Worker          * uncommented if needed). So we continue silently.
680*2b949d04SAndroid Build Coastguard Worker          */
681*2b949d04SAndroid Build Coastguard Worker         return false;
682*2b949d04SAndroid Build Coastguard Worker     }
683*2b949d04SAndroid Build Coastguard Worker 
684*2b949d04SAndroid Build Coastguard Worker     darray_foreach(element, group->elements)
685*2b949d04SAndroid Build Coastguard Worker         if (svaleq(to, *element))
686*2b949d04SAndroid Build Coastguard Worker             return true;
687*2b949d04SAndroid Build Coastguard Worker 
688*2b949d04SAndroid Build Coastguard Worker     return false;
689*2b949d04SAndroid Build Coastguard Worker }
690*2b949d04SAndroid Build Coastguard Worker 
691*2b949d04SAndroid Build Coastguard Worker static bool
match_value(struct matcher * m,struct sval val,struct sval to,enum mlvo_match_type match_type)692*2b949d04SAndroid Build Coastguard Worker match_value(struct matcher *m, struct sval val, struct sval to,
693*2b949d04SAndroid Build Coastguard Worker             enum mlvo_match_type match_type)
694*2b949d04SAndroid Build Coastguard Worker {
695*2b949d04SAndroid Build Coastguard Worker     if (match_type == MLVO_MATCH_WILDCARD)
696*2b949d04SAndroid Build Coastguard Worker         return true;
697*2b949d04SAndroid Build Coastguard Worker     if (match_type == MLVO_MATCH_GROUP)
698*2b949d04SAndroid Build Coastguard Worker         return match_group(m, val, to);
699*2b949d04SAndroid Build Coastguard Worker     return svaleq(val, to);
700*2b949d04SAndroid Build Coastguard Worker }
701*2b949d04SAndroid Build Coastguard Worker 
702*2b949d04SAndroid Build Coastguard Worker static bool
match_value_and_mark(struct matcher * m,struct sval val,struct matched_sval * to,enum mlvo_match_type match_type)703*2b949d04SAndroid Build Coastguard Worker match_value_and_mark(struct matcher *m, struct sval val,
704*2b949d04SAndroid Build Coastguard Worker                      struct matched_sval *to, enum mlvo_match_type match_type)
705*2b949d04SAndroid Build Coastguard Worker {
706*2b949d04SAndroid Build Coastguard Worker     bool matched = match_value(m, val, to->sval, match_type);
707*2b949d04SAndroid Build Coastguard Worker     if (matched)
708*2b949d04SAndroid Build Coastguard Worker         to->matched = true;
709*2b949d04SAndroid Build Coastguard Worker     return matched;
710*2b949d04SAndroid Build Coastguard Worker }
711*2b949d04SAndroid Build Coastguard Worker 
712*2b949d04SAndroid Build Coastguard Worker /*
713*2b949d04SAndroid Build Coastguard Worker  * This function performs %-expansion on @value (see overview above),
714*2b949d04SAndroid Build Coastguard Worker  * and appends the result to @to.
715*2b949d04SAndroid Build Coastguard Worker  */
716*2b949d04SAndroid Build Coastguard Worker static bool
append_expanded_kccgst_value(struct matcher * m,struct scanner * s,darray_char * to,struct sval value)717*2b949d04SAndroid Build Coastguard Worker append_expanded_kccgst_value(struct matcher *m, struct scanner *s,
718*2b949d04SAndroid Build Coastguard Worker                              darray_char *to, struct sval value)
719*2b949d04SAndroid Build Coastguard Worker {
720*2b949d04SAndroid Build Coastguard Worker     const char *str = value.start;
721*2b949d04SAndroid Build Coastguard Worker     darray_char expanded = darray_new();
722*2b949d04SAndroid Build Coastguard Worker     char ch;
723*2b949d04SAndroid Build Coastguard Worker     bool expanded_plus, to_plus;
724*2b949d04SAndroid Build Coastguard Worker 
725*2b949d04SAndroid Build Coastguard Worker     /*
726*2b949d04SAndroid Build Coastguard Worker      * Some ugly hand-lexing here, but going through the scanner is more
727*2b949d04SAndroid Build Coastguard Worker      * trouble than it's worth, and the format is ugly on its own merit.
728*2b949d04SAndroid Build Coastguard Worker      */
729*2b949d04SAndroid Build Coastguard Worker     for (unsigned i = 0; i < value.len; ) {
730*2b949d04SAndroid Build Coastguard Worker         enum rules_mlvo mlv;
731*2b949d04SAndroid Build Coastguard Worker         xkb_layout_index_t idx;
732*2b949d04SAndroid Build Coastguard Worker         char pfx, sfx;
733*2b949d04SAndroid Build Coastguard Worker         struct matched_sval *expanded_value;
734*2b949d04SAndroid Build Coastguard Worker 
735*2b949d04SAndroid Build Coastguard Worker         /* Check if that's a start of an expansion. */
736*2b949d04SAndroid Build Coastguard Worker         if (str[i] != '%') {
737*2b949d04SAndroid Build Coastguard Worker             /* Just a normal character. */
738*2b949d04SAndroid Build Coastguard Worker             darray_appends_nullterminate(expanded, &str[i++], 1);
739*2b949d04SAndroid Build Coastguard Worker             continue;
740*2b949d04SAndroid Build Coastguard Worker         }
741*2b949d04SAndroid Build Coastguard Worker         if (++i >= value.len) goto error;
742*2b949d04SAndroid Build Coastguard Worker 
743*2b949d04SAndroid Build Coastguard Worker         pfx = sfx = 0;
744*2b949d04SAndroid Build Coastguard Worker 
745*2b949d04SAndroid Build Coastguard Worker         /* Check for prefix. */
746*2b949d04SAndroid Build Coastguard Worker         if (str[i] == '(' || str[i] == '+' || str[i] == '|' ||
747*2b949d04SAndroid Build Coastguard Worker             str[i] == '_' || str[i] == '-') {
748*2b949d04SAndroid Build Coastguard Worker             pfx = str[i];
749*2b949d04SAndroid Build Coastguard Worker             if (str[i] == '(') sfx = ')';
750*2b949d04SAndroid Build Coastguard Worker             if (++i >= value.len) goto error;
751*2b949d04SAndroid Build Coastguard Worker         }
752*2b949d04SAndroid Build Coastguard Worker 
753*2b949d04SAndroid Build Coastguard Worker         /* Mandatory model/layout/variant specifier. */
754*2b949d04SAndroid Build Coastguard Worker         switch (str[i++]) {
755*2b949d04SAndroid Build Coastguard Worker         case 'm': mlv = MLVO_MODEL; break;
756*2b949d04SAndroid Build Coastguard Worker         case 'l': mlv = MLVO_LAYOUT; break;
757*2b949d04SAndroid Build Coastguard Worker         case 'v': mlv = MLVO_VARIANT; break;
758*2b949d04SAndroid Build Coastguard Worker         default: goto error;
759*2b949d04SAndroid Build Coastguard Worker         }
760*2b949d04SAndroid Build Coastguard Worker 
761*2b949d04SAndroid Build Coastguard Worker         /* Check for index. */
762*2b949d04SAndroid Build Coastguard Worker         idx = XKB_LAYOUT_INVALID;
763*2b949d04SAndroid Build Coastguard Worker         if (i < value.len && str[i] == '[') {
764*2b949d04SAndroid Build Coastguard Worker             int consumed;
765*2b949d04SAndroid Build Coastguard Worker 
766*2b949d04SAndroid Build Coastguard Worker             if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
767*2b949d04SAndroid Build Coastguard Worker                 scanner_err(s, "invalid index in %%-expansion; may only index layout or variant");
768*2b949d04SAndroid Build Coastguard Worker                 goto error;
769*2b949d04SAndroid Build Coastguard Worker             }
770*2b949d04SAndroid Build Coastguard Worker 
771*2b949d04SAndroid Build Coastguard Worker             consumed = extract_layout_index(str + i, value.len - i, &idx);
772*2b949d04SAndroid Build Coastguard Worker             if (consumed == -1) goto error;
773*2b949d04SAndroid Build Coastguard Worker             i += consumed;
774*2b949d04SAndroid Build Coastguard Worker         }
775*2b949d04SAndroid Build Coastguard Worker 
776*2b949d04SAndroid Build Coastguard Worker         /* Check for suffix, if there supposed to be one. */
777*2b949d04SAndroid Build Coastguard Worker         if (sfx != 0) {
778*2b949d04SAndroid Build Coastguard Worker             if (i >= value.len) goto error;
779*2b949d04SAndroid Build Coastguard Worker             if (str[i++] != sfx) goto error;
780*2b949d04SAndroid Build Coastguard Worker         }
781*2b949d04SAndroid Build Coastguard Worker 
782*2b949d04SAndroid Build Coastguard Worker         /* Get the expanded value. */
783*2b949d04SAndroid Build Coastguard Worker         expanded_value = NULL;
784*2b949d04SAndroid Build Coastguard Worker 
785*2b949d04SAndroid Build Coastguard Worker         if (mlv == MLVO_LAYOUT) {
786*2b949d04SAndroid Build Coastguard Worker             if (idx != XKB_LAYOUT_INVALID &&
787*2b949d04SAndroid Build Coastguard Worker                 idx < darray_size(m->rmlvo.layouts) &&
788*2b949d04SAndroid Build Coastguard Worker                 darray_size(m->rmlvo.layouts) > 1)
789*2b949d04SAndroid Build Coastguard Worker                 expanded_value = &darray_item(m->rmlvo.layouts, idx);
790*2b949d04SAndroid Build Coastguard Worker             else if (idx == XKB_LAYOUT_INVALID &&
791*2b949d04SAndroid Build Coastguard Worker                      darray_size(m->rmlvo.layouts) == 1)
792*2b949d04SAndroid Build Coastguard Worker                 expanded_value = &darray_item(m->rmlvo.layouts, 0);
793*2b949d04SAndroid Build Coastguard Worker         }
794*2b949d04SAndroid Build Coastguard Worker         else if (mlv == MLVO_VARIANT) {
795*2b949d04SAndroid Build Coastguard Worker             if (idx != XKB_LAYOUT_INVALID &&
796*2b949d04SAndroid Build Coastguard Worker                 idx < darray_size(m->rmlvo.variants) &&
797*2b949d04SAndroid Build Coastguard Worker                 darray_size(m->rmlvo.variants) > 1)
798*2b949d04SAndroid Build Coastguard Worker                 expanded_value = &darray_item(m->rmlvo.variants, idx);
799*2b949d04SAndroid Build Coastguard Worker             else if (idx == XKB_LAYOUT_INVALID &&
800*2b949d04SAndroid Build Coastguard Worker                      darray_size(m->rmlvo.variants) == 1)
801*2b949d04SAndroid Build Coastguard Worker                 expanded_value = &darray_item(m->rmlvo.variants, 0);
802*2b949d04SAndroid Build Coastguard Worker         }
803*2b949d04SAndroid Build Coastguard Worker         else if (mlv == MLVO_MODEL) {
804*2b949d04SAndroid Build Coastguard Worker             expanded_value = &m->rmlvo.model;
805*2b949d04SAndroid Build Coastguard Worker         }
806*2b949d04SAndroid Build Coastguard Worker 
807*2b949d04SAndroid Build Coastguard Worker         /* If we didn't get one, skip silently. */
808*2b949d04SAndroid Build Coastguard Worker         if (!expanded_value || expanded_value->sval.len == 0)
809*2b949d04SAndroid Build Coastguard Worker             continue;
810*2b949d04SAndroid Build Coastguard Worker 
811*2b949d04SAndroid Build Coastguard Worker         if (pfx != 0)
812*2b949d04SAndroid Build Coastguard Worker             darray_appends_nullterminate(expanded, &pfx, 1);
813*2b949d04SAndroid Build Coastguard Worker         darray_appends_nullterminate(expanded,
814*2b949d04SAndroid Build Coastguard Worker                                      expanded_value->sval.start,
815*2b949d04SAndroid Build Coastguard Worker                                      expanded_value->sval.len);
816*2b949d04SAndroid Build Coastguard Worker         if (sfx != 0)
817*2b949d04SAndroid Build Coastguard Worker             darray_appends_nullterminate(expanded, &sfx, 1);
818*2b949d04SAndroid Build Coastguard Worker         expanded_value->matched = true;
819*2b949d04SAndroid Build Coastguard Worker     }
820*2b949d04SAndroid Build Coastguard Worker 
821*2b949d04SAndroid Build Coastguard Worker     /*
822*2b949d04SAndroid Build Coastguard Worker      * Appending  bar to  foo ->  foo (not an error if this happens)
823*2b949d04SAndroid Build Coastguard Worker      * Appending +bar to  foo ->  foo+bar
824*2b949d04SAndroid Build Coastguard Worker      * Appending  bar to +foo ->  bar+foo
825*2b949d04SAndroid Build Coastguard Worker      * Appending +bar to +foo -> +foo+bar
826*2b949d04SAndroid Build Coastguard Worker      */
827*2b949d04SAndroid Build Coastguard Worker 
828*2b949d04SAndroid Build Coastguard Worker     ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0));
829*2b949d04SAndroid Build Coastguard Worker     expanded_plus = (ch == '+' || ch == '|');
830*2b949d04SAndroid Build Coastguard Worker     ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0));
831*2b949d04SAndroid Build Coastguard Worker     to_plus = (ch == '+' || ch == '|');
832*2b949d04SAndroid Build Coastguard Worker 
833*2b949d04SAndroid Build Coastguard Worker     if (expanded_plus || darray_empty(*to))
834*2b949d04SAndroid Build Coastguard Worker         darray_appends_nullterminate(*to, expanded.item, expanded.size);
835*2b949d04SAndroid Build Coastguard Worker     else if (to_plus)
836*2b949d04SAndroid Build Coastguard Worker         darray_prepends_nullterminate(*to, expanded.item, expanded.size);
837*2b949d04SAndroid Build Coastguard Worker 
838*2b949d04SAndroid Build Coastguard Worker     darray_free(expanded);
839*2b949d04SAndroid Build Coastguard Worker     return true;
840*2b949d04SAndroid Build Coastguard Worker 
841*2b949d04SAndroid Build Coastguard Worker error:
842*2b949d04SAndroid Build Coastguard Worker     darray_free(expanded);
843*2b949d04SAndroid Build Coastguard Worker     scanner_err(s, "invalid %%-expansion in value; not used");
844*2b949d04SAndroid Build Coastguard Worker     return false;
845*2b949d04SAndroid Build Coastguard Worker }
846*2b949d04SAndroid Build Coastguard Worker 
847*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_verify(struct matcher * m,struct scanner * s)848*2b949d04SAndroid Build Coastguard Worker matcher_rule_verify(struct matcher *m, struct scanner *s)
849*2b949d04SAndroid Build Coastguard Worker {
850*2b949d04SAndroid Build Coastguard Worker     if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
851*2b949d04SAndroid Build Coastguard Worker         m->rule.num_kccgst_values != m->mapping.num_kccgst) {
852*2b949d04SAndroid Build Coastguard Worker         scanner_err(s, "invalid rule: must have same number of values as mapping line; ignoring rule");
853*2b949d04SAndroid Build Coastguard Worker         m->rule.skip = true;
854*2b949d04SAndroid Build Coastguard Worker     }
855*2b949d04SAndroid Build Coastguard Worker }
856*2b949d04SAndroid Build Coastguard Worker 
857*2b949d04SAndroid Build Coastguard Worker static void
matcher_rule_apply_if_matches(struct matcher * m,struct scanner * s)858*2b949d04SAndroid Build Coastguard Worker matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s)
859*2b949d04SAndroid Build Coastguard Worker {
860*2b949d04SAndroid Build Coastguard Worker     for (unsigned i = 0; i < m->mapping.num_mlvo; i++) {
861*2b949d04SAndroid Build Coastguard Worker         enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i];
862*2b949d04SAndroid Build Coastguard Worker         struct sval value = m->rule.mlvo_value_at_pos[i];
863*2b949d04SAndroid Build Coastguard Worker         enum mlvo_match_type match_type = m->rule.match_type_at_pos[i];
864*2b949d04SAndroid Build Coastguard Worker         struct matched_sval *to;
865*2b949d04SAndroid Build Coastguard Worker         bool matched = false;
866*2b949d04SAndroid Build Coastguard Worker 
867*2b949d04SAndroid Build Coastguard Worker         if (mlvo == MLVO_MODEL) {
868*2b949d04SAndroid Build Coastguard Worker             to = &m->rmlvo.model;
869*2b949d04SAndroid Build Coastguard Worker             matched = match_value_and_mark(m, value, to, match_type);
870*2b949d04SAndroid Build Coastguard Worker         }
871*2b949d04SAndroid Build Coastguard Worker         else if (mlvo == MLVO_LAYOUT) {
872*2b949d04SAndroid Build Coastguard Worker             xkb_layout_index_t idx = m->mapping.layout_idx;
873*2b949d04SAndroid Build Coastguard Worker             idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
874*2b949d04SAndroid Build Coastguard Worker             to = &darray_item(m->rmlvo.layouts, idx);
875*2b949d04SAndroid Build Coastguard Worker             matched = match_value_and_mark(m, value, to, match_type);
876*2b949d04SAndroid Build Coastguard Worker         }
877*2b949d04SAndroid Build Coastguard Worker         else if (mlvo == MLVO_VARIANT) {
878*2b949d04SAndroid Build Coastguard Worker             xkb_layout_index_t idx = m->mapping.layout_idx;
879*2b949d04SAndroid Build Coastguard Worker             idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
880*2b949d04SAndroid Build Coastguard Worker             to = &darray_item(m->rmlvo.variants, idx);
881*2b949d04SAndroid Build Coastguard Worker             matched = match_value_and_mark(m, value, to, match_type);
882*2b949d04SAndroid Build Coastguard Worker         }
883*2b949d04SAndroid Build Coastguard Worker         else if (mlvo == MLVO_OPTION) {
884*2b949d04SAndroid Build Coastguard Worker             darray_foreach(to, m->rmlvo.options) {
885*2b949d04SAndroid Build Coastguard Worker                 matched = match_value_and_mark(m, value, to, match_type);
886*2b949d04SAndroid Build Coastguard Worker                 if (matched)
887*2b949d04SAndroid Build Coastguard Worker                     break;
888*2b949d04SAndroid Build Coastguard Worker             }
889*2b949d04SAndroid Build Coastguard Worker         }
890*2b949d04SAndroid Build Coastguard Worker 
891*2b949d04SAndroid Build Coastguard Worker         if (!matched)
892*2b949d04SAndroid Build Coastguard Worker             return;
893*2b949d04SAndroid Build Coastguard Worker     }
894*2b949d04SAndroid Build Coastguard Worker 
895*2b949d04SAndroid Build Coastguard Worker     for (unsigned i = 0; i < m->mapping.num_kccgst; i++) {
896*2b949d04SAndroid Build Coastguard Worker         enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i];
897*2b949d04SAndroid Build Coastguard Worker         struct sval value = m->rule.kccgst_value_at_pos[i];
898*2b949d04SAndroid Build Coastguard Worker         append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value);
899*2b949d04SAndroid Build Coastguard Worker     }
900*2b949d04SAndroid Build Coastguard Worker 
901*2b949d04SAndroid Build Coastguard Worker     /*
902*2b949d04SAndroid Build Coastguard Worker      * If a rule matches in a rule set, the rest of the set should be
903*2b949d04SAndroid Build Coastguard Worker      * skipped. However, rule sets matching against options may contain
904*2b949d04SAndroid Build Coastguard Worker      * several legitimate rules, so they are processed entirely.
905*2b949d04SAndroid Build Coastguard Worker      */
906*2b949d04SAndroid Build Coastguard Worker     if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
907*2b949d04SAndroid Build Coastguard Worker         m->mapping.skip = true;
908*2b949d04SAndroid Build Coastguard Worker }
909*2b949d04SAndroid Build Coastguard Worker 
910*2b949d04SAndroid Build Coastguard Worker static enum rules_token
gettok(struct matcher * m,struct scanner * s)911*2b949d04SAndroid Build Coastguard Worker gettok(struct matcher *m, struct scanner *s)
912*2b949d04SAndroid Build Coastguard Worker {
913*2b949d04SAndroid Build Coastguard Worker     return lex(s, &m->val);
914*2b949d04SAndroid Build Coastguard Worker }
915*2b949d04SAndroid Build Coastguard Worker 
916*2b949d04SAndroid Build Coastguard Worker static bool
matcher_match(struct matcher * m,struct scanner * s,unsigned include_depth,const char * string,size_t len,const char * file_name)917*2b949d04SAndroid Build Coastguard Worker matcher_match(struct matcher *m, struct scanner *s,
918*2b949d04SAndroid Build Coastguard Worker               unsigned include_depth,
919*2b949d04SAndroid Build Coastguard Worker               const char *string, size_t len,
920*2b949d04SAndroid Build Coastguard Worker               const char *file_name)
921*2b949d04SAndroid Build Coastguard Worker {
922*2b949d04SAndroid Build Coastguard Worker     enum rules_token tok;
923*2b949d04SAndroid Build Coastguard Worker 
924*2b949d04SAndroid Build Coastguard Worker     if (!m)
925*2b949d04SAndroid Build Coastguard Worker         return false;
926*2b949d04SAndroid Build Coastguard Worker 
927*2b949d04SAndroid Build Coastguard Worker initial:
928*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
929*2b949d04SAndroid Build Coastguard Worker     case TOK_BANG:
930*2b949d04SAndroid Build Coastguard Worker         goto bang;
931*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_LINE:
932*2b949d04SAndroid Build Coastguard Worker         goto initial;
933*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_FILE:
934*2b949d04SAndroid Build Coastguard Worker         goto finish;
935*2b949d04SAndroid Build Coastguard Worker     default:
936*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
937*2b949d04SAndroid Build Coastguard Worker     }
938*2b949d04SAndroid Build Coastguard Worker 
939*2b949d04SAndroid Build Coastguard Worker bang:
940*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
941*2b949d04SAndroid Build Coastguard Worker     case TOK_GROUP_NAME:
942*2b949d04SAndroid Build Coastguard Worker         matcher_group_start_new(m, m->val.string);
943*2b949d04SAndroid Build Coastguard Worker         goto group_name;
944*2b949d04SAndroid Build Coastguard Worker     case TOK_INCLUDE:
945*2b949d04SAndroid Build Coastguard Worker         goto include_statement;
946*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
947*2b949d04SAndroid Build Coastguard Worker         matcher_mapping_start_new(m);
948*2b949d04SAndroid Build Coastguard Worker         matcher_mapping_set_mlvo(m, s, m->val.string);
949*2b949d04SAndroid Build Coastguard Worker         goto mapping_mlvo;
950*2b949d04SAndroid Build Coastguard Worker     default:
951*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
952*2b949d04SAndroid Build Coastguard Worker     }
953*2b949d04SAndroid Build Coastguard Worker 
954*2b949d04SAndroid Build Coastguard Worker group_name:
955*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
956*2b949d04SAndroid Build Coastguard Worker     case TOK_EQUALS:
957*2b949d04SAndroid Build Coastguard Worker         goto group_element;
958*2b949d04SAndroid Build Coastguard Worker     default:
959*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
960*2b949d04SAndroid Build Coastguard Worker     }
961*2b949d04SAndroid Build Coastguard Worker 
962*2b949d04SAndroid Build Coastguard Worker group_element:
963*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
964*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
965*2b949d04SAndroid Build Coastguard Worker         matcher_group_add_element(m, s, m->val.string);
966*2b949d04SAndroid Build Coastguard Worker         goto group_element;
967*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_LINE:
968*2b949d04SAndroid Build Coastguard Worker         goto initial;
969*2b949d04SAndroid Build Coastguard Worker     default:
970*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
971*2b949d04SAndroid Build Coastguard Worker     }
972*2b949d04SAndroid Build Coastguard Worker 
973*2b949d04SAndroid Build Coastguard Worker include_statement:
974*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
975*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
976*2b949d04SAndroid Build Coastguard Worker         matcher_include(m, s, include_depth, m->val.string);
977*2b949d04SAndroid Build Coastguard Worker         goto initial;
978*2b949d04SAndroid Build Coastguard Worker     default:
979*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
980*2b949d04SAndroid Build Coastguard Worker     }
981*2b949d04SAndroid Build Coastguard Worker 
982*2b949d04SAndroid Build Coastguard Worker mapping_mlvo:
983*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
984*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
985*2b949d04SAndroid Build Coastguard Worker         if (!m->mapping.skip)
986*2b949d04SAndroid Build Coastguard Worker             matcher_mapping_set_mlvo(m, s, m->val.string);
987*2b949d04SAndroid Build Coastguard Worker         goto mapping_mlvo;
988*2b949d04SAndroid Build Coastguard Worker     case TOK_EQUALS:
989*2b949d04SAndroid Build Coastguard Worker         goto mapping_kccgst;
990*2b949d04SAndroid Build Coastguard Worker     default:
991*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
992*2b949d04SAndroid Build Coastguard Worker     }
993*2b949d04SAndroid Build Coastguard Worker 
994*2b949d04SAndroid Build Coastguard Worker mapping_kccgst:
995*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
996*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
997*2b949d04SAndroid Build Coastguard Worker         if (!m->mapping.skip)
998*2b949d04SAndroid Build Coastguard Worker             matcher_mapping_set_kccgst(m, s, m->val.string);
999*2b949d04SAndroid Build Coastguard Worker         goto mapping_kccgst;
1000*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_LINE:
1001*2b949d04SAndroid Build Coastguard Worker         if (!m->mapping.skip)
1002*2b949d04SAndroid Build Coastguard Worker             matcher_mapping_verify(m, s);
1003*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo_first;
1004*2b949d04SAndroid Build Coastguard Worker     default:
1005*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
1006*2b949d04SAndroid Build Coastguard Worker     }
1007*2b949d04SAndroid Build Coastguard Worker 
1008*2b949d04SAndroid Build Coastguard Worker rule_mlvo_first:
1009*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
1010*2b949d04SAndroid Build Coastguard Worker     case TOK_BANG:
1011*2b949d04SAndroid Build Coastguard Worker         goto bang;
1012*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_LINE:
1013*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo_first;
1014*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_FILE:
1015*2b949d04SAndroid Build Coastguard Worker         goto finish;
1016*2b949d04SAndroid Build Coastguard Worker     default:
1017*2b949d04SAndroid Build Coastguard Worker         matcher_rule_start_new(m);
1018*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo_no_tok;
1019*2b949d04SAndroid Build Coastguard Worker     }
1020*2b949d04SAndroid Build Coastguard Worker 
1021*2b949d04SAndroid Build Coastguard Worker rule_mlvo:
1022*2b949d04SAndroid Build Coastguard Worker     tok = gettok(m, s);
1023*2b949d04SAndroid Build Coastguard Worker rule_mlvo_no_tok:
1024*2b949d04SAndroid Build Coastguard Worker     switch (tok) {
1025*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
1026*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1027*2b949d04SAndroid Build Coastguard Worker             matcher_rule_set_mlvo(m, s, m->val.string);
1028*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo;
1029*2b949d04SAndroid Build Coastguard Worker     case TOK_STAR:
1030*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1031*2b949d04SAndroid Build Coastguard Worker             matcher_rule_set_mlvo_wildcard(m, s);
1032*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo;
1033*2b949d04SAndroid Build Coastguard Worker     case TOK_GROUP_NAME:
1034*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1035*2b949d04SAndroid Build Coastguard Worker             matcher_rule_set_mlvo_group(m, s, m->val.string);
1036*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo;
1037*2b949d04SAndroid Build Coastguard Worker     case TOK_EQUALS:
1038*2b949d04SAndroid Build Coastguard Worker         goto rule_kccgst;
1039*2b949d04SAndroid Build Coastguard Worker     default:
1040*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
1041*2b949d04SAndroid Build Coastguard Worker     }
1042*2b949d04SAndroid Build Coastguard Worker 
1043*2b949d04SAndroid Build Coastguard Worker rule_kccgst:
1044*2b949d04SAndroid Build Coastguard Worker     switch (tok = gettok(m, s)) {
1045*2b949d04SAndroid Build Coastguard Worker     case TOK_IDENTIFIER:
1046*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1047*2b949d04SAndroid Build Coastguard Worker             matcher_rule_set_kccgst(m, s, m->val.string);
1048*2b949d04SAndroid Build Coastguard Worker         goto rule_kccgst;
1049*2b949d04SAndroid Build Coastguard Worker     case TOK_END_OF_LINE:
1050*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1051*2b949d04SAndroid Build Coastguard Worker             matcher_rule_verify(m, s);
1052*2b949d04SAndroid Build Coastguard Worker         if (!m->rule.skip)
1053*2b949d04SAndroid Build Coastguard Worker             matcher_rule_apply_if_matches(m, s);
1054*2b949d04SAndroid Build Coastguard Worker         goto rule_mlvo_first;
1055*2b949d04SAndroid Build Coastguard Worker     default:
1056*2b949d04SAndroid Build Coastguard Worker         goto unexpected;
1057*2b949d04SAndroid Build Coastguard Worker     }
1058*2b949d04SAndroid Build Coastguard Worker 
1059*2b949d04SAndroid Build Coastguard Worker unexpected:
1060*2b949d04SAndroid Build Coastguard Worker     switch (tok) {
1061*2b949d04SAndroid Build Coastguard Worker     case TOK_ERROR:
1062*2b949d04SAndroid Build Coastguard Worker         goto error;
1063*2b949d04SAndroid Build Coastguard Worker     default:
1064*2b949d04SAndroid Build Coastguard Worker         goto state_error;
1065*2b949d04SAndroid Build Coastguard Worker     }
1066*2b949d04SAndroid Build Coastguard Worker 
1067*2b949d04SAndroid Build Coastguard Worker finish:
1068*2b949d04SAndroid Build Coastguard Worker     return true;
1069*2b949d04SAndroid Build Coastguard Worker 
1070*2b949d04SAndroid Build Coastguard Worker state_error:
1071*2b949d04SAndroid Build Coastguard Worker     scanner_err(s, "unexpected token");
1072*2b949d04SAndroid Build Coastguard Worker error:
1073*2b949d04SAndroid Build Coastguard Worker     return false;
1074*2b949d04SAndroid Build Coastguard Worker }
1075*2b949d04SAndroid Build Coastguard Worker 
1076*2b949d04SAndroid Build Coastguard Worker static bool
read_rules_file(struct xkb_context * ctx,struct matcher * matcher,unsigned include_depth,FILE * file,const char * path)1077*2b949d04SAndroid Build Coastguard Worker read_rules_file(struct xkb_context *ctx,
1078*2b949d04SAndroid Build Coastguard Worker                 struct matcher *matcher,
1079*2b949d04SAndroid Build Coastguard Worker                 unsigned include_depth,
1080*2b949d04SAndroid Build Coastguard Worker                 FILE *file,
1081*2b949d04SAndroid Build Coastguard Worker                 const char *path)
1082*2b949d04SAndroid Build Coastguard Worker {
1083*2b949d04SAndroid Build Coastguard Worker     bool ret = false;
1084*2b949d04SAndroid Build Coastguard Worker     char *string;
1085*2b949d04SAndroid Build Coastguard Worker     size_t size;
1086*2b949d04SAndroid Build Coastguard Worker     struct scanner scanner;
1087*2b949d04SAndroid Build Coastguard Worker 
1088*2b949d04SAndroid Build Coastguard Worker     ret = map_file(file, &string, &size);
1089*2b949d04SAndroid Build Coastguard Worker     if (!ret) {
1090*2b949d04SAndroid Build Coastguard Worker         log_err(ctx, "Couldn't read rules file \"%s\": %s\n",
1091*2b949d04SAndroid Build Coastguard Worker                 path, strerror(errno));
1092*2b949d04SAndroid Build Coastguard Worker         goto out;
1093*2b949d04SAndroid Build Coastguard Worker     }
1094*2b949d04SAndroid Build Coastguard Worker 
1095*2b949d04SAndroid Build Coastguard Worker     scanner_init(&scanner, matcher->ctx, string, size, path, NULL);
1096*2b949d04SAndroid Build Coastguard Worker 
1097*2b949d04SAndroid Build Coastguard Worker     ret = matcher_match(matcher, &scanner, include_depth, string, size, path);
1098*2b949d04SAndroid Build Coastguard Worker 
1099*2b949d04SAndroid Build Coastguard Worker     unmap_file(string, size);
1100*2b949d04SAndroid Build Coastguard Worker out:
1101*2b949d04SAndroid Build Coastguard Worker     return ret;
1102*2b949d04SAndroid Build Coastguard Worker }
1103*2b949d04SAndroid Build Coastguard Worker 
1104*2b949d04SAndroid Build Coastguard Worker bool
xkb_components_from_rules(struct xkb_context * ctx,const struct xkb_rule_names * rmlvo,struct xkb_component_names * out)1105*2b949d04SAndroid Build Coastguard Worker xkb_components_from_rules(struct xkb_context *ctx,
1106*2b949d04SAndroid Build Coastguard Worker                           const struct xkb_rule_names *rmlvo,
1107*2b949d04SAndroid Build Coastguard Worker                           struct xkb_component_names *out)
1108*2b949d04SAndroid Build Coastguard Worker {
1109*2b949d04SAndroid Build Coastguard Worker     bool ret = false;
1110*2b949d04SAndroid Build Coastguard Worker     FILE *file;
1111*2b949d04SAndroid Build Coastguard Worker     char *path = NULL;
1112*2b949d04SAndroid Build Coastguard Worker     struct matcher *matcher = NULL;
1113*2b949d04SAndroid Build Coastguard Worker     struct matched_sval *mval;
1114*2b949d04SAndroid Build Coastguard Worker     unsigned int offset = 0;
1115*2b949d04SAndroid Build Coastguard Worker 
1116*2b949d04SAndroid Build Coastguard Worker     file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path, &offset);
1117*2b949d04SAndroid Build Coastguard Worker     if (!file)
1118*2b949d04SAndroid Build Coastguard Worker         goto err_out;
1119*2b949d04SAndroid Build Coastguard Worker 
1120*2b949d04SAndroid Build Coastguard Worker     matcher = matcher_new(ctx, rmlvo);
1121*2b949d04SAndroid Build Coastguard Worker 
1122*2b949d04SAndroid Build Coastguard Worker     ret = read_rules_file(ctx, matcher, 0, file, path);
1123*2b949d04SAndroid Build Coastguard Worker     if (!ret ||
1124*2b949d04SAndroid Build Coastguard Worker         darray_empty(matcher->kccgst[KCCGST_KEYCODES]) ||
1125*2b949d04SAndroid Build Coastguard Worker         darray_empty(matcher->kccgst[KCCGST_TYPES]) ||
1126*2b949d04SAndroid Build Coastguard Worker         darray_empty(matcher->kccgst[KCCGST_COMPAT]) ||
1127*2b949d04SAndroid Build Coastguard Worker         /* darray_empty(matcher->kccgst[KCCGST_GEOMETRY]) || */
1128*2b949d04SAndroid Build Coastguard Worker         darray_empty(matcher->kccgst[KCCGST_SYMBOLS])) {
1129*2b949d04SAndroid Build Coastguard Worker         log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1130*2b949d04SAndroid Build Coastguard Worker         ret = false;
1131*2b949d04SAndroid Build Coastguard Worker         goto err_out;
1132*2b949d04SAndroid Build Coastguard Worker     }
1133*2b949d04SAndroid Build Coastguard Worker 
1134*2b949d04SAndroid Build Coastguard Worker     darray_steal(matcher->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL);
1135*2b949d04SAndroid Build Coastguard Worker     darray_steal(matcher->kccgst[KCCGST_TYPES], &out->types, NULL);
1136*2b949d04SAndroid Build Coastguard Worker     darray_steal(matcher->kccgst[KCCGST_COMPAT], &out->compat, NULL);
1137*2b949d04SAndroid Build Coastguard Worker     darray_steal(matcher->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL);
1138*2b949d04SAndroid Build Coastguard Worker     darray_free(matcher->kccgst[KCCGST_GEOMETRY]);
1139*2b949d04SAndroid Build Coastguard Worker 
1140*2b949d04SAndroid Build Coastguard Worker     mval = &matcher->rmlvo.model;
1141*2b949d04SAndroid Build Coastguard Worker     if (!mval->matched && mval->sval.len > 0)
1142*2b949d04SAndroid Build Coastguard Worker         log_err(matcher->ctx, "Unrecognized RMLVO model \"%.*s\" was ignored\n",
1143*2b949d04SAndroid Build Coastguard Worker                 mval->sval.len, mval->sval.start);
1144*2b949d04SAndroid Build Coastguard Worker     darray_foreach(mval, matcher->rmlvo.layouts)
1145*2b949d04SAndroid Build Coastguard Worker         if (!mval->matched && mval->sval.len > 0)
1146*2b949d04SAndroid Build Coastguard Worker             log_err(matcher->ctx, "Unrecognized RMLVO layout \"%.*s\" was ignored\n",
1147*2b949d04SAndroid Build Coastguard Worker                     mval->sval.len, mval->sval.start);
1148*2b949d04SAndroid Build Coastguard Worker     darray_foreach(mval, matcher->rmlvo.variants)
1149*2b949d04SAndroid Build Coastguard Worker         if (!mval->matched && mval->sval.len > 0)
1150*2b949d04SAndroid Build Coastguard Worker             log_err(matcher->ctx, "Unrecognized RMLVO variant \"%.*s\" was ignored\n",
1151*2b949d04SAndroid Build Coastguard Worker                     mval->sval.len, mval->sval.start);
1152*2b949d04SAndroid Build Coastguard Worker     darray_foreach(mval, matcher->rmlvo.options)
1153*2b949d04SAndroid Build Coastguard Worker         if (!mval->matched && mval->sval.len > 0)
1154*2b949d04SAndroid Build Coastguard Worker             log_err(matcher->ctx, "Unrecognized RMLVO option \"%.*s\" was ignored\n",
1155*2b949d04SAndroid Build Coastguard Worker                     mval->sval.len, mval->sval.start);
1156*2b949d04SAndroid Build Coastguard Worker 
1157*2b949d04SAndroid Build Coastguard Worker err_out:
1158*2b949d04SAndroid Build Coastguard Worker     if (file)
1159*2b949d04SAndroid Build Coastguard Worker         fclose(file);
1160*2b949d04SAndroid Build Coastguard Worker     matcher_free(matcher);
1161*2b949d04SAndroid Build Coastguard Worker     free(path);
1162*2b949d04SAndroid Build Coastguard Worker     return ret;
1163*2b949d04SAndroid Build Coastguard Worker }
1164