1 /************************************************************
2 * Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
4 * Permission to use, copy, modify, and distribute this
5 * software and its documentation for any purpose and without
6 * fee is hereby granted, provided that the above copyright
7 * notice appear in all copies and that both that copyright
8 * notice and this permission notice appear in supporting
9 * documentation, and that the name of Silicon Graphics not be
10 * used in advertising or publicity pertaining to distribution
11 * of the software without specific prior written permission.
12 * Silicon Graphics makes no representation about the suitability
13 * of this software for any purpose. It is provided "as is"
14 * without any express or implied warranty.
16 * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
17 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
18 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
19 * GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
20 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
22 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
23 * THE USE OR PERFORMANCE OF THIS SOFTWARE.
25 ********************************************************/
28 * Copyright © 2012 Ran Benita <ran234@gmail.com>
30 * Permission is hereby granted, free of charge, to any person obtaining a
31 * copy of this software and associated documentation files (the "Software"),
32 * to deal in the Software without restriction, including without limitation
33 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
34 * and/or sell copies of the Software, and to permit persons to whom the
35 * Software is furnished to do so, subject to the following conditions:
37 * The above copyright notice and this permission notice (including the next
38 * paragraph) shall be included in all copies or substantial portions of the
41 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
44 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
45 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
46 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
47 * DEALINGS IN THE SOFTWARE.
50 #include "xkbcomp-priv.h"
53 #include "scanner-utils.h"
55 #define MAX_INCLUDE_DEPTH 5
59 /* Values returned with some tokens, like yylval. */
79 return is_graph(ch) && ch != '\\';
82 static enum rules_token
83 lex(struct scanner *s, union lvalue *val)
85 skip_more_whitespace_and_comments:
87 while (chr(s, ' ') || chr(s, '\t'));
96 while (eol(s)) next(s);
97 return TOK_END_OF_LINE;
100 /* Escaped line continuation. */
103 scanner_err(s, "illegal new line escape; must appear at end of line");
107 goto skip_more_whitespace_and_comments;
110 /* See if we're done. */
111 if (eof(s)) return TOK_END_OF_FILE;
114 s->token_line = s->line;
115 s->token_column = s->column;
117 /* Operators and punctuation. */
118 if (chr(s, '!')) return TOK_BANG;
119 if (chr(s, '=')) return TOK_EQUALS;
120 if (chr(s, '*')) return TOK_STAR;
124 val->string.start = s->s + s->pos;
126 while (is_ident(peek(s))) {
130 if (val->string.len == 0) {
131 scanner_err(s, "unexpected character after \'$\'; expected name");
134 return TOK_GROUP_NAME;
137 /* Include statement. */
138 if (lit(s, "include"))
142 if (is_ident(peek(s))) {
143 val->string.start = s->s + s->pos;
145 while (is_ident(peek(s))) {
149 return TOK_IDENTIFIER;
152 scanner_err(s, "unrecognized token");
156 /***====================================================================***/
166 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
168 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
169 [MLVO_MODEL] = SVAL_LIT("model"),
170 [MLVO_LAYOUT] = SVAL_LIT("layout"),
171 [MLVO_VARIANT] = SVAL_LIT("variant"),
172 [MLVO_OPTION] = SVAL_LIT("option"),
184 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
185 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
186 [KCCGST_TYPES] = SVAL_LIT("types"),
187 [KCCGST_COMPAT] = SVAL_LIT("compat"),
188 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
189 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
192 /* We use this to keep score whether an mlvo was matched or not; if not,
193 * we warn the user that his preference was ignored. */
194 struct matched_sval {
198 typedef darray(struct matched_sval) darray_matched_sval;
201 * A broken-down version of xkb_rule_names (without the rules,
205 struct matched_sval model;
206 darray_matched_sval layouts;
207 darray_matched_sval variants;
208 darray_matched_sval options;
213 darray_sval elements;
217 int mlvo_at_pos[_MLVO_NUM_ENTRIES];
218 unsigned int num_mlvo;
219 unsigned int defined_mlvo_mask;
220 xkb_layout_index_t layout_idx, variant_idx;
221 int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
222 unsigned int num_kccgst;
223 unsigned int defined_kccgst_mask;
227 enum mlvo_match_type {
228 MLVO_MATCH_NORMAL = 0,
234 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
235 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
236 unsigned int num_mlvo_values;
237 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
238 unsigned int num_kccgst_values;
243 * This is the main object used to match a given RMLVO against a rules
244 * file and aggragate the results in a KcCGST. It goes through a simple
245 * matching state machine, with tokens as transitions (see
249 struct xkb_context *ctx;
251 struct rule_names rmlvo;
253 darray(struct group) groups;
254 /* Current mapping. */
255 struct mapping mapping;
259 darray_char kccgst[_KCCGST_NUM_ENTRIES];
263 strip_spaces(struct sval v)
265 while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; }
266 while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--;
270 static darray_matched_sval
271 split_comma_separated_mlvo(const char *s)
273 darray_matched_sval arr = darray_new();
276 * Make sure the array returned by this function always includes at
277 * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
281 struct matched_sval val = { .sval = { NULL, 0 } };
282 darray_append(arr, val);
287 struct matched_sval val = { .sval = { s, 0 } };
288 while (*s != '\0' && *s != ',') { s++; val.sval.len++; }
289 val.sval = strip_spaces(val.sval);
290 darray_append(arr, val);
291 if (*s == '\0') break;
298 static struct matcher *
299 matcher_new(struct xkb_context *ctx,
300 const struct xkb_rule_names *rmlvo)
302 struct matcher *m = calloc(1, sizeof(*m));
307 m->rmlvo.model.sval.start = rmlvo->model;
308 m->rmlvo.model.sval.len = strlen_safe(rmlvo->model);
309 m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout);
310 m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant);
311 m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options);
317 matcher_free(struct matcher *m)
322 darray_free(m->rmlvo.layouts);
323 darray_free(m->rmlvo.variants);
324 darray_free(m->rmlvo.options);
325 darray_foreach(group, m->groups)
326 darray_free(group->elements);
327 for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++)
328 darray_free(m->kccgst[i]);
329 darray_free(m->groups);
334 matcher_group_start_new(struct matcher *m, struct sval name)
336 struct group group = { .name = name, .elements = darray_new() };
337 darray_append(m->groups, group);
341 matcher_group_add_element(struct matcher *m, struct scanner *s,
344 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
349 read_rules_file(struct xkb_context *ctx,
350 struct matcher *matcher,
351 unsigned include_depth,
356 matcher_include(struct matcher *m, struct scanner *parent_scanner,
357 unsigned include_depth,
360 struct scanner s; /* parses the !include value */
363 scanner_init(&s, m->ctx, inc.start, inc.len,
364 parent_scanner->file_name, NULL);
365 s.token_line = parent_scanner->token_line;
366 s.token_column = parent_scanner->token_column;
369 if (include_depth >= MAX_INCLUDE_DEPTH) {
370 scanner_err(&s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
375 while (!eof(&s) && !eol(&s)) {
380 else if (chr(&s, 'H')) {
381 const char *home = secure_getenv("HOME");
383 scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set");
386 if (!buf_appends(&s, home)) {
387 scanner_err(&s, "include path after expanding %%H is too long");
391 else if (chr(&s, 'S')) {
392 const char *default_root = xkb_context_include_path_get_system_path(m->ctx);
393 if (!buf_appends(&s, default_root) || !buf_appends(&s, "/rules")) {
394 scanner_err(&s, "include path after expanding %%S is too long");
399 scanner_err(&s, "unknown %% format (%c) in include statement", peek(&s));
404 buf_append(&s, next(&s));
407 if (!buf_append(&s, '\0')) {
408 scanner_err(&s, "include path is too long");
412 file = fopen(s.buf, "r");
414 bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf);
416 log_err(m->ctx, "No components returned from included XKB rules \"%s\"\n", s.buf);
419 log_err(m->ctx, "Failed to open included XKB rules \"%s\"\n", s.buf);
424 matcher_mapping_start_new(struct matcher *m)
426 for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++)
427 m->mapping.mlvo_at_pos[i] = -1;
428 for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++)
429 m->mapping.kccgst_at_pos[i] = -1;
430 m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
431 m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
432 m->mapping.defined_mlvo_mask = 0;
433 m->mapping.defined_kccgst_mask = 0;
434 m->mapping.skip = false;
438 extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
440 /* This function is pretty stupid, but works for now. */
441 *out = XKB_LAYOUT_INVALID;
444 if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']')
446 if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
448 /* To zero-based index. */
449 *out = s[1] - '0' - 1;
454 matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s,
457 enum rules_mlvo mlvo;
458 struct sval mlvo_sval;
460 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
461 mlvo_sval = rules_mlvo_svals[mlvo];
463 if (svaleq_prefix(mlvo_sval, ident))
468 if (mlvo >= _MLVO_NUM_ENTRIES) {
469 scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
470 ident.len, ident.start);
471 m->mapping.skip = true;
475 if (m->mapping.defined_mlvo_mask & (1u << mlvo)) {
476 scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
477 mlvo_sval.len, mlvo_sval.start);
478 m->mapping.skip = true;
482 /* If there are leftovers still, it must be an index. */
483 if (mlvo_sval.len < ident.len) {
484 xkb_layout_index_t idx;
485 int consumed = extract_layout_index(ident.start + mlvo_sval.len,
486 ident.len - mlvo_sval.len, &idx);
487 if ((int) (ident.len - mlvo_sval.len) != consumed) {
488 scanner_err(s, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set",
489 mlvo_sval.len, mlvo_sval.start);
490 m->mapping.skip = true;
494 if (mlvo == MLVO_LAYOUT) {
495 m->mapping.layout_idx = idx;
497 else if (mlvo == MLVO_VARIANT) {
498 m->mapping.variant_idx = idx;
501 scanner_err(s, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set",
502 mlvo_sval.len, mlvo_sval.start);
503 m->mapping.skip = true;
508 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
509 m->mapping.defined_mlvo_mask |= 1u << mlvo;
510 m->mapping.num_mlvo++;
514 matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident)
516 enum rules_kccgst kccgst;
517 struct sval kccgst_sval;
519 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
520 kccgst_sval = rules_kccgst_svals[kccgst];
522 if (svaleq(rules_kccgst_svals[kccgst], ident))
527 if (kccgst >= _KCCGST_NUM_ENTRIES) {
528 scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
529 ident.len, ident.start);
530 m->mapping.skip = true;
534 if (m->mapping.defined_kccgst_mask & (1u << kccgst)) {
535 scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
536 kccgst_sval.len, kccgst_sval.start);
537 m->mapping.skip = true;
541 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
542 m->mapping.defined_kccgst_mask |= 1u << kccgst;
543 m->mapping.num_kccgst++;
547 matcher_mapping_verify(struct matcher *m, struct scanner *s)
549 if (m->mapping.num_mlvo == 0) {
550 scanner_err(s, "invalid mapping: must have at least one value on the left hand side; ignoring rule set");
554 if (m->mapping.num_kccgst == 0) {
555 scanner_err(s, "invalid mapping: must have at least one value on the right hand side; ignoring rule set");
560 * This following is very stupid, but this is how it works.
561 * See the "Notes" section in the overview above.
564 if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) {
565 if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
566 if (darray_size(m->rmlvo.layouts) > 1)
570 if (darray_size(m->rmlvo.layouts) == 1 ||
571 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
576 if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) {
577 if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
578 if (darray_size(m->rmlvo.variants) > 1)
582 if (darray_size(m->rmlvo.variants) == 1 ||
583 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
591 m->mapping.skip = true;
595 matcher_rule_start_new(struct matcher *m)
597 memset(&m->rule, 0, sizeof(m->rule));
598 m->rule.skip = m->mapping.skip;
602 matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s,
604 enum mlvo_match_type match_type)
606 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
607 scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
611 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
612 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
613 m->rule.num_mlvo_values++;
617 matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s)
619 struct sval dummy = { NULL, 0 };
620 matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD);
624 matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s,
627 matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP);
631 matcher_rule_set_mlvo(struct matcher *m, struct scanner *s,
634 matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL);
638 matcher_rule_set_kccgst(struct matcher *m, struct scanner *s,
641 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
642 scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
646 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
647 m->rule.num_kccgst_values++;
651 match_group(struct matcher *m, struct sval group_name, struct sval to)
654 struct sval *element;
657 darray_foreach(group, m->groups) {
658 if (svaleq(group->name, group_name)) {
666 * rules/evdev intentionally uses some undeclared group names
667 * in rules (e.g. commented group definitions which may be
668 * uncommented if needed). So we continue silently.
673 darray_foreach(element, group->elements)
674 if (svaleq(to, *element))
681 match_value(struct matcher *m, struct sval val, struct sval to,
682 enum mlvo_match_type match_type)
684 if (match_type == MLVO_MATCH_WILDCARD)
686 if (match_type == MLVO_MATCH_GROUP)
687 return match_group(m, val, to);
688 return svaleq(val, to);
692 match_value_and_mark(struct matcher *m, struct sval val,
693 struct matched_sval *to, enum mlvo_match_type match_type)
695 bool matched = match_value(m, val, to->sval, match_type);
702 * This function performs %-expansion on @value (see overview above),
703 * and appends the result to @to.
706 append_expanded_kccgst_value(struct matcher *m, struct scanner *s,
707 darray_char *to, struct sval value)
709 const char *str = value.start;
710 darray_char expanded = darray_new();
712 bool expanded_plus, to_plus;
715 * Some ugly hand-lexing here, but going through the scanner is more
716 * trouble than it's worth, and the format is ugly on its own merit.
718 for (unsigned i = 0; i < value.len; ) {
720 xkb_layout_index_t idx;
722 struct matched_sval *expanded_value;
724 /* Check if that's a start of an expansion. */
726 /* Just a normal character. */
727 darray_appends_nullterminate(expanded, &str[i++], 1);
730 if (++i >= value.len) goto error;
734 /* Check for prefix. */
735 if (str[i] == '(' || str[i] == '+' || str[i] == '|' ||
736 str[i] == '_' || str[i] == '-') {
738 if (str[i] == '(') sfx = ')';
739 if (++i >= value.len) goto error;
742 /* Mandatory model/layout/variant specifier. */
744 case 'm': mlv = MLVO_MODEL; break;
745 case 'l': mlv = MLVO_LAYOUT; break;
746 case 'v': mlv = MLVO_VARIANT; break;
750 /* Check for index. */
751 idx = XKB_LAYOUT_INVALID;
752 if (i < value.len && str[i] == '[') {
755 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
756 scanner_err(s, "invalid index in %%-expansion; may only index layout or variant");
760 consumed = extract_layout_index(str + i, value.len - i, &idx);
761 if (consumed == -1) goto error;
765 /* Check for suffix, if there supposed to be one. */
767 if (i >= value.len) goto error;
768 if (str[i++] != sfx) goto error;
771 /* Get the expanded value. */
772 expanded_value = NULL;
774 if (mlv == MLVO_LAYOUT) {
775 if (idx != XKB_LAYOUT_INVALID &&
776 idx < darray_size(m->rmlvo.layouts) &&
777 darray_size(m->rmlvo.layouts) > 1)
778 expanded_value = &darray_item(m->rmlvo.layouts, idx);
779 else if (idx == XKB_LAYOUT_INVALID &&
780 darray_size(m->rmlvo.layouts) == 1)
781 expanded_value = &darray_item(m->rmlvo.layouts, 0);
783 else if (mlv == MLVO_VARIANT) {
784 if (idx != XKB_LAYOUT_INVALID &&
785 idx < darray_size(m->rmlvo.variants) &&
786 darray_size(m->rmlvo.variants) > 1)
787 expanded_value = &darray_item(m->rmlvo.variants, idx);
788 else if (idx == XKB_LAYOUT_INVALID &&
789 darray_size(m->rmlvo.variants) == 1)
790 expanded_value = &darray_item(m->rmlvo.variants, 0);
792 else if (mlv == MLVO_MODEL) {
793 expanded_value = &m->rmlvo.model;
796 /* If we didn't get one, skip silently. */
797 if (!expanded_value || expanded_value->sval.len == 0)
801 darray_appends_nullterminate(expanded, &pfx, 1);
802 darray_appends_nullterminate(expanded,
803 expanded_value->sval.start,
804 expanded_value->sval.len);
806 darray_appends_nullterminate(expanded, &sfx, 1);
807 expanded_value->matched = true;
811 * Appending bar to foo -> foo (not an error if this happens)
812 * Appending +bar to foo -> foo+bar
813 * Appending bar to +foo -> bar+foo
814 * Appending +bar to +foo -> +foo+bar
817 ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0));
818 expanded_plus = (ch == '+' || ch == '|');
819 ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0));
820 to_plus = (ch == '+' || ch == '|');
822 if (expanded_plus || darray_empty(*to))
823 darray_appends_nullterminate(*to, expanded.item, expanded.size);
825 darray_prepends_nullterminate(*to, expanded.item, expanded.size);
827 darray_free(expanded);
831 darray_free(expanded);
832 scanner_err(s, "invalid %%-expansion in value; not used");
837 matcher_rule_verify(struct matcher *m, struct scanner *s)
839 if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
840 m->rule.num_kccgst_values != m->mapping.num_kccgst) {
841 scanner_err(s, "invalid rule: must have same number of values as mapping line; ignoring rule");
847 matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s)
849 for (unsigned i = 0; i < m->mapping.num_mlvo; i++) {
850 enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i];
851 struct sval value = m->rule.mlvo_value_at_pos[i];
852 enum mlvo_match_type match_type = m->rule.match_type_at_pos[i];
853 struct matched_sval *to;
854 bool matched = false;
856 if (mlvo == MLVO_MODEL) {
857 to = &m->rmlvo.model;
858 matched = match_value_and_mark(m, value, to, match_type);
860 else if (mlvo == MLVO_LAYOUT) {
861 xkb_layout_index_t idx = m->mapping.layout_idx;
862 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
863 to = &darray_item(m->rmlvo.layouts, idx);
864 matched = match_value_and_mark(m, value, to, match_type);
866 else if (mlvo == MLVO_VARIANT) {
867 xkb_layout_index_t idx = m->mapping.layout_idx;
868 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
869 to = &darray_item(m->rmlvo.variants, idx);
870 matched = match_value_and_mark(m, value, to, match_type);
872 else if (mlvo == MLVO_OPTION) {
873 darray_foreach(to, m->rmlvo.options) {
874 matched = match_value_and_mark(m, value, to, match_type);
884 for (unsigned i = 0; i < m->mapping.num_kccgst; i++) {
885 enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i];
886 struct sval value = m->rule.kccgst_value_at_pos[i];
887 append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value);
891 * If a rule matches in a rule set, the rest of the set should be
892 * skipped. However, rule sets matching against options may contain
893 * several legitimate rules, so they are processed entirely.
895 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
896 m->mapping.skip = true;
899 static enum rules_token
900 gettok(struct matcher *m, struct scanner *s)
902 return lex(s, &m->val);
906 matcher_match(struct matcher *m, struct scanner *s,
907 unsigned include_depth,
908 const char *string, size_t len,
909 const char *file_name)
911 enum rules_token tok;
917 switch (tok = gettok(m, s)) {
920 case TOK_END_OF_LINE:
922 case TOK_END_OF_FILE:
929 switch (tok = gettok(m, s)) {
931 matcher_group_start_new(m, m->val.string);
934 goto include_statement;
936 matcher_mapping_start_new(m);
937 matcher_mapping_set_mlvo(m, s, m->val.string);
944 switch (tok = gettok(m, s)) {
952 switch (tok = gettok(m, s)) {
954 matcher_group_add_element(m, s, m->val.string);
956 case TOK_END_OF_LINE:
963 switch (tok = gettok(m, s)) {
965 matcher_include(m, s, include_depth, m->val.string);
972 switch (tok = gettok(m, s)) {
974 if (!m->mapping.skip)
975 matcher_mapping_set_mlvo(m, s, m->val.string);
984 switch (tok = gettok(m, s)) {
986 if (!m->mapping.skip)
987 matcher_mapping_set_kccgst(m, s, m->val.string);
989 case TOK_END_OF_LINE:
990 if (!m->mapping.skip)
991 matcher_mapping_verify(m, s);
992 goto rule_mlvo_first;
998 switch (tok = gettok(m, s)) {
1001 case TOK_END_OF_LINE:
1002 goto rule_mlvo_first;
1003 case TOK_END_OF_FILE:
1006 matcher_rule_start_new(m);
1007 goto rule_mlvo_no_tok;
1014 case TOK_IDENTIFIER:
1016 matcher_rule_set_mlvo(m, s, m->val.string);
1020 matcher_rule_set_mlvo_wildcard(m, s);
1022 case TOK_GROUP_NAME:
1024 matcher_rule_set_mlvo_group(m, s, m->val.string);
1033 switch (tok = gettok(m, s)) {
1034 case TOK_IDENTIFIER:
1036 matcher_rule_set_kccgst(m, s, m->val.string);
1038 case TOK_END_OF_LINE:
1040 matcher_rule_verify(m, s);
1042 matcher_rule_apply_if_matches(m, s);
1043 goto rule_mlvo_first;
1060 scanner_err(s, "unexpected token");
1066 read_rules_file(struct xkb_context *ctx,
1067 struct matcher *matcher,
1068 unsigned include_depth,
1075 struct scanner scanner;
1077 ret = map_file(file, &string, &size);
1079 log_err(ctx, "Couldn't read rules file \"%s\": %s\n",
1080 path, strerror(errno));
1084 scanner_init(&scanner, matcher->ctx, string, size, path, NULL);
1086 ret = matcher_match(matcher, &scanner, include_depth, string, size, path);
1088 unmap_file(string, size);
1094 xkb_components_from_rules(struct xkb_context *ctx,
1095 const struct xkb_rule_names *rmlvo,
1096 struct xkb_component_names *out)
1101 struct matcher *matcher = NULL;
1102 struct matched_sval *mval;
1104 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1108 matcher = matcher_new(ctx, rmlvo);
1110 ret = read_rules_file(ctx, matcher, 0, file, path);
1112 darray_empty(matcher->kccgst[KCCGST_KEYCODES]) ||
1113 darray_empty(matcher->kccgst[KCCGST_TYPES]) ||
1114 darray_empty(matcher->kccgst[KCCGST_COMPAT]) ||
1115 /* darray_empty(matcher->kccgst[KCCGST_GEOMETRY]) || */
1116 darray_empty(matcher->kccgst[KCCGST_SYMBOLS])) {
1117 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1122 darray_steal(matcher->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL);
1123 darray_steal(matcher->kccgst[KCCGST_TYPES], &out->types, NULL);
1124 darray_steal(matcher->kccgst[KCCGST_COMPAT], &out->compat, NULL);
1125 darray_steal(matcher->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL);
1126 darray_free(matcher->kccgst[KCCGST_GEOMETRY]);
1128 mval = &matcher->rmlvo.model;
1129 if (!mval->matched && mval->sval.len > 0)
1130 log_err(matcher->ctx, "Unrecognized RMLVO model \"%.*s\" was ignored\n",
1131 mval->sval.len, mval->sval.start);
1132 darray_foreach(mval, matcher->rmlvo.layouts)
1133 if (!mval->matched && mval->sval.len > 0)
1134 log_err(matcher->ctx, "Unrecognized RMLVO layout \"%.*s\" was ignored\n",
1135 mval->sval.len, mval->sval.start);
1136 darray_foreach(mval, matcher->rmlvo.variants)
1137 if (!mval->matched && mval->sval.len > 0)
1138 log_err(matcher->ctx, "Unrecognized RMLVO variant \"%.*s\" was ignored\n",
1139 mval->sval.len, mval->sval.start);
1140 darray_foreach(mval, matcher->rmlvo.options)
1141 if (!mval->matched && mval->sval.len > 0)
1142 log_err(matcher->ctx, "Unrecognized RMLVO option \"%.*s\" was ignored\n",
1143 mval->sval.len, mval->sval.start);
1148 matcher_free(matcher);