Add benchmarking test to rulescomp
[profile/ivi/libxkbcommon.git] / src / 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
32 #ifdef DEBUG
33 #define PR_DEBUG(s)             fprintf(stderr,s)
34 #define PR_DEBUG1(s,a)          fprintf(stderr,s,a)
35 #define PR_DEBUG2(s,a,b)        fprintf(stderr,s,a,b)
36 #else
37 #define PR_DEBUG(s)
38 #define PR_DEBUG1(s,a)
39 #define PR_DEBUG2(s,a,b)
40 #endif
41
42 /***====================================================================***/
43
44 #define DFLT_LINE_SIZE  128
45
46 typedef struct {
47         int     line_num;
48         int     sz_line;
49         int     num_line;
50         char    buf[DFLT_LINE_SIZE];
51         char *  line;
52 } InputLine;
53
54 static void
55 InitInputLine(InputLine *line)
56 {
57     line->line_num= 1;
58     line->num_line= 0;
59     line->sz_line= DFLT_LINE_SIZE;
60     line->line= line->buf;
61 }
62
63 static void
64 FreeInputLine(InputLine *line)
65 {
66     if (line->line!=line->buf)
67         free(line->line);
68     line->line_num= 1;
69     line->num_line= 0;
70     line->sz_line= DFLT_LINE_SIZE;
71     line->line= line->buf;
72 }
73
74 static int
75 InputLineAddChar(InputLine *line,int ch)
76 {
77     if (line->num_line>=line->sz_line) {
78         if (line->line==line->buf) {
79             line->line = malloc(line->sz_line * 2);
80             memcpy(line->line,line->buf,line->sz_line);
81         }
82         else {
83             line->line = realloc(line->line, line->sz_line * 2);
84         }
85         line->sz_line*= 2;
86     }
87     line->line[line->num_line++]= ch;
88     return ch;
89 }
90
91 #define ADD_CHAR(l,c)   ((l)->num_line<(l)->sz_line?\
92                                 (int)((l)->line[(l)->num_line++]= (c)):\
93                                 InputLineAddChar(l,c))
94
95 static bool
96 GetInputLine(FILE *file,InputLine *line,bool checkbang)
97 {
98      int ch;
99      bool endOfFile,spacePending,slashPending,inComment;
100
101      endOfFile= false;
102      while ((!endOfFile)&&(line->num_line==0)) {
103         spacePending= slashPending= inComment= false;
104         while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
105             if (ch=='\\') {
106                 if ((ch=getc(file))==EOF)
107                     break;
108                 if (ch=='\n') {
109                     inComment= false;
110                     ch= ' ';
111                     line->line_num++;
112                 }
113             }
114             if (inComment)
115                 continue;
116             if (ch=='/') {
117                 if (slashPending) {
118                     inComment= true;
119                     slashPending= false;
120                 }
121                 else {
122                     slashPending= true;
123                 }
124                 continue;
125             }
126             else if (slashPending) {
127                 if (spacePending) {
128                     ADD_CHAR(line,' ');
129                     spacePending= false;
130                 }
131                 ADD_CHAR(line,'/');
132                 slashPending= false;
133             }
134             if (isspace(ch)) {
135                 while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
136                     ch= getc(file);
137                 }
138                 if (ch==EOF)
139                     break;
140                 if ((ch!='\n')&&(line->num_line>0))
141                     spacePending= true;
142                 ungetc(ch,file);
143             }
144             else {
145                 if (spacePending) {
146                     ADD_CHAR(line,' ');
147                     spacePending= false;
148                 }
149                 if (checkbang && ch=='!') {
150                     if (line->num_line!=0) {
151                         PR_DEBUG("The '!' legal only at start of line\n");
152                         PR_DEBUG("Line containing '!' ignored\n");
153                         line->num_line= 0;
154                         break;
155                     }
156
157                 }
158                 ADD_CHAR(line,ch);
159             }
160         }
161         if (ch==EOF)
162              endOfFile= true;
163 /*      else line->num_line++;*/
164      }
165      if ((line->num_line==0)&&(endOfFile))
166         return false;
167       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 _RemapSpec {
194         int                     number;
195         int                     num_remap;
196         struct  {
197                 int     word;
198                 int     index;
199                 }               remap[MAX_WORDS];
200 } RemapSpec;
201
202 typedef struct _FileSpec {
203         char *                  name[MAX_WORDS];
204         struct _FileSpec *      pending;
205 } FileSpec;
206
207 typedef struct {
208         const char *            model;
209         const char *            layout[XkbNumKbdGroups+1];
210         const char *            variant[XkbNumKbdGroups+1];
211         char *                  options;
212 } XkbRF_MultiDefsRec, *XkbRF_MultiDefsPtr;
213
214 #define NDX_BUFF_SIZE   4
215
216 /***====================================================================***/
217
218 static char*
219 get_index(char *str, int *ndx)
220 {
221    char ndx_buf[NDX_BUFF_SIZE];
222    char *end;
223
224    if (*str != '[') {
225        *ndx = 0;
226        return str;
227    }
228    str++;
229    end = strchr(str, ']');
230    if (end == NULL) {
231        *ndx = -1;
232        return str - 1;
233    }
234    if ( (end - str) >= NDX_BUFF_SIZE) {
235        *ndx = -1;
236        return end + 1;
237    }
238    strncpy(ndx_buf, str, end - str);
239    ndx_buf[end - str] = '\0';
240    *ndx = atoi(ndx_buf);
241    return end + 1;
242 }
243
244 static void
245 SetUpRemap(InputLine *line,RemapSpec *remap)
246 {
247    char *tok, *str;
248    unsigned present, l_ndx_present, v_ndx_present;
249    int i;
250    size_t len;
251    int ndx;
252    char *strtok_buf;
253 #ifdef DEBUG
254    bool found;
255 #endif
256
257
258    l_ndx_present = v_ndx_present = present= 0;
259    str= &line->line[1];
260    len = remap->number;
261    memset(remap, 0, sizeof(RemapSpec));
262    remap->number = len;
263    while ((tok = strtok_r(str, " ", &strtok_buf)) != NULL) {
264 #ifdef DEBUG
265         found= false;
266 #endif
267         str= NULL;
268         if (strcmp(tok,"=")==0)
269             continue;
270         for (i=0;i<MAX_WORDS;i++) {
271             len = strlen(cname[i]);
272             if (strncmp(cname[i],tok,len)==0) {
273                 if(strlen(tok) > len) {
274                     char *end = get_index(tok+len, &ndx);
275                     if ((i != LAYOUT && i != VARIANT) ||
276                         *end != '\0' || ndx == -1)
277                         break;
278                      if (ndx < 1 || ndx > XkbNumKbdGroups) {
279                         PR_DEBUG2("Illegal %s index: %d\n", cname[i], ndx);
280                         PR_DEBUG1("Index must be in range 1..%d\n",
281                                    XkbNumKbdGroups);
282                         break;
283                      }
284                 } else {
285                     ndx = 0;
286                 }
287 #ifdef DEBUG
288                 found= true;
289 #endif
290                 if (present&(1<<i)) {
291                     if ((i == LAYOUT && l_ndx_present&(1<<ndx)) ||
292                         (i == VARIANT && v_ndx_present&(1<<ndx)) ) {
293                         PR_DEBUG1("Component \"%s\" listed twice\n",tok);
294                         PR_DEBUG("Second definition ignored\n");
295                         break;
296                     }
297                 }
298                 present |= (1<<i);
299                 if (i == LAYOUT)
300                     l_ndx_present |= 1 << ndx;
301                 if (i == VARIANT)
302                     v_ndx_present |= 1 << ndx;
303                 remap->remap[remap->num_remap].word= i;
304                 remap->remap[remap->num_remap++].index= ndx;
305                 break;
306             }
307         }
308 #ifdef DEBUG
309         if (!found) {
310             fprintf(stderr,"Unknown component \"%s\" ignored\n",tok);
311         }
312 #endif
313    }
314    if ((present&PART_MASK)==0) {
315 #ifdef DEBUG
316         unsigned mask= PART_MASK;
317         fprintf(stderr,"Mapping needs at least one of ");
318         for (i=0; (i<MAX_WORDS); i++) {
319             if ((1L<<i)&mask) {
320                 mask&= ~(1L<<i);
321                 if (mask)       fprintf(stderr,"\"%s,\" ",cname[i]);
322                 else            fprintf(stderr,"or \"%s\"\n",cname[i]);
323             }
324         }
325         fprintf(stderr,"Illegal mapping ignored\n");
326 #endif
327         remap->num_remap= 0;
328         return;
329    }
330    if ((present&COMPONENT_MASK)==0) {
331         PR_DEBUG("Mapping needs at least one component\n");
332         PR_DEBUG("Illegal mapping ignored\n");
333         remap->num_remap= 0;
334         return;
335    }
336    if (((present&COMPONENT_MASK)&(1<<KEYMAP))&&
337                                 ((present&COMPONENT_MASK)!=(1<<KEYMAP))) {
338         PR_DEBUG("Keymap cannot appear with other components\n");
339         PR_DEBUG("Illegal mapping ignored\n");
340         remap->num_remap= 0;
341         return;
342    }
343    remap->number++;
344 }
345
346 static bool
347 MatchOneOf(char *wanted,char *vals_defined)
348 {
349     char *str, *next;
350     int want_len = strlen(wanted);
351
352     for (str=vals_defined,next=NULL;str!=NULL;str=next) {
353         int len;
354         next= strchr(str,',');
355         if (next) {
356             len= next-str;
357             next++;
358         }
359         else {
360             len= strlen(str);
361         }
362         if ((len==want_len)&&(strncmp(wanted,str,len)==0))
363             return true;
364     }
365     return false;
366 }
367
368 /***====================================================================***/
369
370 static bool
371 CheckLine(      InputLine *             line,
372                 RemapSpec *             remap,
373                 XkbRF_RulePtr           rule,
374                 XkbRF_GroupPtr          group)
375 {
376     char *str, *tok;
377     int nread, i;
378     FileSpec tmp;
379     char *strtok_buf;
380     bool append = false;
381
382     if (line->line[0]=='!') {
383         if (line->line[1] == '$' ||
384             (line->line[1] == ' ' && line->line[2] == '$')) {
385             char *gname = strchr(line->line, '$');
386             char *words = strchr(gname, ' ');
387             if(!words)
388                 return false;
389             *words++ = '\0';
390             for (; *words; words++) {
391                 if (*words != '=' && *words != ' ')
392                     break;
393             }
394             if (*words == '\0')
395                 return false;
396             group->name = uDupString(gname);
397             group->words = uDupString(words);
398             for (i = 1, words = group->words; *words; words++) {
399                  if ( *words == ' ') {
400                      *words++ = '\0';
401                      i++;
402                  }
403             }
404             group->number = i;
405             return true;
406         } else {
407             SetUpRemap(line,remap);
408             return false;
409         }
410     }
411
412     if (remap->num_remap==0) {
413         PR_DEBUG("Must have a mapping before first line of data\n");
414         PR_DEBUG("Illegal line of data ignored\n");
415         return false;
416     }
417     memset(&tmp, 0, sizeof(FileSpec));
418     str= line->line;
419     for (nread = 0; (tok = strtok_r(str, " ", &strtok_buf)) != NULL; nread++) {
420         str= NULL;
421         if (strcmp(tok,"=")==0) {
422             nread--;
423             continue;
424         }
425         if (nread>remap->num_remap) {
426             PR_DEBUG("Too many words on a line\n");
427             PR_DEBUG1("Extra word \"%s\" ignored\n",tok);
428             continue;
429         }
430         tmp.name[remap->remap[nread].word]= tok;
431         if (*tok == '+' || *tok == '|')
432             append = true;
433     }
434     if (nread<remap->num_remap) {
435         PR_DEBUG1("Too few words on a line: %s\n", line->line);
436         PR_DEBUG("line ignored\n");
437         return false;
438     }
439
440     rule->flags= 0;
441     rule->number = remap->number;
442     if (tmp.name[OPTION])
443          rule->flags|= XkbRF_Option;
444     else if (append)
445          rule->flags|= XkbRF_Append;
446     else
447          rule->flags|= XkbRF_Normal;
448     rule->model= uDupString(tmp.name[MODEL]);
449     rule->layout= uDupString(tmp.name[LAYOUT]);
450     rule->variant= uDupString(tmp.name[VARIANT]);
451     rule->option= uDupString(tmp.name[OPTION]);
452
453     rule->keycodes= uDupString(tmp.name[KEYCODES]);
454     rule->symbols= uDupString(tmp.name[SYMBOLS]);
455     rule->types= uDupString(tmp.name[TYPES]);
456     rule->compat= uDupString(tmp.name[COMPAT]);
457     rule->keymap= uDupString(tmp.name[KEYMAP]);
458
459     rule->layout_num = rule->variant_num = 0;
460     for (i = 0; i < nread; i++) {
461         if (remap->remap[i].index) {
462             if (remap->remap[i].word == LAYOUT)
463                 rule->layout_num = remap->remap[i].index;
464             if (remap->remap[i].word == VARIANT)
465                 rule->variant_num = remap->remap[i].index;
466         }
467     }
468     return true;
469 }
470
471 static char *
472 _Concat(char *str1,char *str2)
473 {
474     int len;
475
476     if ((!str1)||(!str2))
477         return str1;
478     len= strlen(str1)+strlen(str2)+1;
479     str1 = uTypedRealloc(str1, len, char);
480     if (str1)
481         strcat(str1,str2);
482     return str1;
483 }
484
485 static void
486 squeeze_spaces(char *p1)
487 {
488    char *p2;
489    for (p2 = p1; *p2; p2++) {
490        *p1 = *p2;
491        if (*p1 != ' ') p1++;
492    }
493    *p1 = '\0';
494 }
495
496 static bool
497 MakeMultiDefs(XkbRF_MultiDefsPtr mdefs, XkbRF_VarDefsPtr defs)
498 {
499    memset(mdefs, 0, sizeof(XkbRF_MultiDefsRec));
500    mdefs->model = defs->model;
501    mdefs->options = uDupString(defs->options);
502    if (mdefs->options) squeeze_spaces(mdefs->options);
503
504    if (defs->layout) {
505        if (!strchr(defs->layout, ',')) {
506            mdefs->layout[0] = defs->layout;
507        } else {
508            char *p;
509            int i;
510            p = uDupString(defs->layout);
511            if (p == NULL)
512               return false;
513            squeeze_spaces(p);
514            mdefs->layout[1] = p;
515            for (i = 2; i <= XkbNumKbdGroups; i++) {
516               if ((p = strchr(p, ','))) {
517                  *p++ = '\0';
518                  mdefs->layout[i] = p;
519               } else {
520                  break;
521               }
522            }
523            if (p && (p = strchr(p, ',')))
524               *p = '\0';
525        }
526    }
527
528    if (defs->variant) {
529        if (!strchr(defs->variant, ',')) {
530            mdefs->variant[0] = defs->variant;
531        } else {
532            char *p;
533            int i;
534            p = uDupString(defs->variant);
535            if (p == NULL)
536               return false;
537            squeeze_spaces(p);
538            mdefs->variant[1] = p;
539            for (i = 2; i <= XkbNumKbdGroups; i++) {
540               if ((p = strchr(p, ','))) {
541                  *p++ = '\0';
542                  mdefs->variant[i] = p;
543               } else {
544                  break;
545               }
546            }
547            if (p && (p = strchr(p, ',')))
548               *p = '\0';
549        }
550    }
551    return true;
552 }
553
554 static void
555 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
556 {
557     free(defs->options);
558     free(UNCONSTIFY(defs->layout[1]));
559     free(UNCONSTIFY(defs->variant[1]));
560 }
561
562 static void
563 Apply(char *src, char **dst)
564 {
565     if (src) {
566         if (*src == '+' || *src == '!') {
567             *dst= _Concat(*dst, src);
568         } else {
569             if (*dst == NULL)
570                 *dst= uDupString(src);
571         }
572     }
573 }
574
575 static void
576 XkbRF_ApplyRule(        XkbRF_RulePtr           rule,
577                         struct xkb_component_names *    names)
578 {
579     rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
580
581     Apply(rule->keycodes, &names->keycodes);
582     Apply(rule->symbols,  &names->symbols);
583     Apply(rule->types,    &names->types);
584     Apply(rule->compat,   &names->compat);
585     Apply(rule->keymap,   &names->keymap);
586 }
587
588 static bool
589 CheckGroup(     XkbRF_RulesPtr          rules,
590                 const char *            group_name,
591                 const char *            name)
592 {
593    int i;
594    const char *p;
595    XkbRF_GroupPtr group;
596
597    for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
598        if (! strcmp(group->name, group_name)) {
599            break;
600        }
601    }
602    if (i == rules->num_groups)
603        return false;
604    for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
605        if (! strcmp(p, name)) {
606            return true;
607        }
608    }
609    return false;
610 }
611
612 static int
613 XkbRF_CheckApplyRule(   XkbRF_RulePtr           rule,
614                         XkbRF_MultiDefsPtr      mdefs,
615                         struct xkb_component_names *    names,
616                         XkbRF_RulesPtr          rules)
617 {
618     bool pending = false;
619
620     if (rule->model != NULL) {
621         if(mdefs->model == NULL)
622             return 0;
623         if (strcmp(rule->model, "*") == 0) {
624             pending = true;
625         } else {
626             if (rule->model[0] == '$') {
627                if (!CheckGroup(rules, rule->model, mdefs->model))
628                   return 0;
629             } else {
630                if (strcmp(rule->model, mdefs->model) != 0)
631                   return 0;
632             }
633         }
634     }
635     if (rule->option != NULL) {
636         if (mdefs->options == NULL)
637             return 0;
638         if ((!MatchOneOf(rule->option,mdefs->options)))
639             return 0;
640     }
641
642     if (rule->layout != NULL) {
643         if(mdefs->layout[rule->layout_num] == NULL ||
644            *mdefs->layout[rule->layout_num] == '\0')
645             return 0;
646         if (strcmp(rule->layout, "*") == 0) {
647             pending = true;
648         } else {
649             if (rule->layout[0] == '$') {
650                if (!CheckGroup(rules, rule->layout,
651                                mdefs->layout[rule->layout_num]))
652                   return 0;
653             } else {
654                if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
655                    return 0;
656             }
657         }
658     }
659     if (rule->variant != NULL) {
660         if (mdefs->variant[rule->variant_num] == NULL ||
661             *mdefs->variant[rule->variant_num] == '\0')
662             return 0;
663         if (strcmp(rule->variant, "*") == 0) {
664             pending = true;
665         } else {
666             if (rule->variant[0] == '$') {
667                if (!CheckGroup(rules, rule->variant,
668                                mdefs->variant[rule->variant_num]))
669                   return 0;
670             } else {
671                if (strcmp(rule->variant,
672                           mdefs->variant[rule->variant_num]) != 0)
673                    return 0;
674             }
675         }
676     }
677     if (pending) {
678         rule->flags|= XkbRF_PendingMatch;
679         return rule->number;
680     }
681     /* exact match, apply it now */
682     XkbRF_ApplyRule(rule,names);
683     return rule->number;
684 }
685
686 static void
687 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
688 {
689     int i;
690     XkbRF_RulePtr rule;
691
692     for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
693         rule->flags&= ~XkbRF_PendingMatch;
694     }
695 }
696
697 static void
698 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,struct xkb_component_names * names)
699 {
700     int i;
701     XkbRF_RulePtr rule;
702
703     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
704         if ((rule->flags&XkbRF_PendingMatch)==0)
705             continue;
706         XkbRF_ApplyRule(rule,names);
707     }
708 }
709
710 static void
711 XkbRF_CheckApplyRules(  XkbRF_RulesPtr          rules,
712                         XkbRF_MultiDefsPtr      mdefs,
713                         struct xkb_component_names *    names,
714                         unsigned int                    flags)
715 {
716     int i;
717     XkbRF_RulePtr rule;
718     int skip;
719
720     for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
721         if ((rule->flags & flags) != flags)
722             continue;
723         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
724         if (skip && !(flags & XkbRF_Option)) {
725             for ( ;(i < rules->num_rules) && (rule->number == skip);
726                   rule++, i++);
727             rule--; i--;
728         }
729     }
730 }
731
732 /***====================================================================***/
733
734 static char *
735 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
736 {
737     char *str, *outstr, *orig, *var;
738     size_t len;
739     int ndx;
740
741     orig= name;
742     str= strchr(name,'%');
743     if (str==NULL)
744         return name;
745     len= strlen(name);
746     while (str!=NULL) {
747         char pfx= str[1];
748         int   extra_len= 0;
749         if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
750             extra_len= 1;
751             str++;
752         }
753         else if (pfx=='(') {
754             extra_len= 2;
755             str++;
756         }
757         var = str + 1;
758         str = get_index(var + 1, &ndx);
759         if (ndx == -1) {
760             str = strchr(str,'%');
761             continue;
762         }
763         if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
764             len+= strlen(mdefs->layout[ndx])+extra_len;
765         else if ((*var=='m')&&mdefs->model)
766             len+= strlen(mdefs->model)+extra_len;
767         else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
768             len+= strlen(mdefs->variant[ndx])+extra_len;
769         if ((pfx=='(')&&(*str==')')) {
770             str++;
771         }
772         str= strchr(&str[0],'%');
773     }
774     name = malloc(len + 1);
775     str= orig;
776     outstr= name;
777     while (*str!='\0') {
778         if (str[0]=='%') {
779             char pfx,sfx;
780             str++;
781             pfx= str[0];
782             sfx= '\0';
783             if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
784                 str++;
785             }
786             else if (pfx=='(') {
787                 sfx= ')';
788                 str++;
789             }
790             else pfx= '\0';
791
792             var = str;
793             str = get_index(var + 1, &ndx);
794             if (ndx == -1) {
795                 continue;
796             }
797             if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
798                 if (pfx) *outstr++= pfx;
799                 strcpy(outstr,mdefs->layout[ndx]);
800                 outstr+= strlen(mdefs->layout[ndx]);
801                 if (sfx) *outstr++= sfx;
802             }
803             else if ((*var=='m')&&(mdefs->model)) {
804                 if (pfx) *outstr++= pfx;
805                 strcpy(outstr,mdefs->model);
806                 outstr+= strlen(mdefs->model);
807                 if (sfx) *outstr++= sfx;
808             }
809             else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
810                 if (pfx) *outstr++= pfx;
811                 strcpy(outstr,mdefs->variant[ndx]);
812                 outstr+= strlen(mdefs->variant[ndx]);
813                 if (sfx) *outstr++= sfx;
814             }
815             if ((pfx=='(')&&(*str==')'))
816                 str++;
817         }
818         else {
819             *outstr++= *str++;
820         }
821     }
822     *outstr++= '\0';
823     if (orig!=name)
824         free(orig);
825     return name;
826 }
827
828 /***====================================================================***/
829
830 bool
831 XkbcRF_GetComponents(   XkbRF_RulesPtr          rules,
832                         XkbRF_VarDefsPtr        defs,
833                         struct xkb_component_names *    names)
834 {
835     XkbRF_MultiDefsRec mdefs;
836
837     MakeMultiDefs(&mdefs, defs);
838
839     memset(names, 0, sizeof(struct xkb_component_names));
840     XkbRF_ClearPartialMatches(rules);
841     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
842     XkbRF_ApplyPartialMatches(rules, names);
843     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
844     XkbRF_ApplyPartialMatches(rules, names);
845     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
846     XkbRF_ApplyPartialMatches(rules, names);
847
848     if (names->keycodes)
849         names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
850     if (names->symbols)
851         names->symbols= XkbRF_SubstituteVars(names->symbols, &mdefs);
852     if (names->types)
853         names->types= XkbRF_SubstituteVars(names->types, &mdefs);
854     if (names->compat)
855         names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
856     if (names->keymap)
857         names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
858
859     FreeMultiDefs(&mdefs);
860     return (names->keycodes && names->symbols && names->types &&
861                 names->compat) || names->keymap;
862 }
863
864 static XkbRF_RulePtr
865 XkbcRF_AddRule(XkbRF_RulesPtr   rules)
866 {
867     if (rules->sz_rules<1) {
868         rules->sz_rules= 16;
869         rules->num_rules= 0;
870         rules->rules= uTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
871     }
872     else if (rules->num_rules>=rules->sz_rules) {
873         rules->sz_rules*= 2;
874         rules->rules= uTypedRealloc(rules->rules,rules->sz_rules,
875                                                         XkbRF_RuleRec);
876     }
877     if (!rules->rules) {
878         rules->sz_rules= rules->num_rules= 0;
879 #ifdef DEBUG
880         fprintf(stderr,"Allocation failure in XkbcRF_AddRule\n");
881 #endif
882         return NULL;
883     }
884     memset(&rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
885     return &rules->rules[rules->num_rules++];
886 }
887
888 static XkbRF_GroupPtr
889 XkbcRF_AddGroup(XkbRF_RulesPtr  rules)
890 {
891     if (rules->sz_groups<1) {
892         rules->sz_groups= 16;
893         rules->num_groups= 0;
894         rules->groups= uTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
895     }
896     else if (rules->num_groups >= rules->sz_groups) {
897         rules->sz_groups *= 2;
898         rules->groups= uTypedRealloc(rules->groups,rules->sz_groups,
899                                      XkbRF_GroupRec);
900     }
901     if (!rules->groups) {
902         rules->sz_groups= rules->num_groups= 0;
903         return NULL;
904     }
905
906     memset(&rules->groups[rules->num_groups], 0, sizeof(XkbRF_GroupRec));
907     return &rules->groups[rules->num_groups++];
908 }
909
910 bool
911 XkbcRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
912 {
913 InputLine       line;
914 RemapSpec       remap;
915 XkbRF_RuleRec   trule,*rule;
916 XkbRF_GroupRec  tgroup,*group;
917
918     if (!(rules && file))
919         return false;
920     memset(&remap, 0, sizeof(RemapSpec));
921     memset(&tgroup, 0, sizeof(XkbRF_GroupRec));
922     InitInputLine(&line);
923     while (GetInputLine(file, &line, true)) {
924         if (CheckLine(&line,&remap,&trule,&tgroup)) {
925             if (tgroup.number) {
926                 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
927                     *group= tgroup;
928                     memset(&tgroup, 0, sizeof(XkbRF_GroupRec));
929                 }
930             } else {
931                 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
932                     *rule= trule;
933                     memset(&trule, 0, sizeof(XkbRF_RuleRec));
934                 }
935             }
936         }
937         line.num_line= 0;
938     }
939     FreeInputLine(&line);
940     return true;
941 }
942
943 static void
944 XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
945 {
946     int i;
947
948     for (i=0;i<var->num_desc;i++) {
949         free(var->desc[i].name);
950         free(var->desc[i].desc);
951         var->desc[i].name= var->desc[i].desc= NULL;
952     }
953     free(var->desc);
954     var->desc= NULL;
955 }
956
957 void
958 XkbcRF_Free(XkbRF_RulesPtr rules)
959 {
960     int i;
961     XkbRF_RulePtr rule;
962     XkbRF_GroupPtr group;
963
964     if (!rules)
965         return;
966     XkbRF_ClearVarDescriptions(&rules->models);
967     XkbRF_ClearVarDescriptions(&rules->layouts);
968     XkbRF_ClearVarDescriptions(&rules->variants);
969     XkbRF_ClearVarDescriptions(&rules->options);
970     if (rules->extra) {
971         for (i = 0; i < rules->num_extra; i++) {
972             XkbRF_ClearVarDescriptions(&rules->extra[i]);
973         }
974         free(rules->extra);
975     }
976     for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
977         free(rule->model);
978         free(rule->layout);
979         free(rule->variant);
980         free(rule->option);
981         free(rule->keycodes);
982         free(rule->symbols);
983         free(rule->types);
984         free(rule->compat);
985         free(rule->keymap);
986     }
987     free(rules->rules);
988
989     for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
990         free(group->name);
991         free(group->words);
992     }
993     free(rules->groups);
994
995     free(rules);
996 }