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