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