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 ********************************************************/
33 #include <sys/types.h>
35 #include "xkbcomp-priv.h"
40 input_line_get(struct xkb_context *ctx, const char *buf, const char *end,
48 while (buf < end && darray_empty(*line)) {
49 space_pending = slash_pending = in_comment = false;
51 while (buf < end && (ch = *buf++) != '\n') {
70 slash_pending = false;
81 darray_append(*line, ' ');
82 space_pending = false;
85 darray_append(*line, '/');
86 slash_pending = false;
90 while (buf < end && isspace(ch) && ch != '\n')
96 if (ch != '\n' && !darray_empty(*line))
103 darray_append(*line, ' ');
104 space_pending = false;
108 if (!darray_empty(*line)) {
110 "The '!' is legal only at start of line; "
111 "Line containing '!' ignored\n");
112 darray_resize(*line, 0);
117 darray_append(*line, ch);
122 if (darray_empty(*line) && buf >= end)
125 darray_append(*line, '\0');
129 /***====================================================================***/
132 /* "Parts" - the MLVO which rules file maps to components. */
139 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
148 #define COMPONENT_MASK \
149 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
155 static const char *cname[] = {
158 [VARIANT] = "variant",
161 [KEYCODES] = "keycodes",
162 [SYMBOLS] = "symbols",
165 [GEOMETRY] = "geometry",
170 const char *layout[XkbNumKbdGroups + 1];
171 const char *variant[XkbNumKbdGroups + 1];
176 /* Sequential id for the mappings. */
198 RULE_FLAG_PENDING_MATCH = (1L << 1),
199 RULE_FLAG_OPTION = (1L << 2),
200 RULE_FLAG_APPEND = (1L << 3),
201 RULE_FLAG_NORMAL = (1L << 4),
224 darray(struct rule) rules;
225 darray(struct group) groups;
228 /***====================================================================***/
231 * Resolve numeric index, such as "[4]" in layout[4]. Missing index
235 get_index(char *str, int *ndx)
237 int empty = 0, consumed = 0, num;
239 sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
244 else if (empty > 0) {
255 * Match a mapping line which opens a rule, e.g:
256 * ! model layout[4] variant[4] = symbols geometry
257 * Which will be followed by lines such as:
258 * * ben basic = +in(ben):4 nec(pc98)
259 * So if the MLVO matches the LHS of some line, we'll get the components
261 * In this example, we will get for the second and fourth columns:
262 * mapping->map[1] = {.word = LAYOUT, .index = 4}
263 * mapping->map[3] = {.word = SYMBOLS, .index = 0}
266 match_mapping_line(struct xkb_context *ctx, darray_char *line,
267 struct mapping *mapping)
270 char *str = darray_mem(*line, 1);
271 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
279 * Remember the last sequential mapping id (incremented if the match
282 tmp = mapping->number;
283 memset(mapping, 0, sizeof(*mapping));
284 mapping->number = tmp;
286 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
293 for (i = 0; i < MAX_WORDS; i++) {
294 len = strlen(cname[i]);
296 if (strncmp(cname[i], tok, len) == 0) {
297 if (strlen(tok) > len) {
298 char *end = get_index(tok + len, &ndx);
300 if ((i != LAYOUT && i != VARIANT) ||
301 *end != '\0' || ndx == -1) {
303 "Illegal %s index: %d\n", cname[i], ndx);
304 log_warn(ctx, "Can only index layout and variant\n");
308 if (ndx < 1 || ndx > XkbNumKbdGroups) {
309 log_warn(ctx, "Illegal %s index: %d\n",
311 log_warn(ctx, "Index must be in range 1..%d\n",
322 if (present & (1 << i)) {
323 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
324 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
326 "Component \"%s\" listed twice; "
327 "Second definition ignored\n", tok);
334 layout_ndx_present |= 1 << ndx;
336 variant_ndx_present |= 1 << ndx;
338 mapping->map[mapping->num_maps].word = i;
339 mapping->map[mapping->num_maps].index = ndx;
346 log_warn(ctx, "Unknown component \"%s\"; Ignored\n", tok);
349 if ((present & PART_MASK) == 0) {
351 "Mapping needs at least one MLVO part; "
352 "Illegal mapping ignored\n");
353 mapping->num_maps = 0;
357 if ((present & COMPONENT_MASK) == 0) {
359 "Mapping needs at least one component; "
360 "Illegal mapping ignored\n");
361 mapping->num_maps = 0;
369 * Match a line such as:
370 * ! $pcmodels = pc101 pc102 pc104 pc105
373 match_group_line(darray_char *line, struct group *group)
376 char *name = strchr(darray_mem(*line, 0), '$');
377 char *words = strchr(name, ' ');
384 for (; *words; words++) {
385 if (*words != '=' && *words != ' ')
392 group->name = strdup(name);
393 group->words = strdup(words);
395 words = group->words;
396 for (i = 1; *words; words++) {
408 /* Match lines following a mapping (see match_mapping_line comment). */
410 match_rule_line(struct xkb_context *ctx, darray_char *line,
411 struct mapping *mapping, struct rule *rule)
417 const char *names[MAX_WORDS] = { NULL };
419 if (mapping->num_maps == 0) {
421 "Must have a mapping before first line of data; "
422 "Illegal line of data ignored\n");
426 str = darray_mem(*line, 0);
428 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL;
432 if (streq(tok, "=")) {
437 if (nread > mapping->num_maps) {
439 "Too many words on a line; "
440 "Extra word \"%s\" ignored\n", tok);
444 names[mapping->map[nread].word] = tok;
445 if (*tok == '+' || *tok == '|')
449 if (nread < mapping->num_maps) {
450 log_warn(ctx, "Too few words on a line: %s; Line ignored\n",
451 darray_mem(*line, 0));
456 rule->number = mapping->number;
459 rule->flags |= RULE_FLAG_OPTION;
461 rule->flags |= RULE_FLAG_APPEND;
463 rule->flags |= RULE_FLAG_NORMAL;
465 rule->model = strdup_safe(names[MODEL]);
466 rule->layout = strdup_safe(names[LAYOUT]);
467 rule->variant = strdup_safe(names[VARIANT]);
468 rule->option = strdup_safe(names[OPTION]);
470 rule->keycodes = strdup_safe(names[KEYCODES]);
471 rule->symbols = strdup_safe(names[SYMBOLS]);
472 rule->types = strdup_safe(names[TYPES]);
473 rule->compat = strdup_safe(names[COMPAT]);
475 rule->layout_num = rule->variant_num = 0;
476 for (i = 0; i < nread; i++) {
477 if (mapping->map[i].index) {
478 if (mapping->map[i].word == LAYOUT)
479 rule->layout_num = mapping->map[i].index;
480 if (mapping->map[i].word == VARIANT)
481 rule->variant_num = mapping->map[i].index;
489 match_line(struct xkb_context *ctx, darray_char *line,
490 struct mapping *mapping, struct rule *rule,
493 if (darray_item(*line, 0) != '!')
494 return match_rule_line(ctx, line, mapping, rule);
496 if (darray_item(*line, 1) == '$' ||
497 (darray_item(*line, 1) == ' ' && darray_item(*line, 2) == '$'))
498 return match_group_line(line, group);
500 match_mapping_line(ctx, line, mapping);
505 squeeze_spaces(char *p1)
509 for (p2 = p1; *p2; p2++) {
519 * Expand the layout and variant of the rule_names and remove extraneous
520 * spaces. If there's one layout/variant, it is kept in
521 * .layout[0]/.variant[0], else is kept in [1], [2] and so on, and [0]
522 * remains empty. For example, this rule_names:
524 * .layout = "us,il,ru,ca"
525 * .variant = ",,,multix"
526 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
527 * Is expanded into this multi_defs:
529 * .layout = {NULL, "us", "il", "ru", "ca"},
530 * .variant = {NULL, "", "", "", "multix"},
531 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
534 make_multi_defs(struct multi_defs *mdefs, const struct xkb_rule_names *mlvo)
539 memset(mdefs, 0, sizeof(*mdefs));
542 mdefs->model = mlvo->model;
546 mdefs->options = strdup(mlvo->options);
547 if (mdefs->options == NULL)
550 squeeze_spaces(mdefs->options);
554 if (!strchr(mlvo->layout, ',')) {
555 mdefs->layout[0] = mlvo->layout;
558 p = strdup(mlvo->layout);
563 mdefs->layout[1] = p;
565 for (i = 2; i <= XkbNumKbdGroups; i++) {
566 if ((p = strchr(p, ','))) {
568 mdefs->layout[i] = p;
575 if (p && (p = strchr(p, ',')))
581 if (!strchr(mlvo->variant, ',')) {
582 mdefs->variant[0] = mlvo->variant;
585 p = strdup(mlvo->variant);
590 mdefs->variant[1] = p;
592 for (i = 2; i <= XkbNumKbdGroups; i++) {
593 if ((p = strchr(p, ','))) {
595 mdefs->variant[i] = p;
602 if (p && (p = strchr(p, ',')))
611 free_multi_defs(struct multi_defs *defs)
615 * See make_multi_defs comment for the hack; the same strdup'd
616 * string is split among the indexes, but the one in [0] is const.
618 free(UNCONSTIFY(defs->layout[1]));
619 free(UNCONSTIFY(defs->variant[1]));
622 /* See apply_rule below. */
624 apply(const char *src, char **dst)
632 if (*src == '+' || *src == '!') {
634 ret = asprintf(dst, "%s%s", *dst, src);
639 else if (*dst == NULL) {
645 * Add the info from the matching rule to the resulting
646 * xkb_component_names. If we already had a match for something
647 * (e.g. keycodes), and the rule is not an appending one (e.g.
648 * +whatever), than we don't override but drop the new one.
651 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
653 /* Clear the flag because it's applied. */
654 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
656 apply(rule->keycodes, &kccgst->keycodes);
657 apply(rule->symbols, &kccgst->symbols);
658 apply(rule->types, &kccgst->types);
659 apply(rule->compat, &kccgst->compat);
663 * Match if name is part of the group, e.g. if the following
665 * ! $qwertz = al cz de hr hu ro si sk
667 * match_group_member(rules, "qwertz", "hr")
671 match_group_member(struct rules *rules, const char *group_name,
676 struct group *iter, *group = NULL;
678 darray_foreach(iter, rules->groups) {
679 if (streq(iter->name, group_name)) {
689 for (i = 0; i < group->number; i++, word += strlen(word) + 1)
690 if (streq(word, name))
696 /* Match @needle out of @sep-seperated @haystack. */
698 match_one_of(const char *haystack, const char *needle, char sep)
700 const char *s = haystack;
701 const size_t len = strlen(needle);
704 if (strncmp(s, needle, len) == 0 && (s[len] == '\0' || s[len] == sep))
713 apply_rule_if_matches(struct rules *rules, struct rule *rule,
714 struct multi_defs *mdefs,
715 struct xkb_component_names *kccgst)
717 bool pending = false;
720 if (mdefs->model == NULL)
723 if (streq(rule->model, "*")) {
726 else if (rule->model[0] == '$') {
727 if (!match_group_member(rules, rule->model, mdefs->model))
730 else if (!streq(rule->model, mdefs->model)) {
736 if (mdefs->options == NULL)
739 if (!match_one_of(mdefs->options, rule->option, ','))
744 if (mdefs->layout[rule->layout_num] == NULL)
747 if (streq(rule->layout, "*")) {
750 else if (rule->layout[0] == '$') {
751 if (!match_group_member(rules, rule->layout,
752 mdefs->layout[rule->layout_num]))
755 else if (!streq(rule->layout, mdefs->layout[rule->layout_num])) {
761 if (mdefs->variant[rule->variant_num] == NULL)
764 if (streq(rule->variant, "*")) {
767 else if (rule->variant[0] == '$') {
768 if (!match_group_member(rules, rule->variant,
769 mdefs->variant[rule->variant_num]))
772 else if (!streq(rule->variant, mdefs->variant[rule->variant_num])) {
778 rule->flags |= RULE_FLAG_PENDING_MATCH;
781 /* Exact match, apply it now. */
782 apply_rule(rule, kccgst);
789 clear_partial_matches(struct rules *rules)
793 darray_foreach(rule, rules->rules)
794 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
798 apply_partial_matches(struct rules *rules, struct xkb_component_names *kccgst)
802 darray_foreach(rule, rules->rules)
803 if (rule->flags & RULE_FLAG_PENDING_MATCH)
804 apply_rule(rule, kccgst);
808 apply_matching_rules(struct rules *rules, struct multi_defs *mdefs,
809 struct xkb_component_names *kccgst, unsigned int flags)
814 darray_foreach(rule, rules->rules) {
815 if ((rule->flags & flags) != flags)
818 if ((flags & RULE_FLAG_OPTION) == 0 && rule->number == skip)
821 skip = apply_rule_if_matches(rules, rule, mdefs, kccgst);
825 /***====================================================================***/
828 substitute_vars(char *name, struct multi_defs *mdefs)
830 char *str, *outstr, *var;
832 size_t len, extra_len;
839 str = strchr(name, '%');
845 while (str != NULL) {
849 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
853 else if (pfx == '(') {
859 str = get_index(var + 1, &ndx);
861 str = strchr(str, '%');
865 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx])
866 len += strlen(mdefs->layout[ndx]) + extra_len;
867 else if (*var == 'm' && mdefs->model)
868 len += strlen(mdefs->model) + extra_len;
869 else if (*var == 'v' && mdefs->variant[ndx] && *mdefs->variant[ndx])
870 len += strlen(mdefs->variant[ndx]) + extra_len;
872 if (pfx == '(' && *str == ')')
875 str = strchr(&str[0], '%');
878 name = malloc(len + 1);
882 while (*str != '\0') {
888 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
891 else if (pfx == '(') {
900 str = get_index(var + 1, &ndx);
904 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
908 strcpy(outstr, mdefs->layout[ndx]);
909 outstr += strlen(mdefs->layout[ndx]);
914 else if (*var == 'm' && mdefs->model) {
918 strcpy(outstr, mdefs->model);
919 outstr += strlen(mdefs->model);
924 else if (*var == 'v' && mdefs->variant[ndx] &&
925 *mdefs->variant[ndx]) {
929 strcpy(outstr, mdefs->variant[ndx]);
930 outstr += strlen(mdefs->variant[ndx]);
936 if (pfx == '(' && *str == ')')
952 /***====================================================================***/
955 get_components(struct rules *rules, const struct xkb_rule_names *mlvo,
956 struct xkb_component_names *kccgst)
958 struct multi_defs mdefs;
960 memset(kccgst, 0, sizeof(*kccgst));
962 make_multi_defs(&mdefs, mlvo);
964 clear_partial_matches(rules);
966 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
967 apply_partial_matches(rules, kccgst);
969 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
970 apply_partial_matches(rules, kccgst);
972 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
973 apply_partial_matches(rules, kccgst);
975 kccgst->keycodes = substitute_vars(kccgst->keycodes, &mdefs);
976 kccgst->symbols = substitute_vars(kccgst->symbols, &mdefs);
977 kccgst->types = substitute_vars(kccgst->types, &mdefs);
978 kccgst->compat = substitute_vars(kccgst->compat, &mdefs);
980 free_multi_defs(&mdefs);
982 if (!kccgst->keycodes || !kccgst->symbols || !kccgst->types ||
984 free(kccgst->keycodes);
985 free(kccgst->symbols);
987 free(kccgst->compat);
993 static struct rules *
994 load_rules(struct xkb_context *ctx, FILE *file)
997 struct mapping mapping;
1000 struct rules *rules;
1001 struct stat stat_buf;
1002 const char *buf, *end;
1004 int fd = fileno(file);
1006 if (fstat(fd, &stat_buf) != 0) {
1007 log_err(ctx, "couldn't stat rules file\n");
1011 orig = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1013 log_err(ctx, "couldn't mmap rules file (%zu bytes)\n",
1014 (size_t) stat_buf.st_size);
1018 rules = calloc(1, sizeof(*rules));
1021 darray_init(rules->rules);
1022 darray_growalloc(rules->rules, 16);
1024 memset(&mapping, 0, sizeof(mapping));
1025 memset(&tgroup, 0, sizeof(tgroup));
1027 darray_growalloc(line, 128);
1030 end = orig + stat_buf.st_size;
1031 while ((buf = input_line_get(ctx, buf, end, &line))) {
1032 if (match_line(ctx, &line, &mapping, &trule, &tgroup)) {
1033 if (tgroup.number) {
1034 darray_append(rules->groups, tgroup);
1035 memset(&tgroup, 0, sizeof(tgroup));
1038 darray_append(rules->rules, trule);
1039 memset(&trule, 0, sizeof(trule));
1043 darray_resize(line, 0);
1046 munmap(orig, stat_buf.st_size);
1053 free_rules(struct rules *rules)
1056 struct group *group;
1061 darray_foreach(rule, rules->rules) {
1064 free(rule->variant);
1066 free(rule->keycodes);
1067 free(rule->symbols);
1071 darray_free(rules->rules);
1073 darray_foreach(group, rules->groups) {
1077 darray_free(rules->groups);
1083 xkb_components_from_rules(struct xkb_context *ctx,
1084 const struct xkb_rule_names *rmlvo,
1085 struct xkb_component_names *out)
1088 struct rules *rules;
1093 file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1095 log_err(ctx, "Could not find \"%s\" rules in XKB path\n",
1097 log_err(ctx, "%zu include paths searched:\n",
1098 darray_size(ctx->includes));
1099 darray_foreach(include, ctx->includes)
1100 log_err(ctx, "\t%s\n", *include);
1101 log_err(ctx, "%zu include paths could not be added:\n",
1102 darray_size(ctx->failed_includes));
1103 darray_foreach(include, ctx->failed_includes)
1104 log_err(ctx, "\t%s\n", *include);
1108 rules = load_rules(ctx, file);
1110 log_err(ctx, "Failed to load XKB rules \"%s\"\n", path);
1114 if (!get_components(rules, rmlvo, out)) {
1115 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);