diff --git a/doc/rules-format.md b/doc/rules-format.md index 5c9dae81..7162e34c 100644 --- a/doc/rules-format.md +++ b/doc/rules-format.md @@ -108,8 +108,10 @@ rules. @anchor rules-wildcard-def Along with matching values by simple string equality and for membership in a [group] defined previously, rules may also contain -**wildcard** values “\*” which *always match*. These usually appear -near the end of a rule set to set *default* values. +**wildcard** values “\*” with the following behavior: +- For `model` and `options`: *always* match. +- For `layout` and `variant`: match any *non-empty* value. +These usually appear near the end of a rule set to set *default* values. ```c ! layout = keycodes diff --git a/src/xkbcomp/rules.c b/src/xkbcomp/rules.c index d8307a50..6772769d 100644 --- a/src/xkbcomp/rules.c +++ b/src/xkbcomp/rules.c @@ -694,10 +694,11 @@ match_group(struct matcher *m, struct sval group_name, struct sval to) static bool match_value(struct matcher *m, struct sval val, struct sval to, - enum mlvo_match_type match_type) + enum mlvo_match_type match_type, bool wildcard_matches_empty) { if (match_type == MLVO_MATCH_WILDCARD) - return true; + /* Wild card match empty values only if explicitly required */ + return wildcard_matches_empty || !!to.len; if (match_type == MLVO_MATCH_GROUP) return match_group(m, val, to); return svaleq(val, to); @@ -705,9 +706,10 @@ match_value(struct matcher *m, struct sval val, struct sval to, static bool match_value_and_mark(struct matcher *m, struct sval val, - struct matched_sval *to, enum mlvo_match_type match_type) + struct matched_sval *to, enum mlvo_match_type match_type, + bool wildcard_matches_empty) { - bool matched = match_value(m, val, to->sval, match_type); + bool matched = match_value(m, val, to->sval, match_type, wildcard_matches_empty); if (matched) to->matched = true; return matched; @@ -868,25 +870,28 @@ matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) struct matched_sval *to; bool matched = false; + /* NOTE: Wild card matches empty values only for model and options, as + * implemented in libxkbfile and xserver. The reason for such different + * treatment is not documented. */ if (mlvo == MLVO_MODEL) { to = &m->rmlvo.model; - matched = match_value_and_mark(m, value, to, match_type); + matched = match_value_and_mark(m, value, to, match_type, true); } else if (mlvo == MLVO_LAYOUT) { xkb_layout_index_t idx = m->mapping.layout_idx; idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); to = &darray_item(m->rmlvo.layouts, idx); - matched = match_value_and_mark(m, value, to, match_type); + matched = match_value_and_mark(m, value, to, match_type, false); } else if (mlvo == MLVO_VARIANT) { xkb_layout_index_t idx = m->mapping.variant_idx; idx = (idx == XKB_LAYOUT_INVALID ? 0 : idx); to = &darray_item(m->rmlvo.variants, idx); - matched = match_value_and_mark(m, value, to, match_type); + matched = match_value_and_mark(m, value, to, match_type, false); } else if (mlvo == MLVO_OPTION) { darray_foreach(to, m->rmlvo.options) { - matched = match_value_and_mark(m, value, to, match_type); + matched = match_value_and_mark(m, value, to, match_type, true); if (matched) break; } diff --git a/test/data/rules/groups b/test/data/rules/groups index 15dc3c58..464cf0de 100644 --- a/test/data/rules/groups +++ b/test/data/rules/groups @@ -13,6 +13,10 @@ $layout_group * = my_symbols+%(v) * * = default_symbols +! layout = symbols + $layout_group = my_symbols+%(v) + * = default_symbols + ! model = types * = default_types diff --git a/test/data/rules/wildcard b/test/data/rules/wildcard new file mode 100644 index 00000000..f49e92c0 --- /dev/null +++ b/test/data/rules/wildcard @@ -0,0 +1,32 @@ +! model = keycodes + * = evdev + +! model = geometry + * = pc(pc104) + +! layout variant = symbols + * * = pc+%l%(v) + +! layout[1] variant[1] = symbols + * * = pc+%l[1]%(v[1]) + +! layout[2] variant[2] = symbols + * * = +%l[2]%(v[2]):2 + +! layout[3] variant[3] = symbols + * * = +%l[3]%(v[3]):3 + +! layout[4] variant[3] = symbols + * * = +%l[4]%(v[4]):4 + +! model layout = compat + * * = complete + +! model layout[1] = compat + * * = complete + +! model layout[2] = compat + * * = complete + +! model = types + * = complete diff --git a/test/rules-file.c b/test/rules-file.c index bd2f9bdd..e17d1dc4 100644 --- a/test/rules-file.c +++ b/test/rules-file.c @@ -159,7 +159,7 @@ main(int argc, char *argv[]) struct test_data test2 = { .rules = "simple", - .model = "", .layout = "", .variant = "", .options = "", + .model = "", .layout = "foo", .variant = "", .options = "", .keycodes = "default_keycodes", .types = "default_types", .compat = "default_compat", .symbols = "default_symbols", @@ -220,6 +220,37 @@ main(int argc, char *argv[]) }; assert(test_rules(ctx, &test7)); + /* Wild card does not match empty entries for layouts and variants */ +#define ENTRY(_model, _layout, _variant, _options, _symbols, _should_fail) \ + { .rules = "wildcard", .model = _model, \ + .layout = _layout, .variant = _variant, .options = _options, \ + .keycodes = "evdev", .types = "complete", .compat = "complete", \ + .symbols = _symbols , .should_fail = _should_fail } + struct test_data wildcard_data[] = { + /* OK: empty model and options and at least one layout+variant combo */ + ENTRY(NULL, "a" , "1" , NULL, "pc+a(1)", false), + ENTRY("" , "a" , "1" , "" , "pc+a(1)", false), + ENTRY("" , "a," , "1," , "" , "pc+a(1)", false), + ENTRY("" , ",b" , ",2" , "" , "+b(2):2", false), + ENTRY("" , "a,b", "1," , "" , "pc+a(1)", false), + ENTRY("" , "a,b", ",2" , "" , "+b(2):2", false), + /* Fails: empty layout or variant */ + ENTRY(NULL, NULL , NULL , NULL, "", true), + ENTRY(NULL, "" , "" , NULL, "", true), + ENTRY(NULL, NULL , "1" , NULL, "", true), + ENTRY(NULL, "" , "1" , NULL, "", true), + ENTRY(NULL, "," , "1,2", NULL, "", true), + ENTRY(NULL, "a" , NULL , NULL, "", true), + ENTRY(NULL, "a" , "" , NULL, "", true), + ENTRY(NULL, "a,b", NULL , NULL, "", true), + ENTRY(NULL, "a,b", "" , NULL, "", true), + ENTRY(NULL, "a,b", "," , NULL, "", true) + }; +#undef ENTRY + for (size_t k = 0; k < ARRAY_SIZE(wildcard_data); k++) { + assert(test_rules(ctx, &wildcard_data[k])); + } + xkb_context_unref(ctx); return 0; }