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 #define DFLT_LINE_SIZE 128
38 char buf[DFLT_LINE_SIZE];
43 input_line_init(struct input_line *line)
45 line->size = DFLT_LINE_SIZE;
47 line->line = line->buf;
51 input_line_deinit(struct input_line *line)
53 if (line->line != line->buf)
56 line->size = DFLT_LINE_SIZE;
57 line->line = line->buf;
61 input_line_add_char(struct input_line *line, int ch)
63 if (line->offset >= line->size) {
64 if (line->line == line->buf) {
65 line->line = malloc(line->size * 2);
66 memcpy(line->line, line->buf, line->size);
69 line->line = realloc(line->line, line->size * 2);
75 line->line[line->offset++] = ch;
80 input_line_get(FILE *file, struct input_line *line)
83 bool end_of_file = false;
88 while (!end_of_file && line->offset == 0) {
89 space_pending = slash_pending = in_comment = false;
91 while ((ch = getc(file)) != '\n' && ch != EOF) {
110 slash_pending = false;
113 slash_pending = true;
121 input_line_add_char(line, ' ');
122 space_pending = false;
125 input_line_add_char(line, '/');
126 slash_pending = false;
130 while (isspace(ch) && ch != '\n' && ch != EOF)
136 if (ch != '\n' && line->offset > 0)
137 space_pending = true;
143 input_line_add_char(line, ' ');
144 space_pending = false;
148 if (line->offset != 0) {
149 WARN("The '!' is legal only at start of line\n");
150 ACTION("Line containing '!' ignored\n");
156 input_line_add_char(line, ch);
164 if (line->offset == 0 && end_of_file)
167 input_line_add_char(line, '\0');
171 /***====================================================================***/
174 /* "Parts" - the MLVO which rules file maps to components. */
181 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
191 #define COMPONENT_MASK \
192 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
193 (1 << GEOMETRY) | (1 << KEYMAP))
198 static const char *cname[] = {
201 [VARIANT] = "variant",
204 [KEYCODES] = "keycodes",
205 [SYMBOLS] = "symbols",
208 [GEOMETRY] = "geometry",
221 const char *layout[XkbNumKbdGroups + 1];
222 const char *variant[XkbNumKbdGroups + 1];
227 /* Sequential id for the mappings. */
249 RULE_FLAG_PENDING_MATCH = (1L << 1),
250 RULE_FLAG_OPTION = (1L << 2),
251 RULE_FLAG_APPEND = (1L << 3),
252 RULE_FLAG_NORMAL = (1L << 4),
282 struct group *groups;
285 /***====================================================================***/
288 * Resolve numeric index, such as "[4]" in layout[4]. Missing index
292 get_index(char *str, int *ndx)
294 int empty = 0, consumed = 0, num;
296 sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
300 } else if (empty > 0) {
310 * Match a mapping line which opens a rule, e.g:
311 * ! model layout[4] variant[4] = symbols geometry
312 * Which will be followed by lines such as:
313 * * ben basic = +in(ben):4 nec(pc98)
314 * So if the MLVO matches the LHS of some line, we'll get the components
316 * In this example, we will get for the second and fourth columns:
317 * mapping->map[1] = {.word = LAYOUT, .index = 4}
318 * mapping->map[3] = {.word = SYMBOLS, .index = 0}
321 match_mapping_line(struct input_line *line, struct mapping *mapping)
324 char *str = &line->line[1];
325 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
333 * Remember the last sequential mapping id (incremented if the match
336 tmp = mapping->number;
337 memset(mapping, 0, sizeof(*mapping));
338 mapping->number = tmp;
340 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
344 if (strcmp(tok, "=") == 0)
347 for (i = 0; i < MAX_WORDS; i++) {
348 len = strlen(cname[i]);
350 if (strncmp(cname[i], tok, len) == 0) {
351 if (strlen(tok) > len) {
352 char *end = get_index(tok + len, &ndx);
354 if ((i != LAYOUT && i != VARIANT) ||
355 *end != '\0' || ndx == -1) {
356 WARN("Illegal %s index: %d\n", cname[i], ndx);
357 WARN("Can only index layout and variant\n");
361 if (ndx < 1 || ndx > XkbNumKbdGroups) {
362 WARN("Illegal %s index: %d\n", cname[i], ndx);
363 WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
372 if (present & (1 << i)) {
373 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
374 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
375 WARN("Component \"%s\" listed twice\n", tok);
376 ACTION("Second definition ignored\n");
383 layout_ndx_present |= 1 << ndx;
385 variant_ndx_present |= 1 << ndx;
387 mapping->map[mapping->num_maps].word = i;
388 mapping->map[mapping->num_maps].index = ndx;
395 WARN("Unknown component \"%s\"\n", tok);
400 if ((present & PART_MASK) == 0) {
401 WARN("Mapping needs at least one MLVO part\n");
402 ACTION("Illegal mapping ignored\n");
403 mapping->num_maps = 0;
407 if ((present & COMPONENT_MASK) == 0) {
408 WARN("Mapping needs at least one component\n");
409 ACTION("Illegal mapping ignored\n");
410 mapping->num_maps = 0;
414 if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
415 ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
416 WARN("Keymap cannot appear with other components\n");
417 ACTION("Illegal mapping ignored\n");
418 mapping->num_maps = 0;
426 * Match a line such as:
427 * ! $pcmodels = pc101 pc102 pc104 pc105
430 match_group_line(struct input_line *line, struct group *group)
433 char *name = strchr(line->line, '$');
434 char *words = strchr(name, ' ');
441 for (; *words; words++) {
442 if (*words != '=' && *words != ' ')
449 group->name = strdup(name);
450 group->words = strdup(words);
452 words = group->words;
453 for (i = 1; *words; words++) {
465 /* Match lines following a mapping (see match_mapping_line comment). */
467 match_rule_line(struct input_line *line, struct mapping *mapping,
474 const char *names[MAX_WORDS] = { NULL };
476 if (mapping->num_maps == 0) {
477 WARN("Must have a mapping before first line of data\n");
478 ACTION("Illegal line of data ignored\n");
484 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
487 if (strcmp(tok, "=") == 0) {
492 if (nread > mapping->num_maps) {
493 WARN("Too many words on a line\n");
494 ACTION("Extra word \"%s\" ignored\n", tok);
498 names[mapping->map[nread].word] = tok;
499 if (*tok == '+' || *tok == '|')
503 if (nread < mapping->num_maps) {
504 WARN("Too few words on a line: %s\n", line->line);
505 ACTION("line ignored\n");
510 rule->number = mapping->number;
513 rule->flags |= RULE_FLAG_OPTION;
515 rule->flags |= RULE_FLAG_APPEND;
517 rule->flags |= RULE_FLAG_NORMAL;
519 rule->model = uDupString(names[MODEL]);
520 rule->layout = uDupString(names[LAYOUT]);
521 rule->variant = uDupString(names[VARIANT]);
522 rule->option = uDupString(names[OPTION]);
524 rule->keycodes = uDupString(names[KEYCODES]);
525 rule->symbols = uDupString(names[SYMBOLS]);
526 rule->types = uDupString(names[TYPES]);
527 rule->compat = uDupString(names[COMPAT]);
528 rule->keymap = uDupString(names[KEYMAP]);
530 rule->layout_num = rule->variant_num = 0;
531 for (i = 0; i < nread; i++) {
532 if (mapping->map[i].index) {
533 if (mapping->map[i].word == LAYOUT)
534 rule->layout_num = mapping->map[i].index;
535 if (mapping->map[i].word == VARIANT)
536 rule->variant_num = mapping->map[i].index;
544 match_line(struct input_line *line, struct mapping *mapping,
545 struct rule *rule, struct group *group)
547 if (line->line[0] != '!')
548 return match_rule_line(line, mapping, rule);
550 if (line->line[1] == '$' || (line->line[1] == ' ' && line->line[2] == '$'))
551 return match_group_line(line, group);
553 match_mapping_line(line, mapping);
558 squeeze_spaces(char *p1)
562 for (p2 = p1; *p2; p2++) {
572 * Expand the layout and variant of a var_defs and remove extraneous spaces.
573 * If there's one layout/variant, it is kept in .layout[0]/.variant[0], else
574 * is kept in [1], [2] and so on, and [0] remains empty.
575 * For example, this var_defs:
577 * .layout = "us,il,ru,ca"
578 * .variant = ",,,multix"
579 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
580 * Is expanded into this multi_defs:
582 * .layout = {NULL, "us", "il", "ru", "ca"},
583 * .variant = {NULL, "", "", "", "multix"},
584 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
587 make_multi_defs(struct multi_defs *mdefs, struct var_defs *defs)
592 memset(mdefs, 0, sizeof(*mdefs));
595 mdefs->model = defs->model;
599 mdefs->options = strdup(defs->options);
600 if (mdefs->options == NULL)
603 squeeze_spaces(mdefs->options);
607 if (!strchr(defs->layout, ',')) {
608 mdefs->layout[0] = defs->layout;
611 p = strdup(defs->layout);
616 mdefs->layout[1] = p;
618 for (i = 2; i <= XkbNumKbdGroups; i++) {
619 if ((p = strchr(p, ','))) {
621 mdefs->layout[i] = p;
628 if (p && (p = strchr(p, ',')))
634 if (!strchr(defs->variant, ',')) {
635 mdefs->variant[0] = defs->variant;
638 p = strdup(defs->variant);
643 mdefs->variant[1] = p;
645 for (i = 2; i <= XkbNumKbdGroups; i++) {
646 if ((p = strchr(p, ','))) {
648 mdefs->variant[i] = p;
654 if (p && (p = strchr(p, ',')))
663 free_multi_defs(struct multi_defs *defs)
667 * See make_multi_defs comment for the hack; the same strdup'd
668 * string is split among the indexes, but the one in [0] is const.
670 free(UNCONSTIFY(defs->layout[1]));
671 free(UNCONSTIFY(defs->variant[1]));
674 /* See apply_rule below. */
676 apply(const char *src, char **dst)
684 if (*src == '+' || *src == '!') {
686 ret = asprintf(dst, "%s%s", *dst, src);
691 else if (*dst == NULL) {
697 * Add the info from the matching rule to the resulting
698 * xkb_component_names. If we already had a match for something
699 * (e.g. keycodes), and the rule is not an appending one (e.g.
700 * +whatever), than we don't override but drop the new one.
703 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
705 /* Clear the flag because it's applied. */
706 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
708 apply(rule->keycodes, &kccgst->keycodes);
709 apply(rule->symbols, &kccgst->symbols);
710 apply(rule->types, &kccgst->types);
711 apply(rule->compat, &kccgst->compat);
712 apply(rule->keymap, &kccgst->keymap);
716 * Match if name is part of the group, e.g. if the following
718 * ! $qwertz = al cz de hr hu ro si sk
720 * match_group_member(rules, "qwertz", "hr")
724 match_group_member(struct rules *rules, const char *group_name,
731 for (i = 0; i < rules->num_groups; i++)
732 if (strcmp(rules->groups[i].name, group_name) == 0)
735 if (i == rules->num_groups)
737 group = &rules->groups[i];
740 for (i = 0; i < group->number; i++, word += strlen(word) + 1)
741 if (strcmp(word, name) == 0)
747 /* Match @needle out of @sep-seperated @haystack. */
749 match_one_of(const char *haystack, const char *needle, char sep)
751 const char *s = strstr(haystack, needle);
756 if (s != haystack && *s != sep)
760 if (*s != '\0' && *s != sep)
767 apply_rule_if_matches(struct rules *rules, struct rule *rule,
768 struct multi_defs *mdefs,
769 struct xkb_component_names *kccgst)
771 bool pending = false;
774 if (mdefs->model == NULL)
777 if (strcmp(rule->model, "*") == 0) {
780 else if (rule->model[0] == '$') {
781 if (!match_group_member(rules, rule->model, mdefs->model))
784 else if (strcmp(rule->model, mdefs->model) != 0) {
790 if (mdefs->options == NULL)
793 if (!match_one_of(mdefs->options, rule->option, ','))
798 if (mdefs->layout[rule->layout_num] == NULL ||
799 *mdefs->layout[rule->layout_num] == '\0')
802 if (strcmp(rule->layout, "*") == 0) {
805 else if (rule->layout[0] == '$') {
806 if (!match_group_member(rules, rule->layout,
807 mdefs->layout[rule->layout_num]))
810 else if (strcmp(rule->layout,
811 mdefs->layout[rule->layout_num]) != 0) {
817 if (mdefs->variant[rule->variant_num] == NULL ||
818 *mdefs->variant[rule->variant_num] == '\0')
821 if (strcmp(rule->variant, "*") == 0) {
823 } else if (rule->variant[0] == '$') {
824 if (!match_group_member(rules, rule->variant,
825 mdefs->variant[rule->variant_num]))
828 else if (strcmp(rule->variant,
829 mdefs->variant[rule->variant_num]) != 0) {
835 rule->flags |= RULE_FLAG_PENDING_MATCH;
837 /* Exact match, apply it now. */
838 apply_rule(rule, kccgst);
845 clear_partial_matches(struct rules *rules)
849 for (i = 0; i < rules->num_rules; i++)
850 rules->rules[i].flags &= ~RULE_FLAG_PENDING_MATCH;
854 apply_partial_matches(struct rules *rules, struct xkb_component_names *kccgst)
858 for (i = 0; i < rules->num_rules; i++)
859 if (rules->rules[i].flags & RULE_FLAG_PENDING_MATCH)
860 apply_rule(&rules->rules[i], kccgst);
864 apply_matching_rules(struct rules *rules, struct multi_defs *mdefs,
865 struct xkb_component_names *kccgst, unsigned int flags)
871 for (i = 0; i < rules->num_rules; i++) {
872 rule = &rules->rules[i];
874 if ((rule->flags & flags) != flags)
877 if ((flags & RULE_FLAG_OPTION) == 0 && rule->number == skip)
880 skip = apply_rule_if_matches(rules, rule, mdefs, kccgst);
884 /***====================================================================***/
887 substitute_vars(char *name, struct multi_defs *mdefs)
889 char *str, *outstr, *var;
891 size_t len, extra_len;
898 str = strchr(name, '%');
904 while (str != NULL) {
908 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
912 else if (pfx == '(') {
918 str = get_index(var + 1, &ndx);
920 str = strchr(str, '%');
924 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx])
925 len += strlen(mdefs->layout[ndx]) + extra_len;
926 else if (*var == 'm' && mdefs->model)
927 len += strlen(mdefs->model) + extra_len;
928 else if (*var == 'v' && mdefs->variant[ndx] && *mdefs->variant[ndx])
929 len += strlen(mdefs->variant[ndx]) + extra_len;
931 if (pfx == '(' && *str == ')')
934 str = strchr(&str[0], '%');
937 name = malloc(len + 1);
941 while (*str != '\0') {
947 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
950 else if (pfx == '(') {
959 str = get_index(var + 1, &ndx);
963 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
967 strcpy(outstr, mdefs->layout[ndx]);
968 outstr += strlen(mdefs->layout[ndx]);
973 else if (*var == 'm' && mdefs->model) {
977 strcpy(outstr, mdefs->model);
978 outstr += strlen(mdefs->model);
983 else if (*var == 'v' && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
987 strcpy(outstr, mdefs->variant[ndx]);
988 outstr += strlen(mdefs->variant[ndx]);
994 if (pfx == '(' && *str == ')')
1010 /***====================================================================***/
1013 get_components(struct rules *rules, struct var_defs *defs,
1014 struct xkb_component_names *kccgst)
1016 struct multi_defs mdefs;
1018 make_multi_defs(&mdefs, defs);
1020 clear_partial_matches(rules);
1022 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
1023 apply_partial_matches(rules, kccgst);
1025 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
1026 apply_partial_matches(rules, kccgst);
1028 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
1029 apply_partial_matches(rules, kccgst);
1031 kccgst->keycodes = substitute_vars(kccgst->keycodes, &mdefs);
1032 kccgst->symbols = substitute_vars(kccgst->symbols, &mdefs);
1033 kccgst->types = substitute_vars(kccgst->types, &mdefs);
1034 kccgst->compat = substitute_vars(kccgst->compat, &mdefs);
1035 kccgst->keymap = substitute_vars(kccgst->keymap, &mdefs);
1037 free_multi_defs(&mdefs);
1039 return (kccgst->keycodes && kccgst->symbols && kccgst->types &&
1040 kccgst->compat) || kccgst->keymap;
1043 static struct rule *
1044 add_rule(struct rules *rules)
1046 if (rules->sz_rules < 1) {
1047 rules->sz_rules = 16;
1048 rules->num_rules = 0;
1049 rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
1051 else if (rules->num_rules >= rules->sz_rules) {
1052 rules->sz_rules *= 2;
1053 rules->rules = realloc(rules->rules,
1054 rules->sz_rules * sizeof(*rules->rules));
1057 if (!rules->rules) {
1058 rules->sz_rules = rules->num_rules = 0;
1062 memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
1063 return &rules->rules[rules->num_rules++];
1066 static struct group *
1067 add_group(struct rules *rules)
1069 if (rules->sz_groups < 1) {
1070 rules->sz_groups = 16;
1071 rules->num_groups = 0;
1072 rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
1074 else if (rules->num_groups >= rules->sz_groups) {
1075 rules->sz_groups *= 2;
1076 rules->groups = realloc(rules->groups,
1077 rules->sz_groups * sizeof(*rules->groups));
1080 if (!rules->groups) {
1081 rules->sz_groups = rules->num_groups = 0;
1085 memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
1086 return &rules->groups[rules->num_groups++];
1089 static struct rules *
1090 XkbcRF_LoadRules(FILE *file)
1092 struct input_line line;
1093 struct mapping mapping;
1094 struct rule trule, *rule;
1095 struct group tgroup, *group;
1096 struct rules *rules;
1098 rules = calloc(1, sizeof(*rules));
1102 memset(&mapping, 0, sizeof(mapping));
1103 memset(&tgroup, 0, sizeof(tgroup));
1104 input_line_init(&line);
1106 while (input_line_get(file, &line)) {
1107 if (match_line(&line, &mapping, &trule, &tgroup)) {
1108 if (tgroup.number) {
1109 group = add_group(rules);
1110 if (group != NULL) {
1112 memset(&tgroup, 0, sizeof(tgroup));
1115 rule = add_rule(rules);
1118 memset(&trule, 0, sizeof(trule));
1124 input_line_deinit(&line);
1130 XkbcRF_Free(struct rules *rules)
1134 struct group *group;
1139 for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1142 free(rule->variant);
1144 free(rule->keycodes);
1145 free(rule->symbols);
1152 for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1156 free(rules->groups);
1161 struct xkb_component_names *
1162 xkb_components_from_rules(struct xkb_context *ctx,
1163 const struct xkb_rule_names *rmlvo)
1168 struct rules *loaded;
1169 struct xkb_component_names *names = NULL;
1170 struct var_defs defs = {
1171 .model = rmlvo->model,
1172 .layout = rmlvo->layout,
1173 .variant = rmlvo->variant,
1174 .options = rmlvo->options,
1177 rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
1180 ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1181 ERROR("%d include paths searched:\n",
1182 xkb_context_num_include_paths(ctx));
1183 for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1184 ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1188 loaded = XkbcRF_LoadRules(rulesFile);
1190 ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
1194 names = calloc(1, sizeof(*names));
1196 ERROR("failed to allocate XKB components\n");
1200 if (!get_components(loaded, &defs, names)) {
1201 free(names->keymap);
1202 free(names->keycodes);
1204 free(names->compat);
1205 free(names->symbols);
1208 ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
1212 XkbcRF_Free(loaded);