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