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"
57 /* Values returned with some tokens, like yylval. */
73 /* C99 is stupid. Just use the 1 variant when there are no args. */
74 #define scanner_error1(scanner, msg) \
75 log_warn((scanner)->ctx, "rules/%s:%u:%u: %s\n", \
76 (scanner)->file_name, \
77 (scanner)->token_line, (scanner)->token_column, msg)
78 #define scanner_error(scanner, fmt, ...) \
79 log_warn((scanner)->ctx, "rules/%s:%u:%u: " fmt "\n", \
80 (scanner)->file_name, \
81 (scanner)->token_line, (scanner)->token_column, __VA_ARGS__)
86 return is_graph(ch) && ch != '\\';
89 static enum rules_token
90 lex(struct scanner *s, union lvalue *val)
92 skip_more_whitespace_and_comments:
94 while (chr(s, ' ') || chr(s, '\t'));
98 while (!eof(s) && !eol(s)) next(s);
103 while (eol(s)) next(s);
104 return TOK_END_OF_LINE;
107 /* Escaped line continuation. */
110 scanner_error1(s, "illegal new line escape; must appear at end of line");
114 goto skip_more_whitespace_and_comments;
117 /* See if we're done. */
118 if (eof(s)) return TOK_END_OF_FILE;
121 s->token_line = s->line;
122 s->token_column = s->column;
124 /* Operators and punctuation. */
125 if (chr(s, '!')) return TOK_BANG;
126 if (chr(s, '=')) return TOK_EQUALS;
127 if (chr(s, '*')) return TOK_STAR;
131 val->string.start = s->s + s->pos;
133 while (is_ident(peek(s))) {
137 if (val->string.len == 0) {
138 scanner_error1(s, "unexpected character after \'$\'; expected name");
141 return TOK_GROUP_NAME;
145 if (is_ident(peek(s))) {
146 val->string.start = s->s + s->pos;
148 while (is_ident(peek(s))) {
152 return TOK_IDENTIFIER;
155 scanner_error1(s, "unrecognized token");
159 /***====================================================================***/
169 #define SVAL_LIT(literal) { literal, sizeof(literal) - 1 }
171 static const struct sval rules_mlvo_svals[_MLVO_NUM_ENTRIES] = {
172 [MLVO_MODEL] = SVAL_LIT("model"),
173 [MLVO_LAYOUT] = SVAL_LIT("layout"),
174 [MLVO_VARIANT] = SVAL_LIT("variant"),
175 [MLVO_OPTION] = SVAL_LIT("option"),
187 static const struct sval rules_kccgst_svals[_KCCGST_NUM_ENTRIES] = {
188 [KCCGST_KEYCODES] = SVAL_LIT("keycodes"),
189 [KCCGST_TYPES] = SVAL_LIT("types"),
190 [KCCGST_COMPAT] = SVAL_LIT("compat"),
191 [KCCGST_SYMBOLS] = SVAL_LIT("symbols"),
192 [KCCGST_GEOMETRY] = SVAL_LIT("geometry"),
196 * A broken-down version of xkb_rule_names (without the rules,
202 darray_sval variants;
208 darray_sval elements;
212 int mlvo_at_pos[_MLVO_NUM_ENTRIES];
213 unsigned int num_mlvo;
214 unsigned int defined_mlvo_mask;
215 xkb_layout_index_t layout_idx, variant_idx;
216 int kccgst_at_pos[_KCCGST_NUM_ENTRIES];
217 unsigned int num_kccgst;
218 unsigned int defined_kccgst_mask;
222 enum mlvo_match_type {
223 MLVO_MATCH_NORMAL = 0,
229 struct sval mlvo_value_at_pos[_MLVO_NUM_ENTRIES];
230 enum mlvo_match_type match_type_at_pos[_MLVO_NUM_ENTRIES];
231 unsigned int num_mlvo_values;
232 struct sval kccgst_value_at_pos[_KCCGST_NUM_ENTRIES];
233 unsigned int num_kccgst_values;
238 * This is the main object used to match a given RMLVO against a rules
239 * file and aggragate the results in a KcCGST. It goes through a simple
240 * matching state machine, with tokens as transitions (see
244 struct xkb_context *ctx;
246 struct rule_names rmlvo;
248 struct scanner scanner;
249 darray(struct group) groups;
250 /* Current mapping. */
251 struct mapping mapping;
255 darray_char kccgst[_KCCGST_NUM_ENTRIES];
259 strip_spaces(struct sval v)
261 while (v.len > 0 && is_space(v.start[0])) { v.len--; v.start++; }
262 while (v.len > 0 && is_space(v.start[v.len - 1])) v.len--;
267 split_comma_separated_string(const char *s)
269 darray_sval arr = darray_new();
272 * Make sure the array returned by this function always includes at
273 * least one value, e.g. "" -> { "" } and "," -> { "", "" }.
277 struct sval val = { NULL, 0 };
278 darray_append(arr, val);
283 struct sval val = { s, 0 };
284 while (*s != '\0' && *s != ',') { s++; val.len++; }
285 darray_append(arr, strip_spaces(val));
286 if (*s == '\0') break;
293 static struct matcher *
294 matcher_new(struct xkb_context *ctx,
295 const struct xkb_rule_names *rmlvo)
297 struct matcher *m = calloc(1, sizeof(*m));
302 m->rmlvo.model.start = rmlvo->model;
303 m->rmlvo.model.len = strlen_safe(rmlvo->model);
304 m->rmlvo.layouts = split_comma_separated_string(rmlvo->layout);
305 m->rmlvo.variants = split_comma_separated_string(rmlvo->variant);
306 m->rmlvo.options = split_comma_separated_string(rmlvo->options);
312 matcher_free(struct matcher *m)
317 darray_free(m->rmlvo.layouts);
318 darray_free(m->rmlvo.variants);
319 darray_free(m->rmlvo.options);
320 darray_foreach(group, m->groups)
321 darray_free(group->elements);
322 darray_free(m->groups);
326 #define matcher_error1(matcher, msg) \
327 scanner_error1(&(matcher)->scanner, msg)
328 #define matcher_error(matcher, fmt, ...) \
329 scanner_error(&(matcher)->scanner, fmt, __VA_ARGS__)
332 matcher_group_start_new(struct matcher *m, struct sval name)
334 struct group group = { .name = name, .elements = darray_new() };
335 darray_append(m->groups, group);
339 matcher_group_add_element(struct matcher *m, struct sval element)
341 darray_append(darray_item(m->groups, darray_size(m->groups) - 1).elements,
346 matcher_mapping_start_new(struct matcher *m)
348 for (unsigned i = 0; i < _MLVO_NUM_ENTRIES; i++)
349 m->mapping.mlvo_at_pos[i] = -1;
350 for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++)
351 m->mapping.kccgst_at_pos[i] = -1;
352 m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID;
353 m->mapping.num_mlvo = m->mapping.num_kccgst = 0;
354 m->mapping.defined_mlvo_mask = 0;
355 m->mapping.defined_kccgst_mask = 0;
356 m->mapping.skip = false;
360 extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out)
362 /* This function is pretty stupid, but works for now. */
363 *out = XKB_LAYOUT_INVALID;
366 if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']')
368 if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS)
370 /* To zero-based index. */
371 *out = s[1] - '0' - 1;
376 matcher_mapping_set_mlvo(struct matcher *m, struct sval ident)
378 enum rules_mlvo mlvo;
379 struct sval mlvo_sval;
381 for (mlvo = 0; mlvo < _MLVO_NUM_ENTRIES; mlvo++) {
382 mlvo_sval = rules_mlvo_svals[mlvo];
384 if (svaleq_prefix(mlvo_sval, ident))
389 if (mlvo >= _MLVO_NUM_ENTRIES) {
391 "invalid mapping: %.*s is not a valid value here; "
393 ident.len, ident.start);
394 m->mapping.skip = true;
398 if (m->mapping.defined_mlvo_mask & (1u << mlvo)) {
400 "invalid mapping: %.*s appears twice on the same line; "
402 mlvo_sval.len, mlvo_sval.start);
403 m->mapping.skip = true;
407 /* If there are leftovers still, it must be an index. */
408 if (mlvo_sval.len < ident.len) {
409 xkb_layout_index_t idx;
410 int consumed = extract_layout_index(ident.start + mlvo_sval.len,
411 ident.len - mlvo_sval.len, &idx);
412 if ((int) (ident.len - mlvo_sval.len) != consumed) {
414 "invalid mapping:\" %.*s\" may only be followed by a valid group index; "
416 mlvo_sval.len, mlvo_sval.start);
417 m->mapping.skip = true;
421 if (mlvo == MLVO_LAYOUT) {
422 m->mapping.layout_idx = idx;
424 else if (mlvo == MLVO_VARIANT) {
425 m->mapping.variant_idx = idx;
429 "invalid mapping: \"%.*s\" cannot be followed by a group index; "
431 mlvo_sval.len, mlvo_sval.start);
432 m->mapping.skip = true;
437 m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo;
438 m->mapping.defined_mlvo_mask |= 1u << mlvo;
439 m->mapping.num_mlvo++;
443 matcher_mapping_set_kccgst(struct matcher *m, struct sval ident)
445 enum rules_kccgst kccgst;
446 struct sval kccgst_sval;
448 for (kccgst = 0; kccgst < _KCCGST_NUM_ENTRIES; kccgst++) {
449 kccgst_sval = rules_kccgst_svals[kccgst];
451 if (svaleq(rules_kccgst_svals[kccgst], ident))
456 if (kccgst >= _KCCGST_NUM_ENTRIES) {
458 "invalid mapping: %.*s is not a valid value here; "
460 ident.len, ident.start);
461 m->mapping.skip = true;
465 if (m->mapping.defined_kccgst_mask & (1u << kccgst)) {
467 "invalid mapping: %.*s appears twice on the same line; "
469 kccgst_sval.len, kccgst_sval.start);
470 m->mapping.skip = true;
474 m->mapping.kccgst_at_pos[m->mapping.num_kccgst] = kccgst;
475 m->mapping.defined_kccgst_mask |= 1u << kccgst;
476 m->mapping.num_kccgst++;
480 matcher_mapping_verify(struct matcher *m)
482 if (m->mapping.num_mlvo == 0) {
484 "invalid mapping: must have at least one value on the left hand side; "
485 "ignoring rule set");
489 if (m->mapping.num_kccgst == 0) {
491 "invalid mapping: must have at least one value on the right hand side; "
492 "ignoring rule set");
497 * This following is very stupid, but this is how it works.
498 * See the "Notes" section in the overview above.
501 if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) {
502 if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) {
503 if (darray_size(m->rmlvo.layouts) > 1)
507 if (darray_size(m->rmlvo.layouts) == 1 ||
508 m->mapping.layout_idx >= darray_size(m->rmlvo.layouts))
513 if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) {
514 if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) {
515 if (darray_size(m->rmlvo.variants) > 1)
519 if (darray_size(m->rmlvo.variants) == 1 ||
520 m->mapping.variant_idx >= darray_size(m->rmlvo.variants))
528 m->mapping.skip = true;
532 matcher_rule_start_new(struct matcher *m)
534 memset(&m->rule, 0, sizeof(m->rule));
535 m->rule.skip = m->mapping.skip;
539 matcher_rule_set_mlvo_common(struct matcher *m, struct sval ident,
540 enum mlvo_match_type match_type)
542 if (m->rule.num_mlvo_values + 1 > m->mapping.num_mlvo) {
544 "invalid rule: has more values than the mapping line; "
549 m->rule.match_type_at_pos[m->rule.num_mlvo_values] = match_type;
550 m->rule.mlvo_value_at_pos[m->rule.num_mlvo_values] = ident;
551 m->rule.num_mlvo_values++;
555 matcher_rule_set_mlvo_wildcard(struct matcher *m)
557 struct sval dummy = { NULL, 0 };
558 matcher_rule_set_mlvo_common(m, dummy, MLVO_MATCH_WILDCARD);
562 matcher_rule_set_mlvo_group(struct matcher *m, struct sval ident)
564 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_GROUP);
568 matcher_rule_set_mlvo(struct matcher *m, struct sval ident)
570 matcher_rule_set_mlvo_common(m, ident, MLVO_MATCH_NORMAL);
574 matcher_rule_set_kccgst(struct matcher *m, struct sval ident)
576 if (m->rule.num_kccgst_values + 1 > m->mapping.num_kccgst) {
578 "invalid rule: has more values than the mapping line; "
583 m->rule.kccgst_value_at_pos[m->rule.num_kccgst_values] = ident;
584 m->rule.num_kccgst_values++;
588 match_group(struct matcher *m, struct sval group_name, struct sval to)
591 struct sval *element;
594 darray_foreach(group, m->groups) {
595 if (svaleq(group->name, group_name)) {
603 * rules/evdev intentionally uses some undeclared group names
604 * in rules (e.g. commented group definitions which may be
605 * uncommented if needed). So we continue silently.
610 darray_foreach(element, group->elements)
611 if (svaleq(to, *element))
618 match_value(struct matcher *m, struct sval val, struct sval to,
619 enum mlvo_match_type match_type)
621 if (match_type == MLVO_MATCH_WILDCARD)
623 if (match_type == MLVO_MATCH_GROUP)
624 return match_group(m, val, to);
625 return svaleq(val, to);
629 * This function performs %-expansion on @value (see overview above),
630 * and appends the result to @to.
633 append_expanded_kccgst_value(struct matcher *m, darray_char *to,
636 const unsigned original_size = darray_size(*to);
637 const char *s = value.start;
640 * Appending bar to foo -> foo (not an error if this happens)
641 * Appending +bar to foo -> foo+bar
642 * Appending bar to +foo -> bar+foo
643 * Appending +bar to +foo -> +foo+bar
645 if (!darray_empty(*to) && s[0] != '+' && s[0] != '|') {
646 if (darray_item(*to, 0) == '+' || darray_item(*to, 0) == '|')
647 darray_prepends_nullterminate(*to, value.start, value.len);
652 * Some ugly hand-lexing here, but going through the scanner is more
653 * trouble than it's worth, and the format is ugly on its own merit.
655 for (unsigned i = 0; i < value.len; ) {
657 xkb_layout_index_t idx;
659 struct sval expanded;
661 /* Check if that's a start of an expansion. */
663 /* Just a normal character. */
664 darray_appends_nullterminate(*to, &s[i++], 1);
667 if (++i >= value.len) goto error;
671 /* Check for prefix. */
672 if (s[i] == '(' || s[i] == '+' || s[i] == '|' ||
673 s[i] == '_' || s[i] == '-') {
675 if (s[i] == '(') sfx = ')';
676 if (++i >= value.len) goto error;
679 /* Mandatory model/layout/variant specifier. */
681 case 'm': mlv = MLVO_MODEL; break;
682 case 'l': mlv = MLVO_LAYOUT; break;
683 case 'v': mlv = MLVO_VARIANT; break;
687 /* Check for index. */
688 idx = XKB_LAYOUT_INVALID;
689 if (i < value.len && s[i] == '[') {
692 if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) {
694 "invalid index in %%-expansion; "
695 "may only index layout or variant");
699 consumed = extract_layout_index(s + i, value.len - i, &idx);
700 if (consumed == -1) goto error;
704 /* Check for suffix, if there supposed to be one. */
706 if (i >= value.len) goto error;
707 if (s[i++] != sfx) goto error;
710 /* Get the expanded value. */
713 if (mlv == MLVO_LAYOUT) {
714 if (idx != XKB_LAYOUT_INVALID &&
715 idx < darray_size(m->rmlvo.layouts) &&
716 darray_size(m->rmlvo.layouts) > 1)
717 expanded = darray_item(m->rmlvo.layouts, idx);
718 else if (idx == XKB_LAYOUT_INVALID &&
719 darray_size(m->rmlvo.layouts) == 1)
720 expanded = darray_item(m->rmlvo.layouts, 0);
722 else if (mlv == MLVO_VARIANT) {
723 if (idx != XKB_LAYOUT_INVALID &&
724 idx < darray_size(m->rmlvo.variants) &&
725 darray_size(m->rmlvo.variants) > 1)
726 expanded = darray_item(m->rmlvo.variants, idx);
727 else if (idx == XKB_LAYOUT_INVALID &&
728 darray_size(m->rmlvo.variants) == 1)
729 expanded = darray_item(m->rmlvo.variants, 0);
731 else if (mlv == MLVO_MODEL) {
732 expanded = m->rmlvo.model;
735 /* If we didn't get one, skip silently. */
736 if (expanded.len <= 0)
740 darray_appends_nullterminate(*to, &pfx, 1);
741 darray_appends_nullterminate(*to, expanded.start, expanded.len);
743 darray_appends_nullterminate(*to, &sfx, 1);
749 matcher_error1(m, "invalid %%-expansion in value; not used");
750 darray_resize(*to, original_size);
755 matcher_rule_verify(struct matcher *m)
757 if (m->rule.num_mlvo_values != m->mapping.num_mlvo ||
758 m->rule.num_kccgst_values != m->mapping.num_kccgst) {
760 "invalid rule: must have same number of values as mapping line;"
767 matcher_rule_apply_if_matches(struct matcher *m)
769 for (unsigned i = 0; i < m->mapping.num_mlvo; i++) {
770 enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i];
771 struct sval value = m->rule.mlvo_value_at_pos[i];
772 enum mlvo_match_type match_type = m->rule.match_type_at_pos[i];
773 bool matched = false;
775 if (mlvo == MLVO_MODEL) {
776 matched = match_value(m, value, m->rmlvo.model, match_type);
778 else if (mlvo == MLVO_LAYOUT) {
779 xkb_layout_index_t idx = m->mapping.layout_idx;
780 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
781 matched = match_value(m, value,
782 darray_item(m->rmlvo.layouts, idx),
785 else if (mlvo == MLVO_VARIANT) {
786 xkb_layout_index_t idx = m->mapping.layout_idx;
787 idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx);
788 matched = match_value(m, value,
789 darray_item(m->rmlvo.variants, idx),
792 else if (mlvo == MLVO_OPTION) {
794 darray_foreach(option, m->rmlvo.options) {
795 matched = match_value(m, value, *option, match_type);
805 for (unsigned i = 0; i < m->mapping.num_kccgst; i++) {
806 enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i];
807 struct sval value = m->rule.kccgst_value_at_pos[i];
808 append_expanded_kccgst_value(m, &m->kccgst[kccgst], value);
812 * If a rule matches in a rule set, the rest of the set should be
813 * skipped. However, rule sets matching against options may contain
814 * several legitimate rules, so they are processed entirely.
816 if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION)))
817 m->mapping.skip = true;
820 static enum rules_token
821 gettok(struct matcher *m)
823 return lex(&m->scanner, &m->val);
827 matcher_match(struct matcher *m, const char *string, size_t len,
828 const char *file_name, struct xkb_component_names *out)
830 enum rules_token tok;
835 scanner_init(&m->scanner, m->ctx, string, len, file_name);
838 switch (tok = gettok(m)) {
841 case TOK_END_OF_LINE:
843 case TOK_END_OF_FILE:
850 switch (tok = gettok(m)) {
852 matcher_group_start_new(m, m->val.string);
855 matcher_mapping_start_new(m);
856 matcher_mapping_set_mlvo(m, m->val.string);
863 switch (tok = gettok(m)) {
871 switch (tok = gettok(m)) {
873 matcher_group_add_element(m, m->val.string);
875 case TOK_END_OF_LINE:
882 switch (tok = gettok(m)) {
884 if (!m->mapping.skip)
885 matcher_mapping_set_mlvo(m, m->val.string);
894 switch (tok = gettok(m)) {
896 if (!m->mapping.skip)
897 matcher_mapping_set_kccgst(m, m->val.string);
899 case TOK_END_OF_LINE:
900 if (!m->mapping.skip)
901 matcher_mapping_verify(m);
902 goto rule_mlvo_first;
908 switch (tok = gettok(m)) {
911 case TOK_END_OF_LINE:
912 goto rule_mlvo_first;
913 case TOK_END_OF_FILE:
916 matcher_rule_start_new(m);
917 goto rule_mlvo_no_tok;
926 matcher_rule_set_mlvo(m, m->val.string);
930 matcher_rule_set_mlvo_wildcard(m);
934 matcher_rule_set_mlvo_group(m, m->val.string);
943 switch (tok = gettok(m)) {
946 matcher_rule_set_kccgst(m, m->val.string);
948 case TOK_END_OF_LINE:
950 matcher_rule_verify(m);
952 matcher_rule_apply_if_matches(m);
953 goto rule_mlvo_first;
967 if (darray_empty(m->kccgst[KCCGST_KEYCODES]) ||
968 darray_empty(m->kccgst[KCCGST_TYPES]) ||
969 darray_empty(m->kccgst[KCCGST_COMPAT]) ||
970 /* darray_empty(m->kccgst[KCCGST_GEOMETRY]) || */
971 darray_empty(m->kccgst[KCCGST_SYMBOLS]))
974 out->keycodes = darray_mem(m->kccgst[KCCGST_KEYCODES], 0);
975 out->types = darray_mem(m->kccgst[KCCGST_TYPES], 0);
976 out->compat = darray_mem(m->kccgst[KCCGST_COMPAT], 0);
977 /* out->geometry = darray_mem(m->kccgst[KCCGST_GEOMETRY], 0); */
978 darray_free(m->kccgst[KCCGST_GEOMETRY]);
979 out->symbols = darray_mem(m->kccgst[KCCGST_SYMBOLS], 0);
984 matcher_error1(m, "unexpected token");
990 xkb_components_from_rules(struct xkb_context *ctx,
991 const struct xkb_rule_names *rmlvo,
992 struct xkb_component_names *out)
999 struct matcher *matcher;
1001 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1005 ret = map_file(file, &string, &size);
1007 log_err(ctx, "Couldn't read rules file: %s\n", strerror(errno));
1011 matcher = matcher_new(ctx, rmlvo);
1012 ret = matcher_match(matcher, string, size, rmlvo->rules, out);
1014 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1015 matcher_free(matcher);
1017 unmap_file(string, size);