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>
39 input_line_get(struct xkb_context *ctx, const char *buf, const char *end,
47 while (buf < end && darray_empty(*line)) {
48 space_pending = slash_pending = in_comment = false;
50 while (buf < end && (ch = *buf++) != '\n') {
69 slash_pending = false;
80 darray_append(*line, ' ');
81 space_pending = false;
84 darray_append(*line, '/');
85 slash_pending = false;
89 while (buf < end && isspace(ch) && ch != '\n')
95 if (ch != '\n' && !darray_empty(*line))
102 darray_append(*line, ' ');
103 space_pending = false;
107 if (!darray_empty(*line)) {
109 "The '!' is legal only at start of line; "
110 "Line containing '!' ignored\n");
111 darray_resize(*line, 0);
116 darray_append(*line, ch);
121 if (darray_empty(*line) && buf >= end)
124 darray_append(*line, '\0');
128 /***====================================================================***/
131 /* "Parts" - the MLVO which rules file maps to components. */
138 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
147 #define COMPONENT_MASK \
148 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
154 static const char *cname[] = {
157 [VARIANT] = "variant",
160 [KEYCODES] = "keycodes",
161 [SYMBOLS] = "symbols",
164 [GEOMETRY] = "geometry",
169 const char *layout[XkbNumKbdGroups + 1];
170 const char *variant[XkbNumKbdGroups + 1];
175 /* Sequential id for the mappings. */
197 RULE_FLAG_PENDING_MATCH = (1L << 1),
198 RULE_FLAG_OPTION = (1L << 2),
199 RULE_FLAG_APPEND = (1L << 3),
200 RULE_FLAG_NORMAL = (1L << 4),
223 darray(struct rule) rules;
224 darray(struct group) groups;
227 /***====================================================================***/
230 * Resolve numeric index, such as "[4]" in layout[4]. Missing index
234 get_index(char *str, int *ndx)
236 int empty = 0, consumed = 0, num;
238 sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
243 else if (empty > 0) {
254 * Match a mapping line which opens a rule, e.g:
255 * ! model layout[4] variant[4] = symbols geometry
256 * Which will be followed by lines such as:
257 * * ben basic = +in(ben):4 nec(pc98)
258 * So if the MLVO matches the LHS of some line, we'll get the components
260 * In this example, we will get for the second and fourth columns:
261 * mapping->map[1] = {.word = LAYOUT, .index = 4}
262 * mapping->map[3] = {.word = SYMBOLS, .index = 0}
265 match_mapping_line(struct xkb_context *ctx, darray_char *line,
266 struct mapping *mapping)
269 char *str = darray_mem(*line, 1);
270 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
278 * Remember the last sequential mapping id (incremented if the match
281 tmp = mapping->number;
282 memset(mapping, 0, sizeof(*mapping));
283 mapping->number = tmp;
285 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
292 for (i = 0; i < MAX_WORDS; i++) {
293 len = strlen(cname[i]);
295 if (strncmp(cname[i], tok, len) == 0) {
296 if (strlen(tok) > len) {
297 char *end = get_index(tok + len, &ndx);
299 if ((i != LAYOUT && i != VARIANT) ||
300 *end != '\0' || ndx == -1) {
302 "Illegal %s index: %d\n", cname[i], ndx);
303 log_warn(ctx, "Can only index layout and variant\n");
307 if (ndx < 1 || ndx > XkbNumKbdGroups) {
308 log_warn(ctx, "Illegal %s index: %d\n",
310 log_warn(ctx, "Index must be in range 1..%d\n",
321 if (present & (1 << i)) {
322 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
323 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
325 "Component \"%s\" listed twice; "
326 "Second definition ignored\n", tok);
333 layout_ndx_present |= 1 << ndx;
335 variant_ndx_present |= 1 << ndx;
337 mapping->map[mapping->num_maps].word = i;
338 mapping->map[mapping->num_maps].index = ndx;
345 log_warn(ctx, "Unknown component \"%s\"; Ignored\n", tok);
348 if ((present & PART_MASK) == 0) {
350 "Mapping needs at least one MLVO part; "
351 "Illegal mapping ignored\n");
352 mapping->num_maps = 0;
356 if ((present & COMPONENT_MASK) == 0) {
358 "Mapping needs at least one component; "
359 "Illegal mapping ignored\n");
360 mapping->num_maps = 0;
368 * Match a line such as:
369 * ! $pcmodels = pc101 pc102 pc104 pc105
372 match_group_line(darray_char *line, struct group *group)
375 char *name = strchr(darray_mem(*line, 0), '$');
376 char *words = strchr(name, ' ');
383 for (; *words; words++) {
384 if (*words != '=' && *words != ' ')
391 group->name = strdup(name);
392 group->words = strdup(words);
394 words = group->words;
395 for (i = 1; *words; words++) {
407 /* Match lines following a mapping (see match_mapping_line comment). */
409 match_rule_line(struct xkb_context *ctx, darray_char *line,
410 struct mapping *mapping, struct rule *rule)
416 const char *names[MAX_WORDS] = { NULL };
418 if (mapping->num_maps == 0) {
420 "Must have a mapping before first line of data; "
421 "Illegal line of data ignored\n");
425 str = darray_mem(*line, 0);
427 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL;
431 if (streq(tok, "=")) {
436 if (nread > mapping->num_maps) {
438 "Too many words on a line; "
439 "Extra word \"%s\" ignored\n", tok);
443 names[mapping->map[nread].word] = tok;
444 if (*tok == '+' || *tok == '|')
448 if (nread < mapping->num_maps) {
449 log_warn(ctx, "Too few words on a line: %s; Line ignored\n",
450 darray_mem(*line, 0));
455 rule->number = mapping->number;
458 rule->flags |= RULE_FLAG_OPTION;
460 rule->flags |= RULE_FLAG_APPEND;
462 rule->flags |= RULE_FLAG_NORMAL;
464 rule->model = strdup_safe(names[MODEL]);
465 rule->layout = strdup_safe(names[LAYOUT]);
466 rule->variant = strdup_safe(names[VARIANT]);
467 rule->option = strdup_safe(names[OPTION]);
469 rule->keycodes = strdup_safe(names[KEYCODES]);
470 rule->symbols = strdup_safe(names[SYMBOLS]);
471 rule->types = strdup_safe(names[TYPES]);
472 rule->compat = strdup_safe(names[COMPAT]);
474 rule->layout_num = rule->variant_num = 0;
475 for (i = 0; i < nread; i++) {
476 if (mapping->map[i].index) {
477 if (mapping->map[i].word == LAYOUT)
478 rule->layout_num = mapping->map[i].index;
479 if (mapping->map[i].word == VARIANT)
480 rule->variant_num = mapping->map[i].index;
488 match_line(struct xkb_context *ctx, darray_char *line,
489 struct mapping *mapping, struct rule *rule,
492 if (darray_item(*line, 0) != '!')
493 return match_rule_line(ctx, line, mapping, rule);
495 if (darray_item(*line, 1) == '$' ||
496 (darray_item(*line, 1) == ' ' && darray_item(*line, 2) == '$'))
497 return match_group_line(line, group);
499 match_mapping_line(ctx, line, mapping);
504 squeeze_spaces(char *p1)
508 for (p2 = p1; *p2; p2++) {
518 * Expand the layout and variant of the rule_names and remove extraneous
519 * spaces. If there's one layout/variant, it is kept in
520 * .layout[0]/.variant[0], else is kept in [1], [2] and so on, and [0]
521 * remains empty. For example, this rule_names:
523 * .layout = "us,il,ru,ca"
524 * .variant = ",,,multix"
525 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
526 * Is expanded into this multi_defs:
528 * .layout = {NULL, "us", "il", "ru", "ca"},
529 * .variant = {NULL, "", "", "", "multix"},
530 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
533 make_multi_defs(struct multi_defs *mdefs, const struct xkb_rule_names *mlvo)
538 memset(mdefs, 0, sizeof(*mdefs));
541 mdefs->model = mlvo->model;
545 mdefs->options = strdup(mlvo->options);
546 if (mdefs->options == NULL)
549 squeeze_spaces(mdefs->options);
553 if (!strchr(mlvo->layout, ',')) {
554 mdefs->layout[0] = mlvo->layout;
557 p = strdup(mlvo->layout);
562 mdefs->layout[1] = p;
564 for (i = 2; i <= XkbNumKbdGroups; i++) {
565 if ((p = strchr(p, ','))) {
567 mdefs->layout[i] = p;
574 if (p && (p = strchr(p, ',')))
580 if (!strchr(mlvo->variant, ',')) {
581 mdefs->variant[0] = mlvo->variant;
584 p = strdup(mlvo->variant);
589 mdefs->variant[1] = p;
591 for (i = 2; i <= XkbNumKbdGroups; i++) {
592 if ((p = strchr(p, ','))) {
594 mdefs->variant[i] = p;
601 if (p && (p = strchr(p, ',')))
610 free_multi_defs(struct multi_defs *defs)
614 * See make_multi_defs comment for the hack; the same strdup'd
615 * string is split among the indexes, but the one in [0] is const.
617 free(UNCONSTIFY(defs->layout[1]));
618 free(UNCONSTIFY(defs->variant[1]));
621 /* See apply_rule below. */
623 apply(const char *src, char **dst)
631 if (*src == '+' || *src == '!') {
633 ret = asprintf(dst, "%s%s", *dst, src);
638 else if (*dst == NULL) {
644 * Add the info from the matching rule to the resulting
645 * xkb_component_names. If we already had a match for something
646 * (e.g. keycodes), and the rule is not an appending one (e.g.
647 * +whatever), than we don't override but drop the new one.
650 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
652 /* Clear the flag because it's applied. */
653 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
655 apply(rule->keycodes, &kccgst->keycodes);
656 apply(rule->symbols, &kccgst->symbols);
657 apply(rule->types, &kccgst->types);
658 apply(rule->compat, &kccgst->compat);
662 * Match if name is part of the group, e.g. if the following
664 * ! $qwertz = al cz de hr hu ro si sk
666 * match_group_member(rules, "qwertz", "hr")
670 match_group_member(struct rules *rules, const char *group_name,
675 struct group *iter, *group = NULL;
677 darray_foreach(iter, rules->groups) {
678 if (streq(iter->name, group_name)) {
688 for (i = 0; i < group->number; i++, word += strlen(word) + 1)
689 if (streq(word, name))
695 /* Match @needle out of @sep-seperated @haystack. */
697 match_one_of(const char *haystack, const char *needle, char sep)
699 const char *s = haystack;
700 const size_t len = strlen(needle);
703 if (strncmp(s, needle, len) == 0 && (s[len] == '\0' || s[len] == sep))
712 apply_rule_if_matches(struct rules *rules, struct rule *rule,
713 struct multi_defs *mdefs,
714 struct xkb_component_names *kccgst)
716 bool pending = false;
719 if (mdefs->model == NULL)
722 if (streq(rule->model, "*")) {
725 else if (rule->model[0] == '$') {
726 if (!match_group_member(rules, rule->model, mdefs->model))
729 else if (!streq(rule->model, mdefs->model)) {
735 if (mdefs->options == NULL)
738 if (!match_one_of(mdefs->options, rule->option, ','))
743 if (mdefs->layout[rule->layout_num] == NULL)
746 if (streq(rule->layout, "*")) {
749 else if (rule->layout[0] == '$') {
750 if (!match_group_member(rules, rule->layout,
751 mdefs->layout[rule->layout_num]))
754 else if (!streq(rule->layout, mdefs->layout[rule->layout_num])) {
760 if (mdefs->variant[rule->variant_num] == NULL)
763 if (streq(rule->variant, "*")) {
766 else if (rule->variant[0] == '$') {
767 if (!match_group_member(rules, rule->variant,
768 mdefs->variant[rule->variant_num]))
771 else if (!streq(rule->variant, mdefs->variant[rule->variant_num])) {
777 rule->flags |= RULE_FLAG_PENDING_MATCH;
780 /* Exact match, apply it now. */
781 apply_rule(rule, kccgst);
788 clear_partial_matches(struct rules *rules)
792 darray_foreach(rule, rules->rules)
793 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
797 apply_partial_matches(struct rules *rules, struct xkb_component_names *kccgst)
801 darray_foreach(rule, rules->rules)
802 if (rule->flags & RULE_FLAG_PENDING_MATCH)
803 apply_rule(rule, kccgst);
807 apply_matching_rules(struct rules *rules, struct multi_defs *mdefs,
808 struct xkb_component_names *kccgst, unsigned int flags)
813 darray_foreach(rule, rules->rules) {
814 if ((rule->flags & flags) != flags)
817 if ((flags & RULE_FLAG_OPTION) == 0 && rule->number == skip)
820 skip = apply_rule_if_matches(rules, rule, mdefs, kccgst);
824 /***====================================================================***/
827 substitute_vars(char *name, struct multi_defs *mdefs)
829 char *str, *outstr, *var;
831 size_t len, extra_len;
838 str = strchr(name, '%');
844 while (str != NULL) {
848 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
852 else if (pfx == '(') {
858 str = get_index(var + 1, &ndx);
860 str = strchr(str, '%');
864 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx])
865 len += strlen(mdefs->layout[ndx]) + extra_len;
866 else if (*var == 'm' && mdefs->model)
867 len += strlen(mdefs->model) + extra_len;
868 else if (*var == 'v' && mdefs->variant[ndx] && *mdefs->variant[ndx])
869 len += strlen(mdefs->variant[ndx]) + extra_len;
871 if (pfx == '(' && *str == ')')
874 str = strchr(&str[0], '%');
877 name = malloc(len + 1);
881 while (*str != '\0') {
887 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
890 else if (pfx == '(') {
899 str = get_index(var + 1, &ndx);
903 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
907 strcpy(outstr, mdefs->layout[ndx]);
908 outstr += strlen(mdefs->layout[ndx]);
913 else if (*var == 'm' && mdefs->model) {
917 strcpy(outstr, mdefs->model);
918 outstr += strlen(mdefs->model);
923 else if (*var == 'v' && mdefs->variant[ndx] &&
924 *mdefs->variant[ndx]) {
928 strcpy(outstr, mdefs->variant[ndx]);
929 outstr += strlen(mdefs->variant[ndx]);
935 if (pfx == '(' && *str == ')')
951 /***====================================================================***/
954 get_components(struct rules *rules, const struct xkb_rule_names *mlvo,
955 struct xkb_component_names *kccgst)
957 struct multi_defs mdefs;
959 memset(kccgst, 0, sizeof(*kccgst));
961 make_multi_defs(&mdefs, mlvo);
963 clear_partial_matches(rules);
965 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
966 apply_partial_matches(rules, kccgst);
968 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
969 apply_partial_matches(rules, kccgst);
971 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
972 apply_partial_matches(rules, kccgst);
974 kccgst->keycodes = substitute_vars(kccgst->keycodes, &mdefs);
975 kccgst->symbols = substitute_vars(kccgst->symbols, &mdefs);
976 kccgst->types = substitute_vars(kccgst->types, &mdefs);
977 kccgst->compat = substitute_vars(kccgst->compat, &mdefs);
979 free_multi_defs(&mdefs);
981 if (!kccgst->keycodes || !kccgst->symbols || !kccgst->types ||
983 free(kccgst->keycodes);
984 free(kccgst->symbols);
986 free(kccgst->compat);
992 static struct rules *
993 load_rules(struct xkb_context *ctx, FILE *file)
996 struct mapping mapping;
1000 struct stat stat_buf;
1001 const char *buf, *end;
1003 int fd = fileno(file);
1005 if (fstat(fd, &stat_buf) != 0) {
1006 log_err(ctx, "couldn't stat rules file\n");
1010 orig = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1012 log_err(ctx, "couldn't mmap rules file (%zu bytes)\n",
1013 (size_t) stat_buf.st_size);
1017 rules = calloc(1, sizeof(*rules));
1020 darray_init(rules->rules);
1021 darray_growalloc(rules->rules, 16);
1023 memset(&mapping, 0, sizeof(mapping));
1024 memset(&tgroup, 0, sizeof(tgroup));
1026 darray_growalloc(line, 128);
1029 end = orig + stat_buf.st_size;
1030 while ((buf = input_line_get(ctx, buf, end, &line))) {
1031 if (match_line(ctx, &line, &mapping, &trule, &tgroup)) {
1032 if (tgroup.number) {
1033 darray_append(rules->groups, tgroup);
1034 memset(&tgroup, 0, sizeof(tgroup));
1037 darray_append(rules->rules, trule);
1038 memset(&trule, 0, sizeof(trule));
1042 darray_resize(line, 0);
1045 munmap(orig, stat_buf.st_size);
1052 free_rules(struct rules *rules)
1055 struct group *group;
1060 darray_foreach(rule, rules->rules) {
1063 free(rule->variant);
1065 free(rule->keycodes);
1066 free(rule->symbols);
1070 darray_free(rules->rules);
1072 darray_foreach(group, rules->groups) {
1076 darray_free(rules->groups);
1082 xkb_components_from_rules(struct xkb_context *ctx,
1083 const struct xkb_rule_names *rmlvo,
1084 struct xkb_component_names *out)
1087 struct rules *rules;
1092 file = XkbFindFileInPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1094 log_err(ctx, "Could not find \"%s\" rules in XKB path\n",
1096 log_err(ctx, "%zu include paths searched:\n",
1097 darray_size(ctx->includes));
1098 darray_foreach(include, ctx->includes)
1099 log_err(ctx, "\t%s\n", *include);
1100 log_err(ctx, "%zu include paths could not be added:\n",
1101 darray_size(ctx->failed_includes));
1102 darray_foreach(include, ctx->failed_includes)
1103 log_err(ctx, "\t%s\n", *include);
1107 rules = load_rules(ctx, file);
1109 log_err(ctx, "Failed to load XKB rules \"%s\"\n", path);
1113 if (!get_components(rules, rmlvo, out)) {
1114 log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);