cd87327ed5e8664747927114bbd086d43b3b9b7d
[profile/ivi/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
30 #include "rules.h"
31 #include "path.h"
32
33 #define DFLT_LINE_SIZE 128
34
35 struct input_line {
36     size_t size;
37     size_t offset;
38     char buf[DFLT_LINE_SIZE];
39     char *line;
40 };
41
42 static void
43 input_line_init(struct input_line *line)
44 {
45     line->size = DFLT_LINE_SIZE;
46     line->offset = 0;
47     line->line = line->buf;
48 }
49
50 static void
51 input_line_deinit(struct input_line *line)
52 {
53     if (line->line != line->buf)
54         free(line->line);
55     line->offset = 0;
56     line->size = DFLT_LINE_SIZE;
57     line->line = line->buf;
58 }
59
60 static int
61 input_line_add_char(struct input_line *line, int ch)
62 {
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);
67         }
68         else {
69             line->line = realloc(line->line, line->size * 2);
70         }
71
72         line->size *= 2;
73     }
74
75     line->line[line->offset++] = ch;
76     return ch;
77 }
78
79 static bool
80 input_line_get(FILE *file, struct input_line *line)
81 {
82     int ch;
83     bool end_of_file = false;
84     bool space_pending;
85     bool slash_pending;
86     bool in_comment;
87
88     while (!end_of_file && line->offset == 0) {
89         space_pending = slash_pending = in_comment = false;
90
91         while ((ch = getc(file)) != '\n' && ch != EOF) {
92             if (ch == '\\') {
93                 ch = getc(file);
94
95                 if (ch == EOF)
96                     break;
97
98                 if (ch == '\n') {
99                     in_comment = false;
100                     ch = ' ';
101                 }
102             }
103
104             if (in_comment)
105                 continue;
106
107             if (ch == '/') {
108                 if (slash_pending) {
109                     in_comment = true;
110                     slash_pending = false;
111                 }
112                 else {
113                     slash_pending = true;
114                 }
115
116                 continue;
117             }
118
119             if (slash_pending) {
120                 if (space_pending) {
121                     input_line_add_char(line, ' ');
122                     space_pending = false;
123                 }
124
125                 input_line_add_char(line, '/');
126                 slash_pending = false;
127             }
128
129             if (isspace(ch)) {
130                 while (isspace(ch) && ch != '\n' && ch != EOF)
131                     ch = getc(file);
132
133                 if (ch == EOF)
134                     break;
135
136                 if (ch != '\n' && line->offset > 0)
137                     space_pending = true;
138
139                 ungetc(ch, file);
140             }
141             else {
142                 if (space_pending) {
143                     input_line_add_char(line, ' ');
144                     space_pending = false;
145                 }
146
147                 if (ch == '!') {
148                     if (line->offset != 0) {
149                         WARN("The '!' is legal only at start of line\n");
150                         ACTION("Line containing '!' ignored\n");
151                         line->offset = 0;
152                         break;
153                     }
154                 }
155
156                 input_line_add_char(line, ch);
157             }
158         }
159
160         if (ch == EOF)
161             end_of_file = true;
162     }
163
164     if (line->offset == 0 && end_of_file)
165         return false;
166
167     input_line_add_char(line, '\0');
168     return true;
169 }
170
171 /***====================================================================***/
172
173 enum {
174     /* "Parts" - the MLVO which rules file maps to components. */
175     MODEL = 0,
176     LAYOUT,
177     VARIANT,
178     OPTION,
179
180 #define PART_MASK \
181     ((1 << MODEL) | (1 << LAYOUT) | (1 << VARIANT) | (1 << OPTION))
182
183     /* Components */
184     KEYCODES,
185     SYMBOLS,
186     TYPES,
187     COMPAT,
188     GEOMETRY,
189     KEYMAP,
190
191 #define COMPONENT_MASK \
192     ((1 << KEYCODES) | (1 << SYMBOLS) | (1 << TYPES) | (1 << COMPAT) | \
193      (1 << GEOMETRY) | (1 << KEYMAP))
194
195     MAX_WORDS
196 };
197
198 static const char *cname[] = {
199     [MODEL] = "model",
200     [LAYOUT] = "layout",
201     [VARIANT] = "variant",
202     [OPTION] = "option",
203
204     [KEYCODES] = "keycodes",
205     [SYMBOLS] = "symbols",
206     [TYPES] = "types",
207     [COMPAT] = "compat",
208     [GEOMETRY] = "geometry",
209     [KEYMAP] = "keymap",
210 };
211
212 struct var_defs {
213     const char *model;
214     const char *layout;
215     const char *variant;
216     const char *options;
217 };
218
219 struct multi_defs {
220     const char *model;
221     const char *layout[XkbNumKbdGroups + 1];
222     const char *variant[XkbNumKbdGroups + 1];
223     char *options;
224 };
225
226 struct mapping {
227     /* Sequential id for the mappings. */
228     int number;
229     size_t num_maps;
230
231     struct {
232         int word;
233         int index;
234     } map[MAX_WORDS];
235 };
236
237 struct var_desc {
238     char *name;
239     char *desc;
240 };
241
242 struct describe_vars {
243     size_t sz_desc;
244     size_t num_desc;
245     struct var_desc *desc;
246 };
247
248 struct group {
249     int number;
250     char *name;
251     char *words;
252 };
253
254 enum rule_flag {
255     RULE_FLAG_PENDING_MATCH = (1L << 1),
256     RULE_FLAG_OPTION        = (1L << 2),
257     RULE_FLAG_APPEND        = (1L << 3),
258     RULE_FLAG_NORMAL        = (1L << 4),
259 };
260
261 struct rule {
262     int number;
263
264     char *model;
265     char *layout;
266     int layout_num;
267     char *variant;
268     int variant_num;
269     char *option;
270
271     /* yields */
272
273     char *keycodes;
274     char *symbols;
275     char *types;
276     char *compat;
277     char *keymap;
278     unsigned flags;
279 };
280
281 struct rules {
282     struct describe_vars models;
283     struct describe_vars layouts;
284     struct describe_vars variants;
285     struct describe_vars options;
286
287     size_t sz_rules;
288     size_t num_rules;
289     struct rule *rules;
290
291     size_t sz_groups;
292     size_t num_groups;
293     struct group *groups;
294 };
295
296 /***====================================================================***/
297
298 /*
299  * Resolve numeric index, such as "[4]" in layout[4]. Missing index
300  * means zero.
301  */
302 static char *
303 get_index(char *str, int *ndx)
304 {
305     int empty = 0, consumed = 0, num;
306
307     sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
308     if (consumed > 0) {
309         *ndx = num;
310         str += consumed;
311     } else if (empty > 0) {
312         *ndx = -1;
313     } else {
314         *ndx = 0;
315     }
316
317     return str;
318 }
319
320 /*
321  * Match a mapping line which opens a rule, e.g:
322  * ! model      layout[4]       variant[4]      =       symbols       geometry
323  * Which will be followed by lines such as:
324  *   *          ben             basic           =       +in(ben):4    nec(pc98)
325  * So if the MLVO matches the LHS of some line, we'll get the components
326  * on the RHS.
327  * In this example, we will get for the second and fourth columns:
328  * mapping->map[1] = {.word = LAYOUT, .index = 4}
329  * mapping->map[3] = {.word = SYMBOLS, .index = 0}
330  */
331 static void
332 match_mapping_line(struct input_line *line, struct mapping *mapping)
333 {
334     char *tok;
335     char *str = &line->line[1];
336     unsigned present = 0, layout_ndx_present = 0, variant_ndx_present = 0;
337     int i, tmp;
338     size_t len;
339     int ndx;
340     char *strtok_buf;
341     bool found;
342
343     /*
344      * Remember the last sequential mapping id (incremented if the match
345      * is successful).
346      */
347     tmp = mapping->number;
348     memset(mapping, 0, sizeof(*mapping));
349     mapping->number = tmp;
350
351     while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
352         found = false;
353         str = NULL;
354
355         if (strcmp(tok, "=") == 0)
356             continue;
357
358         for (i = 0; i < MAX_WORDS; i++) {
359             len = strlen(cname[i]);
360
361             if (strncmp(cname[i], tok, len) == 0) {
362                 if (strlen(tok) > len) {
363                     char *end = get_index(tok + len, &ndx);
364
365                     if ((i != LAYOUT && i != VARIANT) ||
366                         *end != '\0' || ndx == -1) {
367                         WARN("Illegal %s index: %d\n", cname[i], ndx);
368                         WARN("Can only index layout and variant\n");
369                         break;
370                     }
371
372                     if (ndx < 1 || ndx > XkbNumKbdGroups) {
373                         WARN("Illegal %s index: %d\n", cname[i], ndx);
374                         WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
375                         break;
376                     }
377                 } else {
378                     ndx = 0;
379                 }
380
381                 found = true;
382
383                 if (present & (1 << i)) {
384                     if ((i == LAYOUT && layout_ndx_present & (1 << ndx)) ||
385                         (i == VARIANT && variant_ndx_present & (1 << ndx))) {
386                         WARN("Component \"%s\" listed twice\n", tok);
387                         ACTION("Second definition ignored\n");
388                         break;
389                     }
390                 }
391
392                 present |= (1 << i);
393                 if (i == LAYOUT)
394                     layout_ndx_present |= 1 << ndx;
395                 if (i == VARIANT)
396                     variant_ndx_present |= 1 << ndx;
397
398                 mapping->map[mapping->num_maps].word = i;
399                 mapping->map[mapping->num_maps].index = ndx;
400                 mapping->num_maps++;
401                 break;
402             }
403         }
404
405         if (!found) {
406             WARN("Unknown component \"%s\"\n", tok);
407             ACTION("ignored\n");
408         }
409     }
410
411     if ((present & PART_MASK) == 0) {
412         WARN("Mapping needs at least one MLVO part\n");
413         ACTION("Illegal mapping ignored\n");
414         mapping->num_maps = 0;
415         return;
416     }
417
418     if ((present & COMPONENT_MASK) == 0) {
419         WARN("Mapping needs at least one component\n");
420         ACTION("Illegal mapping ignored\n");
421         mapping->num_maps = 0;
422         return;
423     }
424
425     if (((present & COMPONENT_MASK) & (1 << KEYMAP)) &&
426         ((present & COMPONENT_MASK) != (1 << KEYMAP))) {
427         WARN("Keymap cannot appear with other components\n");
428         ACTION("Illegal mapping ignored\n");
429         mapping->num_maps = 0;
430         return;
431     }
432
433     mapping->number++;
434 }
435
436 /*
437  * Match a line such as:
438  * ! $pcmodels = pc101 pc102 pc104 pc105
439  */
440 static bool
441 match_group_line(struct input_line *line, struct group *group)
442 {
443     int i;
444     char *name = strchr(line->line, '$');
445     char *words = strchr(name, ' ');
446
447     if (!words)
448         return false;
449
450     *words++ = '\0';
451
452     for (; *words; words++) {
453         if (*words != '=' && *words != ' ')
454             break;
455     }
456
457     if (*words == '\0')
458         return false;
459
460     group->name = strdup(name);
461     group->words = strdup(words);
462
463     words = group->words;
464     for (i = 1; *words; words++) {
465         if (*words == ' ') {
466             *words++ = '\0';
467             i++;
468         }
469     }
470     group->number = i;
471
472     return true;
473
474 }
475
476 /* Match lines following a mapping (see match_mapping_line comment). */
477 static bool
478 match_rule_line(struct input_line *line, struct mapping *mapping,
479                 struct rule *rule)
480 {
481     char *str, *tok;
482     int nread, i;
483     char *strtok_buf;
484     bool append = false;
485     const char *names[MAX_WORDS] = { NULL };
486
487     if (mapping->num_maps == 0) {
488         WARN("Must have a mapping before first line of data\n");
489         ACTION("Illegal line of data ignored\n");
490         return false;
491     }
492
493     str = line->line;
494
495     for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
496         str = NULL;
497
498         if (strcmp(tok, "=") == 0) {
499             nread--;
500             continue;
501         }
502
503         if (nread > mapping->num_maps) {
504             WARN("Too many words on a line\n");
505             ACTION("Extra word \"%s\" ignored\n", tok);
506             continue;
507         }
508
509         names[mapping->map[nread].word] = tok;
510         if (*tok == '+' || *tok == '|')
511             append = true;
512     }
513
514     if (nread < mapping->num_maps) {
515         WARN("Too few words on a line: %s\n", line->line);
516         ACTION("line ignored\n");
517         return false;
518     }
519
520     rule->flags = 0;
521     rule->number = mapping->number;
522
523     if (names[OPTION])
524         rule->flags |= RULE_FLAG_OPTION;
525     else if (append)
526         rule->flags |= RULE_FLAG_APPEND;
527     else
528         rule->flags |= RULE_FLAG_NORMAL;
529
530     rule->model = uDupString(names[MODEL]);
531     rule->layout = uDupString(names[LAYOUT]);
532     rule->variant = uDupString(names[VARIANT]);
533     rule->option = uDupString(names[OPTION]);
534
535     rule->keycodes = uDupString(names[KEYCODES]);
536     rule->symbols = uDupString(names[SYMBOLS]);
537     rule->types = uDupString(names[TYPES]);
538     rule->compat = uDupString(names[COMPAT]);
539     rule->keymap = uDupString(names[KEYMAP]);
540
541     rule->layout_num = rule->variant_num = 0;
542     for (i = 0; i < nread; i++) {
543         if (mapping->map[i].index) {
544             if (mapping->map[i].word == LAYOUT)
545                 rule->layout_num = mapping->map[i].index;
546             if (mapping->map[i].word == VARIANT)
547                 rule->variant_num = mapping->map[i].index;
548         }
549     }
550
551     return true;
552 }
553
554 static bool
555 match_line(struct input_line *line, struct mapping *mapping,
556            struct rule *rule, struct group *group)
557 {
558     if (line->line[0] != '!')
559         return match_rule_line(line, mapping, rule);
560
561     if (line->line[1] == '$' || (line->line[1] == ' ' && line->line[2] == '$'))
562         return match_group_line(line, group);
563
564     match_mapping_line(line, mapping);
565     return false;
566 }
567
568 static void
569 squeeze_spaces(char *p1)
570 {
571    char *p2;
572
573    for (p2 = p1; *p2; p2++) {
574        *p1 = *p2;
575        if (*p1 != ' ')
576            p1++;
577    }
578
579    *p1 = '\0';
580 }
581
582 /*
583  * Expand the layout and variant of a var_defs and remove extraneous spaces.
584  * If there's one layout/variant, it is kept in .layout[0]/.variant[0], else
585  * is kept in [1], [2] and so on, and [0] remains empty.
586  * For example, this var_defs:
587  *      .model  = "pc105",
588  *      .layout = "us,il,ru,ca"
589  *      .variant = ",,,multix"
590  *      .options = "grp:alts_toggle,   ctrl:nocaps,  compose:rwin"
591  * Is expanded into this multi_defs:
592  *      .model = "pc105"
593  *      .layout = {NULL, "us", "il", "ru", "ca"},
594  *      .variant = {NULL, "", "", "", "multix"},
595  *      .options = "grp:alts_toggle,ctrl:nocaps,compose:rwin"
596  */
597 static bool
598 make_multi_defs(struct multi_defs *mdefs, struct var_defs *defs)
599 {
600     char *p;
601     int i;
602
603     memset(mdefs, 0, sizeof(*mdefs));
604
605     if (defs->model) {
606         mdefs->model = defs->model;
607     }
608
609     if (defs->options) {
610         mdefs->options = strdup(defs->options);
611         if (mdefs->options == NULL)
612             return false;
613
614         squeeze_spaces(mdefs->options);
615     }
616
617     if (defs->layout) {
618         if (!strchr(defs->layout, ',')) {
619             mdefs->layout[0] = defs->layout;
620         }
621         else {
622             p = strdup(defs->layout);
623             if (p == NULL)
624                 return false;
625
626             squeeze_spaces(p);
627             mdefs->layout[1] = p;
628
629             for (i = 2; i <= XkbNumKbdGroups; i++) {
630                 if ((p = strchr(p, ','))) {
631                     *p++ = '\0';
632                     mdefs->layout[i] = p;
633                 }
634                 else {
635                     break;
636                 }
637             }
638
639             if (p && (p = strchr(p, ',')))
640                 *p = '\0';
641         }
642     }
643
644     if (defs->variant) {
645         if (!strchr(defs->variant, ',')) {
646             mdefs->variant[0] = defs->variant;
647         }
648         else {
649             p = strdup(defs->variant);
650             if (p == NULL)
651                 return false;
652
653             squeeze_spaces(p);
654             mdefs->variant[1] = p;
655
656             for (i = 2; i <= XkbNumKbdGroups; i++) {
657                 if ((p = strchr(p, ','))) {
658                     *p++ = '\0';
659                     mdefs->variant[i] = p;
660                 } else {
661                     break;
662                 }
663             }
664
665             if (p && (p = strchr(p, ',')))
666                 *p = '\0';
667         }
668     }
669
670     return true;
671 }
672
673 static void
674 free_multi_defs(struct multi_defs *defs)
675 {
676     free(defs->options);
677     /*
678      * See make_multi_defs comment for the hack; the same strdup'd
679      * string is split among the indexes, but the one in [0] is const.
680      */
681     free(UNCONSTIFY(defs->layout[1]));
682     free(UNCONSTIFY(defs->variant[1]));
683 }
684
685 /* See apply_rule below. */
686 static void
687 apply(const char *src, char **dst)
688 {
689     int ret;
690     char *tmp;
691
692     if (!src)
693         return;
694
695     if (*src == '+' || *src == '!') {
696         tmp = *dst;
697         ret = asprintf(dst, "%s%s", *dst, src);
698         if (ret < 0)
699             *dst = NULL;
700         free(tmp);
701     }
702     else if (*dst == NULL) {
703         *dst = strdup(src);
704     }
705 }
706
707 /*
708  * Add the info from the matching rule to the resulting
709  * xkb_component_names. If we already had a match for something
710  * (e.g. keycodes), and the rule is not an appending one (e.g.
711  * +whatever), than we don't override but drop the new one.
712  */
713 static void
714 apply_rule(struct rule *rule, struct xkb_component_names *kccgst)
715 {
716     /* Clear the flag because it's applied. */
717     rule->flags &= ~RULE_FLAG_PENDING_MATCH;
718
719     apply(rule->keycodes, &kccgst->keycodes);
720     apply(rule->symbols, &kccgst->symbols);
721     apply(rule->types, &kccgst->types);
722     apply(rule->compat, &kccgst->compat);
723     apply(rule->keymap, &kccgst->keymap);
724 }
725
726 static bool
727 CheckGroup(struct rules *rules, const char *group_name, const char *name)
728 {
729    int i;
730    const char *p;
731    struct group *group;
732
733    for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
734        if (! strcmp(group->name, group_name)) {
735            break;
736        }
737    }
738    if (i == rules->num_groups)
739        return false;
740    for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
741        if (! strcmp(p, name)) {
742            return true;
743        }
744    }
745    return false;
746 }
747
748 static bool
749 MatchOneOf(char *wanted, char *vals_defined)
750 {
751     char *str, *next;
752     int want_len = strlen(wanted);
753
754     for (str = vals_defined, next = NULL; str != NULL; str = next) {
755         int len;
756         next = strchr(str, ',');
757         if (next) {
758             len = next-str;
759             next++;
760         }
761         else {
762             len = strlen(str);
763         }
764
765         if (len == want_len && strncmp(wanted, str, len) == 0)
766             return true;
767     }
768
769     return false;
770 }
771
772 static int
773 XkbRF_CheckApplyRule(struct rule *rule, struct multi_defs *mdefs,
774                      struct xkb_component_names *names, struct rules *rules)
775 {
776     bool pending = false;
777
778     if (rule->model != NULL) {
779         if(mdefs->model == NULL)
780             return 0;
781         if (strcmp(rule->model, "*") == 0) {
782             pending = true;
783         } else {
784             if (rule->model[0] == '$') {
785                if (!CheckGroup(rules, rule->model, mdefs->model))
786                   return 0;
787             } else {
788                if (strcmp(rule->model, mdefs->model) != 0)
789                   return 0;
790             }
791         }
792     }
793     if (rule->option != NULL) {
794         if (mdefs->options == NULL)
795             return 0;
796         if ((!MatchOneOf(rule->option,mdefs->options)))
797             return 0;
798     }
799
800     if (rule->layout != NULL) {
801         if(mdefs->layout[rule->layout_num] == NULL ||
802            *mdefs->layout[rule->layout_num] == '\0')
803             return 0;
804         if (strcmp(rule->layout, "*") == 0) {
805             pending = true;
806         } else {
807             if (rule->layout[0] == '$') {
808                if (!CheckGroup(rules, rule->layout,
809                                mdefs->layout[rule->layout_num]))
810                   return 0;
811             } else {
812                if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
813                    return 0;
814             }
815         }
816     }
817     if (rule->variant != NULL) {
818         if (mdefs->variant[rule->variant_num] == NULL ||
819             *mdefs->variant[rule->variant_num] == '\0')
820             return 0;
821         if (strcmp(rule->variant, "*") == 0) {
822             pending = true;
823         } else {
824             if (rule->variant[0] == '$') {
825                if (!CheckGroup(rules, rule->variant,
826                                mdefs->variant[rule->variant_num]))
827                   return 0;
828             } else {
829                if (strcmp(rule->variant,
830                           mdefs->variant[rule->variant_num]) != 0)
831                    return 0;
832             }
833         }
834     }
835     if (pending) {
836         rule->flags |= RULE_FLAG_PENDING_MATCH;
837         return rule->number;
838     }
839     /* exact match, apply it now */
840     apply_rule(rule, names);
841     return rule->number;
842 }
843
844 static void
845 XkbRF_ClearPartialMatches(struct rules *rules)
846 {
847     int i;
848     struct rule *rule;
849
850     for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
851         rule->flags &= ~RULE_FLAG_PENDING_MATCH;
852     }
853 }
854
855 static void
856 XkbRF_ApplyPartialMatches(struct rules *rules,
857                           struct xkb_component_names *names)
858 {
859     int i;
860     struct rule *rule;
861
862     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
863         if ((rule->flags & RULE_FLAG_PENDING_MATCH)==0)
864             continue;
865         apply_rule(rule, names);
866     }
867 }
868
869 static void
870 XkbRF_CheckApplyRules(struct rules *rules, struct multi_defs *mdefs,
871                       struct xkb_component_names *names, unsigned int flags)
872 {
873     int i;
874     struct rule *rule;
875     int skip;
876
877     for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
878         if ((rule->flags & flags) != flags)
879             continue;
880         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
881         if (skip && !(flags & RULE_FLAG_OPTION)) {
882             for ( ;(i < rules->num_rules) && (rule->number == skip);
883                   rule++, i++);
884             rule--; i--;
885         }
886     }
887 }
888
889 /***====================================================================***/
890
891 static char *
892 XkbRF_SubstituteVars(char *name, struct multi_defs *mdefs)
893 {
894     char *str, *outstr, *orig, *var;
895     size_t len;
896     int ndx;
897
898     if (!name)
899         return NULL;
900
901     orig= name;
902     str= strchr(name,'%');
903     if (str==NULL)
904         return name;
905     len= strlen(name);
906     while (str!=NULL) {
907         char pfx= str[1];
908         int   extra_len= 0;
909         if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
910             extra_len= 1;
911             str++;
912         }
913         else if (pfx=='(') {
914             extra_len= 2;
915             str++;
916         }
917         var = str + 1;
918         str = get_index(var + 1, &ndx);
919         if (ndx == -1) {
920             str = strchr(str,'%');
921             continue;
922         }
923         if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
924             len+= strlen(mdefs->layout[ndx])+extra_len;
925         else if ((*var=='m')&&mdefs->model)
926             len+= strlen(mdefs->model)+extra_len;
927         else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
928             len+= strlen(mdefs->variant[ndx])+extra_len;
929         if ((pfx=='(')&&(*str==')')) {
930             str++;
931         }
932         str= strchr(&str[0],'%');
933     }
934     name = malloc(len + 1);
935     str= orig;
936     outstr= name;
937     while (*str!='\0') {
938         if (str[0]=='%') {
939             char pfx,sfx;
940             str++;
941             pfx= str[0];
942             sfx= '\0';
943             if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
944                 str++;
945             }
946             else if (pfx=='(') {
947                 sfx= ')';
948                 str++;
949             }
950             else pfx= '\0';
951
952             var = str;
953             str = get_index(var + 1, &ndx);
954             if (ndx == -1) {
955                 continue;
956             }
957             if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
958                 if (pfx) *outstr++= pfx;
959                 strcpy(outstr,mdefs->layout[ndx]);
960                 outstr+= strlen(mdefs->layout[ndx]);
961                 if (sfx) *outstr++= sfx;
962             }
963             else if ((*var=='m')&&(mdefs->model)) {
964                 if (pfx) *outstr++= pfx;
965                 strcpy(outstr,mdefs->model);
966                 outstr+= strlen(mdefs->model);
967                 if (sfx) *outstr++= sfx;
968             }
969             else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
970                 if (pfx) *outstr++= pfx;
971                 strcpy(outstr,mdefs->variant[ndx]);
972                 outstr+= strlen(mdefs->variant[ndx]);
973                 if (sfx) *outstr++= sfx;
974             }
975             if ((pfx=='(')&&(*str==')'))
976                 str++;
977         }
978         else {
979             *outstr++= *str++;
980         }
981     }
982     *outstr++= '\0';
983     if (orig!=name)
984         free(orig);
985     return name;
986 }
987
988 /***====================================================================***/
989
990 static bool
991 XkbcRF_GetComponents(struct rules *rules, struct var_defs *defs,
992                      struct xkb_component_names *names)
993 {
994     struct multi_defs mdefs;
995
996     make_multi_defs(&mdefs, defs);
997
998     memset(names, 0, sizeof(struct xkb_component_names));
999     XkbRF_ClearPartialMatches(rules);
1000     XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_NORMAL);
1001     XkbRF_ApplyPartialMatches(rules, names);
1002     XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_APPEND);
1003     XkbRF_ApplyPartialMatches(rules, names);
1004     XkbRF_CheckApplyRules(rules, &mdefs, names, RULE_FLAG_OPTION);
1005     XkbRF_ApplyPartialMatches(rules, names);
1006
1007     names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
1008     names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
1009     names->types = XkbRF_SubstituteVars(names->types, &mdefs);
1010     names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
1011     names->keymap = XkbRF_SubstituteVars(names->keymap, &mdefs);
1012
1013     free_multi_defs(&mdefs);
1014
1015     return (names->keycodes && names->symbols && names->types &&
1016             names->compat) || names->keymap;
1017 }
1018
1019 static struct rule *
1020 XkbcRF_AddRule(struct rules *rules)
1021 {
1022     if (rules->sz_rules<1) {
1023         rules->sz_rules= 16;
1024         rules->num_rules= 0;
1025         rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
1026     }
1027     else if (rules->num_rules>=rules->sz_rules) {
1028         rules->sz_rules*= 2;
1029         rules->rules = realloc(rules->rules,
1030                                rules->sz_rules * sizeof(*rules->rules));
1031     }
1032     if (!rules->rules) {
1033         rules->sz_rules= rules->num_rules= 0;
1034         return NULL;
1035     }
1036     memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
1037     return &rules->rules[rules->num_rules++];
1038 }
1039
1040 static struct group *
1041 XkbcRF_AddGroup(struct rules *rules)
1042 {
1043     if (rules->sz_groups<1) {
1044         rules->sz_groups= 16;
1045         rules->num_groups= 0;
1046         rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
1047     }
1048     else if (rules->num_groups >= rules->sz_groups) {
1049         rules->sz_groups *= 2;
1050         rules->groups = realloc(rules->groups,
1051                                 rules->sz_groups * sizeof(*rules->groups));
1052     }
1053     if (!rules->groups) {
1054         rules->sz_groups= rules->num_groups= 0;
1055         return NULL;
1056     }
1057
1058     memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
1059     return &rules->groups[rules->num_groups++];
1060 }
1061
1062 static struct rules *
1063 XkbcRF_LoadRules(FILE *file)
1064 {
1065     struct input_line line;
1066     struct mapping mapping;
1067     struct rule trule, *rule;
1068     struct group tgroup, *group;
1069     struct rules *rules;
1070
1071     rules = calloc(1, sizeof(*rules));
1072     if (!rules)
1073         return NULL;
1074
1075     memset(&mapping, 0, sizeof(mapping));
1076     memset(&tgroup, 0, sizeof(tgroup));
1077     input_line_init(&line);
1078
1079     while (input_line_get(file, &line)) {
1080         if (match_line(&line, &mapping, &trule, &tgroup)) {
1081             if (tgroup.number) {
1082                 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
1083                     *group= tgroup;
1084                     memset(&tgroup, 0, sizeof(tgroup));
1085                 }
1086             } else {
1087                 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
1088                     *rule= trule;
1089                     memset(&trule, 0, sizeof(trule));
1090                 }
1091             }
1092         }
1093         line.offset = 0;
1094     }
1095     input_line_deinit(&line);
1096     return rules;
1097 }
1098
1099 static void
1100 XkbRF_ClearVarDescriptions(struct describe_vars *var)
1101 {
1102     int i;
1103
1104     for (i=0;i<var->num_desc;i++) {
1105         free(var->desc[i].name);
1106         free(var->desc[i].desc);
1107         var->desc[i].name= var->desc[i].desc= NULL;
1108     }
1109     free(var->desc);
1110     var->desc= NULL;
1111 }
1112
1113 static void
1114 XkbcRF_Free(struct rules *rules)
1115 {
1116     int i;
1117     struct rule *rule;
1118     struct group *group;
1119
1120     if (!rules)
1121         return;
1122     XkbRF_ClearVarDescriptions(&rules->models);
1123     XkbRF_ClearVarDescriptions(&rules->layouts);
1124     XkbRF_ClearVarDescriptions(&rules->variants);
1125     XkbRF_ClearVarDescriptions(&rules->options);
1126
1127     for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1128         free(rule->model);
1129         free(rule->layout);
1130         free(rule->variant);
1131         free(rule->option);
1132         free(rule->keycodes);
1133         free(rule->symbols);
1134         free(rule->types);
1135         free(rule->compat);
1136         free(rule->keymap);
1137     }
1138     free(rules->rules);
1139
1140     for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1141         free(group->name);
1142         free(group->words);
1143     }
1144     free(rules->groups);
1145
1146     free(rules);
1147 }
1148
1149 struct xkb_component_names *
1150 xkb_components_from_rules(struct xkb_context *ctx,
1151                           const struct xkb_rule_names *rmlvo)
1152 {
1153     int i;
1154     FILE *rulesFile;
1155     char *rulesPath;
1156     struct rules *loaded;
1157     struct xkb_component_names *names = NULL;
1158     struct var_defs defs = {
1159         .model = rmlvo->model,
1160         .layout = rmlvo->layout,
1161         .variant = rmlvo->variant,
1162         .options = rmlvo->options,
1163     };
1164
1165     rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
1166                                   &rulesPath);
1167     if (!rulesFile) {
1168         ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1169         ERROR("%d include paths searched:\n",
1170               xkb_context_num_include_paths(ctx));
1171         for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1172             ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1173         return NULL;
1174     }
1175
1176     loaded = XkbcRF_LoadRules(rulesFile);
1177     if (!loaded) {
1178         ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
1179         goto unwind_file;
1180     }
1181
1182     names = calloc(1, sizeof(*names));
1183     if (!names) {
1184         ERROR("failed to allocate XKB components\n");
1185         goto unwind_file;
1186     }
1187
1188     if (!XkbcRF_GetComponents(loaded, &defs, names)) {
1189         free(names->keymap);
1190         free(names->keycodes);
1191         free(names->types);
1192         free(names->compat);
1193         free(names->symbols);
1194         free(names);
1195         names = NULL;
1196         ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
1197     }
1198
1199 unwind_file:
1200     XkbcRF_Free(loaded);
1201     if (rulesFile)
1202         fclose(rulesFile);
1203     free(rulesPath);
1204     return names;
1205 }