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 ********************************************************/
34 input_line_get(FILE *file, darray_char *line)
37 bool end_of_file = false;
42 while (!end_of_file && darray_empty(*line)) {
43 space_pending = slash_pending = in_comment = false;
45 while ((ch = getc(file)) != '\n' && ch != EOF) {
64 slash_pending = false;
75 darray_append(*line, ' ');
76 space_pending = false;
79 darray_append(*line, '/');
80 slash_pending = false;
84 while (isspace(ch) && ch != '\n' && ch != EOF)
90 if (ch != '\n' && !darray_empty(*line))
97 darray_append(*line, ' ');
98 space_pending = false;
102 if (!darray_empty(*line)) {
103 WARN("The '!' is legal only at start of line\n");
104 ACTION("Line containing '!' ignored\n");
105 darray_resize(*line, 0);
110 darray_append(*line, ch);
118 if (darray_empty(*line) && end_of_file)
121 darray_append(*line, '\0');
125 /***====================================================================***/
128 /* "Parts" - the MLVO which rules file maps to components. */
135 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
145 #define COMPONENT_MASK \
146 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
147 (1 << GEOMETRY) | (1 << KEYMAP))
152 static const char *cname[] = {
155 [VARIANT] = "variant",
158 [KEYCODES] = "keycodes",
159 [SYMBOLS] = "symbols",
162 [GEOMETRY] = "geometry",
168 const char *layout[XkbNumKbdGroups + 1];
169 const char *variant[XkbNumKbdGroups + 1];
174 /* Sequential id for the mappings. */
196 RULE_FLAG_PENDING_MATCH = (1L << 1),
197 RULE_FLAG_OPTION = (1L << 2),
198 RULE_FLAG_APPEND = (1L << 3),
199 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);
242 } else if (empty > 0) {
252 * Match a mapping line which opens a rule, e.g:
253 * ! model layout[4] variant[4] = symbols geometry
254 * Which will be followed by lines such as:
255 * * ben basic = +in(ben):4 nec(pc98)
256 * So if the MLVO matches the LHS of some line, we'll get the components
258 * In this example, we will get for the second and fourth columns:
259 * mapping->map[1] = {.word = LAYOUT, .index = 4}
260 * mapping->map[3] = {.word = SYMBOLS, .index = 0}
263 match_mapping_line(darray_char *line, struct mapping *mapping)
266 char *str = darray_mem(*line, 1);
267 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
275 * Remember the last sequential mapping id (incremented if the match
278 tmp = mapping->number;
279 memset(mapping, 0, sizeof(*mapping));
280 mapping->number = tmp;
282 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
286 if (strcmp(tok, "=") == 0)
289 for (i = 0; i < MAX_WORDS; i++) {
290 len = strlen(cname[i]);
292 if (strncmp(cname[i], tok, len) == 0) {
293 if (strlen(tok) > len) {
294 char *end = get_index(tok + len, &ndx);
296 if ((i != LAYOUT && i != VARIANT) ||
297 *end != '\0' || ndx == -1) {
298 WARN("Illegal %s index: %d\n", cname[i], ndx);
299 WARN("Can only index layout and variant\n");
303 if (ndx < 1 || ndx > XkbNumKbdGroups) {
304 WARN("Illegal %s index: %d\n", cname[i], ndx);
305 WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
314 if (present & (1 << i)) {
315 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
316 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
317 WARN("Component \"%s\" listed twice\n", tok);
318 ACTION("Second definition ignored\n");
325 layout_ndx_present |= 1 << ndx;
327 variant_ndx_present |= 1 << ndx;
329 mapping->map[mapping->num_maps].word = i;
330 mapping->map[mapping->num_maps].index = ndx;
337 WARN("Unknown component \"%s\"\n", tok);
342 if ((present & PART_MASK) == 0) {
343 WARN("Mapping needs at least one MLVO part\n");
344 ACTION("Illegal mapping ignored\n");
345 mapping->num_maps = 0;
349 if ((present & COMPONENT_MASK) == 0) {
350 WARN("Mapping needs at least one component\n");
351 ACTION("Illegal mapping ignored\n");
352 mapping->num_maps = 0;
356 if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
357 ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
358 WARN("Keymap cannot appear with other components\n");
359 ACTION("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(darray_char *line, struct mapping *mapping,
416 const char *names[MAX_WORDS] = { NULL };
418 if (mapping->num_maps == 0) {
419 WARN("Must have a mapping before first line of data\n");
420 ACTION("Illegal line of data ignored\n");
424 str = darray_mem(*line, 0);
426 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
429 if (strcmp(tok, "=") == 0) {
434 if (nread > mapping->num_maps) {
435 WARN("Too many words on a line\n");
436 ACTION("Extra word \"%s\" ignored\n", tok);
440 names[mapping->map[nread].word] = tok;
441 if (*tok == '+' || *tok == '|')
445 if (nread < mapping->num_maps) {
446 WARN("Too few words on a line: %s\n", darray_mem(*line, 0));
447 ACTION("line ignored\n");
452 rule->number = mapping->number;
455 rule->flags |= RULE_FLAG_OPTION;
457 rule->flags |= RULE_FLAG_APPEND;
459 rule->flags |= RULE_FLAG_NORMAL;
461 rule->model = uDupString(names[MODEL]);
462 rule->layout = uDupString(names[LAYOUT]);
463 rule->variant = uDupString(names[VARIANT]);
464 rule->option = uDupString(names[OPTION]);
466 rule->keycodes = uDupString(names[KEYCODES]);
467 rule->symbols = uDupString(names[SYMBOLS]);
468 rule->types = uDupString(names[TYPES]);
469 rule->compat = uDupString(names[COMPAT]);
470 rule->keymap = uDupString(names[KEYMAP]);
472 rule->layout_num = rule->variant_num = 0;
473 for (i = 0; i < nread; i++) {
474 if (mapping->map[i].index) {
475 if (mapping->map[i].word == LAYOUT)
476 rule->layout_num = mapping->map[i].index;
477 if (mapping->map[i].word == VARIANT)
478 rule->variant_num = mapping->map[i].index;
486 match_line(darray_char *line, struct mapping *mapping,
487 struct rule *rule, struct group *group)
489 if (darray_item(*line, 0) != '!')
490 return match_rule_line(line, mapping, rule);
492 if (darray_item(*line, 1) == '$' ||
493 (darray_item(*line, 1) == ' ' && darray_item(*line, 2) == '$'))
494 return match_group_line(line, group);
496 match_mapping_line(line, mapping);
501 squeeze_spaces(char *p1)
505 for (p2 = p1; *p2; p2++) {
515 * Expand the layout and variant of the rule_names and remove extraneous
516 * spaces. If there's one layout/variant, it is kept in
517 * .layout[0]/.variant[0], else is kept in [1], [2] and so on, and [0]
518 * remains empty. For example, this rule_names:
520 * .layout = "us,il,ru,ca"
521 * .variant = ",,,multix"
522 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
523 * Is expanded into this multi_defs:
525 * .layout = {NULL, "us", "il", "ru", "ca"},
526 * .variant = {NULL, "", "", "", "multix"},
527 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
530 make_multi_defs(struct multi_defs *mdefs, const struct xkb_rule_names *mlvo)
535 memset(mdefs, 0, sizeof(*mdefs));
538 mdefs->model = mlvo->model;
542 mdefs->options = strdup(mlvo->options);
543 if (mdefs->options == NULL)
546 squeeze_spaces(mdefs->options);
550 if (!strchr(mlvo->layout, ',')) {
551 mdefs->layout[0] = mlvo->layout;
554 p = strdup(mlvo->layout);
559 mdefs->layout[1] = p;
561 for (i = 2; i <= XkbNumKbdGroups; i++) {
562 if ((p = strchr(p, ','))) {
564 mdefs->layout[i] = p;
571 if (p && (p = strchr(p, ',')))
577 if (!strchr(mlvo->variant, ',')) {
578 mdefs->variant[0] = mlvo->variant;
581 p = strdup(mlvo->variant);
586 mdefs->variant[1] = p;
588 for (i = 2; i <= XkbNumKbdGroups; i++) {
589 if ((p = strchr(p, ','))) {
591 mdefs->variant[i] = p;
597 if (p && (p = strchr(p, ',')))
606 free_multi_defs(struct multi_defs *defs)
610 * See make_multi_defs comment for the hack; the same strdup'd
611 * string is split among the indexes, but the one in [0] is const.
613 free(UNCONSTIFY(defs->layout[1]));
614 free(UNCONSTIFY(defs->variant[1]));
617 /* See apply_rule below. */
619 apply(const char *src, char **dst)
627 if (*src == '+' || *src == '!') {
629 ret = asprintf(dst, "%s%s", *dst, src);
634 else if (*dst == NULL) {
640 * Add the info from the matching rule to the resulting
641 * xkb_component_names. If we already had a match for something
642 * (e.g. keycodes), and the rule is not an appending one (e.g.
643 * +whatever), than we don't override but drop the new one.
646 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
648 /* Clear the flag because it's applied. */
649 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
651 apply(rule->keycodes, &kccgst->keycodes);
652 apply(rule->symbols, &kccgst->symbols);
653 apply(rule->types, &kccgst->types);
654 apply(rule->compat, &kccgst->compat);
655 apply(rule->keymap, &kccgst->keymap);
659 * Match if name is part of the group, e.g. if the following
661 * ! $qwertz = al cz de hr hu ro si sk
663 * match_group_member(rules, "qwertz", "hr")
667 match_group_member(struct rules *rules, const char *group_name,
672 struct group *iter, *group = NULL;
674 darray_foreach(iter, rules->groups) {
675 if (strcmp(iter->name, group_name) == 0) {
685 for (i = 0; i < group->number; i++, word += strlen(word) + 1)
686 if (strcmp(word, name) == 0)
692 /* Match @needle out of @sep-seperated @haystack. */
694 match_one_of(const char *haystack, const char *needle, char sep)
696 const char *s = strstr(haystack, needle);
701 if (s != haystack && *s != sep)
705 if (*s != '\0' && *s != 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 (strcmp(rule->model, "*") == 0) {
725 else if (rule->model[0] == '$') {
726 if (!match_group_member(rules, rule->model, mdefs->model))
729 else if (strcmp(rule->model, mdefs->model) != 0) {
735 if (mdefs->options == NULL)
738 if (!match_one_of(mdefs->options, rule->option, ','))
743 if (mdefs->layout[rule->layout_num] == NULL)
746 if (strcmp(rule->layout, "*") == 0) {
749 else if (rule->layout[0] == '$') {
750 if (!match_group_member(rules, rule->layout,
751 mdefs->layout[rule->layout_num]))
754 else if (strcmp(rule->layout,
755 mdefs->layout[rule->layout_num]) != 0) {
761 if (mdefs->variant[rule->variant_num] == NULL)
764 if (strcmp(rule->variant, "*") == 0) {
766 } else if (rule->variant[0] == '$') {
767 if (!match_group_member(rules, rule->variant,
768 mdefs->variant[rule->variant_num]))
771 else if (strcmp(rule->variant,
772 mdefs->variant[rule->variant_num]) != 0) {
778 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] && *mdefs->variant[ndx]) {
927 strcpy(outstr, mdefs->variant[ndx]);
928 outstr += strlen(mdefs->variant[ndx]);
934 if (pfx == '(' && *str == ')')
950 /***====================================================================***/
953 get_components(struct rules *rules, const struct xkb_rule_names *mlvo,
954 struct xkb_component_names *kccgst)
956 struct multi_defs mdefs;
958 make_multi_defs(&mdefs, mlvo);
960 clear_partial_matches(rules);
962 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
963 apply_partial_matches(rules, kccgst);
965 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
966 apply_partial_matches(rules, kccgst);
968 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
969 apply_partial_matches(rules, kccgst);
971 kccgst->keycodes = substitute_vars(kccgst->keycodes, &mdefs);
972 kccgst->symbols = substitute_vars(kccgst->symbols, &mdefs);
973 kccgst->types = substitute_vars(kccgst->types, &mdefs);
974 kccgst->compat = substitute_vars(kccgst->compat, &mdefs);
975 kccgst->keymap = substitute_vars(kccgst->keymap, &mdefs);
977 free_multi_defs(&mdefs);
979 return (kccgst->keycodes && kccgst->symbols && kccgst->types &&
980 kccgst->compat) || kccgst->keymap;
983 static struct rules *
984 load_rules(FILE *file)
987 struct mapping mapping;
992 rules = calloc(1, sizeof(*rules));
995 darray_init(rules->rules);
996 darray_growalloc(rules->rules, 16);
998 memset(&mapping, 0, sizeof(mapping));
999 memset(&tgroup, 0, sizeof(tgroup));
1001 darray_growalloc(line, 128);
1003 while (input_line_get(file, &line)) {
1004 if (match_line(&line, &mapping, &trule, &tgroup)) {
1005 if (tgroup.number) {
1006 darray_append(rules->groups, tgroup);
1007 memset(&tgroup, 0, sizeof(tgroup));
1009 darray_append(rules->rules, trule);
1010 memset(&trule, 0, sizeof(trule));
1014 darray_resize(line, 0);
1022 free_rules(struct rules *rules)
1025 struct group *group;
1030 darray_foreach(rule, rules->rules) {
1033 free(rule->variant);
1035 free(rule->keycodes);
1036 free(rule->symbols);
1041 darray_free(rules->rules);
1043 darray_foreach(group, rules->groups) {
1047 darray_free(rules->groups);
1052 struct xkb_component_names *
1053 xkb_components_from_rules(struct xkb_context *ctx,
1054 const struct xkb_rule_names *rmlvo)
1059 struct rules *rules;
1060 struct xkb_component_names *kccgst = NULL;
1062 file = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile, &path);
1064 ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1065 ERROR("%d include paths searched:\n",
1066 xkb_context_num_include_paths(ctx));
1067 for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1068 ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1072 rules = load_rules(file);
1074 ERROR("failed to load XKB rules \"%s\"\n", path);
1078 kccgst = calloc(1, sizeof(*kccgst));
1080 ERROR("failed to allocate XKB components\n");
1084 if (!get_components(rules, rmlvo, kccgst)) {
1085 free(kccgst->keymap);
1086 free(kccgst->keycodes);
1087 free(kccgst->types);
1088 free(kccgst->compat);
1089 free(kccgst->symbols);
1092 ERROR("no components returned from XKB rules \"%s\"\n", path);