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.
52 #include "xkbcomp-priv.h"
55 #include "scanner-utils.h"
57 #define MAX_INCLUDE_DEPTH 5
61 /* Values returned with some tokens, like yylval. */
81 return is_graph(ch) && ch != '\\';
84 static enum rules_token
85 lex(struct scanner *s, union lvalue *val)
87 skip_more_whitespace_and_comments:
89 while (scanner_chr(s, ' ') || scanner_chr(s, '\t') || scanner_chr(s, '\r'));
92 if (scanner_lit(s, "//")) {
93 scanner_skip_to_eol(s);
98 while (scanner_eol(s)) scanner_next(s);
99 return TOK_END_OF_LINE;
102 /* Escaped line continuation. */
103 if (scanner_chr(s, '\\')) {
105 scanner_chr(s, '\r');
106 if (!scanner_eol(s)) {
107 scanner_err(s, "illegal new line escape; must appear at end of line");
111 goto skip_more_whitespace_and_comments;
114 /* See if we're done. */
115 if (scanner_eof(s)) return TOK_END_OF_FILE;
118 s->token_line = s->line;
119 s->token_column = s->column;
121 /* Operators and punctuation. */
122 if (scanner_chr(s, '!')) return TOK_BANG;
123 if (scanner_chr(s, '=')) return TOK_EQUALS;
124 if (scanner_chr(s, '*')) return TOK_STAR;
127 if (scanner_chr(s, '$')) {
128 val->string.start = s->s + s->pos;
130 while (is_ident(scanner_peek(s))) {
134 if (val->string.len == 0) {
135 scanner_err(s, "unexpected character after \'$\'; expected name");
138 return TOK_GROUP_NAME;
141 /* Include statement. */
142 if (scanner_lit(s, "include"))
146 if (is_ident(scanner_peek(s))) {
147 val->string.start = s->s + s->pos;
149 while (is_ident(scanner_peek(s))) {
153 return TOK_IDENTIFIER;
156 scanner_err(s, "unrecognized token");
160 /***====================================================================***/
170 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
172 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
173 [MLVO_MODEL] = SVAL_LIT("model"),
174 [MLVO_LAYOUT] = SVAL_LIT("layout"),
175 [MLVO_VARIANT] = SVAL_LIT("variant"),
176 [MLVO_OPTION] = SVAL_LIT("option"),
188 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
189 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
190 [KCCGST_TYPES] = SVAL_LIT("types"),
191 [KCCGST_COMPAT] = SVAL_LIT("compat"),
192 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
193 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
196 /* We use this to keep score whether an mlvo was matched or not; if not,
197 * we warn the user that his preference was ignored. */
198 struct matched_sval {
202 typedef darray(struct matched_sval) darray_matched_sval;
205 * A broken-down version of xkb_rule_names (without the rules,
209 struct matched_sval model;
210 darray_matched_sval layouts;
211 darray_matched_sval variants;
212 darray_matched_sval options;
217 darray_sval elements;
221 int mlvo_at_pos[_MLVO_NUM_ENTRIES];
222 unsigned int num_mlvo;
223 unsigned int defined_mlvo_mask;
224 xkb_layout_index_t layout_idx, variant_idx;
225 int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
226 unsigned int num_kccgst;
227 unsigned int defined_kccgst_mask;
231 enum mlvo_match_type {
232 MLVO_MATCH_NORMAL = 0,
238 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
239 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
240 unsigned int num_mlvo_values;
241 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
242 unsigned int num_kccgst_values;
247 * This is the main object used to match a given RMLVO against a rules
248 * file and aggragate the results in a KcCGST. It goes through a simple
249 * matching state machine, with tokens as transitions (see
253 struct xkb_context *ctx;
255 struct rule_names rmlvo;
257 darray(struct group) groups;
258 /* Current mapping. */
259 struct mapping mapping;
263 darray_char kccgst[_KCCGST_NUM_ENTRIES];
267 strip_spaces(struct sval v)
269 while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; }
270 while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--;
274 static darray_matched_sval
275 split_comma_separated_mlvo(const char *s)
277 darray_matched_sval arr = darray_new();
280 * Make sure the array returned by this function always includes at
281 * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
285 struct matched_sval val = { .sval = { NULL, 0 } };
286 darray_append(arr, val);
291 struct matched_sval val = { .sval = { s, 0 } };
292 while (*s != '\0' && *s != ',') { s++; val.sval.len++; }
293 val.sval = strip_spaces(val.sval);
294 darray_append(arr, val);
295 if (*s == '\0') break;
302 static struct matcher *
303 matcher_new(struct xkb_context *ctx,
304 const struct xkb_rule_names *rmlvo)
306 struct matcher *m = calloc(1, sizeof(*m));
311 m->rmlvo.model.sval.start = rmlvo->model;
312 m->rmlvo.model.sval.len = strlen_safe(rmlvo->model);
313 m->rmlvo.layouts = split_comma_separated_mlvo(rmlvo->layout);
314 m->rmlvo.variants = split_comma_separated_mlvo(rmlvo->variant);
315 m->rmlvo.options = split_comma_separated_mlvo(rmlvo->options);
321 matcher_free(struct matcher *m)
326 darray_free(m->rmlvo.layouts);
327 darray_free(m->rmlvo.variants);
328 darray_free(m->rmlvo.options);
329 darray_foreach(group, m->groups)
330 darray_free(group->elements);
331 for (int i = 0; i < _KCCGST_NUM_ENTRIES; i++)
332 darray_free(m->kccgst[i]);
333 darray_free(m->groups);
338 matcher_group_start_new(struct matcher *m, struct sval name)
340 struct group group = { .name = name, .elements = darray_new() };
341 darray_append(m->groups, group);
345 matcher_group_add_element(struct matcher *m, struct scanner *s,
348 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
353 read_rules_file(struct xkb_context *ctx,
354 struct matcher *matcher,
355 unsigned include_depth,
360 matcher_include(struct matcher *m, struct scanner *parent_scanner,
361 unsigned include_depth,
364 struct scanner s; /* parses the !include value */
367 scanner_init(&s, m->ctx, inc.start, inc.len,
368 parent_scanner->file_name, NULL);
369 s.token_line = parent_scanner->token_line;
370 s.token_column = parent_scanner->token_column;
373 if (include_depth >= MAX_INCLUDE_DEPTH) {
374 scanner_err(&s, "maximum include depth (%d) exceeded; maybe there is an include loop?",
379 while (!scanner_eof(&s) && !scanner_eol(&s)) {
380 if (scanner_chr(&s, '%')) {
381 if (scanner_chr(&s, '%')) {
382 scanner_buf_append(&s, '%');
384 else if (scanner_chr(&s, 'H')) {
385 const char *home = xkb_context_getenv(m->ctx, "HOME");
387 scanner_err(&s, "%%H was used in an include statement, but the HOME environment variable is not set");
390 if (!scanner_buf_appends(&s, home)) {
391 scanner_err(&s, "include path after expanding %%H is too long");
395 else if (scanner_chr(&s, 'S')) {
396 const char *default_root = xkb_context_include_path_get_system_path(m->ctx);
397 if (!scanner_buf_appends(&s, default_root) || !scanner_buf_appends(&s, "/rules")) {
398 scanner_err(&s, "include path after expanding %%S is too long");
402 else if (scanner_chr(&s, 'E')) {
403 const char *default_root = xkb_context_include_path_get_extra_path(m->ctx);
404 if (!scanner_buf_appends(&s, default_root) || !scanner_buf_appends(&s, "/rules")) {
405 scanner_err(&s, "include path after expanding %%E is too long");
410 scanner_err(&s, "unknown %% format (%c) in include statement", scanner_peek(&s));
415 scanner_buf_append(&s, scanner_next(&s));
418 if (!scanner_buf_append(&s, '\0')) {
419 scanner_err(&s, "include path is too long");
423 file = fopen(s.buf, "rb");
425 bool ret = read_rules_file(m->ctx, m, include_depth + 1, file, s.buf);
427 log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
428 "No components returned from included XKB rules \"%s\"\n",
432 log_err(m->ctx, XKB_LOG_MESSAGE_NO_ID,
433 "Failed to open included XKB rules \"%s\"\n",
439 matcher_mapping_start_new(struct matcher *m)
441 for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++)
442 m->mapping.mlvo_at_pos[i] = -1;
443 for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++)
444 m->mapping.kccgst_at_pos[i] = -1;
445 m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
446 m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
447 m->mapping.defined_mlvo_mask = 0;
448 m->mapping.defined_kccgst_mask = 0;
449 m->mapping.skip = false;
453 extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
455 /* This function is pretty stupid, but works for now. */
456 *out = XKB_LAYOUT_INVALID;
459 if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']')
461 if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
463 /* To zero-based index. */
464 *out = s[1] - '0' - 1;
469 matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s,
472 enum rules_mlvo mlvo;
473 struct sval mlvo_sval;
475 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
476 mlvo_sval = rules_mlvo_svals[mlvo];
478 if (svaleq_prefix(mlvo_sval, ident))
483 if (mlvo >= _MLVO_NUM_ENTRIES) {
484 scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
485 ident.len, ident.start);
486 m->mapping.skip = true;
490 if (m->mapping.defined_mlvo_mask & (1u << mlvo)) {
491 scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
492 mlvo_sval.len, mlvo_sval.start);
493 m->mapping.skip = true;
497 /* If there are leftovers still, it must be an index. */
498 if (mlvo_sval.len < ident.len) {
499 xkb_layout_index_t idx;
500 int consumed = extract_layout_index(ident.start + mlvo_sval.len,
501 ident.len - mlvo_sval.len, &idx);
502 if ((int) (ident.len - mlvo_sval.len) != consumed) {
503 scanner_err(s, "invalid mapping: \"%.*s\" may only be followed by a valid group index; ignoring rule set",
504 mlvo_sval.len, mlvo_sval.start);
505 m->mapping.skip = true;
509 if (mlvo == MLVO_LAYOUT) {
510 m->mapping.layout_idx = idx;
512 else if (mlvo == MLVO_VARIANT) {
513 m->mapping.variant_idx = idx;
516 scanner_err(s, "invalid mapping: \"%.*s\" cannot be followed by a group index; ignoring rule set",
517 mlvo_sval.len, mlvo_sval.start);
518 m->mapping.skip = true;
523 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
524 m->mapping.defined_mlvo_mask |= 1u << mlvo;
525 m->mapping.num_mlvo++;
529 matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident)
531 enum rules_kccgst kccgst;
532 struct sval kccgst_sval;
534 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
535 kccgst_sval = rules_kccgst_svals[kccgst];
537 if (svaleq(rules_kccgst_svals[kccgst], ident))
542 if (kccgst >= _KCCGST_NUM_ENTRIES) {
543 scanner_err(s, "invalid mapping: %.*s is not a valid value here; ignoring rule set",
544 ident.len, ident.start);
545 m->mapping.skip = true;
549 if (m->mapping.defined_kccgst_mask & (1u << kccgst)) {
550 scanner_err(s, "invalid mapping: %.*s appears twice on the same line; ignoring rule set",
551 kccgst_sval.len, kccgst_sval.start);
552 m->mapping.skip = true;
556 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
557 m->mapping.defined_kccgst_mask |= 1u << kccgst;
558 m->mapping.num_kccgst++;
562 matcher_mapping_verify(struct matcher *m, struct scanner *s)
564 if (m->mapping.num_mlvo == 0) {
565 scanner_err(s, "invalid mapping: must have at least one value on the left hand side; ignoring rule set");
569 if (m->mapping.num_kccgst == 0) {
570 scanner_err(s, "invalid mapping: must have at least one value on the right hand side; ignoring rule set");
575 * This following is very stupid, but this is how it works.
576 * See the "Notes" section in the overview above.
579 if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) {
580 if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
581 if (darray_size(m->rmlvo.layouts) > 1)
585 if (darray_size(m->rmlvo.layouts) == 1 ||
586 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
591 if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) {
592 if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
593 if (darray_size(m->rmlvo.variants) > 1)
597 if (darray_size(m->rmlvo.variants) == 1 ||
598 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
606 m->mapping.skip = true;
610 matcher_rule_start_new(struct matcher *m)
612 memset(&m->rule, 0, sizeof(m->rule));
613 m->rule.skip = m->mapping.skip;
617 matcher_rule_set_mlvo_common(struct matcher *m, struct scanner *s,
619 enum mlvo_match_type match_type)
621 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
622 scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
626 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
627 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
628 m->rule.num_mlvo_values++;
632 matcher_rule_set_mlvo_wildcard(struct matcher *m, struct scanner *s)
634 struct sval dummy = { NULL, 0 };
635 matcher_rule_set_mlvo_common(m, s, dummy, MLVO_MATCH_WILDCARD);
639 matcher_rule_set_mlvo_group(struct matcher *m, struct scanner *s,
642 matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_GROUP);
646 matcher_rule_set_mlvo(struct matcher *m, struct scanner *s,
649 matcher_rule_set_mlvo_common(m, s, ident, MLVO_MATCH_NORMAL);
653 matcher_rule_set_kccgst(struct matcher *m, struct scanner *s,
656 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
657 scanner_err(s, "invalid rule: has more values than the mapping line; ignoring rule");
661 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
662 m->rule.num_kccgst_values++;
666 match_group(struct matcher *m, struct sval group_name, struct sval to)
669 struct sval *element;
672 darray_foreach(group, m->groups) {
673 if (svaleq(group->name, group_name)) {
681 * rules/evdev intentionally uses some undeclared group names
682 * in rules (e.g. commented group definitions which may be
683 * uncommented if needed). So we continue silently.
688 darray_foreach(element, group->elements)
689 if (svaleq(to, *element))
696 match_value(struct matcher *m, struct sval val, struct sval to,
697 enum mlvo_match_type match_type)
699 if (match_type == MLVO_MATCH_WILDCARD)
701 if (match_type == MLVO_MATCH_GROUP)
702 return match_group(m, val, to);
703 return svaleq(val, to);
707 match_value_and_mark(struct matcher *m, struct sval val,
708 struct matched_sval *to, enum mlvo_match_type match_type)
710 bool matched = match_value(m, val, to->sval, match_type);
717 * This function performs %-expansion on @value (see overview above),
718 * and appends the result to @to.
721 append_expanded_kccgst_value(struct matcher *m, struct scanner *s,
722 darray_char *to, struct sval value)
724 const char *str = value.start;
725 darray_char expanded = darray_new();
727 bool expanded_plus, to_plus;
730 * Some ugly hand-lexing here, but going through the scanner is more
731 * trouble than it's worth, and the format is ugly on its own merit.
733 for (unsigned i = 0; i < value.len; ) {
735 xkb_layout_index_t idx;
737 struct matched_sval *expanded_value;
739 /* Check if that's a start of an expansion. */
741 /* Just a normal character. */
742 darray_appends_nullterminate(expanded, &str[i++], 1);
745 if (++i >= value.len) goto error;
749 /* Check for prefix. */
750 if (str[i] == '(' || str[i] == '+' || str[i] == '|' ||
751 str[i] == '_' || str[i] == '-') {
753 if (str[i] == '(') sfx = ')';
754 if (++i >= value.len) goto error;
757 /* Mandatory model/layout/variant specifier. */
759 case 'm': mlv = MLVO_MODEL; break;
760 case 'l': mlv = MLVO_LAYOUT; break;
761 case 'v': mlv = MLVO_VARIANT; break;
765 /* Check for index. */
766 idx = XKB_LAYOUT_INVALID;
767 if (i < value.len && str[i] == '[') {
770 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
771 scanner_err(s, "invalid index in %%-expansion; may only index layout or variant");
775 consumed = extract_layout_index(str + i, value.len - i, &idx);
776 if (consumed == -1) goto error;
780 /* Check for suffix, if there supposed to be one. */
782 if (i >= value.len) goto error;
783 if (str[i++] != sfx) goto error;
786 /* Get the expanded value. */
787 expanded_value = NULL;
789 if (mlv == MLVO_LAYOUT) {
790 if (idx != XKB_LAYOUT_INVALID &&
791 idx < darray_size(m->rmlvo.layouts) &&
792 darray_size(m->rmlvo.layouts) > 1)
793 expanded_value = &darray_item(m->rmlvo.layouts, idx);
794 else if (idx == XKB_LAYOUT_INVALID &&
795 darray_size(m->rmlvo.layouts) == 1)
796 expanded_value = &darray_item(m->rmlvo.layouts, 0);
798 else if (mlv == MLVO_VARIANT) {
799 if (idx != XKB_LAYOUT_INVALID &&
800 idx < darray_size(m->rmlvo.variants) &&
801 darray_size(m->rmlvo.variants) > 1)
802 expanded_value = &darray_item(m->rmlvo.variants, idx);
803 else if (idx == XKB_LAYOUT_INVALID &&
804 darray_size(m->rmlvo.variants) == 1)
805 expanded_value = &darray_item(m->rmlvo.variants, 0);
807 else if (mlv == MLVO_MODEL) {
808 expanded_value = &m->rmlvo.model;
811 /* If we didn't get one, skip silently. */
812 if (!expanded_value || expanded_value->sval.len == 0)
816 darray_appends_nullterminate(expanded, &pfx, 1);
817 darray_appends_nullterminate(expanded,
818 expanded_value->sval.start,
819 expanded_value->sval.len);
821 darray_appends_nullterminate(expanded, &sfx, 1);
822 expanded_value->matched = true;
826 * Appending bar to foo -> foo (not an error if this happens)
827 * Appending +bar to foo -> foo+bar
828 * Appending bar to +foo -> bar+foo
829 * Appending +bar to +foo -> +foo+bar
832 ch = (darray_empty(expanded) ? '\0' : darray_item(expanded, 0));
833 expanded_plus = (ch == '+' || ch == '|');
834 ch = (darray_empty(*to) ? '\0' : darray_item(*to, 0));
835 to_plus = (ch == '+' || ch == '|');
837 if (expanded_plus || darray_empty(*to))
838 darray_appends_nullterminate(*to, expanded.item, expanded.size);
840 darray_prepends_nullterminate(*to, expanded.item, expanded.size);
842 darray_free(expanded);
846 darray_free(expanded);
847 scanner_err(s, "invalid %%-expansion in value; not used");
852 matcher_rule_verify(struct matcher *m, struct scanner *s)
854 if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
855 m->rule.num_kccgst_values != m->mapping.num_kccgst) {
856 scanner_err(s, "invalid rule: must have same number of values as mapping line; ignoring rule");
862 matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s)
864 for (unsigned i = 0; i < m->mapping.num_mlvo; i++) {
865 enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i];
866 struct sval value = m->rule.mlvo_value_at_pos[i];
867 enum mlvo_match_type match_type = m->rule.match_type_at_pos[i];
868 struct matched_sval *to;
869 bool matched = false;
871 if (mlvo == MLVO_MODEL) {
872 to = &m->rmlvo.model;
873 matched = match_value_and_mark(m, value, to, match_type);
875 else if (mlvo == MLVO_LAYOUT) {
876 xkb_layout_index_t idx = m->mapping.layout_idx;
877 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
878 to = &darray_item(m->rmlvo.layouts, idx);
879 matched = match_value_and_mark(m, value, to, match_type);
881 else if (mlvo == MLVO_VARIANT) {
882 xkb_layout_index_t idx = m->mapping.layout_idx;
883 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
884 to = &darray_item(m->rmlvo.variants, idx);
885 matched = match_value_and_mark(m, value, to, match_type);
887 else if (mlvo == MLVO_OPTION) {
888 darray_foreach(to, m->rmlvo.options) {
889 matched = match_value_and_mark(m, value, to, match_type);
899 for (unsigned i = 0; i < m->mapping.num_kccgst; i++) {
900 enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i];
901 struct sval value = m->rule.kccgst_value_at_pos[i];
902 append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value);
906 * If a rule matches in a rule set, the rest of the set should be
907 * skipped. However, rule sets matching against options may contain
908 * several legitimate rules, so they are processed entirely.
910 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
911 m->mapping.skip = true;
914 static enum rules_token
915 gettok(struct matcher *m, struct scanner *s)
917 return lex(s, &m->val);
921 matcher_match(struct matcher *m, struct scanner *s,
922 unsigned include_depth,
923 const char *string, size_t len,
924 const char *file_name)
926 enum rules_token tok;
932 switch (tok = gettok(m, s)) {
935 case TOK_END_OF_LINE:
937 case TOK_END_OF_FILE:
944 switch (tok = gettok(m, s)) {
946 matcher_group_start_new(m, m->val.string);
949 goto include_statement;
951 matcher_mapping_start_new(m);
952 matcher_mapping_set_mlvo(m, s, m->val.string);
959 switch (tok = gettok(m, s)) {
967 switch (tok = gettok(m, s)) {
969 matcher_group_add_element(m, s, m->val.string);
971 case TOK_END_OF_LINE:
978 switch (tok = gettok(m, s)) {
980 matcher_include(m, s, include_depth, m->val.string);
987 switch (tok = gettok(m, s)) {
989 if (!m->mapping.skip)
990 matcher_mapping_set_mlvo(m, s, m->val.string);
999 switch (tok = gettok(m, s)) {
1000 case TOK_IDENTIFIER:
1001 if (!m->mapping.skip)
1002 matcher_mapping_set_kccgst(m, s, m->val.string);
1003 goto mapping_kccgst;
1004 case TOK_END_OF_LINE:
1005 if (!m->mapping.skip)
1006 matcher_mapping_verify(m, s);
1007 goto rule_mlvo_first;
1013 switch (tok = gettok(m, s)) {
1016 case TOK_END_OF_LINE:
1017 goto rule_mlvo_first;
1018 case TOK_END_OF_FILE:
1021 matcher_rule_start_new(m);
1022 goto rule_mlvo_no_tok;
1029 case TOK_IDENTIFIER:
1031 matcher_rule_set_mlvo(m, s, m->val.string);
1035 matcher_rule_set_mlvo_wildcard(m, s);
1037 case TOK_GROUP_NAME:
1039 matcher_rule_set_mlvo_group(m, s, m->val.string);
1048 switch (tok = gettok(m, s)) {
1049 case TOK_IDENTIFIER:
1051 matcher_rule_set_kccgst(m, s, m->val.string);
1053 case TOK_END_OF_LINE:
1055 matcher_rule_verify(m, s);
1057 matcher_rule_apply_if_matches(m, s);
1058 goto rule_mlvo_first;
1075 scanner_err(s, "unexpected token");
1081 read_rules_file(struct xkb_context *ctx,
1082 struct matcher *matcher,
1083 unsigned include_depth,
1090 struct scanner scanner;
1092 ret = map_file(file, &string, &size);
1094 log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
1095 "Couldn't read rules file \"%s\": %s\n",
1096 path, strerror(errno));
1100 scanner_init(&scanner, matcher->ctx, string, size, path, NULL);
1102 ret = matcher_match(matcher, &scanner, include_depth, string, size, path);
1104 unmap_file(string, size);
1110 xkb_components_from_rules(struct xkb_context *ctx,
1111 const struct xkb_rule_names *rmlvo,
1112 struct xkb_component_names *out)
1117 struct matcher *matcher = NULL;
1118 struct matched_sval *mval;
1119 unsigned int offset = 0;
1121 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path, &offset);
1125 matcher = matcher_new(ctx, rmlvo);
1127 ret = read_rules_file(ctx, matcher, 0, file, path);
1129 darray_empty(matcher->kccgst[KCCGST_KEYCODES]) ||
1130 darray_empty(matcher->kccgst[KCCGST_TYPES]) ||
1131 darray_empty(matcher->kccgst[KCCGST_COMPAT]) ||
1132 /* darray_empty(matcher->kccgst[KCCGST_GEOMETRY]) || */
1133 darray_empty(matcher->kccgst[KCCGST_SYMBOLS])) {
1134 log_err(ctx, XKB_LOG_MESSAGE_NO_ID,
1135 "No components returned from XKB rules \"%s\"\n", path);
1140 darray_steal(matcher->kccgst[KCCGST_KEYCODES], &out->keycodes, NULL);
1141 darray_steal(matcher->kccgst[KCCGST_TYPES], &out->types, NULL);
1142 darray_steal(matcher->kccgst[KCCGST_COMPAT], &out->compat, NULL);
1143 darray_steal(matcher->kccgst[KCCGST_SYMBOLS], &out->symbols, NULL);
1144 darray_free(matcher->kccgst[KCCGST_GEOMETRY]);
1146 mval = &matcher->rmlvo.model;
1147 if (!mval->matched && mval->sval.len > 0)
1148 log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
1149 "Unrecognized RMLVO model \"%.*s\" was ignored\n",
1150 mval->sval.len, mval->sval.start);
1151 darray_foreach(mval, matcher->rmlvo.layouts)
1152 if (!mval->matched && mval->sval.len > 0)
1153 log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
1154 "Unrecognized RMLVO layout \"%.*s\" was ignored\n",
1155 mval->sval.len, mval->sval.start);
1156 darray_foreach(mval, matcher->rmlvo.variants)
1157 if (!mval->matched && mval->sval.len > 0)
1158 log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
1159 "Unrecognized RMLVO variant \"%.*s\" was ignored\n",
1160 mval->sval.len, mval->sval.start);
1161 darray_foreach(mval, matcher->rmlvo.options)
1162 if (!mval->matched && mval->sval.len > 0)
1163 log_err(matcher->ctx, XKB_LOG_MESSAGE_NO_ID,
1164 "Unrecognized RMLVO option \"%.*s\" was ignored\n",
1165 mval->sval.len, mval->sval.start);
1170 matcher_free(matcher);