diff --git a/changes/api/+modern-rules.feature.md b/changes/api/+modern-rules.feature.md new file mode 100644 index 00000000..d27af48b --- /dev/null +++ b/changes/api/+modern-rules.feature.md @@ -0,0 +1,14 @@ +Rules: Added support for special layouts indexes: +- *single*: matches a single layout; `layout[single]` is the same as without + explicit index: `layout`. +- *first*: matches the first layout/variant, no matter how many layouts are in + the RMLVO configuration. Acts as both `layout` and `layout[1]`. +- *later*: matches all but the first layout. This is an index range. Acts as + `layout[2]` .. `layout[4]`. +- *any*: matches layout at any position. This is an index range. + +Also added the special index `%i` which correspond to the index of the matched +layout. + +See the [documentation](https://xkbcommon.org/doc/current/rule-file-format.html) +for further information. diff --git a/doc/rules-format.md b/doc/rules-format.md index 7162e34c..99c4dc80 100644 --- a/doc/rules-format.md +++ b/doc/rules-format.md @@ -143,7 +143,9 @@ RuleSet ::= Mapping { Rule } Mapping ::= { Mlvo } "=" { Kccgst } "\n" Mlvo ::= "model" | "option" | ("layout" | "variant") [ Index ] -Index ::= "[" 1..XKB_NUM_GROUPS "]" +Index ::= "[" ({ NumericIndex } | { SpecialIndex }) "]" +NumericIndex ::= 1..XKB_MAX_GROUPS +SpecialIndex ::= "single" | "first" | "later" | "any" Kccgst ::= "keycodes" | "symbols" | "types" | "compat" | "geometry" Rule ::= { MlvoValue } "=" { KccgstValue } "\n" @@ -175,13 +177,46 @@ or %%H seems to do the job though. `/usr/share/X11/xkb/rules`). + **Note:** This feature is supported by libxkbcommon but not by the legacy X11 + tools. + +- @anchor rules-special-indexes + (Since version `1.8.0`) + The following *special indexes* can be used to avoid repetition and clarify + the semantics: + +
+
`single`
+
+ Matches a single layout; `layout[single]` is the same as without + explicit index: `layout`. +
+
`first`
+
+ Matches the first layout/variant, no matter how many layouts are in + the RMLVO configuration. Acts as both `layout` and `layout[1]`. +
+
`later`
+
+ Matches all but the first layout. This is an index *range*. + Acts as `layout[2]` .. `layout[4]`. +
+
any
+
+ Matches layout at any position. This is an index *range*. + Acts as `layout`, `layout[1]` .. `layout[4]`. +
+
+ + When using a layout index *range* (`later`, `any`), the @ref rules-i-expansion "%i expansion" + can be used in the `KccgstValue` to refer to the index of the matched layout. - The order of values in a `Rule` must be the same as the `Mapping` it follows. The mapping line determines the meaning of the values in the rules which follow in the `RuleSet`. - If a `Rule` is matched, **%-expansion** is performed on the -`KccgstValue`, as follows: + `KccgstValue`, as follows:
\%m, \%l, \%v
@@ -216,6 +251,18 @@ or %%H seems to do the job though.
As above, but prefixed by ‘(’ and suffixed by ‘)’.
+
+ @anchor rules-i-expansion + `%%i`, + `%%l[%%i]`, + `%(l[%%i])`, + etc. +
+
+ (Since version `1.8.0`) + In case the mapping uses a @ref rules-special-indexes "special index", + `%%i` corresponds to the index of the matched layout. +
In case the expansion is *invalid*, as described above, it is *skipped* @@ -329,6 +376,16 @@ we would have the following resolutions of [symbols]: | `us,es` | | `pc+us+es:2` | #2, #3 | | `us,es,fr` | `intl,,bepo` | `pc+us(intl)+es:2+fr(bepo):3` | #2, #3, #4 | +Since version `1.8.0`, the previous code can be replaced with simply: + +```c +! layout[first] = symbols + * = pc+%l[%i]%(v[%i]) + +! layout[later] = symbols + * = +%l[%i]%(v[%i]):%i +``` + ### Example: layout, option and symbols {#rules-options-example} Using the following example: diff --git a/src/darray.h b/src/darray.h index b75d85f9..b559d7b9 100644 --- a/src/darray.h +++ b/src/darray.h @@ -83,6 +83,7 @@ typedef darray (unsigned long) darray_ulong; /*** Access ***/ #define darray_item(arr, i) ((arr).item[i]) +#define darray_items(arr) ((arr).item) #define darray_size(arr) ((arr).size) #define darray_empty(arr) ((arr).size == 0) diff --git a/src/xkbcomp/rules.c b/src/xkbcomp/rules.c index 4b3f793f..cf5d9da6 100644 --- a/src/xkbcomp/rules.c +++ b/src/xkbcomp/rules.c @@ -49,6 +49,8 @@ #include "config.h" +#include + #include "xkbcomp-priv.h" #include "rules.h" #include "include.h" @@ -225,11 +227,26 @@ struct mapping { int mlvo_at_pos[_MLVO_NUM_ENTRIES]; unsigned int num_mlvo; unsigned int defined_mlvo_mask; - xkb_layout_index_t layout_idx, variant_idx; + bool has_layout_idx_range; + /* This member has 2 uses: + * • Keep track of layout and variant indexes while parsing MLVO headers. + * • Store layout/variant range afterwards. + * Thus provide 2 structs to reflect these semantics in the code. */ + union { + struct { xkb_layout_index_t layout_idx, variant_idx; }; + struct { xkb_layout_index_t layout_idx_min, layout_idx_max; }; + }; + /* This member has 2 uses: + * • Check if the mapping is active by interpreting the value as a boolean. + * • Keep track of the remaining layout indexes to match. + * Thus provide 2 names to reflect these semantics in the code. */ + union { + xkb_layout_mask_t active; + xkb_layout_mask_t layouts_candidates_mask; + }; int kccgst_at_pos[_KCCGST_NUM_ENTRIES]; unsigned int num_kccgst; unsigned int defined_kccgst_mask; - bool skip; }; enum mlvo_match_type { @@ -499,29 +516,91 @@ matcher_mapping_start_new(struct matcher *m) m->mapping.mlvo_at_pos[i] = -1; for (unsigned i = 0; i < _KCCGST_NUM_ENTRIES; i++) m->mapping.kccgst_at_pos[i] = -1; + m->mapping.has_layout_idx_range = false; m->mapping.layout_idx = m->mapping.variant_idx = XKB_LAYOUT_INVALID; m->mapping.num_mlvo = m->mapping.num_kccgst = 0; m->mapping.defined_mlvo_mask = 0; m->mapping.defined_kccgst_mask = 0; - m->mapping.skip = false; + m->mapping.active = true; } +/* Parse Kccgst layout index: + * "[%i]" or "[n]", where "n" is a decimal number */ static int extract_layout_index(const char *s, size_t max_len, xkb_layout_index_t *out) { /* This function is pretty stupid, but works for now. */ *out = XKB_LAYOUT_INVALID; - if (max_len < 3) - return -1; - if (s[0] != '[' || !is_digit(s[1]) || s[2] != ']') + if (max_len < 3 || s[0] != '[') return -1; - if (s[1] - '0' < 1 || s[1] - '0' > XKB_MAX_GROUPS) + if (max_len > 3 && s[1] == '%' && s[2] == 'i' && s[3] == ']') { + /* Special index: %i */ + return 4; /* == length "[%i]" */ + } + /* Numeric index */ + +#define parse_layout_int_index(s, endptr, index, out) \ + char *endptr; \ + long index = strtol(&s[1], &endptr, 10); \ + if (index < 0 || endptr[0] != ']' || index > XKB_MAX_GROUPS) \ + return -1; \ + /* To zero-based index. */ \ + *out = (xkb_layout_index_t)index - 1; \ + return (int)(endptr - s + 1) /* == length "[index]" */ + + parse_layout_int_index(s, endptr, index, out); +} + +/* Special layout indexes */ +#define LAYOUT_INDEX_SINGLE XKB_LAYOUT_INVALID +#define LAYOUT_INDEX_FIRST (XKB_LAYOUT_INVALID - 3) +#define LAYOUT_INDEX_LATER (XKB_LAYOUT_INVALID - 2) +#define LAYOUT_INDEX_ANY (XKB_LAYOUT_INVALID - 1) + +#if XKB_MAX_GROUPS >= LAYOUT_INDEX_FIRST + #error "Cannot define special indexes" +#endif +#if LAYOUT_INDEX_FIRST >= LAYOUT_INDEX_LATER || \ + LAYOUT_INDEX_LATER >= LAYOUT_INDEX_ANY || \ + LAYOUT_INDEX_ANY >= XKB_LAYOUT_INVALID || \ + LAYOUT_INDEX_SINGLE != XKB_LAYOUT_INVALID + #error "Special indexes must respect certain order" +#endif + +#define LAYOUT_INDEX_SINGLE_STR "single" +#define LAYOUT_INDEX_FIRST_STR "first" +#define LAYOUT_INDEX_LATER_STR "later" +#define LAYOUT_INDEX_ANY_STR "any" + +/* Parse index of layout/variant in MLVO mapping */ +static int +extract_mapping_layout_index(const char *s, size_t max_len, + xkb_layout_index_t *out) +{ + *out = XKB_LAYOUT_INVALID; + if (max_len < 3 || s[0] != '[') return -1; - /* To zero-based index. */ - *out = s[1] - '0' - 1; - return 3; +#define if_index(s, index, out) \ + /* Compare s against "index]" */ \ + if (strncmp(s, index##_STR "]", sizeof(index##_STR)) == 0) { \ + *out = index; \ + return sizeof(index##_STR) + 1; /* == length("[index]") */ \ + } + else if_index(&s[1], LAYOUT_INDEX_SINGLE, out) + else if_index(&s[1], LAYOUT_INDEX_FIRST, out) + else if_index(&s[1], LAYOUT_INDEX_LATER, out) + else if_index(&s[1], LAYOUT_INDEX_ANY, out) + else { + /* Numeric index */ + parse_layout_int_index(s, endptr, index, out); + } +#undef if_index +#undef LAYOUT_INDEX_SINGLE } +#define is_mlvo_mask_defined(m, mlvo) \ + ((m)->mapping.defined_mlvo_mask & (1u << (mlvo))) + static void matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s, struct sval ident) @@ -542,30 +621,31 @@ matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s, "invalid mapping: %.*s is not a valid value here; " "ignoring rule set", ident.len, ident.start); - m->mapping.skip = true; + m->mapping.active = false; return; } - if (m->mapping.defined_mlvo_mask & (1u << mlvo)) { + if (is_mlvo_mask_defined(m, mlvo)) { scanner_err(s, XKB_LOG_MESSAGE_NO_ID, "invalid mapping: %.*s appears twice on the same line; " "ignoring rule set", mlvo_sval.len, mlvo_sval.start); - m->mapping.skip = true; + m->mapping.active = false; return; } /* If there are leftovers still, it must be an index. */ if (mlvo_sval.len < ident.len) { xkb_layout_index_t idx; - int consumed = extract_layout_index(ident.start + mlvo_sval.len, - ident.len - mlvo_sval.len, &idx); + int consumed = extract_mapping_layout_index(ident.start + mlvo_sval.len, + ident.len - mlvo_sval.len, + &idx); if ((int) (ident.len - mlvo_sval.len) != consumed) { scanner_err(s, XKB_LOG_MESSAGE_NO_ID, "invalid mapping: \"%.*s\" may only be followed by a " "valid group index; ignoring rule set", mlvo_sval.len, mlvo_sval.start); - m->mapping.skip = true; + m->mapping.active = false; return; } @@ -580,16 +660,66 @@ matcher_mapping_set_mlvo(struct matcher *m, struct scanner *s, "invalid mapping: \"%.*s\" cannot be followed by a group " "index; ignoring rule set", mlvo_sval.len, mlvo_sval.start); - m->mapping.skip = true; + m->mapping.active = false; return; } } + /* Check that if both layout and variant are defined, then they must have + the same index */ + if (((mlvo == MLVO_LAYOUT && is_mlvo_mask_defined(m, MLVO_VARIANT)) || + (mlvo == MLVO_VARIANT && is_mlvo_mask_defined(m, MLVO_LAYOUT))) && + m->mapping.layout_idx != m->mapping.variant_idx) { + scanner_err(s, XKB_LOG_MESSAGE_NO_ID, + "invalid mapping: \"layout\" index must be the same as the " + "\"variant\" index"); + m->mapping.active = false; + return; + } + m->mapping.mlvo_at_pos[m->mapping.num_mlvo] = mlvo; m->mapping.defined_mlvo_mask |= 1u << mlvo; m->mapping.num_mlvo++; } +static void +matcher_mapping_set_layout_bounds(struct matcher *m) +{ + /* Handle case where one of the index is XKB_LAYOUT_INVALID */ + xkb_layout_index_t idx = MIN(m->mapping.layout_idx, m->mapping.variant_idx); + switch (idx) { + case LAYOUT_INDEX_LATER: + m->mapping.has_layout_idx_range = true; + m->mapping.layout_idx_min = 1; + m->mapping.layout_idx_max = MIN(XKB_MAX_GROUPS, + darray_size(m->rmlvo.layouts)); + m->mapping.layouts_candidates_mask = + /* All but the first layout */ + ((1u << m->mapping.layout_idx_max) - 1) & ~1; + break; + case LAYOUT_INDEX_ANY: + m->mapping.has_layout_idx_range = true; + m->mapping.layout_idx_min = 0; + m->mapping.layout_idx_max = MIN(XKB_MAX_GROUPS, + darray_size(m->rmlvo.layouts)); + m->mapping.layouts_candidates_mask = + /* All layouts */ + (1u << m->mapping.layout_idx_max) - 1; + break; + case LAYOUT_INDEX_FIRST: + case XKB_LAYOUT_INVALID: + /* No index or first index */ + idx = 0; + /* fallthrough */ + default: + /* Mere layout index */ + m->mapping.has_layout_idx_range = false; + m->mapping.layout_idx_min = idx; + m->mapping.layout_idx_max = idx + 1; + m->mapping.layouts_candidates_mask = 1u << idx; + } +} + static void matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ident) { @@ -609,7 +739,7 @@ matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ide "invalid mapping: %.*s is not a valid value here; " "ignoring rule set", ident.len, ident.start); - m->mapping.skip = true; + m->mapping.active = false; return; } @@ -618,7 +748,7 @@ matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ide "invalid mapping: %.*s appears twice on the same line; " "ignoring rule set", kccgst_sval.len, kccgst_sval.start); - m->mapping.skip = true; + m->mapping.active = false; return; } @@ -627,7 +757,7 @@ matcher_mapping_set_kccgst(struct matcher *m, struct scanner *s, struct sval ide m->mapping.num_kccgst++; } -static void +static bool matcher_mapping_verify(struct matcher *m, struct scanner *s) { if (m->mapping.num_mlvo == 0) { @@ -649,41 +779,62 @@ matcher_mapping_verify(struct matcher *m, struct scanner *s) * See the "Notes" section in the overview above. */ - if (m->mapping.defined_mlvo_mask & (1u << MLVO_LAYOUT)) { - if (m->mapping.layout_idx == XKB_LAYOUT_INVALID) { - if (darray_size(m->rmlvo.layouts) > 1) - goto skip; - } - else { - if (darray_size(m->rmlvo.layouts) == 1 || - m->mapping.layout_idx >= darray_size(m->rmlvo.layouts)) - goto skip; + if (is_mlvo_mask_defined(m, MLVO_LAYOUT)) { + switch (m->mapping.layout_idx) { + case XKB_LAYOUT_INVALID: + /* Layout rule without index matches when + * exactly 1 layout is specified */ + if (darray_size(m->rmlvo.layouts) > 1) + goto skip; + break; + case LAYOUT_INDEX_ANY: + case LAYOUT_INDEX_LATER: + case LAYOUT_INDEX_FIRST: + /* No restrictions */ + break; + default: + /* Layout rule with index matches when at least 2 layouts are + * specified. Index must be in valid range. */ + if (darray_size(m->rmlvo.layouts) < 2 || + m->mapping.layout_idx >= darray_size(m->rmlvo.layouts)) + goto skip; } } - if (m->mapping.defined_mlvo_mask & (1u << MLVO_VARIANT)) { - if (m->mapping.variant_idx == XKB_LAYOUT_INVALID) { - if (darray_size(m->rmlvo.variants) > 1) - goto skip; - } - else { - if (darray_size(m->rmlvo.variants) == 1 || - m->mapping.variant_idx >= darray_size(m->rmlvo.variants)) - goto skip; + if (is_mlvo_mask_defined(m, MLVO_VARIANT)) { + switch (m->mapping.variant_idx) { + case XKB_LAYOUT_INVALID: + /* Variant rule without index matches + * when exactly 1 variant is specified */ + if (darray_size(m->rmlvo.variants) > 1) + goto skip; + break; + case LAYOUT_INDEX_ANY: + case LAYOUT_INDEX_LATER: + case LAYOUT_INDEX_FIRST: + /* No restriction */ + break; + default: + /* Variant rule with index matches when at least 2 variants are + * specified. Index must be in valid range. */ + if (darray_size(m->rmlvo.variants) < 2 || + m->mapping.variant_idx >= darray_size(m->rmlvo.variants)) + goto skip; } } - return; + return true; skip: - m->mapping.skip = true; + m->mapping.active = false; + return false; } static void matcher_rule_start_new(struct matcher *m) { memset(&m->rule, 0, sizeof(m->rule)); - m->rule.skip = m->mapping.skip; + m->rule.skip = !m->mapping.active; } static void @@ -795,114 +946,168 @@ match_value_and_mark(struct matcher *m, struct sval val, /* * This function performs %-expansion on @value (see overview above), - * and appends the result to @to. + * and appends the result to @expanded. */ static bool -append_expanded_kccgst_value(struct matcher *m, struct scanner *s, - darray_char *to, struct sval value) +expand_rmlvo_in_kccgst_value(struct matcher *m, struct scanner *s, + struct sval value, xkb_layout_index_t layout_idx, + darray_char *expanded, unsigned *i) { const char *str = value.start; - darray_char expanded = darray_new(); - char ch; - bool expanded_plus, to_plus; /* * Some ugly hand-lexing here, but going through the scanner is more * trouble than it's worth, and the format is ugly on its own merit. */ - for (unsigned i = 0; i < value.len; ) { - enum rules_mlvo mlv; - xkb_layout_index_t idx; - char pfx, sfx; - struct matched_sval *expanded_value; + enum rules_mlvo mlv; + xkb_layout_index_t idx; + char pfx, sfx; + struct matched_sval *expanded_value; + + /* %i not as layout/variant index "%l[%i]" but as qualifier ":%i" */ + if (str[*i] == 'i' && + (*i + 1 == value.len || is_merge_mode_prefix(str[*i + 1]))) + { + (*i)++; + char index_str[MAX_LAYOUT_INDEX_STR_LENGTH + 1]; + int count = snprintf(index_str, sizeof(index_str), "%"PRIu32, + layout_idx + 1); + darray_appends_nullterminate(*expanded, index_str, count); + return true; + } - /* Check if that's a start of an expansion. */ - if (str[i] != '%') { - /* Just a normal character. */ - darray_appends_nullterminate(expanded, &str[i++], 1); - continue; - } - if (++i >= value.len) goto error; + pfx = sfx = 0; - pfx = sfx = 0; + /* Check for prefix. */ + if (str[*i] == '(' || + is_merge_mode_prefix(str[*i]) || + str[*i] == '_' || str[*i] == '-') { + pfx = str[*i]; + if (str[*i] == '(') sfx = ')'; + if (++(*i) >= value.len) goto error; + } - /* Check for prefix. */ - if (str[i] == '(' || - is_merge_mode_prefix(str[i]) || - str[i] == '_' || str[i] == '-') { - pfx = str[i]; - if (str[i] == '(') sfx = ')'; - if (++i >= value.len) goto error; - } + /* Mandatory model/layout/variant specifier. */ + switch (str[(*i)++]) { + case 'm': mlv = MLVO_MODEL; break; + case 'l': mlv = MLVO_LAYOUT; break; + case 'v': mlv = MLVO_VARIANT; break; + default: goto error; + } - /* Mandatory model/layout/variant specifier. */ - switch (str[i++]) { - case 'm': mlv = MLVO_MODEL; break; - case 'l': mlv = MLVO_LAYOUT; break; - case 'v': mlv = MLVO_VARIANT; break; - default: goto error; + /* Check for index. */ + idx = XKB_LAYOUT_INVALID; + bool expanded_index = false; + if (*i < value.len && str[*i] == '[') { + if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) { + scanner_err(s, XKB_LOG_MESSAGE_NO_ID, + "invalid index in %%-expansion; " + "may only index layout or variant"); + goto error; } - /* Check for index. */ - idx = XKB_LAYOUT_INVALID; - if (i < value.len && str[i] == '[') { - int consumed; - - if (mlv != MLVO_LAYOUT && mlv != MLVO_VARIANT) { - scanner_err(s, XKB_LOG_MESSAGE_NO_ID, - "invalid index in %%-expansion; " - "may only index layout or variant"); - goto error; - } - - consumed = extract_layout_index(str + i, value.len - i, &idx); - if (consumed == -1) goto error; - i += consumed; + int consumed = extract_layout_index(str + (*i), value.len - (*i), &idx); + if (consumed == -1) goto error; + if (idx == XKB_LAYOUT_INVALID) { + /* %i encountered */ + idx = layout_idx; + expanded_index = true; } + *i += consumed; + } - /* Check for suffix, if there supposed to be one. */ - if (sfx != 0) { - if (i >= value.len) goto error; - if (str[i++] != sfx) goto error; - } + /* Check for suffix, if there supposed to be one. */ + if (sfx != 0) { + if (*i >= value.len) goto error; + if (str[(*i)++] != sfx) goto error; + } - /* Get the expanded value. */ - expanded_value = NULL; + /* Get the expanded value. */ + expanded_value = NULL; - if (mlv == MLVO_LAYOUT) { - if (idx != XKB_LAYOUT_INVALID && - idx < darray_size(m->rmlvo.layouts) && - darray_size(m->rmlvo.layouts) > 1) - expanded_value = &darray_item(m->rmlvo.layouts, idx); - else if (idx == XKB_LAYOUT_INVALID && - darray_size(m->rmlvo.layouts) == 1) + if (mlv == MLVO_LAYOUT) { + if (idx == XKB_LAYOUT_INVALID) { + /* No index provided: match only if single layout */ + if (darray_size(m->rmlvo.layouts) == 1) expanded_value = &darray_item(m->rmlvo.layouts, 0); + /* Some index provided: expand only if it is %i or + * if there are multiple layouts */ + } else if (idx < darray_size(m->rmlvo.layouts) && + (expanded_index || darray_size(m->rmlvo.layouts) > 1)) { + expanded_value = &darray_item(m->rmlvo.layouts, idx); } - else if (mlv == MLVO_VARIANT) { - if (idx != XKB_LAYOUT_INVALID && - idx < darray_size(m->rmlvo.variants) && - darray_size(m->rmlvo.variants) > 1) - expanded_value = &darray_item(m->rmlvo.variants, idx); - else if (idx == XKB_LAYOUT_INVALID && - darray_size(m->rmlvo.variants) == 1) + } + else if (mlv == MLVO_VARIANT) { + if (idx == XKB_LAYOUT_INVALID) { + /* No index provided: match only if single variant */ + if (darray_size(m->rmlvo.variants) == 1) expanded_value = &darray_item(m->rmlvo.variants, 0); + /* Some index provided: expand only if it is %i or + * if there are multiple variants */ + } else if (idx < darray_size(m->rmlvo.variants) && + (expanded_index || darray_size(m->rmlvo.variants) > 1)) { + expanded_value = &darray_item(m->rmlvo.variants, idx); } - else if (mlv == MLVO_MODEL) { - expanded_value = &m->rmlvo.model; - } + } + else if (mlv == MLVO_MODEL) { + expanded_value = &m->rmlvo.model; + } - /* If we didn't get one, skip silently. */ - if (!expanded_value || expanded_value->sval.len == 0) - continue; - - if (pfx != 0) - darray_appends_nullterminate(expanded, &pfx, 1); - darray_appends_nullterminate(expanded, - expanded_value->sval.start, - expanded_value->sval.len); - if (sfx != 0) - darray_appends_nullterminate(expanded, &sfx, 1); - expanded_value->matched = true; + /* If we didn't get one, skip silently. */ + if (!expanded_value || expanded_value->sval.len == 0) { + return true; + } + + if (pfx != 0) + darray_appends_nullterminate(*expanded, &pfx, 1); + darray_appends_nullterminate(*expanded, + expanded_value->sval.start, + expanded_value->sval.len); + if (sfx != 0) + darray_appends_nullterminate(*expanded, &sfx, 1); + expanded_value->matched = true; + + return true; + +error: + scanner_err(s, XKB_LOG_MESSAGE_NO_ID, + "invalid %%-expansion in value; not used"); + return false; +} + +/* + * This function performs %-expansion on @value (see overview above), + * and appends the result to @to. + */ +static bool +append_expanded_kccgst_value(struct matcher *m, struct scanner *s, + darray_char *to, struct sval value, + xkb_layout_index_t layout_idx) +{ + const char *str = value.start; + darray_char expanded = darray_new(); + char ch; + bool expanded_plus, to_plus; + + for (unsigned i = 0; i < value.len; ) { + /* Check if that's a start of an expansion */ + switch (str[i]) { + /* Expansion */ + case '%': + if (++i >= value.len || + !expand_rmlvo_in_kccgst_value(m, s, value, layout_idx, + &expanded, &i)) + goto error; + break; + /* New item */ + case MERGE_OVERRIDE_PREFIX: + case MERGE_AUGMENT_PREFIX: + darray_appends_nullterminate(expanded, &str[i++], 1); + break; + /* Just a normal character. */ + default: + darray_appends_nullterminate(expanded, &str[i++], 1); + } } /* @@ -918,17 +1123,19 @@ append_expanded_kccgst_value(struct matcher *m, struct scanner *s, to_plus = is_merge_mode_prefix(ch); if (expanded_plus || darray_empty(*to)) - darray_appends_nullterminate(*to, expanded.item, expanded.size); + darray_appends_nullterminate(*to, + darray_items(expanded), + darray_size(expanded)); else if (to_plus) - darray_prepends_nullterminate(*to, expanded.item, expanded.size); + darray_prepends_nullterminate(*to, + darray_items(expanded), + darray_size(expanded)); darray_free(expanded); return true; error: darray_free(expanded); - scanner_err(s, XKB_LOG_MESSAGE_NO_ID, - "invalid %%-expansion in value; not used"); return false; } @@ -947,6 +1154,10 @@ matcher_rule_verify(struct matcher *m, struct scanner *s) static void matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) { + /* Initial candidates (used if m->mapping.has_layout_idx_range == true) */ + xkb_layout_mask_t candidate_layouts = m->mapping.layouts_candidates_mask; + xkb_layout_index_t idx; + /* Loop over MLVO pattern components */ for (unsigned i = 0; i < m->mapping.num_mlvo; i++) { enum rules_mlvo mlvo = m->mapping.mlvo_at_pos[i]; struct sval value = m->rule.mlvo_value_at_pos[i]; @@ -962,20 +1173,44 @@ matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) matched = match_value_and_mark(m, value, to, match_type, WILDCARD_MATCH_ALL); } +#define process_component(_component, m, value, idx, candidate_layouts, to, \ + match_type, matched) \ + if (m->mapping.has_layout_idx_range) { \ + /* Special index: loop over the index range */ \ + for (idx = m->mapping.layout_idx_min; \ + idx < m->mapping.layout_idx_max; \ + idx++) \ + { \ + /* Process only if index not skipped */ \ + xkb_layout_mask_t mask = 1u << idx; \ + if (candidate_layouts & mask) { \ + to = &darray_item(m->rmlvo._component, idx); \ + if (match_value_and_mark(m, value, to, match_type, \ + WILDCARD_MATCH_NONEMPTY)) { \ + /* Mark matched, keep index */ \ + matched = true; \ + } else { \ + /* Not matched, remove index */ \ + candidate_layouts &= ~mask; \ + } \ + } \ + } \ + } else { \ + /* Numeric index or no index */ \ + to = &darray_item(m->rmlvo._component, \ + m->mapping.layout_idx_min); \ + matched = match_value_and_mark(m, value, to, match_type, \ + WILDCARD_MATCH_NONEMPTY); \ + } 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, - WILDCARD_MATCH_NONEMPTY); + process_component(layouts, m, value, idx, candidate_layouts, to, + match_type, matched) } 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, - WILDCARD_MATCH_NONEMPTY); + process_component(variants, m, value, idx, candidate_layouts, to, + match_type, matched) } +#undef process_component else if (mlvo == MLVO_OPTION) { darray_foreach(to, m->rmlvo.options) { matched = match_value_and_mark(m, value, to, match_type, @@ -989,10 +1224,29 @@ matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) return; } - for (unsigned i = 0; i < m->mapping.num_kccgst; i++) { - enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i]; - struct sval value = m->rule.kccgst_value_at_pos[i]; - append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value); + if (m->mapping.has_layout_idx_range) { + /* Special index: loop over the index range */ + for (idx = m->mapping.layout_idx_min; + idx < m->mapping.layout_idx_max; + idx++) + { + if (candidate_layouts & (1u << idx)) { + for (unsigned i = 0; i < m->mapping.num_kccgst; i++) { + enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i]; + struct sval value = m->rule.kccgst_value_at_pos[i]; + append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], + value, idx); + } + } + } + } else { + /* Numeric index or no index */ + for (unsigned i = 0; i < m->mapping.num_kccgst; i++) { + enum rules_kccgst kccgst = m->mapping.kccgst_at_pos[i]; + struct sval value = m->rule.kccgst_value_at_pos[i]; + append_expanded_kccgst_value(m, s, &m->kccgst[kccgst], value, + m->mapping.layout_idx_min); + } } /* @@ -1000,8 +1254,9 @@ matcher_rule_apply_if_matches(struct matcher *m, struct scanner *s) * skipped. However, rule sets matching against options may contain * several legitimate rules, so they are processed entirely. */ - if (!(m->mapping.defined_mlvo_mask & (1 << MLVO_OPTION))) - m->mapping.skip = true; + if (!(is_mlvo_mask_defined(m, MLVO_OPTION))) { + m->mapping.layouts_candidates_mask &= ~candidate_layouts; + } } static enum rules_token @@ -1087,7 +1342,7 @@ matcher_match(struct matcher *m, struct scanner *s, mapping_mlvo: switch (tok = gettok(m, s)) { case TOK_IDENTIFIER: - if (!m->mapping.skip) + if (m->mapping.active) matcher_mapping_set_mlvo(m, s, m->val.string); goto mapping_mlvo; case TOK_EQUALS: @@ -1099,12 +1354,12 @@ matcher_match(struct matcher *m, struct scanner *s, mapping_kccgst: switch (tok = gettok(m, s)) { case TOK_IDENTIFIER: - if (!m->mapping.skip) + if (m->mapping.active) matcher_mapping_set_kccgst(m, s, m->val.string); goto mapping_kccgst; case TOK_END_OF_LINE: - if (!m->mapping.skip) - matcher_mapping_verify(m, s); + if (m->mapping.active && matcher_mapping_verify(m, s)) + matcher_mapping_set_layout_bounds(m); goto rule_mlvo_first; default: goto unexpected; diff --git a/src/xkbcomp/rules.h b/src/xkbcomp/rules.h index 5381b156..46b754c9 100644 --- a/src/xkbcomp/rules.h +++ b/src/xkbcomp/rules.h @@ -29,4 +29,15 @@ xkb_components_from_rules(struct xkb_context *ctx, const struct xkb_rule_names *rmlvo, struct xkb_component_names *out); +/* Maximum length of a layout index string: + * [NOTE] Currently XKB_MAX_GROUPS is 4, but the following code is + * future-proof for all possible indexes. + * + * length = ceiling (bitsize(xkb_layout_index_t) * logBase 10 2) + * < ceiling (bitsize(xkb_layout_index_t) * 5 / 16) + * < 1 + floor (bitsize(xkb_layout_index_t) * 5 / 16) + */ +#define MAX_LAYOUT_INDEX_STR_LENGTH \ + (1 + ((sizeof(xkb_layout_index_t) * CHAR_BIT * 5) >> 4)) + #endif diff --git a/test/data/.editorconfig b/test/data/.editorconfig new file mode 100644 index 00000000..329c45c4 --- /dev/null +++ b/test/data/.editorconfig @@ -0,0 +1,8 @@ +[*] +charset = utf-8 +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = 8 +max_line_length = 80 diff --git a/test/data/rules/evdev-modern b/test/data/rules/evdev-modern new file mode 100644 index 00000000..ea334788 --- /dev/null +++ b/test/data/rules/evdev-modern @@ -0,0 +1,715 @@ +// DO NOT EDIT THIS FILE - IT WAS AUTOGENERATED BY merge.py FROM rules/*.part +// +// +// Rules for resolving XKB components for use with XFree86 +// Copyright 1996 by Joseph Moss +// +// 2002 Modifier: Ivan Pascal The XFree86 Project +// + +// If you want non-latin layouts implicitly include the en_US layout +// uncomment lines below +//! $nonlatin = am ara ben bd bg bt by cs deva ge gh gr guj guru il \ +// in ir iku jp kan kh kr la lao lk mk mm mn mv mal olck \ +// ori pk ru scc sy syr tel th tj tam ua uz + +// PC models +! $pcmodels = pc86 pc101 pc102 pc104 pc104alt pc105 + +// Jolla devices and keyboards +! $jollamodels = jollasbj + +// Microsoft models (using MS geometry) +! $msmodels = microsoft microsoft4000 microsoft7000 microsoftpro microsoftprousb microsoftprose microsoftsurface + +// Nokia devices and keyboards +! $nokiamodels = nokiasu8w nokiarx44 nokiarx51 + +// TypeMatrix geometries +! $tmgeometries = tm2020 tm2030PS2 tm2030USB tm2030USB-102 tm2030USB-106 + +// Layouts that provide further specializations for the OLPC +! $olpclayouts = af am ara br ca es et fr it kh kz in mn np ru th tr us + +! $macbooks = macbook78 macbook79 +! $maclaptop = ibook powerbook macbook78 macbook79 +! $applealu = applealu_ansi applealu_iso applealu_jis +! $macs = macintosh macintosh_old ibook powerbook macbook78 macbook79 + +! $macvendorlayouts = ch de dk fi fr gb is it latam nl no pt se us + +! $azerty = be fr +! $qwertz = al cz de hr hu ro si sk + + +// all layouts with 3rd and 4th groups +! $threelevellayouts = al az \ + be br bt \ + ca ch cs cz \ + de dk \ + ee es \ + fi fo fr \ + gb gr \ + hu \ + ie ir is it \ + latam \ + lk lt \ + mn mt \ + nl no \ + pl pt \ + ro \ + se sk \ + tr \ + us \ + vn \ + za + +! $thinkpads = thinkpad thinkpad60 thinkpadz60 + +! $sun = sun_type6_jp sun_type6_usb sun_type6_euro_usb \ + sun_type6_jp_usb sun_type6_unix_usb sun_type7_jp_usb \ + sun_type7_usb sun_type7_euro_usb sun_type7_unix_usb + +! $sun_jp = sun_type6_jp sun_type6_jp_usb sun_type7_jp_usb + +// Sun Type_6_7 keyboards with custom layouts +! $sun_custom = ara be br ca ch cz de dk \ + ee es fi fr gb gr it jp \ + kr lt lv nl no pl pt ro \ + ru se sk tr tw ua us + +! $sun_var = sun_type6 sun_type6_suncompat sun_type6_de sun_type6_fr \ + sun_type7 sun_type7_suncompat suncompat + +! $sun_compat = sun_type6 sun_type6_suncompat sun_type7_suncompat suncompat + + +! $evdevkbds = ibm_spacesaver + +! $dvoraklayouts = br ca de ee es fr gb no pl se us + +! model = keycodes + applealu_jis = evdev+macintosh(jisevdev) + $jollamodels = evdev+jolla(jolla) + olpc = evdev+olpc(olpc) + olpcm = evdev+olpc(olpcm) + * = evdev + +! layout[1] = keycodes + $azerty = +aliases(azerty) + $qwertz = +aliases(qwertz) + * = +aliases(qwerty) + +! layout = keycodes + $azerty = +aliases(azerty) + $qwertz = +aliases(qwertz) + * = +aliases(qwerty) + +! option = keycodes + +! model layout = geometry + thinkpad us = thinkpad(us) + +! model = geometry + microsoftelite = microsoft(elite) + $msmodels = microsoft(natural) + dell101 = dell(dell101) + dellm65 = dell(dellm65) + latitude = dell(latitude) + flexpro = keytronic(FlexPro) + hp6000 = hp(omnibook) + hpmini110 = hp(mini110) + hpdv5 = hp(dv5) + omnikey101 = northgate(omnikey101) + sanwaskbkg3 = sanwa(sanwaskbkg3) + $pcmodels = pc(%m) + everex = everex(STEPnote) + thinkpad = thinkpad(intl) + thinkpad60 = thinkpad(60) + thinkpadz60 = thinkpad(60) + apex300 = steelseries(apex300) + $tmgeometries = typematrix(%m) + winbook = winbook(XP5) + pc98 = nec(pc98) + $applealu = macintosh(%m) + $macbooks = macintosh(%m) + $macs = macintosh(macintosh) + hhk = hhk(basic) + kinesis = kinesis(model100) + $nokiamodels = nokia(%m) + sun_type6_jp = sun(type6jp) + sun_type6_usb = sun(type6) + sun_type6_euro_usb = sun(type6tuv) + sun_type6_jp_usb = sun(type6jp) + sun_type6_unix_usb = sun(type6unix) + sun_type7_jp_usb = sun(type6jp) + sun_type7_usb = sun(type7) + sun_type7_euro_usb = sun(type7tuv) + sun_type7_unix_usb = sun(type7unix) + * = pc(pc104) + +! model layout[first] variant[first] = symbols + * ben basic = pc+in(ben) + * ben probhat = pc+in(ben_probhat) + * dev basic = pc+in(deva) + * dvorak $dvoraklayouts = pc+%v(dvorak) + * dvorak basic = pc+us(dvorak) + * dvorak pl_basic = pc+pl(dvorak) + * dvorak pl = pc+pl(dvorak_quotes) + * dvorak pl_altquotes = pc+pl(dvorak_altquotes) + * dzdwi basic = pc+bt(basic) + * fi basic = pc+fi(classic) + * ge azerty_tskapo = pc+fr(geo) + * guj basic = pc+in(guj) + * gur basic = pc+in(guru) + * ie laptop = pc+ie(basic) + * ie CloGaelachLaptop = pc+ie(CloGaelach) + * in urd = pc+in(urd-phonetic) + * iu basic = pc+ca(ike) + * lo basic = pc+la(basic) + * kan basic = pc+in(kan) + * mal basic = pc+in(mal) + * mal mlplusnum = pc+in(mal) + * ogham basic = pc+ie(ogam) + * ogham laptop = pc+ie(ogam) + * ogham is434 = pc+ie(ogam_is434) + * ogham is434laptop = pc+ie(ogam_is434) + * ori basic = pc+in(ori) + * ro de = pc+ro(winkeys) + * ro us = pc+ro(std) + * ro academic = pc+ro(std) + * ro std_comma = pc+ro(std) + * ro comma = pc+ro(basic) + * ru os = pc+ru(os_legacy) + * pk urd = pc+pk(urd-phonetic) + * sapmi basic = pc+no(smi) + * sapmi nodeadkeys = pc+no(smi_nodeadkeys) + * sapmi sefi = pc+fi(smi) + * sin phonetic-static = pc+in(sin_phonetic) + * syr basic = pc+sy(syc) + * syr phonetic = pc+sy(syc_phonetic) + * tam INSCRIPT = pc+in(tam) + * tam UNI = pc+in(tam_unicode) + * tam NUMERAL-KEYBOARD = pc+in(tam_keyboard_with_numerals) + * tam TAB = pc+in(tam_TAB) + * tam TSCII = pc+in(tam_TSCII) + * tel basic = pc+in(tel) + * yu basic = pc+srp(latin) + * yu unicode = pc+srp(latinunicode) + * yu yz = pc+srp(latinyz) + * yu unicodeyz = pc+srp(latinunicodeyz) + classmate us intl = pc+us(classmate-intl) + classmate us alt-intl = pc+us(classmate-alt-intl) + classmate us altgr-intl = pc+us(classmate-altgr-intl) + nokiarx51 cz qwerty = nokia_vndr/rx-51(cz_qwerty) + * $sun_custom $sun_var = pc+sun_vndr/%l%(v) + +! model layout[first] = symbols + * ar = pc+ara + * ben = pc+in(ben) + * bs = pc+ba + * cs = pc+rs + * cz_qwerty = pc+cz(qwerty) + * dev = pc+in(deva) + * dvorak = pc+us(dvorak) + * dzdwi = pc+bt + * el = pc+gr + * en_US = pc+latin + * guj = pc+in(guj) + * gur = pc+in(guru) + * iu = pc+ca(ike) + * lo = pc+la + * kan = pc+in(kan) + * mi = pc+mao + * ogham = pc+ie(ogam) + * ori = pc+ie(ori) + * sapmi = pc+no(smi) + * sr = pc+srp + * syr = pc+sy(syc) + * tel = pc+in(tel) + * tml = pc+in(tam) + * yu = pc+srp + * fr-latin9 = pc+fr(latin9) + * us_intl = pc+us(alt-intl) + * ben(basic) = pc+in(ben) + * ben(probhat) = pc+in(ben_probhat) + * dev(basic) = pc+in(deva) + * dvorak($dvoraklayouts) = pc+%v(dvorak) + * dvorak(basic) = pc+us(dvorak) + * dvorak(pl_basic) = pc+pl(dvorak) + * dvorak(pl) = pc+pl(dvorak_quotes) + * dvorak(pl_altquotes) = pc+pl(dvorak_altquotes) + * dzdwi(basic) = pc+bt(basic) + * fi(basic) = pc+fi(classic) + * ge(azerty_tskapo) = pc+fr(geo) + * guj(basic) = pc+in(guj) + * gur(basic) = pc+in(guru) + * ie(laptop) = pc+ie(basic) + * ie(CloGaelachLaptop) = pc+ie(CloGaelach) + * in(urd) = pc+in(urd-phonetic) + * iu(basic) = pc+ca(ike) + * lo(basic) = pc+la(basic) + * kan(basic) = pc+in(kan) + * mal(basic) = pc+in(mal) + * mal(mlplusnum) = pc+in(mal) + * ogham(basic) = pc+ie(ogam) + * ogham(laptop) = pc+ie(ogam) + * ogham(is434) = pc+ie(ogam_is434) + * ogham(is434laptop) = pc+ie(ogam_is434) + * ori(basic) = pc+in(ori) + * ro(de) = pc+ro(winkeys) + * ro(us) = pc+ro(std) + * ro(academic) = pc+ro(std) + * ro(std_comma) = pc+ro(std) + * ro(comma) = pc+ro(basic) + * ru(os) = pc+ru(os_legacy) + * pk(urd) = pc+pk(urd-phonetic) + * sapmi(basic) = pc+no(smi) + * sapmi(nodeadkeys) = pc+no(smi_nodeadkeys) + * sapmi(sefi) = pc+fi(smi) + * sin(phonetic-static) = pc+in(sin_phonetic) + * syr(basic) = pc+sy(syc) + * syr(phonetic) = pc+sy(syc_phonetic) + * tam(INSCRIPT) = pc+in(tam) + * tam(UNI) = pc+in(tam_unicode) + * tam(NUMERAL-KEYBOARD) = pc+in(tam_keyboard_with_numerals) + * tam(TAB) = pc+in(tam_TAB) + * tam(TSCII) = pc+in(tam_TSCII) + * tel(basic) = pc+in(tel) + * yu(basic) = pc+srp(latin) + * yu(unicode) = pc+srp(latinunicode) + * yu(yz) = pc+srp(latinyz) + * yu(unicodeyz) = pc+srp(latinunicodeyz) + +! model layout = symbols + ataritt $nonlatin = xfree68_vndr/ataritt(us)+%l[%i]%(v[%i]):2 + ataritt * = xfree68_vndr/ataritt(us)+%l[%i]%(v[%i]) + amiga $nonlatin = xfree68_vndr/amiga(usa1)+%l[%i]%(v[%i]):2 + amiga * = xfree68_vndr/amiga(usa1)+%l[%i]%(v[%i]) + classmate us = pc+%l[%i](classmate) + empty * = empty(basic) + * empty = empty(basic) + jollasbj $nonlatin = jolla_vndr/sbj(common)+us+%l[%i]%(v[%i]):2 + jollasbj * = jolla_vndr/sbj(common)+%l[%i]%(v[%i]) + $sun $sun_custom = pc+sun_vndr/%l[%i]%(v[%i]) + pc98 nec_vndr/jp = nec_vndr/jp(pc98) + macintosh_old us = macintosh_vndr/us(oldmac) + macintosh_old en_US = macintosh_vndr/us(oldmac) + macintosh_old $macvendorlayouts = macintosh_vndr/us(oldmac)+macintosh_vndr/%l%(v) + macintosh_old $nonlatin = macintosh_vndr/us(oldmac)+%l[%i]%(v[%i]):2 + macintosh_old * = macintosh_vndr/us(oldmac)+%l[%i]%(v[%i]) + applealu_jis jp = macintosh_vndr/apple(alukbd)+macintosh_vndr/jp(usmac)+macintosh_vndr/jp(mac):2 + applealu_jis * = macintosh_vndr/apple(alukbd)+%l[%i]%(v[%i])+macintosh_vndr/jp(mac):2 + $applealu $macvendorlayouts = macintosh_vndr/apple(alukbd)+macintosh_vndr/%l[%i]%(v[%i]) + $applealu * = macintosh_vndr/apple(alukbd)+%l[%i]%(v[%i]) + $macs en_US = pc+macintosh_vndr/us(extended) + $macs $macvendorlayouts = pc+macintosh_vndr/%l[%i]%(v[%i]) + nokiarx44 * = nokia_vndr/rx-44(%l[%i]) + nokiarx51 cz(qwerty) = nokia_vndr/rx-51(common)+nokia_vndr/rx-51(cz_qwerty) + nokiarx51 * = nokia_vndr/rx-51(common)+nokia_vndr/rx-51(%l[%i]%_v[%i]) + nokiasu8w * = nokia_vndr/su-8w(%l[%i]) + olpc $olpclayouts = olpc+%l[%i]%(m) + olpc * = olpc+%l[%i]%(v[%i]) + olpcm $olpclayouts = olpc+%l[%i]%(m) + olpcm * = olpc+%l[%i]%(v[%i]) + $thinkpads br = pc+br(thinkpad) + sl-c3x00 * = pc+sharp_vndr/sl-c3x00(basic) + ws003sh * = pc+sharp_vndr/ws003sh(basic) + ws007sh * = pc+sharp_vndr/ws007sh(basic) + ws011sh * = pc+sharp_vndr/ws011sh(basic) + ws020sh * = pc+sharp_vndr/ws020sh(basic) + * $nonlatin = pc+us+%l[%i]%(v[%i]):2 + +! model layout[first] = symbols + * * = pc+%l[%i]%(v[%i]) + +! model layout[later] = symbols + * ar = +ara%(v[%i]):%i + * ben = +in(ben):%i + * bs = +ba%(v[%i]):%i + * cs = +rs%(v[%i]):%i + * cz_qwerty = +cz(qwerty):%i + * dev = +in(deva):%i + * dvorak = +us(dvorak):%i + * dzdwi = +bt%(v[%i]):%i + * el = +gr%(v[%i]):%i + * en_US = +latin%(v[%i]):%i + * guj = +in(guj):%i + * gur = +in(guru):%i + * iu = +ca(ike):%i + * lo = +la%(v[%i]):%i + * kan = +in(kan):%i + * mi = +mao%(v[%i]):%i + * ogham = +ie(ogam):%i + * ori = +ie(ori):%i + * sapmi = +no(smi):%i + * sr = +srp%(v[%i]):%i + * syr = +sy(syc):%i + * tel = +in(tel):%i + * tml = +in(tam):%i + * yu = +srp%(v[%i]):%i + * fr-latin9 = +fr(latin9):%i + * us_intl = +us(alt-intl):%i + * ben(basic) = +in(ben):%i + * ben(probhat) = +in(ben_probhat):%i + * dev(basic) = +in(deva):%i + * dvorak($dvoraklayouts) = +%v(dvorak):%i + * dvorak(basic) = +us(dvorak):%i + * dvorak(pl_basic) = +pl(dvorak):%i + * dvorak(pl) = +pl(dvorak_quotes):%i + * dvorak(pl_altquotes) = +pl(dvorak_altquotes):%i + * dzdwi(basic) = +bt(basic):%i + * fi(basic) = +fi(classic):%i + * ge(azerty_tskapo) = +fr(geo):%i + * guj(basic) = +in(guj):%i + * gur(basic) = +in(guru):%i + * ie(laptop) = +ie(basic):%i + * ie(CloGaelachLaptop) = +ie(CloGaelach):%i + * in(urd) = +in(urd-phonetic):%i + * iu(basic) = +ca(ike):%i + * lo(basic) = +la(basic):%i + * kan(basic) = +in(kan):%i + * mal(basic) = +in(mal):%i + * mal(mlplusnum) = +in(mal):%i + * ogham(basic) = +ie(ogam):%i + * ogham(laptop) = +ie(ogam):%i + * ogham(is434) = +ie(ogam_is434):%i + * ogham(is434laptop) = +ie(ogam_is434):%i + * ori(basic) = +in(ori):%i + * ro(de) = +ro(winkeys):%i + * ro(us) = +ro(std):%i + * ro(academic) = +ro(std):%i + * ro(std_comma) = +ro(std):%i + * ro(comma) = +ro(basic):%i + * ru(os) = +ru(os_legacy):%i + * pk(urd) = +pk(urd-phonetic):%i + * sapmi(basic) = +no(smi):%i + * sapmi(nodeadkeys) = +no(smi_nodeadkeys):%i + * sapmi(sefi) = +fi(smi):%i + * sin(phonetic-static) = +in(sin_phonetic):%i + * syr(basic) = +sy(syc):%i + * syr(phonetic) = +sy(syc_phonetic):%i + * tam(INSCRIPT) = +in(tam):%i + * tam(UNI) = +in(tam_unicode):%i + * tam(NUMERAL-KEYBOARD) = +in(tam_keyboard_with_numerals):%i + * tam(TAB) = +in(tam_TAB):%i + * tam(TSCII) = +in(tam_TSCII):%i + * tel(basic) = +in(tel):%i + * yu(basic) = +srp(latin):%i + * yu(unicode) = +srp(latinunicode):%i + * yu(yz) = +srp(latinyz):%i + * yu(unicodeyz) = +srp(latinunicodeyz):%i + nokiarx51 cz(qwerty) = +nokia_vndr/rx-51(cz_qwerty):%i + nokiarx51 * = +nokia_vndr/rx-51(%l[%i]%_v[%i]):%i + $sun $sun_custom = +sun_vndr/%l[%i]%(v[%i]):%i + * * = +%l[%i]%(v[%i]):%i + +! model layout[later] variant[later] = symbols + * ben basic = +in(ben):%i + * ben probhat = +in(ben_probhat):%i + * dev basic = +in(deva):%i + * dvorak $dvoraklayouts = +%v(dvorak):%i + * dvorak basic = +us(dvorak):%i + * dvorak pl_basic = +pl(dvorak):%i + * dvorak pl = +pl(dvorak_quotes):%i + * dvorak pl_altquotes = +pl(dvorak_altquotes):%i + * dzdwi basic = +bt(basic):%i + * fi basic = +fi(classic):%i + * ge azerty_tskapo = +fr(geo):%i + * guj basic = +in(guj):%i + * gur basic = +in(guru):%i + * ie laptop = +ie(basic):%i + * ie CloGaelachLaptop = +ie(CloGaelach):%i + * in urd = +in(urd-phonetic):%i + * iu basic = +ca(ike):%i + * lo basic = +la(basic):%i + * kan basic = +in(kan):%i + * mal basic = +in(mal):%i + * mal mlplusnum = +in(mal):%i + * ogham basic = +ie(ogam):%i + * ogham laptop = +ie(ogam):%i + * ogham is434 = +ie(ogam_is434):%i + * ogham is434laptop = +ie(ogam_is434):%i + * ori basic = +in(ori):%i + * ro de = +ro(winkeys):%i + * ro us = +ro(std):%i + * ro academic = +ro(std):%i + * ro std_comma = +ro(std):%i + * ro comma = +ro(basic):%i + * ru os = +ru(os_legacy):%i + * pk urd = +pk(urd-phonetic):%i + * sapmi basic = +no(smi):%i + * sapmi nodeadkeys = +no(smi_nodeadkeys):%i + * sapmi sefi = +fi(smi):%i + * sin phonetic-static = +in(sin_phonetic):%i + * syr basic = +sy(syc):%i + * syr phonetic = +sy(syc_phonetic):%i + * tam INSCRIPT = +in(tam):%i + * tam UNI = +in(tam_unicode):%i + * tam NUMERAL-KEYBOARD = +in(tam_keyboard_with_numerals):%i + * tam TAB = +in(tam_TAB):%i + * tam TSCII = +in(tam_TSCII):%i + * tel basic = +in(tel):%i + * yu basic = +srp(latin):%i + * yu unicode = +srp(latinunicode):%i + * yu yz = +srp(latinyz):%i + * yu unicodeyz = +srp(latinunicodeyz):%i + +! model = symbols + $evdevkbds = +inet(evdev)+inet(%m) + chromebook = +inet(evdev)+inet(chromebook) + applealu_jis = +inet(evdev)+macintosh_vndr/jp(alujiskeys) + * = +inet(evdev) + +! layout[any] variant[any] = compat + de neo = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de adnw = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de koy = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de bone = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de bone_eszett_home = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de neo_qwertz = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + de neo_qwerty = +caps(caps_lock):%i+misc(assign_shift_left_action):%i+level5(level5_lock):%i + jp $sun_compat = +complete+japan(kana_lock):%i + +! model layout[single] = compat + pc98 nec_vndr/jp = pc98(basic) + * jp = complete+japan + olpc * = olpc + olpcm * = olpc + * * = complete + +! model layout[first] = compat + * * = complete + +! model = types + $macs = complete+numpad(mac) + $applealu = complete+numpad(mac) + $nokiamodels = complete+nokia + * = complete + +! layout[any] option = symbols + $threelevellayouts grp:alts_toggle = +level3(ralt_switch_for_alts_toggle):%i + * misc:typo = +typo(base):%i + * misc:apl = +apl(level3):%i + +! option = symbols + grp:shift_toggle = +group(shifts_toggle) + altwin:menu = +altwin(menu) + altwin:menu_win = +altwin(menu_win) + altwin:meta_alt = +altwin(meta_alt) + altwin:alt_win = +altwin(alt_win) + altwin:ctrl_win = +altwin(ctrl_win) + altwin:ctrl_alt_win = +altwin(ctrl_alt_win) + altwin:meta_win = +altwin(meta_win) + altwin:left_meta_win = +altwin(left_meta_win) + altwin:hyper_win = +altwin(hyper_win) + altwin:alt_super_win = +altwin(alt_super_win) + altwin:swap_lalt_lwin = +altwin(swap_lalt_lwin) + altwin:swap_alt_win = +altwin(swap_alt_win) + altwin:prtsc_rwin = +altwin(prtsc_rwin) + grab:debug = +srvr_ctrl(grab_debug) + grp:switch = +group(switch) + grp:lswitch = +group(lswitch) + grp:win_switch = +group(win_switch) + grp:lwin_switch = +group(lwin_switch) + grp:rwin_switch = +group(rwin_switch) + grp:menu_switch = +group(menu_switch) + grp:toggle = +group(toggle) + grp:shifts_toggle = +group(shifts_toggle) + grp:ctrls_toggle = +group(ctrls_toggle) + grp:alts_toggle = +group(alts_toggle) + grp:caps_toggle = +capslock(grouplock) + grp:caps_switch = +capslock(groupshift) + grp:shift_caps_toggle = +group(shift_caps_toggle) + grp:shift_caps_switch = +group(shift_caps_switch) + grp:win_space_toggle = +group(win_space_toggle) + grp:win_menu_switch = +group(win_menu_switch) + grp:alt_caps_toggle = +group(alt_caps_toggle) + grp:alt_space_toggle = +group(alt_space_toggle) + grp:menu_toggle = +group(menu_toggle) + grp:lwin_toggle = +group(lwin_toggle) + grp:rwin_toggle = +group(rwin_toggle) + grp:lshift_toggle = +group(lshift_toggle) + grp:rshift_toggle = +group(rshift_toggle) + grp:rctrl_switch = +group(rctrl_switch) + grp:lctrl_toggle = +group(lctrl_toggle) + grp:rctrl_toggle = +group(rctrl_toggle) + grp:lalt_toggle = +group(lalt_toggle) + grp:sclk_toggle = +group(sclk_toggle) + grp:lctrl_rctrl_switch = +group(lctrl_rctrl_switch) + grp:lctrl_lwin_rctrl_menu = +group(lctrl_lwin_rctrl_menu) + grp:lctrl_lalt_toggle = +group(lctrl_lalt_toggle) + grp:rctrl_ralt_toggle = +group(rctrl_ralt_toggle) + grp:ctrl_alt_toggle = +group(ctrl_alt_toggle) + grp:ctrl_alt_toggle_bidir = +group(ctrl_alt_toggle_bidir) + grp:lctrl_lshift_toggle = +group(lctrl_lshift_toggle) + grp:rctrl_rshift_toggle = +group(rctrl_rshift_toggle) + grp:ctrl_shift_toggle = +group(ctrl_shift_toggle) + grp:ctrl_shift_toggle_bidir = +group(ctrl_shift_toggle_bidir) + grp:lalt_lshift_toggle = +group(lalt_lshift_toggle) + grp:ralt_rshift_toggle = +group(ralt_rshift_toggle) + grp:alt_shift_toggle = +group(alt_shift_toggle) + grp:alt_shift_toggle_bidir = +group(alt_shift_toggle_bidir) + grp:lctrl_lwin_toggle = +group(lctrl_lwin_toggle) + grp:menu_latch_group2 = +group(menu_latch_group2) + grp:menu_latch_group2_lock = +group(menu_latch_group2_lock) + grp:menu_latch = +group(menu_latch) + grp:menu_latch_lock = +group(menu_latch_lock) + grp:menu_latch_negative = +group(menu_latch_negative) + grp:menu_latch_negative_lock = +group(menu_latch_negative_lock) + lv3:switch = +level3(switch) + lv3:ralt_switch = +level3(ralt_switch) + lv3:ralt_switch_multikey = +level3(ralt_switch_multikey) + lv3:ralt_alt = +level3(ralt_alt) + lv3:lalt_switch = +level3(lalt_switch) + lv3:alt_switch = +level3(alt_switch) + lv3:menu_switch = +level3(menu_switch) + lv3:win_switch = +level3(win_switch) + lv3:lwin_switch = +level3(lwin_switch) + lv3:rwin_switch = +level3(rwin_switch) + lv3:enter_switch = +level3(enter_switch) + lv3:4_switch_isolated = +level3(4_switch_isolated) + lv3:9_switch_isolated = +level3(9_switch_isolated) + caps:capslock = +capslock(capslock) + caps:numlock = +capslock(numlock) + caps:shiftlock = +capslock(shiftlock) + caps:swapescape = +capslock(swapescape) + caps:escape = +capslock(escape) + caps:escape_shifted_capslock = +capslock(escape_shifted_capslock) + caps:backspace = +capslock(backspace) + caps:super = +capslock(super) + caps:hyper = +capslock(hyper) + caps:menu = +capslock(menu) + caps:none = +capslock(none) + caps:ctrl_modifier = +capslock(ctrl_modifier) + ctrl:nocaps = +ctrl(nocaps) + ctrl:lctrl_meta = +ctrl(lctrl_meta) + ctrl:swapcaps = +ctrl(swapcaps) + ctrl:swapcaps_hyper = +ctrl(swapcaps_hyper) + ctrl:swapcaps_and_switch_layout = +ctrl(swapcaps_and_switch_layout) + ctrl:ac_ctrl = +ctrl(ac_ctrl) + ctrl:aa_ctrl = +ctrl(aa_ctrl) + ctrl:rctrl_ralt = +ctrl(rctrl_ralt) + ctrl:menu_rctrl = +ctrl(menu_rctrl) + ctrl:ralt_rctrl = +ctrl(ralt_rctrl) + ctrl:swap_lalt_lctl = +ctrl(swap_lalt_lctl) + ctrl:swap_lwin_lctl = +ctrl(swap_lwin_lctl) + ctrl:swap_rwin_rctl = +ctrl(swap_rwin_rctl) + ctrl:swap_lalt_lctl_lwin = +ctrl(swap_lalt_lctl_lwin) + compose:ralt = +compose(ralt) + compose:lwin = +compose(lwin) + compose:lwin-altgr = +compose(lwin-altgr) + compose:rwin = +compose(rwin) + compose:rwin-altgr = +compose(rwin-altgr) + compose:menu = +compose(menu) + compose:menu-altgr = +compose(menu-altgr) + compose:lctrl = +compose(lctrl) + compose:lctrl-altgr = +compose(lctrl-altgr) + compose:rctrl = +compose(rctrl) + compose:rctrl-altgr = +compose(rctrl-altgr) + compose:caps = +compose(caps) + compose:caps-altgr = +compose(caps-altgr) + compose:102 = +compose(102) + compose:102-altgr = +compose(102-altgr) + compose:paus = +compose(paus) + compose:prsc = +compose(prsc) + compose:sclk = +compose(sclk) + srvrkeys:none = +srvr_ctrl(no_srvr_keys) + eurosign:e = +eurosign(e) + eurosign:2 = +eurosign(2) + eurosign:4 = +eurosign(4) + eurosign:5 = +eurosign(5) + rupeesign:4 = +rupeesign(4) + keypad:oss = +keypad(oss) + keypad:legacy = +keypad(legacy) + keypad:legacy_wang = +keypad(legacy_wang) + keypad:oss_wang = +keypad(oss_wang) + keypad:future = +keypad(future) + keypad:future_wang = +keypad(future_wang) + keypad:hex = +keypad(ops)+keypad(hex) + keypad:atm = +keypad(ops)+keypad(hex)+keypad(atm) + nbsp:none = +nbsp(none) + nbsp:level2 = +nbsp(level2) + nbsp:level3 = +nbsp(level3) + nbsp:level3s = +nbsp(level3s) + nbsp:level3n = +nbsp(level3n) + nbsp:level4 = +nbsp(level4) + nbsp:level4n = +nbsp(level4n) + nbsp:level4nl = +nbsp(level4nl) + nbsp:zwnj2 = +nbsp(zwnj2) + nbsp:zwnj2zwj3 = +nbsp(zwnj2zwj3) + nbsp:zwnj2zwj3nb4 = +nbsp(zwnj2zwj3nb4) + nbsp:zwnj2nb3 = +nbsp(zwnj2nb3) + nbsp:zwnj2nb3s = +nbsp(zwnj2nb3s) + nbsp:zwnj2nb3zwj4 = +nbsp(zwnj2nb3zwj4) + nbsp:zwnj2nb3nnb4 = +nbsp(zwnj2nb3nnb4) + nbsp:zwnj3zwj4 = +nbsp(zwnj3zwj4) + japan:nicola_f_bs = +jp(nicola_f_bs) + japan:hztg_escape = +jp(hztg_escape) + korean:ralt_hangul = +kr(ralt_hangul) + korean:rctrl_hangul = +kr(rctrl_hangul) + korean:ralt_hanja = +kr(ralt_hanja) + korean:rctrl_hanja = +kr(rctrl_hanja) + kpdl:dot = +kpdl(dot) + kpdl:comma = +kpdl(comma) + kpdl:dotoss = +kpdl(dotoss) + kpdl:dotoss_latin9 = +kpdl(dotoss_latin9) + kpdl:commaoss = +kpdl(commaoss) + kpdl:momayyezoss = +kpdl(momayyezoss) + kpdl:kposs = +kpdl(kposs) + kpdl:semi = +kpdl(semi) + shift:breaks_caps = +shift(breaks_caps) + esperanto:qwerty = +epo(qwerty) + esperanto:dvorak = +epo(dvorak) + esperanto:colemak = +epo(colemak) + terminate:ctrl_alt_bksp = +terminate(ctrl_alt_bksp) + keypad:pointerkeys = +keypad(pointerkeys) + apple:alupckeys = +macintosh_vndr/apple(alupckeys) + shift:both_capslock = +shift(both_capslock) + shift:lshift_both_capslock = +shift(lshift_both_capslock) + shift:rshift_both_capslock = +shift(rshift_both_capslock) + shift:both_capslock_cancel = +shift(both_capslock_cancel) + shift:lshift_both_capslock_cancel = +shift(lshift_both_capslock_cancel) + shift:rshift_both_capslock_cancel = +shift(rshift_both_capslock_cancel) + shift:both_shiftlock = +shift(both_shiftlock) + shift:lshift_both_shiftlock = +shift(lshift_both_shiftlock) + shift:rshift_both_shiftlock = +shift(rshift_both_shiftlock) + solaris:sun_compat = +sun_vndr/solaris(sun_compat) + lv3:caps_switch = +level3(caps_switch) + lv3:bksl_switch = +level3(bksl_switch) + lv3:lsgt_switch = +level3(lsgt_switch) + lv3:caps_switch_latch = +level3(caps_switch_latch) + lv3:bksl_switch_latch = +level3(bksl_switch_latch) + lv3:lsgt_switch_latch = +level3(lsgt_switch_latch) + lv5:lsgt_switch = +level5(lsgt_switch) + lv5:ralt_switch = +level5(ralt_switch) + lv5:lsgt_switch_lock = +level5(lsgt_switch_lock) + lv5:ralt_switch_lock = +level5(ralt_switch_lock) + lv5:lwin_switch_lock = +level5(lwin_switch_lock) + lv5:rwin_switch_lock = +level5(rwin_switch_lock) + lv5:lsgt_switch_lock_cancel = +level5(lsgt_switch_lock_cancel) + lv5:ralt_switch_lock_cancel = +level5(ralt_switch_lock_cancel) + lv5:lwin_switch_lock_cancel = +level5(lwin_switch_lock_cancel) + lv5:rwin_switch_lock_cancel = +level5(rwin_switch_lock_cancel) + parens:swap_brackets = +parens(swap_brackets) + + +! option = compat + grp_led:num = +lednum(group_lock) + grp_led:caps = +ledcaps(group_lock) + grp_led:scroll = +ledscroll(group_lock) + mod_led:compose = +ledcompose(compose) + japan:kana_lock = +japan(kana_lock) + caps:shiftlock = +ledcaps(shift_lock) + grab:break_actions = +xfree86(grab_break) + + +! option = types + caps:internal = +caps(internal) + caps:internal_nocancel = +caps(internal_nocancel) + caps:shift = +caps(shift) + caps:shift_nocancel = +caps(shift_nocancel) + numpad:pc = +numpad(pc) + numpad:mac = +numpad(mac) + numpad:microsoft = +numpad(microsoft) + numpad:shift3 = +numpad(shift3) diff --git a/test/data/rules/special_indexes b/test/data/rules/special_indexes new file mode 100644 index 00000000..ccb69d78 --- /dev/null +++ b/test/data/rules/special_indexes @@ -0,0 +1,70 @@ +! model = keycodes + my_model = my_keycodes + * = default_keycodes + +! layout[single] variant = symbols // valid + layout_a my_variant = symbols_a+extra_variant + +! layout[single] = symbols + layout_a = symbols_A + +! layout = symbols + layout_b = symbols_B + layout_c = symbols_C:%i // valid, but unusual + layout_d = symbols_D + layout_e = symbols_E + * = %l[%i]%(v[%i]) // valid, but unusual + +! layout[first] = symbols + layout_a = symbols_a:1 + layout_b = symbols_b:1 + layout_c = symbols_c:1 + layout_d = symbols_d:%i // valid, but unusual + layout_e = symbols_e:1 + * = %l[%i]%(v[%i]) // valid, cannot be easily expressed otherwise + +! layout[first] = symbols + layout_e = %+l // different output if single or multiple layouts + +! layout[later] = symbols + layout_a = +symbols_x:%i + layout_b = +symbols_y:%i + * = +%l[%i]%(v[%i]):%i + +! layout[any] = symbols + layout_c = +symbols_z:%i + +! layout[any] variant[any] = symbols + * extra = +foo:%i|bar:%i + +! layout[1] variant = symbols // invalid mapping + * * = +symbols_AAA:%i + +! layout variant[1] = symbols // invalid mapping + * * = +symbols_BBB:%i + +! layout[1] variant[2] = symbols // invalid mapping + * * = +symbols_CCC:%i + +! layout[any] variant = symbols // invalid mapping + * * = +symbols_DDD:%i + +! layout variant[any] = symbols // invalid mapping + * * = +symbols_EEE:%i + +! layout[any] variant[1] = symbols // invalid mapping + * * = +symbols_FFF:%i + +! layout[any] variant[first] = symbols // invalid mapping + * * = +symbols_GGG:%i + +! model = types + my_model = my_types + * = default_types + +! model = compat + my_model = my_compat + * = default_compat + +! option = symbols + my_option = +extra_option diff --git a/test/data/rules/special_indexes-limit b/test/data/rules/special_indexes-limit new file mode 100644 index 00000000..2e85d2dc --- /dev/null +++ b/test/data/rules/special_indexes-limit @@ -0,0 +1,16 @@ +! model = keycodes + * = default_keycodes + +! layout[first] = symbols + * = x:1 + +! layout[later] = symbols + * = +x:%i + +! model = types + * = default_types + +! model = compat + * = default_compat + +! option = symbols diff --git a/test/log.c b/test/log.c index e23ec34d..fdcf9608 100644 --- a/test/log.c +++ b/test/log.c @@ -109,9 +109,9 @@ main(void) log_err(ctx, XKB_LOG_MESSAGE_NO_ID, "third error: %lu\n", 115415UL); log_vrb(ctx, 0, XKB_LOG_MESSAGE_NO_ID, "third verbose 0\n"); - printf("%s", log_string.item); + printf("%s", darray_items(log_string)); - assert(streq(log_string.item, + assert(streq(darray_items(log_string), "warning: first warning: 87\n" "error: first error: 115415\n" "warning: first verbose 5\n" diff --git a/test/rules-file.c b/test/rules-file.c index e17d1dc4..3968db97 100644 --- a/test/rules-file.c +++ b/test/rules-file.c @@ -73,10 +73,10 @@ test_rules(struct xkb_context *ctx, struct test_data *data) fprintf(stderr, "Received : %s\t%s\t%s\t%s\n", kccgst.keycodes, kccgst.types, kccgst.compat, kccgst.symbols); - passed = streq(kccgst.keycodes, data->keycodes) && - streq(kccgst.types, data->types) && - streq(kccgst.compat, data->compat) && - streq(kccgst.symbols, data->symbols); + passed = streq_not_null(kccgst.keycodes, data->keycodes) && + streq_not_null(kccgst.types, data->types) && + streq_not_null(kccgst.compat, data->compat) && + streq_not_null(kccgst.symbols, data->symbols); free(kccgst.keycodes); free(kccgst.types); @@ -251,6 +251,115 @@ main(int argc, char *argv[]) assert(test_rules(ctx, &wildcard_data[k])); } + /* Prepare data with too much layouts */ + char too_much_layouts[(2 + MAX_LAYOUT_INDEX_STR_LENGTH) * (XKB_MAX_GROUPS + 1)] = { 0 }; + char too_much_symbols[(3 + MAX_LAYOUT_INDEX_STR_LENGTH) * XKB_MAX_GROUPS] = { 0 }; + size_t i = 0; + for (xkb_layout_index_t l = 0; l <= XKB_MAX_GROUPS; l++) { + i += snprintf(&too_much_layouts[i], sizeof(too_much_layouts) - i, + "x%"PRIu32",", l + 1); + } + too_much_layouts[--i] = '\0'; + i = 0; + for (xkb_layout_index_t l = 0; l < XKB_MAX_GROUPS; l++) { + i += snprintf(&too_much_symbols[i], sizeof(too_much_symbols) - i, + "x:%"PRIu32"+", l + 1); + } + too_much_symbols[--i] = '\0'; + +#define ENTRY2(_rules, _model, _layout, _variant, _options, \ + _keycodes, _types, _compat, _symbols, _should_fail) \ + { .rules = _rules, .model = _model, \ + .layout = _layout, .variant = _variant, .options = _options, \ + .keycodes = _keycodes, \ + .types = _types, \ + .compat = _compat, \ + .symbols = _symbols , .should_fail = _should_fail } +#define ENTRY(layout, variant, options, symbols, should_fail) \ + ENTRY2("special_indexes", NULL, layout, variant, options, \ + "default_keycodes", "default_types", "default_compat", symbols, \ + should_fail) + struct test_data special_indexes_first_data[] = { + /* Test index ranges: layout vs layout[first] */ + ENTRY("layout_a", NULL, NULL, "symbols_A", false), + ENTRY("layout_e", NULL, NULL, "symbols_E+layout_e", false), + ENTRY("a", NULL, NULL, "a", false), + ENTRY("a", "1", NULL, "a(1)", false), + /* Test index ranges: invalid layout qualifier */ + ENTRY("layout_c", NULL, NULL, "symbols_C:1+symbols_z:1", false), + /* Test index ranges: invalid layout[first] qualifier */ + ENTRY("layout_d", NULL, NULL, "symbols_D", false), + /* Test index ranges: multiple layouts */ + ENTRY("a,b", NULL, NULL, "a+b:2", false), + ENTRY("a,b", ",c", NULL, "a+b(c):2", false), + ENTRY("layout_e,layout_a", NULL, NULL, "symbols_e:1+symbols_x:2", false), + ENTRY("layout_a,layout_b,layout_c,layout_d", NULL, NULL, + "symbols_a:1+symbols_y:2+layout_c:3+layout_d:4+symbols_z:3", false), + ENTRY("layout_a,layout_b,layout_c,layout_d", + "extra,,,extra", NULL, + "symbols_a:1+symbols_y:2+layout_c:3+layout_d(extra):4+symbols_z:3" + "+foo:1|bar:1+foo:4|bar:4", false), + /* NOTE: 5 layouts is intentional; + * will require update when raising XKB_MAX_LAYOUTS */ + ENTRY("layout_a,layout_b,layout_c,layout_d,layout_e", NULL, NULL, + "symbols_a:1+symbols_y:2+layout_c:3+layout_d:4+symbols_z:3", false), +#undef ENTRY + /* Test index ranges: too much layouts */ + ENTRY2("special_indexes-limit", NULL, too_much_layouts, NULL, NULL, + "default_keycodes", "default_types", "default_compat", too_much_symbols, false), +#define ENTRY(model, layout, variant, options, compat, symbols, should_fail) \ + ENTRY2("evdev-modern", model, layout, variant, options, \ + "evdev+aliases(qwerty)", "complete", compat, symbols, should_fail) + /* evdev-modern: 1 layout */ + ENTRY("whatever", "ar", NULL, NULL, "complete", "pc+ara+inet(evdev)", false), + ENTRY("whatever", "ben", "probhat", NULL, "complete", "pc+in(ben_probhat)+inet(evdev)", false), + ENTRY("ataritt", "es", NULL, NULL, "complete", "xfree68_vndr/ataritt(us)+es+inet(evdev)", false), + ENTRY("ataritt", "jp", NULL, NULL, "complete+japan", "xfree68_vndr/ataritt(us)+jp+inet(evdev)", false), + ENTRY2("evdev-modern", "olpc", "us", NULL, NULL, + "evdev+olpc(olpc)+aliases(qwerty)", "complete", "olpc", "olpc+us(olpc)+inet(evdev)", false), + ENTRY2("evdev-modern", "olpc", "jp", NULL, NULL, + "evdev+olpc(olpc)+aliases(qwerty)", "complete", "complete+japan", "olpc+jp+inet(evdev)", false), + ENTRY("pc104", "jp", NULL, NULL, "complete+japan", "pc+jp+inet(evdev)", false), + ENTRY("pc104", "jp", "xxx", NULL, "complete+japan", "pc+jp(xxx)+inet(evdev)", false), + ENTRY("pc104", "es", NULL, NULL, "complete", "pc+es+inet(evdev)", false), + ENTRY("pc104", "es", "xxx", NULL, "complete", "pc+es(xxx)+inet(evdev)", false), + ENTRY2("evdev-modern", "pc104", "de", "neo", NULL, + "evdev+aliases(qwertz)", "complete", + "complete+caps(caps_lock):1+misc(assign_shift_left_action):1+level5(level5_lock):1", + "pc+de(neo)+inet(evdev)", false), + ENTRY("pc104", "br", NULL, "misc:typo,misc:apl", "complete", + "pc+br+inet(evdev)+typo(base):1+apl(level3):1", false), + /* evdev-modern: 2 layouts */ + ENTRY("whatever", "ar,pt", NULL, NULL, "complete", "pc+ara+pt:2+inet(evdev)", false), + ENTRY("whatever", "pt,ar", NULL, NULL, "complete", "pc+pt+ara:2+inet(evdev)", false), + ENTRY("whatever", "ben,gb", "probhat,", NULL, "complete", + "pc+in(ben_probhat)+gb:2+inet(evdev)", false), + ENTRY("whatever", "gb,ben", ",probhat", NULL, "complete", + "pc+gb+in(ben):2+in(ben_probhat):2+inet(evdev)", false), + ENTRY("whatever", "ben,ar", "probhat,", NULL, "complete", + "pc+in(ben_probhat)+ara:2+inet(evdev)", false), + ENTRY("ataritt", "jp,es", NULL, NULL, "complete", "pc+jp+es:2+inet(evdev)", false), + ENTRY("ataritt", "es,jp", NULL, NULL, "complete", "pc+es+jp:2+inet(evdev)", false), + ENTRY2("evdev-modern", "olpc", "jp,es", NULL, NULL, + "evdev+olpc(olpc)+aliases(qwerty)", "complete", "complete", "pc+jp+es:2+inet(evdev)", false), + ENTRY2("evdev-modern", "olpc", "es,jp", NULL, NULL, + "evdev+olpc(olpc)+aliases(qwerty)", "complete", "complete", "pc+es+jp:2+inet(evdev)", false), + ENTRY("pc104", "jp,es", NULL, NULL, "complete", "pc+jp+es:2+inet(evdev)", false), + ENTRY("pc104", "jp,es", "xxx,yyy", NULL, "complete", "pc+jp(xxx)+es(yyy):2+inet(evdev)", false), + ENTRY("pc104", "latin,jp", NULL, NULL, "complete", "pc+latin+jp:2+inet(evdev)", false), + ENTRY("pc104", "latin,jp", "xxx,yyy", NULL, "complete", "pc+latin(xxx)+jp(yyy):2+inet(evdev)", false), + ENTRY2("evdev-modern", "pc104", "gb,de", ",neo", NULL, + "evdev+aliases(qwerty)", "complete", + "complete+caps(caps_lock):2+misc(assign_shift_left_action):2+level5(level5_lock):2", + "pc+gb+de(neo):2+inet(evdev)", false), + ENTRY("pc104", "ca,br", NULL, "misc:typo,misc:apl", "complete", + "pc+ca+br:2+inet(evdev)+typo(base):1+typo(base):2+apl(level3):1+apl(level3):2", false), +#undef ENTRY + }; + for (size_t k = 0; k < ARRAY_SIZE(special_indexes_first_data); k++) { + assert(test_rules(ctx, &special_indexes_first_data[k])); + } + xkb_context_unref(ctx); return 0; }