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"
58 * The purpose of this file is to map between configuration values that
59 * are easy for a user to specify and understand, and the configuration
60 * values xkbcomp uses and understands.
61 * xkbcomp uses the xkb_component_names struct, which maps directly to
62 * include statements of the appropriate sections, called for short
63 * KcCGST (see keycodes.c, types.c, compat.c, symbols.c; geometry.c was
64 * removed). These are not really intuitive or straight-forward for
66 * Instead, the user passes in a xkb_rule_names struct, which consists
67 * of the name of a rules file (in Linux this is usually "evdev"), a
68 * keyboard model (e.g. "pc105"), a set of layouts (which will end up
69 * in different groups, e.g. "us,fr"), variants (used to alter/augment
70 * the respective layout, e.g. "intl,dvorak"), and a set of options
71 * (used to tweak some general behavior of the keyboard, e.g.
72 * "ctrl:nocaps,compose:menu" to make the Caps Lock key act like Ctrl
73 * and the Menu key like Compose). We call these RMLVO.
77 * The file consists of rule sets, each consisting of rules (one per
78 * line), which match the MLVO values on the left hand side, and, if
79 * the values match to the values the user passed in, results in the
80 * values on the right hand side being added to the resulting KcCGST.
81 * Since some values are related and repeated often, it is possible
82 * to group them together and refer to them by a group name in the
84 * Along with matching values by simple string equality, and for
85 * membership in a group defined previously, rules may also contain
86 * "wildcard" values - "*" - which always match. These usually appear
91 * (It might be helpful to look at a file like rules/evdev along with
92 * this grammer. Comments, whitespace, etc. are not shown.)
94 * File ::= { "!" (Group | RuleSet) }
96 * Group ::= GroupName "=" { GroupElement } "\n"
97 * GroupName ::= "$"<ident>
98 * GroupElement ::= <ident>
100 * RuleSet ::= Mapping { Rule }
102 * Mapping ::= { Mlvo } "=" { Kccgst } "\n"
103 * Mlvo ::= "model" | "option" | ("layout" | "variant") [ Index ]
104 * Index ::= "[" 1..XKB_NUM_GROUPS "]"
105 * Kccgst ::= "keycodes" | "symbols" | "types" | "compat" | "geometry"
107 * Rule ::= { MlvoValue } "=" { KccgstValue } "\n"
108 * MlvoValue ::= "*" | GroupName | <ident>
109 * KccgstValue ::= <ident>
112 * - The order of values in a Rule must be the same as the Mapping it
113 * follows. The mapping line determines the meaning of the values in
114 * the rules which follow in the RuleSet.
115 * - If a Rule is matched, %-expansion is performed on the KccgstValue,
118 * The model, layout or variant, if only one was given (e.g.
119 * %l for "us,il" is invalid).
121 * Layout or variant for the specified group Index, if more than
122 * one was given (e.g. %l[1] for "us" is invalid).
123 * %+m, %+l, %+v, %+l[1], %+v[1]
124 * As above, but prefixed with '+'. Similarly, '|', '-', '_' may be
125 * used instead of '+'.
126 * %(m), %(l), %(l[1]), %(v), %(v[1]):
127 * As above, but prefixed by '(' and suffixed by ')'.
128 * In case the expansion is invalid, as described above, it is
129 * skipped (the rest of the string is still processed); this includes
130 * the prefix and suffix (that's why you shouldn't use e.g. "(%v[1])").
133 /* Scanner / Lexer */
135 /* Values returned with some tokens, like yylval. */
141 * Holds the location in the file of the last processed token,
159 /* C99 is stupid. Just use the 1 variant when there are no args. */
160 #define scanner_error1(scanner, loc, msg) \
161 log_warn(scanner->ctx, "rules/%s:%d:%d: " msg "\n", \
162 scanner->file_name, loc->line, loc->column)
163 #define scanner_error(scanner, loc, fmt, ...) \
164 log_warn(scanner->ctx, "rules/%s:%d:%d: " fmt "\n", \
165 scanner->file_name, loc->line, loc->column, __VA_ARGS__)
167 static enum rules_token
168 lex(struct scanner *s, union lvalue *val, struct location *loc)
170 skip_more_whitespace_and_comments:
172 while (chr(s, ' ') || chr(s, '\t'));
176 while (!eof(s) && !eol(s)) next(s);
181 while (eol(s)) next(s);
182 return TOK_END_OF_LINE;
185 /* Escaped line continuation. */
188 scanner_error1(s, loc,
189 "illegal new line escape; must appear at end of line");
193 goto skip_more_whitespace_and_comments;
196 /* See if we're done. */
197 if (eof(s)) return TOK_END_OF_FILE;
201 loc->column = s->column;
203 /* Operators and punctuation. */
204 if (chr(s, '!')) return TOK_BANG;
205 if (chr(s, '=')) return TOK_EQUALS;
206 if (chr(s, '*')) return TOK_STAR;
210 val->string.start = s->s + s->pos;
212 while (isgraph(peek(s))) {
216 if (val->string.len == 0) {
217 scanner_error1(s, loc,
218 "unexpected character after \'$\'; expected name");
221 return TOK_GROUP_NAME;
225 if (isgraph(peek(s))) {
226 val->string.start = s->s + s->pos;
228 while (isgraph(peek(s))) {
232 return TOK_IDENTIFIER;
235 scanner_error1(s, loc, "unrecognized token");
239 /***====================================================================***/
249 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
251 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
252 [MLVO_MODEL] = SVAL_LIT("model"),
253 [MLVO_LAYOUT] = SVAL_LIT("layout"),
254 [MLVO_VARIANT] = SVAL_LIT("variant"),
255 [MLVO_OPTION] = SVAL_LIT("option"),
267 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
268 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
269 [KCCGST_TYPES] = SVAL_LIT("types"),
270 [KCCGST_COMPAT] = SVAL_LIT("compat"),
271 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
272 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
276 * A broken-down version of xkb_rule_names (without the rules,
282 darray_sval variants;
288 darray_sval elements;
292 int mlvo_at_pos[_MLVO_NUM_ENTRIES];
293 unsigned int num_mlvo;
294 unsigned int defined_mlvo_mask;
295 xkb_layout_index_t layout_idx, variant_idx;
296 int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
297 unsigned int num_kccgst;
298 unsigned int defined_kccgst_mask;
302 enum mlvo_match_type {
303 MLVO_MATCH_NORMAL = 0,
309 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
310 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
311 unsigned int num_mlvo_values;
312 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
313 unsigned int num_kccgst_values;
318 * This is the main object used to match a given RMLVO against a rules
319 * file and aggragate the results in a KcCGST. It goes through a simple
320 * matching state machine, with tokens as transitions (see
324 struct xkb_context *ctx;
326 struct rule_names rmlvo;
329 struct scanner scanner;
330 darray(struct group) groups;
331 /* Current mapping. */
332 struct mapping mapping;
336 darray_char kccgst[_KCCGST_NUM_ENTRIES];
340 strip_spaces(struct sval v)
342 while (v.len > 0 && isspace(v.start[0])) { v.len--; v.start++; }
343 while (v.len > 0 && isspace(v.start[v.len - 1])) v.len--;
348 split_comma_separated_string(const char *s)
350 darray_sval arr = darray_new();
351 struct sval val = { NULL, 0 };
354 * Make sure the array returned by this function always includes at
355 * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
359 darray_append(arr, val);
364 val.start = s; val.len = 0;
365 while (*s != '\0' && *s != ',') { s++; val.len++; }
366 darray_append(arr, strip_spaces(val));
367 if (*s == '\0') break;
374 static struct matcher *
375 matcher_new(struct xkb_context *ctx,
376 const struct xkb_rule_names *rmlvo)
378 struct matcher *m = calloc(1, sizeof(*m));
383 m->rmlvo.model.start = rmlvo->model;
384 m->rmlvo.model.len = rmlvo->model ? strlen(rmlvo->model) : 0;
385 m->rmlvo.layouts = split_comma_separated_string(rmlvo->layout);
386 m->rmlvo.variants = split_comma_separated_string(rmlvo->variant);
387 m->rmlvo.options = split_comma_separated_string(rmlvo->options);
393 matcher_free(struct matcher *m)
398 darray_free(m->rmlvo.layouts);
399 darray_free(m->rmlvo.variants);
400 darray_free(m->rmlvo.options);
401 darray_foreach(group, m->groups)
402 darray_free(group->elements);
403 darray_free(m->groups);
407 /* C99 is stupid. Just use the 1 variant when there are no args. */
408 #define matcher_error1(matcher, msg) \
409 log_warn(matcher->ctx, "rules/%s:%d:%d: " msg "\n", \
410 matcher->scanner.file_name, matcher->loc.line, \
412 #define matcher_error(matcher, fmt, ...) \
413 log_warn(matcher->ctx, "rules/%s:%d:%d: " fmt "\n", \
414 matcher->scanner.file_name, matcher->loc.line, \
415 matcher->loc.column, __VA_ARGS__)
418 matcher_group_start_new(struct matcher *m, struct sval name)
420 struct group group = { .name = name, .elements = darray_new() };
421 darray_append(m->groups, group);
425 matcher_group_add_element(struct matcher *m, struct sval element)
427 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
432 matcher_mapping_start_new(struct matcher *m)
435 for (i = 0; i < _MLVO_NUM_ENTRIES; i++)
436 m->mapping.mlvo_at_pos[i] = -1;
437 for (i = 0; i < _KCCGST_NUM_ENTRIES; i++)
438 m->mapping.kccgst_at_pos[i] = -1;
439 m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
440 m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
441 m->mapping.defined_mlvo_mask = 0;
442 m->mapping.defined_kccgst_mask = 0;
443 m->mapping.skip = false;
447 extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
449 /* This function is pretty stupid, but works for now. */
450 *out = XKB_LAYOUT_INVALID;
453 if (s[0] != '[' || !isdigit(s[1]) || s[2] != ']')
455 if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
457 /* To zero-based index. */
458 *out = s[1] - '0' - 1;
463 matcher_mapping_set_mlvo(struct matcher *m, struct sval ident)
465 enum rules_mlvo mlvo;
466 struct sval mlvo_sval;
467 xkb_layout_index_t idx;
470 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
471 mlvo_sval = rules_mlvo_svals[mlvo];
473 if (svaleq_prefix(mlvo_sval, ident))
478 if (mlvo >= _MLVO_NUM_ENTRIES) {
480 "invalid mapping: %.*s is not a valid value here; "
482 ident.len, ident.start);
483 m->mapping.skip = true;
487 if (m->mapping.defined_mlvo_mask & (1 << mlvo)) {
489 "invalid mapping: %.*s appears twice on the same line; "
491 mlvo_sval.len, mlvo_sval.start);
492 m->mapping.skip = true;
496 /* If there are leftovers still, it must be an index. */
497 if (mlvo_sval.len < ident.len) {
498 consumed = extract_layout_index(ident.start + mlvo_sval.len,
499 ident.len - mlvo_sval.len, &idx);
500 if ((int) (ident.len - mlvo_sval.len) != consumed) {
502 "invalid mapping:\" %.*s\" may only be followed by a valid group index; "
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;
517 "invalid mapping: \"%.*s\" cannot be followed by a group index; "
519 mlvo_sval.len, mlvo_sval.start);
520 m->mapping.skip = true;
525 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
526 m->mapping.defined_mlvo_mask |= 1 << mlvo;
527 m->mapping.num_mlvo++;
531 matcher_mapping_set_kccgst(struct matcher *m, struct sval ident)
533 enum rules_kccgst kccgst;
534 struct sval kccgst_sval;
536 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
537 kccgst_sval = rules_kccgst_svals[kccgst];
539 if (svaleq(rules_kccgst_svals[kccgst], ident))
544 if (kccgst >= _KCCGST_NUM_ENTRIES) {
546 "invalid mapping: %.*s is not a valid value here; "
548 ident.len, ident.start);
549 m->mapping.skip = true;
553 if (m->mapping.defined_kccgst_mask & (1 << kccgst)) {
555 "invalid mapping: %.*s appears twice on the same line; "
557 kccgst_sval.len, kccgst_sval.start);
558 m->mapping.skip = true;
562 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
563 m->mapping.defined_kccgst_mask |= 1 << kccgst;
564 m->mapping.num_kccgst++;
568 matcher_mapping_verify(struct matcher *m)
570 if (m->mapping.num_mlvo == 0) {
572 "invalid mapping: must have at least one value on the left hand side; "
573 "ignoring rule set");
577 if (m->mapping.num_kccgst == 0) {
579 "invalid mapping: must have at least one value on the right hand side; "
580 "ignoring rule set");
585 * This following is very stupid, but this is how it works.
586 * See the "Notes" section in the overview above.
589 if (m->mapping.defined_mlvo_mask & (1 << MLVO_LAYOUT)) {
590 if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
591 if (darray_size(m->rmlvo.layouts) > 1)
595 if (darray_size(m->rmlvo.layouts) == 1 ||
596 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
601 if (m->mapping.defined_mlvo_mask & (1 << MLVO_VARIANT)) {
602 if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
603 if (darray_size(m->rmlvo.variants) > 1)
607 if (darray_size(m->rmlvo.variants) == 1 ||
608 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
616 m->mapping.skip = true;
620 matcher_rule_start_new(struct matcher *m)
622 memset(&m->rule, 0, sizeof(m->rule));
623 m->rule.skip = m->mapping.skip;
627 matcher_rule_set_mlvo_common(struct matcher *m, struct sval ident,
628 enum mlvo_match_type match_type)
630 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
632 "invalid rule: has more values than the mapping line; "
637 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
638 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
639 m->rule.num_mlvo_values++;
643 matcher_rule_set_mlvo_wildcard(struct matcher *m)
645 struct sval dummy = { NULL, 0 };
646 matcher_rule_set_mlvo_common(m, dummy, MLVO_MATCH_WILDCARD);
650 matcher_rule_set_mlvo_group(struct matcher *m, struct sval ident)
652 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_GROUP);
656 matcher_rule_set_mlvo(struct matcher *m, struct sval ident)
658 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_NORMAL);
662 matcher_rule_set_kccgst(struct matcher *m, struct sval ident)
664 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
666 "invalid rule: has more values than the mapping line; "
671 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
672 m->rule.num_kccgst_values++;
676 match_group(struct matcher *m, struct sval group_name, struct sval to)
679 struct sval *element;
682 darray_foreach(group, m->groups) {
683 if (svaleq(group->name, group_name)) {
691 * rules/evdev intentionally uses some undeclared group names
692 * in rules (e.g. commented group definitions which may be
693 * uncommented if needed). So we continue silently.
698 darray_foreach(element, group->elements)
699 if (svaleq(to, *element))
706 match_value(struct matcher *m, struct sval val, struct sval to,
707 enum mlvo_match_type match_type)
709 if (match_type == MLVO_MATCH_WILDCARD)
711 if (match_type == MLVO_MATCH_GROUP)
712 return match_group(m, val, to);
713 return svaleq(val, to);
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, darray_char *to,
725 size_t original_size = darray_size(*to);
726 const char *s = value.start;
727 xkb_layout_index_t idx;
730 struct sval expanded;
734 * Appending bar to foo -> foo (not an error if this happens)
735 * Appending +bar to foo -> foo+bar
736 * Appending bar to +foo -> bar+foo
737 * Appending +bar to +foo -> +foo+bar
739 if (!darray_empty(*to) && s[0] != '+' && s[0] != '|') {
740 if (darray_item(*to, 0) == '+' || darray_item(*to, 0) == '|')
741 darray_prepend_items_nullterminate(*to, value.start, value.len);
746 * Some ugly hand-lexing here, but going through the scanner is more
747 * trouble than it's worth, and the format is ugly on its own merit.
749 for (i = 0; i < value.len; ) {
750 /* Check if that's a start of an expansion. */
752 /* Just a normal character. */
753 darray_append_items_nullterminate(*to, &s[i++], 1);
756 if (++i >= value.len) goto error;
760 /* Check for prefix. */
761 if (s[i] == '(' || s[i] == '+' || s[i] == '|' ||
762 s[i] == '_' || s[i] == '-') {
764 if (s[i] == '(') sfx = ')';
765 if (++i >= value.len) goto error;
768 /* Mandatory model/layout/variant specifier. */
770 case 'm': mlv = MLVO_MODEL; break;
771 case 'l': mlv = MLVO_LAYOUT; break;
772 case 'v': mlv = MLVO_VARIANT; break;
776 /* Check for index. */
777 idx = XKB_LAYOUT_INVALID;
780 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
782 "invalid index in %%-expansion; "
783 "may only index layout or variant");
787 consumed = extract_layout_index(s + i, value.len - i, &idx);
788 if (consumed == -1) goto error;
792 idx = XKB_LAYOUT_INVALID;
796 /* Check for suffix, if there supposed to be one. */
798 if (i >= value.len) goto error;
799 if (s[i++] != sfx) goto error;
802 /* Get the expanded value. */
805 if (mlv == MLVO_LAYOUT) {
806 if (idx != XKB_LAYOUT_INVALID &&
807 idx < darray_size(m->rmlvo.layouts) &&
808 darray_size(m->rmlvo.layouts) > 1)
809 expanded = darray_item(m->rmlvo.layouts, idx);
810 else if (idx == XKB_LAYOUT_INVALID &&
811 darray_size(m->rmlvo.layouts) == 1)
812 expanded = darray_item(m->rmlvo.layouts, 0);
814 else if (mlv == MLVO_VARIANT) {
815 if (idx != XKB_LAYOUT_INVALID &&
816 idx < darray_size(m->rmlvo.variants) &&
817 darray_size(m->rmlvo.variants) > 1)
818 expanded = darray_item(m->rmlvo.variants, idx);
819 else if (idx == XKB_LAYOUT_INVALID &&
820 darray_size(m->rmlvo.variants) == 1)
821 expanded = darray_item(m->rmlvo.variants, 0);
823 else if (mlv == MLVO_MODEL) {
824 expanded = m->rmlvo.model;
827 /* If we didn't get one, skip silently. */
828 if (expanded.len <= 0)
832 darray_append_items_nullterminate(*to, &pfx, 1);
833 darray_append_items_nullterminate(*to, expanded.start, expanded.len);
835 darray_append_items_nullterminate(*to, &sfx, 1);
841 matcher_error1(m, "invalid %%-expansion in value; not used");
842 darray_resize(*to, original_size);
847 matcher_rule_verify(struct matcher *m)
849 if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
850 m->rule.num_kccgst_values != m->mapping.num_kccgst) {
852 "invalid rule: must have same number of values as mapping line;"
859 matcher_rule_apply_if_matches(struct matcher *m)
862 enum rules_mlvo mlvo;
863 enum rules_kccgst kccgst;
864 struct sval value, *option;
865 enum mlvo_match_type match_type;
866 bool matched = false;
867 xkb_layout_index_t idx;
869 for (i = 0; i < m->mapping.num_mlvo; i++) {
870 mlvo = m->mapping.mlvo_at_pos[i];
871 value = m->rule.mlvo_value_at_pos[i];
872 match_type = m->rule.match_type_at_pos[i];
874 if (mlvo == MLVO_MODEL) {
875 matched = match_value(m, value, m->rmlvo.model, match_type);
877 else if (mlvo == MLVO_LAYOUT) {
878 idx = m->mapping.layout_idx;
879 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
880 matched = match_value(m, value,
881 darray_item(m->rmlvo.layouts, idx),
884 else if (mlvo == MLVO_VARIANT) {
885 idx = m->mapping.layout_idx;
886 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
887 matched = match_value(m, value,
888 darray_item(m->rmlvo.variants, idx),
891 else if (mlvo == MLVO_OPTION) {
892 darray_foreach(option, m->rmlvo.options) {
893 matched = match_value(m, value, *option, match_type);
903 for (i = 0; i < m->mapping.num_kccgst; i++) {
904 kccgst = m->mapping.kccgst_at_pos[i];
905 value = m->rule.kccgst_value_at_pos[i];
906 append_expanded_kccgst_value(m, &m->kccgst[kccgst], value);
910 * If a rule matches in a rule set, the rest of the set should be
911 * skipped. However, rule sets matching against options may contain
912 * several legitimate rules, so they are processed entirely.
914 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
915 m->mapping.skip = true;
918 static enum rules_token
919 gettok(struct matcher *m)
921 return lex(&m->scanner, &m->val, &m->loc);
925 matcher_match(struct matcher *m, const char *string, size_t len,
926 const char *file_name, struct xkb_component_names *out)
928 enum rules_token tok;
933 scanner_init(&m->scanner, m->ctx, string, len, file_name);
936 switch (tok = gettok(m)) {
939 case TOK_END_OF_LINE:
941 case TOK_END_OF_FILE:
948 switch (tok = gettok(m)) {
950 matcher_group_start_new(m, m->val.string);
953 matcher_mapping_start_new(m);
954 matcher_mapping_set_mlvo(m, m->val.string);
961 switch (tok = gettok(m)) {
969 switch (tok = gettok(m)) {
971 matcher_group_add_element(m, m->val.string);
973 case TOK_END_OF_LINE:
980 switch (tok = gettok(m)) {
982 if (!m->mapping.skip)
983 matcher_mapping_set_mlvo(m, m->val.string);
992 switch (tok = gettok(m)) {
994 if (!m->mapping.skip)
995 matcher_mapping_set_kccgst(m, m->val.string);
997 case TOK_END_OF_LINE:
998 if (!m->mapping.skip)
999 matcher_mapping_verify(m);
1000 goto rule_mlvo_first;
1006 switch (tok = gettok(m)) {
1009 case TOK_END_OF_LINE:
1010 goto rule_mlvo_first;
1011 case TOK_END_OF_FILE:
1014 matcher_rule_start_new(m);
1015 goto rule_mlvo_no_tok;
1022 case TOK_IDENTIFIER:
1024 matcher_rule_set_mlvo(m, m->val.string);
1028 matcher_rule_set_mlvo_wildcard(m);
1030 case TOK_GROUP_NAME:
1032 matcher_rule_set_mlvo_group(m, m->val.string);
1041 switch (tok = gettok(m)) {
1042 case TOK_IDENTIFIER:
1044 matcher_rule_set_kccgst(m, m->val.string);
1046 case TOK_END_OF_LINE:
1048 matcher_rule_verify(m);
1050 matcher_rule_apply_if_matches(m);
1051 goto rule_mlvo_first;
1065 if (darray_empty(m->kccgst[KCCGST_KEYCODES]) ||
1066 darray_empty(m->kccgst[KCCGST_TYPES]) ||
1067 darray_empty(m->kccgst[KCCGST_COMPAT]) ||
1068 /* darray_empty(m->kccgst[KCCGST_GEOMETRY]) || */
1069 darray_empty(m->kccgst[KCCGST_SYMBOLS]))
1072 out->keycodes = darray_mem(m->kccgst[KCCGST_KEYCODES], 0);
1073 out->types = darray_mem(m->kccgst[KCCGST_TYPES], 0);
1074 out->compat = darray_mem(m->kccgst[KCCGST_COMPAT], 0);
1075 /* out->geometry = darray_mem(m->kccgst[KCCGST_GEOMETRY], 0); */
1076 darray_free(m->kccgst[KCCGST_GEOMETRY]);
1077 out->symbols = darray_mem(m->kccgst[KCCGST_SYMBOLS], 0);
1082 matcher_error1(m, "unexpected token");
1088 xkb_components_from_rules(struct xkb_context *ctx,
1089 const struct xkb_rule_names *rmlvo,
1090 struct xkb_component_names *out)
1097 struct matcher *matcher;
1099 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1103 ret = map_file(file, &string, &size);
1105 log_err(ctx, "Couldn't read rules file: %s\n", strerror(errno));
1109 matcher = matcher_new(ctx, rmlvo);
1110 ret = matcher_match(matcher, string, size, rmlvo->rules, out);
1112 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1113 matcher_free(matcher);
1115 unmap_file(string, size);