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