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 ********************************************************/
34 #include <sys/types.h>
36 #include "xkbcomp-priv.h"
43 * The purpose of this file is to map between configuration values that
44 * are easy for a user to specify and understand, and the configuration
45 * values xkbcomp uses and understands.
46 * xkbcomp uses the xkb_component_names struct, which maps directly to
47 * include statements of the appropriate sections, called for short
48 * KcCGST (see keycodes.c, types.c, compat.c, symbols.c; geometry.c was
49 * removed). These are not really intuitive or straight-forward for
51 * Instead, the user passes in a xkb_rule_names struct, which consists
52 * of the name of a rules file (in Linux this is usually "evdev"), a
53 * keyboard model (e.g. "pc105"), a set of layouts (which will end up
54 * in different groups, e.g. "us,fr"), variants (used to alter/augment
55 * the respective layout, e.g. "intl,dvorak"), and a set of options
56 * (used to tweak some general behavior of the keyboard, e.g.
57 * "ctrl:nocaps,compose:menu" to make the Caps Lock key act like Ctrl
58 * and the Menu key like Compose). We call these RMLVO.
62 * The file consists of rule sets, each consisting of rules (one per
63 * line), which match the MLVO values on the left hand side, and, if
64 * the values match to the values the user passed in, results in the
65 * values on the right hand side being added to the resulting KcCGST.
66 * Since some values are related and repeated often, it is possible
67 * to group them together and refer to them by a group name in the
69 * Along with matching values by simple string equality, and for
70 * membership in a group defined previously, rules may also contain
71 * "wildcard" values - "*" - which always match. These usually appear
76 * (It might be helpful to look at a file like rules/evdev along with
77 * this grammer. Comments, whitespace, etc. are not shown.)
79 * File ::= { "!" (Group | RuleSet) }
81 * Group ::= GroupName "=" { GroupElement } "\n"
82 * GroupName ::= "$"<ident>
83 * GroupElement ::= <ident>
85 * RuleSet ::= Mapping { Rule }
87 * Mapping ::= { Mlvo } "=" { Kccgst } "\n"
88 * Mlvo ::= "model" | "option" | ("layout" | "variant") [ Index ]
89 * Index ::= "[" 1..XkbNumKbdGroups "]"
90 * Kccgst ::= "keycodes" | "symbols" | "types" | "compat" | "geometry"
92 * Rule ::= { MlvoValue } "=" { KccgstValue } "\n"
93 * MlvoValue ::= "*" | GroupName | <ident>
94 * KccgstValue ::= <ident>
97 * - The order of values in a Rule must be the same as the Mapping it
98 * follows. The mapping line determines the meaning of the values in
99 * the rules which follow in the RuleSet.
100 * - If a Rule is matched, %-expansion is performed on the KccgstValue,
103 * The model, layout or variant, if only one was given (e.g.
104 * %l for "us,il" is invalid).
106 * Layout or variant for the specified group Index, if more than
107 * one was given (e.g. %l[1] for "us" is invalid).
108 * %+m, %+l, %+v, %+l[1], %+v[1]
109 * As above, but prefixed with '+'. Similarly, '|', '-', '_' may be
110 * used instead of '+'.
111 * %(m), %(l), %(l[1]), %(v), %(v[1]):
112 * As above, but prefixed by '(' and suffixed by ')'.
113 * In case the expansion is invalid, as described above, it is
114 * skipped (the rest of the string is still processed); this includes
115 * the prefix and suffix (that's why you shouldn't use e.g. "(%v[1])").
118 /* Scanner / Lexer */
120 /* Point to some substring in the file; used to avoid copying. */
125 typedef darray(struct sval) darray_sval;
128 svaleq(struct sval s1, struct sval s2)
130 return s1.len == s2.len && strncmp(s1.start, s2.start, s1.len) == 0;
134 svaleq_prefix(struct sval s1, struct sval s2)
136 return s1.len <= s2.len && strncmp(s1.start, s2.start, s1.len) == 0;
139 /* Values returned with some tokens, like yylval. */
145 * Holds the location in the file of the last processed token,
157 const char *file_name;
158 struct xkb_context *ctx;
173 scanner_init(struct scanner *s, struct xkb_context *ctx,
174 const char *string, size_t len, const char *file_name)
179 s->line = s->column = 1;
180 s->file_name = file_name;
184 /* C99 is stupid. Just use the 1 variant when there are no args. */
185 #define scanner_error1(scanner, loc, msg) \
186 log_warn(scanner->ctx, "rules/%s:%d:%d: " msg "\n", \
187 scanner->file_name, loc->line, loc->column)
188 #define scanner_error(scanner, loc, fmt, ...) \
189 log_warn(scanner->ctx, "rules/%s:%d:%d: " fmt "\n", \
190 scanner->file_name, loc->line, loc->column, __VA_ARGS__)
193 peek(struct scanner *s)
199 eof(struct scanner *s)
201 return s->s[s->pos] == '\0' || s->pos >= s->len;
205 eol(struct scanner *s)
207 return s->s[s->pos] == '\n';
211 * Use the check_nl variant when the current char might be a new line;
212 * just an optimization.
215 next(struct scanner *s)
218 return s->s[s->pos++];
222 next_check_nl(struct scanner *s)
231 return s->s[s->pos++];
235 chr(struct scanner *s, char ch)
239 s->pos++; s->column++;
244 str(struct scanner *s, const char *string, size_t len)
246 if (strncasecmp(s->s + s->pos, string, len) != 0)
248 s->pos += len; s->column += len;
252 #define lit(s, literal) str(s, literal, sizeof(literal) - 1)
254 static enum rules_token
255 lex(struct scanner *s, union lvalue *val, struct location *loc)
257 skip_more_whitespace_and_comments:
259 while (chr(s, ' ') || chr(s, '\t'));
263 while (!eof(s) && !eol(s)) next(s);
268 while (eol(s)) next_check_nl(s);
269 return TOK_END_OF_LINE;
272 /* Escaped line continuation. */
275 scanner_error1(s, loc,
276 "illegal new line escape; must appear at end of line");
280 goto skip_more_whitespace_and_comments;
283 /* See if we're done. */
284 if (eof(s)) return TOK_END_OF_FILE;
288 loc->column = s->column;
290 /* Operators and punctuation. */
291 if (chr(s, '!')) return TOK_BANG;
292 if (chr(s, '=')) return TOK_EQUALS;
293 if (chr(s, '*')) return TOK_STAR;
297 val->string.start = s->s + s->pos;
299 while (isgraph(peek(s))) {
303 if (val->string.len == 0) {
304 scanner_error1(s, loc,
305 "unexpected character after \'$\'; expected name");
308 return TOK_GROUP_NAME;
312 if (isgraph(peek(s))) {
313 val->string.start = s->s + s->pos;
315 while (isgraph(peek(s))) {
319 return TOK_IDENTIFIER;
322 scanner_error1(s, loc, "unrecognized token");
326 /***====================================================================***/
336 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
338 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
339 [MLVO_MODEL] = SVAL_LIT("model"),
340 [MLVO_LAYOUT] = SVAL_LIT("layout"),
341 [MLVO_VARIANT] = SVAL_LIT("variant"),
342 [MLVO_OPTION] = SVAL_LIT("option"),
354 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
355 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
356 [KCCGST_TYPES] = SVAL_LIT("types"),
357 [KCCGST_COMPAT] = SVAL_LIT("compat"),
358 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
359 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
363 * A broken-down version of xkb_rule_names (without the rules,
369 darray_sval variants;
375 darray_sval elements;
379 int mlvo_at_pos[_MLVO_NUM_ENTRIES];
380 unsigned int num_mlvo;
381 unsigned int defined_mlvo_mask;
382 xkb_group_index_t layout_idx, variant_idx;
383 int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
384 unsigned int num_kccgst;
385 unsigned int defined_kccgst_mask;
389 enum mlvo_match_type {
390 MLVO_MATCH_NORMAL = 0,
396 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
397 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
398 unsigned int num_mlvo_values;
399 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
400 unsigned int num_kccgst_values;
405 * This is the main object used to match a given RMLVO against a rules
406 * file and aggragate the results in a KcCGST. It goes through a simple
407 * matching state machine, with tokens as transitions (see
411 struct xkb_context *ctx;
413 struct rule_names rmlvo;
416 struct scanner scanner;
417 darray(struct group) groups;
418 /* Current mapping. */
419 struct mapping mapping;
423 darray_char kccgst[_KCCGST_NUM_ENTRIES];
427 strip_spaces(struct sval v)
429 while (v.len > 0 && isspace(v.start[0])) { v.len--; v.start++; }
430 while (v.len > 0 && isspace(v.start[v.len - 1])) v.len--;
435 split_comma_separated_string(const char *s)
437 darray_sval arr = darray_new();
438 struct sval val = { NULL, 0 };
441 * Make sure the array returned by this function always includes at
442 * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
446 darray_append(arr, val);
451 val.start = s; val.len = 0;
452 while (*s != '\0' && *s != ',') { s++; val.len++; }
453 darray_append(arr, strip_spaces(val));
454 if (*s == '\0') break;
461 static struct matcher *
462 matcher_new(struct xkb_context *ctx,
463 const struct xkb_rule_names *rmlvo)
465 struct matcher *m = calloc(1, sizeof(*m));
470 m->rmlvo.model.start = rmlvo->model;
471 m->rmlvo.model.len = rmlvo->model ? strlen(rmlvo->model) : 0;
472 m->rmlvo.layouts = split_comma_separated_string(rmlvo->layout);
473 m->rmlvo.variants = split_comma_separated_string(rmlvo->variant);
474 m->rmlvo.options = split_comma_separated_string(rmlvo->options);
480 matcher_free(struct matcher *m)
485 darray_free(m->rmlvo.layouts);
486 darray_free(m->rmlvo.variants);
487 darray_free(m->rmlvo.options);
488 darray_foreach(group, m->groups)
489 darray_free(group->elements);
490 darray_free(m->groups);
494 /* C99 is stupid. Just use the 1 variant when there are no args. */
495 #define matcher_error1(matcher, msg) \
496 log_warn(matcher->ctx, "rules/%s:%d:%d: " msg "\n", \
497 matcher->scanner.file_name, matcher->loc.line, \
499 #define matcher_error(matcher, fmt, ...) \
500 log_warn(matcher->ctx, "rules/%s:%d:%d: " fmt "\n", \
501 matcher->scanner.file_name, matcher->loc.line, \
502 matcher->loc.column, __VA_ARGS__)
505 matcher_group_start_new(struct matcher *m, struct sval name)
507 struct group group = { .name = name, .elements = darray_new() };
508 darray_append(m->groups, group);
512 matcher_group_add_element(struct matcher *m, struct sval element)
514 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
519 matcher_mapping_start_new(struct matcher *m)
522 for (i = 0; i < _MLVO_NUM_ENTRIES; i++)
523 m->mapping.mlvo_at_pos[i] = -1;
524 for (i = 0; i < _KCCGST_NUM_ENTRIES; i++)
525 m->mapping.kccgst_at_pos[i] = -1;
526 m->mapping.layout_idx = m->mapping.variant_idx = XKB_GROUP_INVALID;
527 m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
528 m->mapping.defined_mlvo_mask = 0;
529 m->mapping.defined_kccgst_mask = 0;
530 m->mapping.skip = false;
534 extract_group_index(const char *s, size_t max_len, xkb_group_index_t *out)
536 /* This function is pretty stupid, but works for now. */
539 if (s[0] != '[' || !isdigit(s[1]) || s[2] != ']')
541 if (s[1] - '0' < 1 || s[1] - '0' > XkbNumKbdGroups)
543 /* To zero-based index. */
544 *out = s[1] - '0' - 1;
549 matcher_mapping_set_mlvo(struct matcher *m, struct sval ident)
551 enum rules_mlvo mlvo;
552 struct sval mlvo_sval;
553 xkb_group_index_t idx;
556 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
557 mlvo_sval = rules_mlvo_svals[mlvo];
559 if (svaleq_prefix(mlvo_sval, ident))
564 if (mlvo >= _MLVO_NUM_ENTRIES) {
566 "invalid mapping: %.*s is not a valid value here; "
568 ident.len, ident.start);
569 m->mapping.skip = true;
573 if (m->mapping.defined_mlvo_mask & (1 << mlvo)) {
575 "invalid mapping: %.*s appears twice on the same line; "
577 mlvo_sval.len, mlvo_sval.start);
578 m->mapping.skip = true;
582 /* If there are leftovers still, it must be an index. */
583 if (mlvo_sval.len < ident.len) {
584 consumed = extract_group_index(ident.start + mlvo_sval.len,
585 ident.len - mlvo_sval.len, &idx);
586 if ((int) (ident.len - mlvo_sval.len) != consumed) {
588 "invalid mapping:\" %.*s\" may only be followed by a valid group index; "
590 mlvo_sval.len, mlvo_sval.start);
591 m->mapping.skip = true;
595 if (mlvo == MLVO_LAYOUT) {
596 m->mapping.layout_idx = idx;
598 else if (mlvo == MLVO_VARIANT) {
599 m->mapping.variant_idx = idx;
603 "invalid mapping: \"%.*s\" cannot be followed by a group index; "
605 mlvo_sval.len, mlvo_sval.start);
606 m->mapping.skip = true;
611 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
612 m->mapping.defined_mlvo_mask |= 1 << mlvo;
613 m->mapping.num_mlvo++;
617 matcher_mapping_set_kccgst(struct matcher *m, struct sval ident)
619 enum rules_kccgst kccgst;
620 struct sval kccgst_sval;
622 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
623 kccgst_sval = rules_kccgst_svals[kccgst];
625 if (svaleq(rules_kccgst_svals[kccgst], ident))
630 if (kccgst >= _KCCGST_NUM_ENTRIES) {
632 "invalid mapping: %.*s is not a valid value here; "
634 ident.len, ident.start);
635 m->mapping.skip = true;
639 if (m->mapping.defined_kccgst_mask & (1 << kccgst)) {
641 "invalid mapping: %.*s appears twice on the same line; "
643 kccgst_sval.len, kccgst_sval.start);
644 m->mapping.skip = true;
648 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
649 m->mapping.defined_kccgst_mask |= 1 << kccgst;
650 m->mapping.num_kccgst++;
654 matcher_mapping_verify(struct matcher *m)
656 if (m->mapping.num_mlvo == 0) {
658 "invalid mapping: must have at least one value on the left hand side; "
659 "ignoring rule set");
663 if (m->mapping.num_kccgst == 0) {
665 "invalid mapping: must have at least one value on the right hand side; "
666 "ignoring rule set");
671 * This following is very stupid, but this is how it works.
672 * See the "Notes" section in the overview above.
675 if (m->mapping.defined_mlvo_mask & (1 << MLVO_LAYOUT)) {
676 if (m->mapping.layout_idx == XKB_GROUP_INVALID) {
677 if (darray_size(m->rmlvo.layouts) > 1)
681 if (darray_size(m->rmlvo.layouts) == 1 ||
682 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
687 if (m->mapping.defined_mlvo_mask & (1 << MLVO_VARIANT)) {
688 if (m->mapping.variant_idx == XKB_GROUP_INVALID) {
689 if (darray_size(m->rmlvo.variants) > 1)
693 if (darray_size(m->rmlvo.variants) == 1 ||
694 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
702 m->mapping.skip = true;
706 matcher_rule_start_new(struct matcher *m)
708 memset(&m->rule, 0, sizeof(m->rule));
709 m->rule.skip = m->mapping.skip;
713 matcher_rule_set_mlvo_common(struct matcher *m, struct sval ident,
714 enum mlvo_match_type match_type)
716 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
718 "invalid rule: has more values than the mapping line; "
723 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
724 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
725 m->rule.num_mlvo_values++;
729 matcher_rule_set_mlvo_wildcard(struct matcher *m)
731 struct sval dummy = { NULL, 0 };
732 matcher_rule_set_mlvo_common(m, dummy, MLVO_MATCH_WILDCARD);
736 matcher_rule_set_mlvo_group(struct matcher *m, struct sval ident)
738 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_GROUP);
742 matcher_rule_set_mlvo(struct matcher *m, struct sval ident)
744 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_NORMAL);
748 matcher_rule_set_kccgst(struct matcher *m, struct sval ident)
750 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
752 "invalid rule: has more values than the mapping line; "
757 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
758 m->rule.num_kccgst_values++;
762 match_group(struct matcher *m, struct sval group_name, struct sval to)
765 struct sval *element;
768 darray_foreach(group, m->groups) {
769 if (svaleq(group->name, group_name)) {
777 * rules/evdev intentionally uses some undeclared group names
778 * in rules (e.g. commented group definitions which may be
779 * uncommented if needed). So we continue silently.
784 darray_foreach(element, group->elements)
785 if (svaleq(to, *element))
792 match_value(struct matcher *m, struct sval val, struct sval to,
793 enum mlvo_match_type match_type)
795 if (match_type == MLVO_MATCH_WILDCARD)
797 if (match_type == MLVO_MATCH_GROUP)
798 return match_group(m, val, to);
799 return svaleq(val, to);
803 * This function performs %-expansion on @value (see overview above),
804 * and appends the result to @to.
807 append_expanded_kccgst_value(struct matcher *m, darray_char *to,
811 size_t original_size = darray_size(*to);
812 const char *s = value.start;
813 xkb_group_index_t idx;
816 struct sval expanded;
820 * Appending bar to foo -> foo (not an error if this happens)
821 * Appending +bar to foo -> foo+bar
822 * Appending bar to +foo -> bar+foo
823 * Appending +bar to +foo -> +foo+bar
825 if (!darray_empty(*to) && s[0] != '+' && s[0] != '|') {
826 if (darray_item(*to, 0) == '+' || darray_item(*to, 0) == '|')
827 darray_prepend_items_nullterminate(*to, value.start, value.len);
832 * Some ugly hand-lexing here, but going through the scanner is more
833 * trouble than it's worth, and the format is ugly on its own merit.
835 for (i = 0; i < value.len; ) {
836 /* Check if that's a start of an expansion. */
838 /* Just a normal character. */
839 darray_append_items_nullterminate(*to, &s[i++], 1);
842 if (++i >= value.len) goto error;
846 /* Check for prefix. */
847 if (s[i] == '(' || s[i] == '+' || s[i] == '|' ||
848 s[i] == '_' || s[i] == '-') {
850 if (s[i] == '(') sfx = ')';
851 if (++i >= value.len) goto error;
854 /* Mandatory model/layout/variant specifier. */
856 case 'm': mlv = MLVO_MODEL; break;
857 case 'l': mlv = MLVO_LAYOUT; break;
858 case 'v': mlv = MLVO_VARIANT; break;
862 /* Check for index. */
865 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
867 "invalid index in %%-expansion; "
868 "may only index layout or variant");
872 consumed = extract_group_index(s + i, value.len - i, &idx);
873 if (consumed == -1) goto error;
877 idx = XKB_GROUP_INVALID;
881 /* Check for suffix, if there supposed to be one. */
883 if (i >= value.len) goto error;
884 if (s[i++] != sfx) goto error;
887 /* Get the expanded value. */
890 if (mlv == MLVO_LAYOUT) {
891 if (idx != XKB_GROUP_INVALID &&
892 idx < darray_size(m->rmlvo.layouts) &&
893 darray_size(m->rmlvo.layouts) > 1)
894 expanded = darray_item(m->rmlvo.layouts, idx);
895 else if (idx == XKB_GROUP_INVALID &&
896 darray_size(m->rmlvo.layouts) == 1)
897 expanded = darray_item(m->rmlvo.layouts, 0);
899 else if (mlv == MLVO_VARIANT) {
900 if (idx != XKB_GROUP_INVALID &&
901 idx < darray_size(m->rmlvo.variants) &&
902 darray_size(m->rmlvo.variants) > 1)
903 expanded = darray_item(m->rmlvo.variants, idx);
904 else if (idx == XKB_GROUP_INVALID &&
905 darray_size(m->rmlvo.variants) == 1)
906 expanded = darray_item(m->rmlvo.variants, 0);
908 else if (mlv == MLVO_MODEL) {
909 expanded = m->rmlvo.model;
912 /* If we didn't get one, skip silently. */
913 if (expanded.len <= 0)
917 darray_append_items_nullterminate(*to, &pfx, 1);
918 darray_append_items_nullterminate(*to, expanded.start, expanded.len);
920 darray_append_items_nullterminate(*to, &sfx, 1);
926 matcher_error1(m, "invalid %%-expansion in value; not used");
927 darray_resize(*to, original_size);
932 matcher_rule_verify(struct matcher *m)
934 if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
935 m->rule.num_kccgst_values != m->mapping.num_kccgst) {
937 "invalid rule: must have same number of values as mapping line;"
944 matcher_rule_apply_if_matches(struct matcher *m)
947 enum rules_mlvo mlvo;
948 enum rules_kccgst kccgst;
949 struct sval value, *option;
950 enum mlvo_match_type match_type;
951 bool matched = false;
952 xkb_group_index_t idx;
954 for (i = 0; i < m->mapping.num_mlvo; i++) {
955 mlvo = m->mapping.mlvo_at_pos[i];
956 value = m->rule.mlvo_value_at_pos[i];
957 match_type = m->rule.match_type_at_pos[i];
959 if (mlvo == MLVO_MODEL) {
960 matched = match_value(m, value, m->rmlvo.model, match_type);
962 else if (mlvo == MLVO_LAYOUT) {
963 idx = m->mapping.layout_idx;
964 idx = (idx == XKB_GROUP_INVALID ? 0 : idx);
965 matched = match_value(m, value,
966 darray_item(m->rmlvo.layouts, idx),
969 else if (mlvo == MLVO_VARIANT) {
970 idx = m->mapping.layout_idx;
971 idx = (idx == XKB_GROUP_INVALID ? 0 : idx);
972 matched = match_value(m, value,
973 darray_item(m->rmlvo.variants, idx),
976 else if (mlvo == MLVO_OPTION) {
977 darray_foreach(option, m->rmlvo.options) {
978 matched = match_value(m, value, *option, match_type);
988 for (i = 0; i < m->mapping.num_kccgst; i++) {
989 kccgst = m->mapping.kccgst_at_pos[i];
990 value = m->rule.kccgst_value_at_pos[i];
991 append_expanded_kccgst_value(m, &m->kccgst[kccgst], value);
995 * If a rule matches in a rule set, the rest of the set should be
996 * skipped. However, rule sets matching against options may contain
997 * several legitimate rules, so they are processed entirely.
999 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
1000 m->mapping.skip = true;
1003 static enum rules_token
1004 gettok(struct matcher *m)
1006 return lex(&m->scanner, &m->val, &m->loc);
1010 matcher_match(struct matcher *m, const char *string, size_t len,
1011 const char *file_name, struct xkb_component_names *out)
1013 enum rules_token tok;
1018 scanner_init(&m->scanner, m->ctx, string, len, file_name);
1021 switch (tok = gettok(m)) {
1024 case TOK_END_OF_LINE:
1026 case TOK_END_OF_FILE:
1033 switch (tok = gettok(m)) {
1034 case TOK_GROUP_NAME:
1035 matcher_group_start_new(m, m->val.string);
1037 case TOK_IDENTIFIER:
1038 matcher_mapping_start_new(m);
1039 matcher_mapping_set_mlvo(m, m->val.string);
1046 switch (tok = gettok(m)) {
1054 switch (tok = gettok(m)) {
1055 case TOK_IDENTIFIER:
1056 matcher_group_add_element(m, m->val.string);
1058 case TOK_END_OF_LINE:
1065 switch (tok = gettok(m)) {
1066 case TOK_IDENTIFIER:
1067 if (!m->mapping.skip)
1068 matcher_mapping_set_mlvo(m, m->val.string);
1071 goto mapping_kccgst;
1077 switch (tok = gettok(m)) {
1078 case TOK_IDENTIFIER:
1079 if (!m->mapping.skip)
1080 matcher_mapping_set_kccgst(m, m->val.string);
1081 goto mapping_kccgst;
1082 case TOK_END_OF_LINE:
1083 if (!m->mapping.skip)
1084 matcher_mapping_verify(m);
1085 goto rule_mlvo_first;
1091 switch (tok = gettok(m)) {
1094 case TOK_END_OF_LINE:
1095 goto rule_mlvo_first;
1096 case TOK_END_OF_FILE:
1099 matcher_rule_start_new(m);
1100 goto rule_mlvo_no_tok;
1107 case TOK_IDENTIFIER:
1109 matcher_rule_set_mlvo(m, m->val.string);
1113 matcher_rule_set_mlvo_wildcard(m);
1115 case TOK_GROUP_NAME:
1117 matcher_rule_set_mlvo_group(m, m->val.string);
1126 switch (tok = gettok(m)) {
1127 case TOK_IDENTIFIER:
1129 matcher_rule_set_kccgst(m, m->val.string);
1131 case TOK_END_OF_LINE:
1133 matcher_rule_verify(m);
1135 matcher_rule_apply_if_matches(m);
1136 goto rule_mlvo_first;
1150 if (darray_empty(m->kccgst[KCCGST_KEYCODES]) ||
1151 darray_empty(m->kccgst[KCCGST_TYPES]) ||
1152 darray_empty(m->kccgst[KCCGST_COMPAT]) ||
1153 /* darray_empty(m->kccgst[KCCGST_GEOMETRY]) || */
1154 darray_empty(m->kccgst[KCCGST_SYMBOLS]))
1157 out->keycodes = darray_mem(m->kccgst[KCCGST_KEYCODES], 0);
1158 out->types = darray_mem(m->kccgst[KCCGST_TYPES], 0);
1159 out->compat = darray_mem(m->kccgst[KCCGST_COMPAT], 0);
1160 /* out->geometry = darray_mem(m->kccgst[KCCGST_GEOMETRY], 0); */
1161 darray_free(m->kccgst[KCCGST_GEOMETRY]);
1162 out->symbols = darray_mem(m->kccgst[KCCGST_SYMBOLS], 0);
1167 matcher_error1(m, "unexpected token");
1173 xkb_components_from_rules(struct xkb_context *ctx,
1174 const struct xkb_rule_names *rmlvo,
1175 struct xkb_component_names *out)
1182 struct stat stat_buf;
1184 struct matcher *matcher;
1186 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1188 log_err(ctx, "Could not find \"%s\" rules in XKB path\n",
1190 log_err(ctx, "%zu include paths searched:\n",
1191 darray_size(ctx->includes));
1192 darray_foreach(include, ctx->includes)
1193 log_err(ctx, "\t%s\n", *include);
1194 log_err(ctx, "%zu include paths could not be added:\n",
1195 darray_size(ctx->failed_includes));
1196 darray_foreach(include, ctx->failed_includes)
1197 log_err(ctx, "\t%s\n", *include);
1203 if (fstat(fd, &stat_buf) != 0) {
1204 log_err(ctx, "Couldn't stat rules file\n");
1208 string = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1210 log_err(ctx, "Couldn't mmap rules file (%zu bytes)\n",
1211 (size_t) stat_buf.st_size);
1215 matcher = matcher_new(ctx, rmlvo);
1216 ret = matcher_match(matcher, string, stat_buf.st_size, rmlvo->rules, out);
1218 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1219 matcher_free(matcher);
1221 munmap(string, stat_buf.st_size);