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(struct xkb_context *ctx, 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)) {
104 "The '!' is legal only at start of line; "
105 "Line containing '!' ignored\n");
106 darray_resize(*line, 0);
111 darray_append(*line, ch);
119 if (darray_empty(*line) && end_of_file)
122 darray_append(*line, '\0');
126 /***====================================================================***/
129 /* "Parts" - the MLVO which rules file maps to components. */
136 ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
145 #define COMPONENT_MASK \
146 ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
152 static const char *cname[] = {
155 [VARIANT] = "variant",
158 [KEYCODES] = "keycodes",
159 [SYMBOLS] = "symbols",
162 [GEOMETRY] = "geometry",
167 const char *layout[XkbNumKbdGroups + 1];
168 const char *variant[XkbNumKbdGroups + 1];
173 /* Sequential id for the mappings. */
195 RULE_FLAG_PENDING_MATCH = (1L << 1),
196 RULE_FLAG_OPTION = (1L << 2),
197 RULE_FLAG_APPEND = (1L << 3),
198 RULE_FLAG_NORMAL = (1L << 4),
221 darray(struct rule) rules;
222 darray(struct group) groups;
225 /***====================================================================***/
228 * Resolve numeric index, such as "[4]" in layout[4]. Missing index
232 get_index(char *str, int *ndx)
234 int empty = 0, consumed = 0, num;
236 sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
241 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(struct xkb_context *ctx, darray_char *line,
264 struct mapping *mapping)
267 char *str = darray_mem(*line, 1);
268 unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
276 * Remember the last sequential mapping id (incremented if the match
279 tmp = mapping->number;
280 memset(mapping, 0, sizeof(*mapping));
281 mapping->number = tmp;
283 while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
290 for (i = 0; i < MAX_WORDS; i++) {
291 len = strlen(cname[i]);
293 if (strncmp(cname[i], tok, len) == 0) {
294 if (strlen(tok) > len) {
295 char *end = get_index(tok + len, &ndx);
297 if ((i != LAYOUT && i != VARIANT) ||
298 *end != '\0' || ndx == -1) {
300 "Illegal %s index: %d\n", cname[i], ndx);
301 log_warn(ctx, "Can only index layout and variant\n");
305 if (ndx < 1 || ndx > XkbNumKbdGroups) {
306 log_warn(ctx, "Illegal %s index: %d\n",
308 log_warn(ctx, "Index must be in range 1..%d\n",
319 if (present & (1 << i)) {
320 if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
321 (i == VARIANT && variant_ndx_present & (1 << ndx))) {
323 "Component \"%s\" listed twice; "
324 "Second definition ignored\n", tok);
331 layout_ndx_present |= 1 << ndx;
333 variant_ndx_present |= 1 << ndx;
335 mapping->map[mapping->num_maps].word = i;
336 mapping->map[mapping->num_maps].index = ndx;
343 log_warn(ctx, "Unknown component \"%s\"; Ignored\n", tok);
346 if ((present & PART_MASK) == 0) {
348 "Mapping needs at least one MLVO part; "
349 "Illegal mapping ignored\n");
350 mapping->num_maps = 0;
354 if ((present & COMPONENT_MASK) == 0) {
356 "Mapping needs at least one component; "
357 "Illegal mapping ignored\n");
358 mapping->num_maps = 0;
366 * Match a line such as:
367 * ! $pcmodels = pc101 pc102 pc104 pc105
370 match_group_line(darray_char *line, struct group *group)
373 char *name = strchr(darray_mem(*line, 0), '$');
374 char *words = strchr(name, ' ');
381 for (; *words; words++) {
382 if (*words != '=' && *words != ' ')
389 group->name = strdup(name);
390 group->words = strdup(words);
392 words = group->words;
393 for (i = 1; *words; words++) {
405 /* Match lines following a mapping (see match_mapping_line comment). */
407 match_rule_line(struct xkb_context *ctx, darray_char *line,
408 struct mapping *mapping, struct rule *rule)
414 const char *names[MAX_WORDS] = { NULL };
416 if (mapping->num_maps == 0) {
418 "Must have a mapping before first line of data; "
419 "Illegal line of data ignored\n");
423 str = darray_mem(*line, 0);
425 for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL;
429 if (streq(tok, "=")) {
434 if (nread > mapping->num_maps) {
436 "Too many words on a line; "
437 "Extra word \"%s\" ignored\n", tok);
441 names[mapping->map[nread].word] = tok;
442 if (*tok == '+' || *tok == '|')
446 if (nread < mapping->num_maps) {
447 log_warn(ctx, "Too few words on a line: %s; Line ignored\n",
448 darray_mem(*line, 0));
453 rule->number = mapping->number;
456 rule->flags |= RULE_FLAG_OPTION;
458 rule->flags |= RULE_FLAG_APPEND;
460 rule->flags |= RULE_FLAG_NORMAL;
462 rule->model = strdup_safe(names[MODEL]);
463 rule->layout = strdup_safe(names[LAYOUT]);
464 rule->variant = strdup_safe(names[VARIANT]);
465 rule->option = strdup_safe(names[OPTION]);
467 rule->keycodes = strdup_safe(names[KEYCODES]);
468 rule->symbols = strdup_safe(names[SYMBOLS]);
469 rule->types = strdup_safe(names[TYPES]);
470 rule->compat = strdup_safe(names[COMPAT]);
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(struct xkb_context *ctx, darray_char *line,
487 struct mapping *mapping, struct rule *rule,
490 if (darray_item(*line, 0) != '!')
491 return match_rule_line(ctx, line, mapping, rule);
493 if (darray_item(*line, 1) == '$' ||
494 (darray_item(*line, 1) == ' ' && darray_item(*line, 2) == '$'))
495 return match_group_line(line, group);
497 match_mapping_line(ctx, line, mapping);
502 squeeze_spaces(char *p1)
506 for (p2 = p1; *p2; p2++) {
516 * Expand the layout and variant of the rule_names and remove extraneous
517 * spaces. If there's one layout/variant, it is kept in
518 * .layout[0]/.variant[0], else is kept in [1], [2] and so on, and [0]
519 * remains empty. For example, this rule_names:
521 * .layout = "us,il,ru,ca"
522 * .variant = ",,,multix"
523 * .options = "grp:alts_toggle, ctrl:nocaps, compose:rwin"
524 * Is expanded into this multi_defs:
526 * .layout = {NULL, "us", "il", "ru", "ca"},
527 * .variant = {NULL, "", "", "", "multix"},
528 * .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
531 make_multi_defs(struct multi_defs *mdefs, const struct xkb_rule_names *mlvo)
536 memset(mdefs, 0, sizeof(*mdefs));
539 mdefs->model = mlvo->model;
543 mdefs->options = strdup(mlvo->options);
544 if (mdefs->options == NULL)
547 squeeze_spaces(mdefs->options);
551 if (!strchr(mlvo->layout, ',')) {
552 mdefs->layout[0] = mlvo->layout;
555 p = strdup(mlvo->layout);
560 mdefs->layout[1] = p;
562 for (i = 2; i <= XkbNumKbdGroups; i++) {
563 if ((p = strchr(p, ','))) {
565 mdefs->layout[i] = p;
572 if (p && (p = strchr(p, ',')))
578 if (!strchr(mlvo->variant, ',')) {
579 mdefs->variant[0] = mlvo->variant;
582 p = strdup(mlvo->variant);
587 mdefs->variant[1] = p;
589 for (i = 2; i <= XkbNumKbdGroups; i++) {
590 if ((p = strchr(p, ','))) {
592 mdefs->variant[i] = p;
599 if (p && (p = strchr(p, ',')))
608 free_multi_defs(struct multi_defs *defs)
612 * See make_multi_defs comment for the hack; the same strdup'd
613 * string is split among the indexes, but the one in [0] is const.
615 free(UNCONSTIFY(defs->layout[1]));
616 free(UNCONSTIFY(defs->variant[1]));
619 /* See apply_rule below. */
621 apply(const char *src, char **dst)
629 if (*src == '+' || *src == '!') {
631 ret = asprintf(dst, "%s%s", *dst, src);
636 else if (*dst == NULL) {
642 * Add the info from the matching rule to the resulting
643 * xkb_component_names. If we already had a match for something
644 * (e.g. keycodes), and the rule is not an appending one (e.g.
645 * +whatever), than we don't override but drop the new one.
648 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
650 /* Clear the flag because it's applied. */
651 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
653 apply(rule->keycodes, &kccgst->keycodes);
654 apply(rule->symbols, &kccgst->symbols);
655 apply(rule->types, &kccgst->types);
656 apply(rule->compat, &kccgst->compat);
660 * Match if name is part of the group, e.g. if the following
662 * ! $qwertz = al cz de hr hu ro si sk
664 * match_group_member(rules, "qwertz", "hr")
668 match_group_member(struct rules *rules, const char *group_name,
673 struct group *iter, *group = NULL;
675 darray_foreach(iter, rules->groups) {
676 if (streq(iter->name, group_name)) {
686 for (i = 0; i < group->number; i++, word += strlen(word) + 1)
687 if (streq(word, name))
693 /* Match @needle out of @sep-seperated @haystack. */
695 match_one_of(const char *haystack, const char *needle, char sep)
697 const char *s = haystack;
698 const size_t len = strlen(needle);
701 if (strncmp(s, needle, len) == 0 && (s[len] == '\0' || s[len] == sep))
710 apply_rule_if_matches(struct rules *rules, struct rule *rule,
711 struct multi_defs *mdefs,
712 struct xkb_component_names *kccgst)
714 bool pending = false;
717 if (mdefs->model == NULL)
720 if (streq(rule->model, "*")) {
723 else if (rule->model[0] == '$') {
724 if (!match_group_member(rules, rule->model, mdefs->model))
727 else if (!streq(rule->model, mdefs->model)) {
733 if (mdefs->options == NULL)
736 if (!match_one_of(mdefs->options, rule->option, ','))
741 if (mdefs->layout[rule->layout_num] == NULL)
744 if (streq(rule->layout, "*")) {
747 else if (rule->layout[0] == '$') {
748 if (!match_group_member(rules, rule->layout,
749 mdefs->layout[rule->layout_num]))
752 else if (!streq(rule->layout, mdefs->layout[rule->layout_num])) {
758 if (mdefs->variant[rule->variant_num] == NULL)
761 if (streq(rule->variant, "*")) {
764 else if (rule->variant[0] == '$') {
765 if (!match_group_member(rules, rule->variant,
766 mdefs->variant[rule->variant_num]))
769 else if (!streq(rule->variant, mdefs->variant[rule->variant_num])) {
775 rule->flags |= RULE_FLAG_PENDING_MATCH;
778 /* Exact match, apply it now. */
779 apply_rule(rule, kccgst);
786 clear_partial_matches(struct rules *rules)
790 darray_foreach(rule, rules->rules)
791 rule->flags &= ~RULE_FLAG_PENDING_MATCH;
795 apply_partial_matches(struct rules *rules, struct xkb_component_names *kccgst)
799 darray_foreach(rule, rules->rules)
800 if (rule->flags & RULE_FLAG_PENDING_MATCH)
801 apply_rule(rule, kccgst);
805 apply_matching_rules(struct rules *rules, struct multi_defs *mdefs,
806 struct xkb_component_names *kccgst, unsigned int flags)
811 darray_foreach(rule, rules->rules) {
812 if ((rule->flags & flags) != flags)
815 if ((flags & RULE_FLAG_OPTION) == 0 && rule->number == skip)
818 skip = apply_rule_if_matches(rules, rule, mdefs, kccgst);
822 /***====================================================================***/
825 substitute_vars(char *name, struct multi_defs *mdefs)
827 char *str, *outstr, *var;
829 size_t len, extra_len;
836 str = strchr(name, '%');
842 while (str != NULL) {
846 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
850 else if (pfx == '(') {
856 str = get_index(var + 1, &ndx);
858 str = strchr(str, '%');
862 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx])
863 len += strlen(mdefs->layout[ndx]) + extra_len;
864 else if (*var == 'm' && mdefs->model)
865 len += strlen(mdefs->model) + extra_len;
866 else if (*var == 'v' && mdefs->variant[ndx] && *mdefs->variant[ndx])
867 len += strlen(mdefs->variant[ndx]) + extra_len;
869 if (pfx == '(' && *str == ')')
872 str = strchr(&str[0], '%');
875 name = malloc(len + 1);
879 while (*str != '\0') {
885 if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
888 else if (pfx == '(') {
897 str = get_index(var + 1, &ndx);
901 if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
905 strcpy(outstr, mdefs->layout[ndx]);
906 outstr += strlen(mdefs->layout[ndx]);
911 else if (*var == 'm' && mdefs->model) {
915 strcpy(outstr, mdefs->model);
916 outstr += strlen(mdefs->model);
921 else if (*var == 'v' && mdefs->variant[ndx] &&
922 *mdefs->variant[ndx]) {
926 strcpy(outstr, mdefs->variant[ndx]);
927 outstr += strlen(mdefs->variant[ndx]);
933 if (pfx == '(' && *str == ')')
949 /***====================================================================***/
952 get_components(struct rules *rules, const struct xkb_rule_names *mlvo,
953 struct xkb_component_names *kccgst)
955 struct multi_defs mdefs;
957 make_multi_defs(&mdefs, mlvo);
959 clear_partial_matches(rules);
961 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
962 apply_partial_matches(rules, kccgst);
964 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
965 apply_partial_matches(rules, kccgst);
967 apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
968 apply_partial_matches(rules, kccgst);
970 kccgst->keycodes = substitute_vars(kccgst->keycodes, &mdefs);
971 kccgst->symbols = substitute_vars(kccgst->symbols, &mdefs);
972 kccgst->types = substitute_vars(kccgst->types, &mdefs);
973 kccgst->compat = substitute_vars(kccgst->compat, &mdefs);
975 free_multi_defs(&mdefs);
977 return kccgst->keycodes && kccgst->symbols && kccgst->types &&
981 static struct rules *
982 load_rules(struct xkb_context *ctx, FILE *file)
985 struct mapping mapping;
990 rules = calloc(1, sizeof(*rules));
993 darray_init(rules->rules);
994 darray_growalloc(rules->rules, 16);
996 memset(&mapping, 0, sizeof(mapping));
997 memset(&tgroup, 0, sizeof(tgroup));
999 darray_growalloc(line, 128);
1001 while (input_line_get(ctx, file, &line)) {
1002 if (match_line(ctx, &line, &mapping, &trule, &tgroup)) {
1003 if (tgroup.number) {
1004 darray_append(rules->groups, tgroup);
1005 memset(&tgroup, 0, sizeof(tgroup));
1008 darray_append(rules->rules, trule);
1009 memset(&trule, 0, sizeof(trule));
1013 darray_resize(line, 0);
1021 free_rules(struct rules *rules)
1024 struct group *group;
1029 darray_foreach(rule, rules->rules) {
1032 free(rule->variant);
1034 free(rule->keycodes);
1035 free(rule->symbols);
1039 darray_free(rules->rules);
1041 darray_foreach(group, rules->groups) {
1045 darray_free(rules->groups);
1050 struct xkb_component_names *
1051 xkb_components_from_rules(struct xkb_context *ctx,
1052 const struct xkb_rule_names *rmlvo)
1057 struct rules *rules;
1058 struct xkb_component_names *kccgst = NULL;
1060 file = XkbFindFileInPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1062 log_err(ctx, "could not find \"%s\" rules in XKB path\n",
1064 log_err(ctx, "%d include paths searched:\n",
1065 xkb_context_num_include_paths(ctx));
1066 for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1067 log_err(ctx, "\t%s\n", xkb_context_include_path_get(ctx, i));
1071 rules = load_rules(ctx, file);
1073 log_err(ctx, "failed to load XKB rules \"%s\"\n", path);
1077 kccgst = calloc(1, sizeof(*kccgst));
1079 log_err(ctx, "failed to allocate XKB components\n");
1083 if (!get_components(rules, rmlvo, kccgst)) {
1084 free(kccgst->keycodes);
1085 free(kccgst->types);
1086 free(kccgst->compat);
1087 free(kccgst->symbols);
1090 log_err(ctx, "no components returned from XKB rules \"%s\"\n", path);