00667c990a1905fd35b0817768a65a1d1f6598fc
[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 #define MODEL           0
174 #define LAYOUT          1
175 #define VARIANT         2
176 #define OPTION          3
177 #define KEYCODES        4
178 #define SYMBOLS         5
179 #define TYPES           6
180 #define COMPAT          7
181 #define GEOMETRY        8
182 #define KEYMAP          9
183 #define MAX_WORDS       10
184
185 #define PART_MASK       0x000F
186 #define COMPONENT_MASK  0x03F0
187
188 static  const char * cname[MAX_WORDS] = {
189         "model", "layout", "variant", "option",
190         "keycodes", "symbols", "types", "compat", "geometry", "keymap"
191 };
192
193 struct var_defs {
194     const char *model;
195     const char *layout;
196     const char *variant;
197     const char *options;
198 };
199
200 struct remap_spec {
201     int number;
202     size_t num_remap;
203     struct {
204         int word;
205         int index;
206     } remap[MAX_WORDS];
207 };
208
209 struct file_spec {
210     char *name[MAX_WORDS];
211     struct file_spec *pending;
212 };
213
214 struct multi_defs {
215     const char *model;
216     const char *layout[XkbNumKbdGroups + 1];
217     const char *variant[XkbNumKbdGroups + 1];
218     char *options;
219 };
220
221 struct var_desc {
222     char *name;
223     char *desc;
224 };
225
226 struct describe_vars {
227     size_t sz_desc;
228     size_t num_desc;
229     struct var_desc *desc;
230 };
231
232 struct group {
233     int number;
234     char *name;
235     char *words;
236 };
237
238 #define XkbRF_PendingMatch  (1L<<1)
239 #define XkbRF_Option        (1L<<2)
240 #define XkbRF_Append        (1L<<3)
241 #define XkbRF_Normal        (1L<<4)
242 #define XkbRF_Invalid       (1L<<5)
243
244 struct rule {
245     int number;
246     int layout_num;
247     int variant_num;
248     char *model;
249     char *layout;
250     char *variant;
251     char *option;
252
253     /* yields */
254
255     char *keycodes;
256     char *symbols;
257     char *types;
258     char *compat;
259     char *keymap;
260     unsigned flags;
261 };
262
263 struct rules {
264     struct describe_vars models;
265     struct describe_vars layouts;
266     struct describe_vars variants;
267     struct describe_vars options;
268
269     size_t sz_rules;
270     size_t num_rules;
271     struct rule *rules;
272
273     size_t sz_groups;
274     size_t num_groups;
275     struct group *groups;
276 };
277
278 /***====================================================================***/
279
280 /*
281  * Resolve numeric index, such as "[4]" in layout[4]. Missing index
282  * means zero.
283  */
284 static char *
285 get_index(char *str, int *ndx)
286 {
287     int empty = 0, consumed = 0, num;
288
289     sscanf(str, "[%n%d]%n", &empty, &num, &consumed);
290     if (consumed > 0) {
291         *ndx = num;
292         str += consumed;
293     } else if (empty > 0) {
294         *ndx = -1;
295     } else {
296         *ndx = 0;
297     }
298
299     return str;
300 }
301
302 static void
303 SetUpRemap(struct input_line *line, struct remap_spec *remap)
304 {
305    char *tok, *str;
306    unsigned present, l_ndx_present, v_ndx_present;
307    int i;
308    size_t len;
309    int ndx;
310    char *strtok_buf;
311    bool found;
312
313    l_ndx_present = v_ndx_present = present= 0;
314    str= &line->line[1];
315    len = remap->number;
316    memset(remap, 0, sizeof(*remap));
317    remap->number = len;
318    while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
319         found= false;
320         str= NULL;
321         if (strcmp(tok,"=")==0)
322             continue;
323         for (i=0;i<MAX_WORDS;i++) {
324             len = strlen(cname[i]);
325             if (strncmp(cname[i],tok,len)==0) {
326                 if(strlen(tok) > len) {
327                     char *end = get_index(tok+len, &ndx);
328                     if ((i != LAYOUT && i != VARIANT) ||
329                         *end != '\0' || ndx == -1)
330                         break;
331                      if (ndx < 1 || ndx > XkbNumKbdGroups) {
332                         WARN("Illegal %s index: %d\n", cname[i], ndx);
333                         WARN("Index must be in range 1..%d\n", XkbNumKbdGroups);
334                         break;
335                      }
336                 } else {
337                     ndx = 0;
338                 }
339
340                 found= true;
341
342                 if (present&(1<<i)) {
343                     if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
344                         (i == VARIANT && v_ndx_present&(1<<ndx)) ) {
345                         WARN("Component \"%s\" listed twice\n", tok);
346                         ACTION("Second definition ignored\n");
347                         break;
348                     }
349                 }
350                 present |= (1<<i);
351                 if (i == LAYOUT)
352                     l_ndx_present |= 1 << ndx;
353                 if (i == VARIANT)
354                     v_ndx_present |= 1 << ndx;
355                 remap->remap[remap->num_remap].word= i;
356                 remap->remap[remap->num_remap++].index= ndx;
357                 break;
358             }
359         }
360
361         if (!found) {
362             WARN("Unknown component \"%s\"\n", tok);
363             ACTION("ignored\n");
364         }
365    }
366    if ((present&PART_MASK)==0) {
367         unsigned mask= PART_MASK;
368
369         /* FIXME: Use log function instead of fprintf. */
370         WARN("Mapping needs at least one of ");
371         for (i=0; (i<MAX_WORDS); i++) {
372             if ((1L<<i)&mask) {
373                 mask&= ~(1L<<i);
374                 if (mask)       fprintf(stderr,"\"%s,\" ",cname[i]);
375                 else            fprintf(stderr,"or \"%s\"\n",cname[i]);
376             }
377         }
378         ACTION("Illegal mapping ignored\n");
379
380         remap->num_remap= 0;
381         return;
382    }
383    if ((present&COMPONENT_MASK)==0) {
384         WARN("Mapping needs at least one component\n");
385         ACTION("Illegal mapping ignored\n");
386         remap->num_remap= 0;
387         return;
388    }
389    if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
390                                 ((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
391         WARN("Keymap cannot appear with other components\n");
392         ACTION("Illegal mapping ignored\n");
393         remap->num_remap= 0;
394         return;
395    }
396    remap->number++;
397 }
398
399 static bool
400 MatchOneOf(char *wanted,char *vals_defined)
401 {
402     char *str, *next;
403     int want_len = strlen(wanted);
404
405     for (str=vals_defined,next=NULL;str!=NULL;str=next) {
406         int len;
407         next= strchr(str,',');
408         if (next) {
409             len= next-str;
410             next++;
411         }
412         else {
413             len= strlen(str);
414         }
415         if ((len==want_len)&&(strncmp(wanted,str,len)==0))
416             return true;
417     }
418     return false;
419 }
420
421 /***====================================================================***/
422
423 static bool
424 CheckLine(struct input_line *line, struct remap_spec *remap,
425           struct rule *rule, struct group *group)
426 {
427     char *str, *tok;
428     int nread, i;
429     struct file_spec tmp;
430     char *strtok_buf;
431     bool append = false;
432
433     if (line->line[0]=='!') {
434         if (line->line[1] == '$' ||
435             (line->line[1] == ' ' && line->line[2] == '$')) {
436             char *gname = strchr(line->line, '$');
437             char *words = strchr(gname, ' ');
438             if(!words)
439                 return false;
440             *words++ = '\0';
441             for (; *words; words++) {
442                 if (*words != '=' && *words != ' ')
443                     break;
444             }
445             if (*words == '\0')
446                 return false;
447             group->name = uDupString(gname);
448             group->words = uDupString(words);
449             for (i = 1, words = group->words; *words; words++) {
450                  if ( *words == ' ') {
451                      *words++ = '\0';
452                      i++;
453                  }
454             }
455             group->number = i;
456             return true;
457         } else {
458             SetUpRemap(line,remap);
459             return false;
460         }
461     }
462
463     if (remap->num_remap==0) {
464         WARN("Must have a mapping before first line of data\n");
465         ACTION("Illegal line of data ignored\n");
466         return false;
467     }
468     memset(&tmp, 0, sizeof(tmp));
469     str= line->line;
470     for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
471         str= NULL;
472         if (strcmp(tok,"=")==0) {
473             nread--;
474             continue;
475         }
476         if (nread>remap->num_remap) {
477             WARN("Too many words on a line\n");
478             ACTION("Extra word \"%s\" ignored\n",tok);
479             continue;
480         }
481         tmp.name[remap->remap[nread].word]= tok;
482         if (*tok == '+' || *tok == '|')
483             append = true;
484     }
485     if (nread<remap->num_remap) {
486         WARN("Too few words on a line: %s\n", line->line);
487         ACTION("line ignored\n");
488         return false;
489     }
490
491     rule->flags= 0;
492     rule->number = remap->number;
493     if (tmp.name[OPTION])
494          rule->flags|= XkbRF_Option;
495     else if (append)
496          rule->flags|= XkbRF_Append;
497     else
498          rule->flags|= XkbRF_Normal;
499     rule->model= uDupString(tmp.name[MODEL]);
500     rule->layout= uDupString(tmp.name[LAYOUT]);
501     rule->variant= uDupString(tmp.name[VARIANT]);
502     rule->option= uDupString(tmp.name[OPTION]);
503
504     rule->keycodes= uDupString(tmp.name[KEYCODES]);
505     rule->symbols= uDupString(tmp.name[SYMBOLS]);
506     rule->types= uDupString(tmp.name[TYPES]);
507     rule->compat= uDupString(tmp.name[COMPAT]);
508     rule->keymap= uDupString(tmp.name[KEYMAP]);
509
510     rule->layout_num = rule->variant_num = 0;
511     for (i = 0; i < nread; i++) {
512         if (remap->remap[i].index) {
513             if (remap->remap[i].word == LAYOUT)
514                 rule->layout_num = remap->remap[i].index;
515             if (remap->remap[i].word == VARIANT)
516                 rule->variant_num = remap->remap[i].index;
517         }
518     }
519     return true;
520 }
521
522 static char *
523 _Concat(char *str1,char *str2)
524 {
525     int len;
526
527     if ((!str1)||(!str2))
528         return str1;
529     len= strlen(str1)+strlen(str2)+1;
530     str1 = uTypedRealloc(str1, len, char);
531     if (str1)
532         strcat(str1,str2);
533     return str1;
534 }
535
536 static void
537 squeeze_spaces(char *p1)
538 {
539    char *p2;
540    for (p2 = p1; *p2; p2++) {
541        *p1 = *p2;
542        if (*p1 != ' ') p1++;
543    }
544    *p1 = '\0';
545 }
546
547 static bool
548 MakeMultiDefs(struct multi_defs *mdefs, const struct var_defs *defs)
549 {
550    memset(mdefs, 0, sizeof(*mdefs));
551    mdefs->model = defs->model;
552    mdefs->options = uDupString(defs->options);
553    if (mdefs->options) squeeze_spaces(mdefs->options);
554
555    if (defs->layout) {
556        if (!strchr(defs->layout, ',')) {
557            mdefs->layout[0] = defs->layout;
558        } else {
559            char *p;
560            int i;
561            p = uDupString(defs->layout);
562            if (p == NULL)
563               return false;
564            squeeze_spaces(p);
565            mdefs->layout[1] = p;
566            for (i = 2; i <= XkbNumKbdGroups; i++) {
567               if ((p = strchr(p, ','))) {
568                  *p++ = '\0';
569                  mdefs->layout[i] = p;
570               } else {
571                  break;
572               }
573            }
574            if (p && (p = strchr(p, ',')))
575               *p = '\0';
576        }
577    }
578
579    if (defs->variant) {
580        if (!strchr(defs->variant, ',')) {
581            mdefs->variant[0] = defs->variant;
582        } else {
583            char *p;
584            int i;
585            p = uDupString(defs->variant);
586            if (p == NULL)
587               return false;
588            squeeze_spaces(p);
589            mdefs->variant[1] = p;
590            for (i = 2; i <= XkbNumKbdGroups; i++) {
591               if ((p = strchr(p, ','))) {
592                  *p++ = '\0';
593                  mdefs->variant[i] = p;
594               } else {
595                  break;
596               }
597            }
598            if (p && (p = strchr(p, ',')))
599               *p = '\0';
600        }
601    }
602    return true;
603 }
604
605 static void
606 FreeMultiDefs(struct multi_defs *defs)
607 {
608     free(defs->options);
609     free(UNCONSTIFY(defs->layout[1]));
610     free(UNCONSTIFY(defs->variant[1]));
611 }
612
613 static void
614 Apply(char *src, char **dst)
615 {
616     if (src) {
617         if (*src == '+' || *src == '!') {
618             *dst= _Concat(*dst, src);
619         } else {
620             if (*dst == NULL)
621                 *dst= uDupString(src);
622         }
623     }
624 }
625
626 static void
627 XkbRF_ApplyRule(struct rule *rule, struct xkb_component_names *names)
628 {
629     rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
630
631     Apply(rule->keycodes, &names->keycodes);
632     Apply(rule->symbols,  &names->symbols);
633     Apply(rule->types,    &names->types);
634     Apply(rule->compat,   &names->compat);
635     Apply(rule->keymap,   &names->keymap);
636 }
637
638 static bool
639 CheckGroup(struct rules *rules, const char *group_name, const char *name)
640 {
641    int i;
642    const char *p;
643    struct group *group;
644
645    for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
646        if (! strcmp(group->name, group_name)) {
647            break;
648        }
649    }
650    if (i == rules->num_groups)
651        return false;
652    for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
653        if (! strcmp(p, name)) {
654            return true;
655        }
656    }
657    return false;
658 }
659
660 static int
661 XkbRF_CheckApplyRule(struct rule *rule, struct multi_defs *mdefs,
662                      struct xkb_component_names *names, struct rules *rules)
663 {
664     bool pending = false;
665
666     if (rule->model != NULL) {
667         if(mdefs->model == NULL)
668             return 0;
669         if (strcmp(rule->model, "*") == 0) {
670             pending = true;
671         } else {
672             if (rule->model[0] == '$') {
673                if (!CheckGroup(rules, rule->model, mdefs->model))
674                   return 0;
675             } else {
676                if (strcmp(rule->model, mdefs->model) != 0)
677                   return 0;
678             }
679         }
680     }
681     if (rule->option != NULL) {
682         if (mdefs->options == NULL)
683             return 0;
684         if ((!MatchOneOf(rule->option,mdefs->options)))
685             return 0;
686     }
687
688     if (rule->layout != NULL) {
689         if(mdefs->layout[rule->layout_num] == NULL ||
690            *mdefs->layout[rule->layout_num] == '\0')
691             return 0;
692         if (strcmp(rule->layout, "*") == 0) {
693             pending = true;
694         } else {
695             if (rule->layout[0] == '$') {
696                if (!CheckGroup(rules, rule->layout,
697                                mdefs->layout[rule->layout_num]))
698                   return 0;
699             } else {
700                if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
701                    return 0;
702             }
703         }
704     }
705     if (rule->variant != NULL) {
706         if (mdefs->variant[rule->variant_num] == NULL ||
707             *mdefs->variant[rule->variant_num] == '\0')
708             return 0;
709         if (strcmp(rule->variant, "*") == 0) {
710             pending = true;
711         } else {
712             if (rule->variant[0] == '$') {
713                if (!CheckGroup(rules, rule->variant,
714                                mdefs->variant[rule->variant_num]))
715                   return 0;
716             } else {
717                if (strcmp(rule->variant,
718                           mdefs->variant[rule->variant_num]) != 0)
719                    return 0;
720             }
721         }
722     }
723     if (pending) {
724         rule->flags|= XkbRF_PendingMatch;
725         return rule->number;
726     }
727     /* exact match, apply it now */
728     XkbRF_ApplyRule(rule,names);
729     return rule->number;
730 }
731
732 static void
733 XkbRF_ClearPartialMatches(struct rules *rules)
734 {
735     int i;
736     struct rule *rule;
737
738     for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
739         rule->flags&= ~XkbRF_PendingMatch;
740     }
741 }
742
743 static void
744 XkbRF_ApplyPartialMatches(struct rules *rules,
745                           struct xkb_component_names *names)
746 {
747     int i;
748     struct rule *rule;
749
750     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
751         if ((rule->flags&XkbRF_PendingMatch)==0)
752             continue;
753         XkbRF_ApplyRule(rule,names);
754     }
755 }
756
757 static void
758 XkbRF_CheckApplyRules(struct rules *rules, struct multi_defs *mdefs,
759                       struct xkb_component_names *names, unsigned int flags)
760 {
761     int i;
762     struct rule *rule;
763     int skip;
764
765     for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
766         if ((rule->flags & flags) != flags)
767             continue;
768         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
769         if (skip && !(flags & XkbRF_Option)) {
770             for ( ;(i < rules->num_rules) && (rule->number == skip);
771                   rule++, i++);
772             rule--; i--;
773         }
774     }
775 }
776
777 /***====================================================================***/
778
779 static char *
780 XkbRF_SubstituteVars(char *name, struct multi_defs *mdefs)
781 {
782     char *str, *outstr, *orig, *var;
783     size_t len;
784     int ndx;
785
786     if (!name)
787         return NULL;
788
789     orig= name;
790     str= strchr(name,'%');
791     if (str==NULL)
792         return name;
793     len= strlen(name);
794     while (str!=NULL) {
795         char pfx= str[1];
796         int   extra_len= 0;
797         if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
798             extra_len= 1;
799             str++;
800         }
801         else if (pfx=='(') {
802             extra_len= 2;
803             str++;
804         }
805         var = str + 1;
806         str = get_index(var + 1, &ndx);
807         if (ndx == -1) {
808             str = strchr(str,'%');
809             continue;
810         }
811         if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
812             len+= strlen(mdefs->layout[ndx])+extra_len;
813         else if ((*var=='m')&&mdefs->model)
814             len+= strlen(mdefs->model)+extra_len;
815         else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
816             len+= strlen(mdefs->variant[ndx])+extra_len;
817         if ((pfx=='(')&&(*str==')')) {
818             str++;
819         }
820         str= strchr(&str[0],'%');
821     }
822     name = malloc(len + 1);
823     str= orig;
824     outstr= name;
825     while (*str!='\0') {
826         if (str[0]=='%') {
827             char pfx,sfx;
828             str++;
829             pfx= str[0];
830             sfx= '\0';
831             if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
832                 str++;
833             }
834             else if (pfx=='(') {
835                 sfx= ')';
836                 str++;
837             }
838             else pfx= '\0';
839
840             var = str;
841             str = get_index(var + 1, &ndx);
842             if (ndx == -1) {
843                 continue;
844             }
845             if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
846                 if (pfx) *outstr++= pfx;
847                 strcpy(outstr,mdefs->layout[ndx]);
848                 outstr+= strlen(mdefs->layout[ndx]);
849                 if (sfx) *outstr++= sfx;
850             }
851             else if ((*var=='m')&&(mdefs->model)) {
852                 if (pfx) *outstr++= pfx;
853                 strcpy(outstr,mdefs->model);
854                 outstr+= strlen(mdefs->model);
855                 if (sfx) *outstr++= sfx;
856             }
857             else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
858                 if (pfx) *outstr++= pfx;
859                 strcpy(outstr,mdefs->variant[ndx]);
860                 outstr+= strlen(mdefs->variant[ndx]);
861                 if (sfx) *outstr++= sfx;
862             }
863             if ((pfx=='(')&&(*str==')'))
864                 str++;
865         }
866         else {
867             *outstr++= *str++;
868         }
869     }
870     *outstr++= '\0';
871     if (orig!=name)
872         free(orig);
873     return name;
874 }
875
876 /***====================================================================***/
877
878 static bool
879 XkbcRF_GetComponents(struct rules *rules, const struct var_defs *defs,
880                      struct xkb_component_names *names)
881 {
882     struct multi_defs mdefs;
883
884     MakeMultiDefs(&mdefs, defs);
885
886     memset(names, 0, sizeof(struct xkb_component_names));
887     XkbRF_ClearPartialMatches(rules);
888     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
889     XkbRF_ApplyPartialMatches(rules, names);
890     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
891     XkbRF_ApplyPartialMatches(rules, names);
892     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
893     XkbRF_ApplyPartialMatches(rules, names);
894
895     names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
896     names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
897     names->types = XkbRF_SubstituteVars(names->types, &mdefs);
898     names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
899     names->keymap = XkbRF_SubstituteVars(names->keymap, &mdefs);
900
901     FreeMultiDefs(&mdefs);
902     return (names->keycodes && names->symbols && names->types &&
903                 names->compat) || names->keymap;
904 }
905
906 static struct rule *
907 XkbcRF_AddRule(struct rules *rules)
908 {
909     if (rules->sz_rules<1) {
910         rules->sz_rules= 16;
911         rules->num_rules= 0;
912         rules->rules = calloc(rules->sz_rules, sizeof(*rules->rules));
913     }
914     else if (rules->num_rules>=rules->sz_rules) {
915         rules->sz_rules*= 2;
916         rules->rules = realloc(rules->rules,
917                                rules->sz_rules * sizeof(*rules->rules));
918     }
919     if (!rules->rules) {
920         rules->sz_rules= rules->num_rules= 0;
921         return NULL;
922     }
923     memset(&rules->rules[rules->num_rules], 0, sizeof(*rules->rules));
924     return &rules->rules[rules->num_rules++];
925 }
926
927 static struct group *
928 XkbcRF_AddGroup(struct rules *rules)
929 {
930     if (rules->sz_groups<1) {
931         rules->sz_groups= 16;
932         rules->num_groups= 0;
933         rules->groups = calloc(rules->sz_groups, sizeof(*rules->groups));
934     }
935     else if (rules->num_groups >= rules->sz_groups) {
936         rules->sz_groups *= 2;
937         rules->groups = realloc(rules->groups,
938                                 rules->sz_groups * sizeof(*rules->groups));
939     }
940     if (!rules->groups) {
941         rules->sz_groups= rules->num_groups= 0;
942         return NULL;
943     }
944
945     memset(&rules->groups[rules->num_groups], 0, sizeof(*rules->groups));
946     return &rules->groups[rules->num_groups++];
947 }
948
949 static struct rules *
950 XkbcRF_LoadRules(FILE *file)
951 {
952     struct input_line line;
953     struct remap_spec remap;
954     struct rule trule, *rule;
955     struct group tgroup, *group;
956     struct rules *rules;
957
958     rules = calloc(1, sizeof(*rules));
959     if (!rules)
960         return NULL;
961
962     memset(&remap, 0, sizeof(remap));
963     memset(&tgroup, 0, sizeof(tgroup));
964     input_line_init(&line);
965
966     while (input_line_get(file, &line)) {
967         if (CheckLine(&line,&remap,&trule,&tgroup)) {
968             if (tgroup.number) {
969                 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
970                     *group= tgroup;
971                     memset(&tgroup, 0, sizeof(tgroup));
972                 }
973             } else {
974                 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
975                     *rule= trule;
976                     memset(&trule, 0, sizeof(trule));
977                 }
978             }
979         }
980         line.offset = 0;
981     }
982     input_line_deinit(&line);
983     return rules;
984 }
985
986 static void
987 XkbRF_ClearVarDescriptions(struct describe_vars *var)
988 {
989     int i;
990
991     for (i=0;i<var->num_desc;i++) {
992         free(var->desc[i].name);
993         free(var->desc[i].desc);
994         var->desc[i].name= var->desc[i].desc= NULL;
995     }
996     free(var->desc);
997     var->desc= NULL;
998 }
999
1000 static void
1001 XkbcRF_Free(struct rules *rules)
1002 {
1003     int i;
1004     struct rule *rule;
1005     struct group *group;
1006
1007     if (!rules)
1008         return;
1009     XkbRF_ClearVarDescriptions(&rules->models);
1010     XkbRF_ClearVarDescriptions(&rules->layouts);
1011     XkbRF_ClearVarDescriptions(&rules->variants);
1012     XkbRF_ClearVarDescriptions(&rules->options);
1013
1014     for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1015         free(rule->model);
1016         free(rule->layout);
1017         free(rule->variant);
1018         free(rule->option);
1019         free(rule->keycodes);
1020         free(rule->symbols);
1021         free(rule->types);
1022         free(rule->compat);
1023         free(rule->keymap);
1024     }
1025     free(rules->rules);
1026
1027     for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1028         free(group->name);
1029         free(group->words);
1030     }
1031     free(rules->groups);
1032
1033     free(rules);
1034 }
1035
1036 struct xkb_component_names *
1037 xkb_components_from_rules(struct xkb_context *ctx,
1038                           const struct xkb_rule_names *rmlvo)
1039 {
1040     int i;
1041     FILE *rulesFile;
1042     char *rulesPath;
1043     struct rules *loaded;
1044     struct xkb_component_names *names = NULL;
1045     struct var_defs defs = {
1046         .model = rmlvo->model,
1047         .layout = rmlvo->layout,
1048         .variant = rmlvo->variant,
1049         .options = rmlvo->options,
1050     };
1051
1052     rulesFile = XkbFindFileInPath(ctx, rmlvo->rules, XkmRulesFile,
1053                                   &rulesPath);
1054     if (!rulesFile) {
1055         ERROR("could not find \"%s\" rules in XKB path\n", rmlvo->rules);
1056         ERROR("%d include paths searched:\n",
1057               xkb_context_num_include_paths(ctx));
1058         for (i = 0; i < xkb_context_num_include_paths(ctx); i++)
1059             ERROR("\t%s\n", xkb_context_include_path_get(ctx, i));
1060         return NULL;
1061     }
1062
1063     loaded = XkbcRF_LoadRules(rulesFile);
1064     if (!loaded) {
1065         ERROR("failed to load XKB rules \"%s\"\n", rulesPath);
1066         goto unwind_file;
1067     }
1068
1069     names = calloc(1, sizeof(*names));
1070     if (!names) {
1071         ERROR("failed to allocate XKB components\n");
1072         goto unwind_file;
1073     }
1074
1075     if (!XkbcRF_GetComponents(loaded, &defs, names)) {
1076         free(names->keymap);
1077         free(names->keycodes);
1078         free(names->types);
1079         free(names->compat);
1080         free(names->symbols);
1081         free(names);
1082         names = NULL;
1083         ERROR("no components returned from XKB rules \"%s\"\n", rulesPath);
1084     }
1085
1086 unwind_file:
1087     XkbcRF_Free(loaded);
1088     if (rulesFile)
1089         fclose(rulesFile);
1090     free(rulesPath);
1091     return names;
1092 }