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