Organize xkbcomp/ header files
[platform/upstream/libxkbcommon.git] / src / xkbcomp / rules.c
1 /************************************************************
2  * Copyright (c) 1996 by Silicon Graphics Computer Systems, Inc.
3  *
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.
15  *
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.
24  *
25  ********************************************************/
26
27 #include <stdio.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34
35 #include "xkbcomp-priv.h"
36 #include "rules.h"
37 #include "include.h"
38
39 static const char *
40 input_line_get(struct xkb_context *ctx, const char *buf, const char *end,
41                darray_char *line)
42 {
43     int ch;
44     bool space_pending;
45     bool slash_pending;
46     bool in_comment;
47
48     while (buf < end && darray_empty(*line)) {
49         space_pending = slash_pending = in_comment = false;
50
51         while (buf < end && (ch = *buf++) != '\n') {
52             if (ch == '\\') {
53                 buf++;
54                 if (buf >= end)
55                     break;
56                 ch = *buf;
57
58                 if (ch == '\n') {
59                     in_comment = false;
60                     ch = ' ';
61                 }
62             }
63
64             if (in_comment)
65                 continue;
66
67             if (ch == '/') {
68                 if (slash_pending) {
69                     in_comment = true;
70                     slash_pending = false;
71                 }
72                 else {
73                     slash_pending = true;
74                 }
75
76                 continue;
77             }
78
79             if (slash_pending) {
80                 if (space_pending) {
81                     darray_append(*line, ' ');
82                     space_pending = false;
83                 }
84
85                 darray_append(*line, '/');
86                 slash_pending = false;
87             }
88
89             if (isspace(ch)) {
90                 while (buf < end && isspace(ch) && ch != '\n')
91                     ch = *buf++;
92
93                 if (buf >= end)
94                     break;
95
96                 if (ch != '\n' && !darray_empty(*line))
97                     space_pending = true;
98
99                 buf--;
100             }
101             else {
102                 if (space_pending) {
103                     darray_append(*line, ' ');
104                     space_pending = false;
105                 }
106
107                 if (ch == '!') {
108                     if (!darray_empty(*line)) {
109                         log_warn(ctx,
110                                  "The '!' is legal only at start of line; "
111                                  "Line containing '!' ignored\n");
112                         darray_resize(*line, 0);
113                         break;
114                     }
115                 }
116
117                 darray_append(*line, ch);
118             }
119         }
120     }
121
122     if (darray_empty(*line) && buf >= end)
123         return NULL;
124
125     darray_append(*line, '\0');
126     return buf;
127 }
128
129 /***====================================================================***/
130
131 enum {
132     /* "Parts" - the MLVO which rules file maps to components. */
133     MODEL = 0,
134     LAYOUT,
135     VARIANT,
136     OPTION,
137
138 #define PART_MASK \
139     ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
140
141     /* Components */
142     KEYCODES,
143     SYMBOLS,
144     TYPES,
145     COMPAT,
146     GEOMETRY,
147
148 #define COMPONENT_MASK \
149     ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
150      (1 << GEOMETRY))
151
152     MAX_WORDS
153 };
154
155 static const char *cname[] = {
156     [MODEL] = "model",
157     [LAYOUT] = "layout",
158     [VARIANT] = "variant",
159     [OPTION] = "option",
160
161     [KEYCODES] = "keycodes",
162     [SYMBOLS] = "symbols",
163     [TYPES] = "types",
164     [COMPAT] = "compat",
165     [GEOMETRY] = "geometry",
166 };
167
168 struct multi_defs {
169     const char *model;
170     const char *layout[XkbNumKbdGroups + 1];
171     const char *variant[XkbNumKbdGroups + 1];
172     char *options;
173 };
174
175 struct mapping {
176     /* Sequential id for the mappings. */
177     int number;
178     size_t num_maps;
179
180     struct {
181         int word;
182         int index;
183     } map[MAX_WORDS];
184 };
185
186 struct var_desc {
187     char *name;
188     char *desc;
189 };
190
191 struct group {
192     int number;
193     char *name;
194     char *words;
195 };
196
197 enum rule_flag {
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),
202 };
203
204 struct rule {
205     int number;
206
207     char *model;
208     char *layout;
209     int layout_num;
210     char *variant;
211     int variant_num;
212     char *option;
213
214     /* yields */
215
216     char *keycodes;
217     char *symbols;
218     char *types;
219     char *compat;
220     unsigned flags;
221 };
222
223 struct rules {
224     darray(struct rule) rules;
225     darray(struct group) groups;
226 };
227
228 /***====================================================================***/
229
230 /*
231  * Resolve numeric index, such as "[4]" in layout[4]. Missing index
232  * means zero.
233  */
234 static char *
235 get_index(char *str, int *ndx)
236 {
237     int empty = 0, consumed = 0, num;
238
239     sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
240     if (consumed > 0) {
241         *ndx = num;
242         str += consumed;
243     }
244     else if (empty > 0) {
245         *ndx = -1;
246     }
247     else {
248         *ndx = 0;
249     }
250
251     return str;
252 }
253
254 /*
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
260  * on the RHS.
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}
264  */
265 static void
266 match_mapping_line(struct xkb_context *ctx, darray_char *line,
267                    struct mapping *mapping)
268 {
269     char *tok;
270     char *str = darray_mem(*line, 1);
271     unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
272     int i, tmp;
273     size_t len;
274     int ndx;
275     char *strtok_buf;
276     bool found;
277
278     /*
279      * Remember the last sequential mapping id (incremented if the match
280      * is successful).
281      */
282     tmp = mapping->number;
283     memset(mapping, 0, sizeof(*mapping));
284     mapping->number = tmp;
285
286     while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
287         found = false;
288         str = NULL;
289
290         if (streq(tok, "="))
291             continue;
292
293         for (i = 0; i < MAX_WORDS; i++) {
294             len = strlen(cname[i]);
295
296             if (strncmp(cname[i], tok, len) == 0) {
297                 if (strlen(tok) > len) {
298                     char *end = get_index(tok + len, &ndx);
299
300                     if ((i != LAYOUT && i != VARIANT) ||
301                         *end != '\0' || ndx == -1) {
302                         log_warn(ctx,
303                                  "Illegal %s index: %d\n", cname[i], ndx);
304                         log_warn(ctx, "Can only index layout and variant\n");
305                         break;
306                     }
307
308                     if (ndx < 1 || ndx > XkbNumKbdGroups) {
309                         log_warn(ctx, "Illegal %s index: %d\n",
310                                  cname[i], ndx);
311                         log_warn(ctx, "Index must be in range 1..%d\n",
312                                  XkbNumKbdGroups);
313                         break;
314                     }
315                 }
316                 else {
317                     ndx = 0;
318                 }
319
320                 found = true;
321
322                 if (present & (1 << i)) {
323                     if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
324                         (i == VARIANT && variant_ndx_present & (1 << ndx))) {
325                         log_warn(ctx,
326                                  "Component \"%s\" listed twice; "
327                                  "Second definition ignored\n", tok);
328                         break;
329                     }
330                 }
331
332                 present |= (1 << i);
333                 if (i == LAYOUT)
334                     layout_ndx_present |= 1 << ndx;
335                 if (i == VARIANT)
336                     variant_ndx_present |= 1 << ndx;
337
338                 mapping->map[mapping->num_maps].word = i;
339                 mapping->map[mapping->num_maps].index = ndx;
340                 mapping->num_maps++;
341                 break;
342             }
343         }
344
345         if (!found)
346             log_warn(ctx, "Unknown component \"%s\"; Ignored\n", tok);
347     }
348
349     if ((present & PART_MASK) == 0) {
350         log_warn(ctx,
351                  "Mapping needs at least one MLVO part; "
352                  "Illegal mapping ignored\n");
353         mapping->num_maps = 0;
354         return;
355     }
356
357     if ((present & COMPONENT_MASK) == 0) {
358         log_warn(ctx,
359                  "Mapping needs at least one component; "
360                  "Illegal mapping ignored\n");
361         mapping->num_maps = 0;
362         return;
363     }
364
365     mapping->number++;
366 }
367
368 /*
369  * Match a line such as:
370  * ! $pcmodels = pc101 pc102 pc104 pc105
371  */
372 static bool
373 match_group_line(darray_char *line, struct group *group)
374 {
375     int i;
376     char *name = strchr(darray_mem(*line, 0), '$');
377     char *words = strchr(name, ' ');
378
379     if (!words)
380         return false;
381
382     *words++ = '\0';
383
384     for (; *words; words++) {
385         if (*words != '=' && *words != ' ')
386             break;
387     }
388
389     if (*words == '\0')
390         return false;
391
392     group->name = strdup(name);
393     group->words = strdup(words);
394
395     words = group->words;
396     for (i = 1; *words; words++) {
397         if (*words == ' ') {
398             *words++ = '\0';
399             i++;
400         }
401     }
402     group->number = i;
403
404     return true;
405
406 }
407
408 /* Match lines following a mapping (see match_mapping_line comment). */
409 static bool
410 match_rule_line(struct xkb_context *ctx, darray_char *line,
411                 struct mapping *mapping, struct rule *rule)
412 {
413     char *str, *tok;
414     int nread, i;
415     char *strtok_buf;
416     bool append = false;
417     const char *names[MAX_WORDS] = { NULL };
418
419     if (mapping->num_maps == 0) {
420         log_warn(ctx,
421                  "Must have a mapping before first line of data; "
422                  "Illegal line of data ignored\n");
423         return false;
424     }
425
426     str = darray_mem(*line, 0);
427
428     for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL;
429          nread++) {
430         str = NULL;
431
432         if (streq(tok, "=")) {
433             nread--;
434             continue;
435         }
436
437         if (nread > mapping->num_maps) {
438             log_warn(ctx,
439                      "Too many words on a line; "
440                      "Extra word \"%s\" ignored\n", tok);
441             continue;
442         }
443
444         names[mapping->map[nread].word] = tok;
445         if (*tok == '+' || *tok == '|')
446             append = true;
447     }
448
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));
452         return false;
453     }
454
455     rule->flags = 0;
456     rule->number = mapping->number;
457
458     if (names[OPTION])
459         rule->flags |= RULE_FLAG_OPTION;
460     else if (append)
461         rule->flags |= RULE_FLAG_APPEND;
462     else
463         rule->flags |= RULE_FLAG_NORMAL;
464
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]);
469
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]);
474
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;
482         }
483     }
484
485     return true;
486 }
487
488 static bool
489 match_line(struct xkb_context *ctx, darray_char *line,
490            struct mapping *mapping, struct rule *rule,
491            struct group *group)
492 {
493     if (darray_item(*line, 0) != '!')
494         return match_rule_line(ctx, line, mapping, rule);
495
496     if (darray_item(*line, 1) == '$' ||
497         (darray_item(*line, 1) == ' ' && darray_item(*line, 2) == '$'))
498         return match_group_line(line, group);
499
500     match_mapping_line(ctx, line, mapping);
501     return false;
502 }
503
504 static void
505 squeeze_spaces(char *p1)
506 {
507     char *p2;
508
509     for (p2 = p1; *p2; p2++) {
510         *p1 = *p2;
511         if (*p1 != ' ')
512             p1++;
513     }
514
515     *p1 = '\0';
516 }
517
518 /*
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:
523  *      .model  = "pc105",
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:
528  *      .model = "pc105"
529  *      .layout = {NULL, "us", "il", "ru", "ca"},
530  *      .variant = {NULL, "", "", "", "multix"},
531  *      .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
532  */
533 static bool
534 make_multi_defs(struct multi_defs *mdefs, const struct xkb_rule_names *mlvo)
535 {
536     char *p;
537     int i;
538
539     memset(mdefs, 0, sizeof(*mdefs));
540
541     if (mlvo->model) {
542         mdefs->model = mlvo->model;
543     }
544
545     if (mlvo->options) {
546         mdefs->options = strdup(mlvo->options);
547         if (mdefs->options == NULL)
548             return false;
549
550         squeeze_spaces(mdefs->options);
551     }
552
553     if (mlvo->layout) {
554         if (!strchr(mlvo->layout, ',')) {
555             mdefs->layout[0] = mlvo->layout;
556         }
557         else {
558             p = strdup(mlvo->layout);
559             if (p == NULL)
560                 return false;
561
562             squeeze_spaces(p);
563             mdefs->layout[1] = p;
564
565             for (i = 2; i <= XkbNumKbdGroups; i++) {
566                 if ((p = strchr(p, ','))) {
567                     *p++ = '\0';
568                     mdefs->layout[i] = p;
569                 }
570                 else {
571                     break;
572                 }
573             }
574
575             if (p && (p = strchr(p, ',')))
576                 *p = '\0';
577         }
578     }
579
580     if (mlvo->variant) {
581         if (!strchr(mlvo->variant, ',')) {
582             mdefs->variant[0] = mlvo->variant;
583         }
584         else {
585             p = strdup(mlvo->variant);
586             if (p == NULL)
587                 return false;
588
589             squeeze_spaces(p);
590             mdefs->variant[1] = p;
591
592             for (i = 2; i <= XkbNumKbdGroups; i++) {
593                 if ((p = strchr(p, ','))) {
594                     *p++ = '\0';
595                     mdefs->variant[i] = p;
596                 }
597                 else {
598                     break;
599                 }
600             }
601
602             if (p && (p = strchr(p, ',')))
603                 *p = '\0';
604         }
605     }
606
607     return true;
608 }
609
610 static void
611 free_multi_defs(struct multi_defs *defs)
612 {
613     free(defs->options);
614     /*
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.
617      */
618     free(UNCONSTIFY(defs->layout[1]));
619     free(UNCONSTIFY(defs->variant[1]));
620 }
621
622 /* See apply_rule below. */
623 static void
624 apply(const char *src, char **dst)
625 {
626     int ret;
627     char *tmp;
628
629     if (!src)
630         return;
631
632     if (*src == '+' || *src == '!') {
633         tmp = *dst;
634         ret = asprintf(dst, "%s%s", *dst, src);
635         if (ret < 0)
636             *dst = NULL;
637         free(tmp);
638     }
639     else if (*dst == NULL) {
640         *dst = strdup(src);
641     }
642 }
643
644 /*
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.
649  */
650 static void
651 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
652 {
653     /* Clear the flag because it's applied. */
654     rule->flags &= ~RULE_FLAG_PENDING_MATCH;
655
656     apply(rule->keycodes, &kccgst->keycodes);
657     apply(rule->symbols, &kccgst->symbols);
658     apply(rule->types, &kccgst->types);
659     apply(rule->compat, &kccgst->compat);
660 }
661
662 /*
663  * Match if name is part of the group, e.g. if the following
664  * group is defined:
665  *      ! $qwertz = al cz de hr hu ro si sk
666  * then
667  *      match_group_member(rules, "qwertz", "hr")
668  * will return true.
669  */
670 static bool
671 match_group_member(struct rules *rules, const char *group_name,
672                    const char *name)
673 {
674     int i;
675     const char *word;
676     struct group *iter, *group = NULL;
677
678     darray_foreach(iter, rules->groups) {
679         if (streq(iter->name, group_name)) {
680             group = iter;
681             break;
682         }
683     }
684
685     if (!group)
686         return false;
687
688     word = group->words;
689     for (i = 0; i < group->number; i++, word += strlen(word) + 1)
690         if (streq(word, name))
691             return true;
692
693     return false;
694 }
695
696 /* Match @needle out of @sep-seperated @haystack. */
697 static bool
698 match_one_of(const char *haystack, const char *needle, char sep)
699 {
700     const char *s = haystack;
701     const size_t len = strlen(needle);
702
703     do {
704         if (strncmp(s, needle, len) == 0 && (s[len] == '\0' || s[len] == sep))
705             return true;
706         s = strchr(s, sep);
707     } while (s++);
708
709     return false;
710 }
711
712 static int
713 apply_rule_if_matches(struct rules *rules, struct rule *rule,
714                       struct multi_defs *mdefs,
715                       struct xkb_component_names *kccgst)
716 {
717     bool pending = false;
718
719     if (rule->model) {
720         if (mdefs->model == NULL)
721             return 0;
722
723         if (streq(rule->model, "*")) {
724             pending = true;
725         }
726         else if (rule->model[0] == '$') {
727             if (!match_group_member(rules, rule->model, mdefs->model))
728                 return 0;
729         }
730         else if (!streq(rule->model, mdefs->model)) {
731             return 0;
732         }
733     }
734
735     if (rule->option) {
736         if (mdefs->options == NULL)
737             return 0;
738
739         if (!match_one_of(mdefs->options, rule->option, ','))
740             return 0;
741     }
742
743     if (rule->layout) {
744         if (mdefs->layout[rule->layout_num] == NULL)
745             return 0;
746
747         if (streq(rule->layout, "*")) {
748             pending = true;
749         }
750         else if (rule->layout[0] == '$') {
751             if (!match_group_member(rules, rule->layout,
752                                     mdefs->layout[rule->layout_num]))
753                 return 0;
754         }
755         else if (!streq(rule->layout, mdefs->layout[rule->layout_num])) {
756             return 0;
757         }
758     }
759
760     if (rule->variant) {
761         if (mdefs->variant[rule->variant_num] == NULL)
762             return 0;
763
764         if (streq(rule->variant, "*")) {
765             pending = true;
766         }
767         else if (rule->variant[0] == '$') {
768             if (!match_group_member(rules, rule->variant,
769                                     mdefs->variant[rule->variant_num]))
770                 return 0;
771         }
772         else if (!streq(rule->variant, mdefs->variant[rule->variant_num])) {
773             return 0;
774         }
775     }
776
777     if (pending) {
778         rule->flags |= RULE_FLAG_PENDING_MATCH;
779     }
780     else {
781         /* Exact match, apply it now. */
782         apply_rule(rule, kccgst);
783     }
784
785     return rule->number;
786 }
787
788 static void
789 clear_partial_matches(struct rules *rules)
790 {
791     struct rule *rule;
792
793     darray_foreach(rule, rules->rules)
794         rule->flags &= ~RULE_FLAG_PENDING_MATCH;
795 }
796
797 static void
798 apply_partial_matches(struct rules *rules, struct xkb_component_names *kccgst)
799 {
800     struct rule *rule;
801
802     darray_foreach(rule, rules->rules)
803         if (rule->flags & RULE_FLAG_PENDING_MATCH)
804             apply_rule(rule, kccgst);
805 }
806
807 static void
808 apply_matching_rules(struct rules *rules, struct multi_defs *mdefs,
809                      struct xkb_component_names *kccgst, unsigned int flags)
810 {
811     int skip = -1;
812     struct rule *rule;
813
814     darray_foreach(rule, rules->rules) {
815         if ((rule->flags & flags) != flags)
816             continue;
817
818         if ((flags & RULE_FLAG_OPTION) == 0 && rule->number == skip)
819             continue;
820
821         skip = apply_rule_if_matches(rules, rule, mdefs, kccgst);
822     }
823 }
824
825 /***====================================================================***/
826
827 static char *
828 substitute_vars(char *name, struct multi_defs *mdefs)
829 {
830     char *str, *outstr, *var;
831     char *orig = name;
832     size_t len, extra_len;
833     char pfx, sfx;
834     int ndx;
835
836     if (!name)
837         return NULL;
838
839     str = strchr(name, '%');
840     if (str == NULL)
841         return name;
842
843     len = strlen(name);
844
845     while (str != NULL) {
846         pfx = str[1];
847         extra_len = 0;
848
849         if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
850             extra_len = 1;
851             str++;
852         }
853         else if (pfx == '(') {
854             extra_len = 2;
855             str++;
856         }
857
858         var = str + 1;
859         str = get_index(var + 1, &ndx);
860         if (ndx == -1) {
861             str = strchr(str, '%');
862             continue;
863         }
864
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;
871
872         if (pfx == '(' && *str == ')')
873             str++;
874
875         str = strchr(&str[0], '%');
876     }
877
878     name = malloc(len + 1);
879     str = orig;
880     outstr = name;
881
882     while (*str != '\0') {
883         if (str[0] == '%') {
884             str++;
885             pfx = str[0];
886             sfx = '\0';
887
888             if (pfx == '+' || pfx == '|' || pfx == '_' || pfx == '-') {
889                 str++;
890             }
891             else if (pfx == '(') {
892                 sfx = ')';
893                 str++;
894             }
895             else {
896                 pfx = '\0';
897             }
898
899             var = str;
900             str = get_index(var + 1, &ndx);
901             if (ndx == -1)
902                 continue;
903
904             if (*var == 'l' && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
905                 if (pfx)
906                     *outstr++ = pfx;
907
908                 strcpy(outstr, mdefs->layout[ndx]);
909                 outstr += strlen(mdefs->layout[ndx]);
910
911                 if (sfx)
912                     *outstr++ = sfx;
913             }
914             else if (*var == 'm' && mdefs->model) {
915                 if (pfx)
916                     *outstr++ = pfx;
917
918                 strcpy(outstr, mdefs->model);
919                 outstr += strlen(mdefs->model);
920
921                 if (sfx)
922                     *outstr++ = sfx;
923             }
924             else if (*var == 'v' && mdefs->variant[ndx] &&
925                      *mdefs->variant[ndx]) {
926                 if (pfx)
927                     *outstr++ = pfx;
928
929                 strcpy(outstr, mdefs->variant[ndx]);
930                 outstr += strlen(mdefs->variant[ndx]);
931
932                 if (sfx)
933                     *outstr++ = sfx;
934             }
935
936             if (pfx == '(' && *str == ')')
937                 str++;
938         }
939         else {
940             *outstr++ = *str++;
941         }
942     }
943
944     *outstr++ = '\0';
945
946     if (orig != name)
947         free(orig);
948
949     return name;
950 }
951
952 /***====================================================================***/
953
954 static bool
955 get_components(struct rules *rules, const struct xkb_rule_names *mlvo,
956                struct xkb_component_names *kccgst)
957 {
958     struct multi_defs mdefs;
959
960     memset(kccgst, 0, sizeof(*kccgst));
961
962     make_multi_defs(&mdefs, mlvo);
963
964     clear_partial_matches(rules);
965
966     apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_NORMAL);
967     apply_partial_matches(rules, kccgst);
968
969     apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_APPEND);
970     apply_partial_matches(rules, kccgst);
971
972     apply_matching_rules(rules, &mdefs, kccgst, RULE_FLAG_OPTION);
973     apply_partial_matches(rules, kccgst);
974
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);
979
980     free_multi_defs(&mdefs);
981
982     if (!kccgst->keycodes || !kccgst->symbols || !kccgst->types ||
983         !kccgst->compat) {
984         free(kccgst->keycodes);
985         free(kccgst->symbols);
986         free(kccgst->types);
987         free(kccgst->compat);
988         return false;
989     }
990     return true;
991 }
992
993 static struct rules *
994 load_rules(struct xkb_context *ctx, FILE *file)
995 {
996     darray_char line;
997     struct mapping mapping;
998     struct rule trule;
999     struct group tgroup;
1000     struct rules *rules;
1001     struct stat stat_buf;
1002     const char *buf, *end;
1003     char *orig;
1004     int fd = fileno(file);
1005
1006     if (fstat(fd, &stat_buf) != 0) {
1007         log_err(ctx, "couldn't stat rules file\n");
1008         return NULL;
1009     }
1010
1011     orig = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_SHARED, fd, 0);
1012     if (!orig) {
1013         log_err(ctx, "couldn't mmap rules file (%zu bytes)\n",
1014                 (size_t) stat_buf.st_size);
1015         return NULL;
1016     }
1017
1018     rules = calloc(1, sizeof(*rules));
1019     if (!rules)
1020         return NULL;
1021     darray_init(rules->rules);
1022     darray_growalloc(rules->rules, 16);
1023
1024     memset(&mapping, 0, sizeof(mapping));
1025     memset(&tgroup, 0, sizeof(tgroup));
1026     darray_init(line);
1027     darray_growalloc(line, 128);
1028
1029     buf = orig;
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));
1036             }
1037             else {
1038                 darray_append(rules->rules, trule);
1039                 memset(&trule, 0, sizeof(trule));
1040             }
1041         }
1042
1043         darray_resize(line, 0);
1044     }
1045
1046     munmap(orig, stat_buf.st_size);
1047
1048     darray_free(line);
1049     return rules;
1050 }
1051
1052 static void
1053 free_rules(struct rules *rules)
1054 {
1055     struct rule *rule;
1056     struct group *group;
1057
1058     if (!rules)
1059         return;
1060
1061     darray_foreach(rule, rules->rules) {
1062         free(rule->model);
1063         free(rule->layout);
1064         free(rule->variant);
1065         free(rule->option);
1066         free(rule->keycodes);
1067         free(rule->symbols);
1068         free(rule->types);
1069         free(rule->compat);
1070     }
1071     darray_free(rules->rules);
1072
1073     darray_foreach(group, rules->groups) {
1074         free(group->name);
1075         free(group->words);
1076     }
1077     darray_free(rules->groups);
1078
1079     free(rules);
1080 }
1081
1082 bool
1083 xkb_components_from_rules(struct xkb_context *ctx,
1084                           const struct xkb_rule_names *rmlvo,
1085                           struct xkb_component_names *out)
1086 {
1087     bool ret = false;
1088     struct rules *rules;
1089     FILE *file;
1090     char *path;
1091     char **include;
1092
1093     file = FindFileInXkbPath(ctx, rmlvo->rules, FILE_TYPE_RULES, &path);
1094     if (!file) {
1095         log_err(ctx, "Could not find \"%s\" rules in XKB path\n",
1096                 rmlvo->rules);
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);
1105         return false;
1106     }
1107
1108     rules = load_rules(ctx, file);
1109     if (!rules) {
1110         log_err(ctx, "Failed to load XKB rules \"%s\"\n", path);
1111         goto err_file;
1112     }
1113
1114     if (!get_components(rules, rmlvo, out)) {
1115         log_err(ctx, "No components returned from XKB rules \"%s\"\n", path);
1116         goto err_rules;
1117     }
1118
1119     ret = true;
1120
1121 err_rules:
1122     free_rules(rules);
1123 err_file:
1124     free(path);
1125     fclose(file);
1126     return ret;
1127 }