Don't check for NULL before free()
[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  const 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         const char *            model;
232         const char *            layout[XkbNumKbdGroups+1];
233         const 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            p = _XkbDupString(defs->layout);
535            if (p == NULL)
536               return False;
537            squeeze_spaces(p);
538            mdefs->layout[1] = p;
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            p = _XkbDupString(defs->variant);
559            if (p == NULL)
560               return False;
561            squeeze_spaces(p);
562            mdefs->variant[1] = p;
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     free(defs->options);
582     /* Avoid -Wcast-qual warnings. */
583     free((void *)(uintptr_t)defs->layout[1]);
584     free((void *)(uintptr_t)defs->variant[1]);
585 }
586
587 static void
588 Apply(char *src, char **dst)
589 {
590     if (src) {
591         if (*src == '+' || *src == '!') {
592             *dst= _Concat(*dst, src);
593         } else {
594             if (*dst == NULL)
595                 *dst= _XkbDupString(src);
596         }
597     }
598 }
599
600 static void
601 XkbRF_ApplyRule(        XkbRF_RulePtr           rule,
602                         struct xkb_component_names *    names)
603 {
604     rule->flags&= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
605
606     Apply(rule->keycodes, &names->keycodes);
607     Apply(rule->symbols,  &names->symbols);
608     Apply(rule->types,    &names->types);
609     Apply(rule->compat,   &names->compat);
610     Apply(rule->geometry, &names->geometry);
611     Apply(rule->keymap,   &names->keymap);
612 }
613
614 static Bool
615 CheckGroup(     XkbRF_RulesPtr          rules,
616                 const char *            group_name,
617                 const char *            name)
618 {
619    int i;
620    const char *p;
621    XkbRF_GroupPtr group;
622
623    for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
624        if (! strcmp(group->name, group_name)) {
625            break;
626        }
627    }
628    if (i == rules->num_groups)
629        return False;
630    for (i = 0, p = group->words; i < group->number; i++, p += strlen(p)+1) {
631        if (! strcmp(p, name)) {
632            return True;
633        }
634    }
635    return False;
636 }
637
638 static int
639 XkbRF_CheckApplyRule(   XkbRF_RulePtr           rule,
640                         XkbRF_MultiDefsPtr      mdefs,
641                         struct xkb_component_names *    names,
642                         XkbRF_RulesPtr          rules)
643 {
644     Bool pending = False;
645
646     if (rule->model != NULL) {
647         if(mdefs->model == NULL)
648             return 0;
649         if (strcmp(rule->model, "*") == 0) {
650             pending = True;
651         } else {
652             if (rule->model[0] == '$') {
653                if (!CheckGroup(rules, rule->model, mdefs->model))
654                   return 0;
655             } else {
656                if (strcmp(rule->model, mdefs->model) != 0)
657                   return 0;
658             }
659         }
660     }
661     if (rule->option != NULL) {
662         if (mdefs->options == NULL)
663             return 0;
664         if ((!MatchOneOf(rule->option,mdefs->options)))
665             return 0;
666     }
667
668     if (rule->layout != NULL) {
669         if(mdefs->layout[rule->layout_num] == NULL ||
670            *mdefs->layout[rule->layout_num] == '\0')
671             return 0;
672         if (strcmp(rule->layout, "*") == 0) {
673             pending = True;
674         } else {
675             if (rule->layout[0] == '$') {
676                if (!CheckGroup(rules, rule->layout,
677                                mdefs->layout[rule->layout_num]))
678                   return 0;
679             } else {
680                if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
681                    return 0;
682             }
683         }
684     }
685     if (rule->variant != NULL) {
686         if (mdefs->variant[rule->variant_num] == NULL ||
687             *mdefs->variant[rule->variant_num] == '\0')
688             return 0;
689         if (strcmp(rule->variant, "*") == 0) {
690             pending = True;
691         } else {
692             if (rule->variant[0] == '$') {
693                if (!CheckGroup(rules, rule->variant,
694                                mdefs->variant[rule->variant_num]))
695                   return 0;
696             } else {
697                if (strcmp(rule->variant,
698                           mdefs->variant[rule->variant_num]) != 0)
699                    return 0;
700             }
701         }
702     }
703     if (pending) {
704         rule->flags|= XkbRF_PendingMatch;
705         return rule->number;
706     }
707     /* exact match, apply it now */
708     XkbRF_ApplyRule(rule,names);
709     return rule->number;
710 }
711
712 static void
713 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
714 {
715 register int    i;
716 XkbRF_RulePtr   rule;
717
718     for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
719         rule->flags&= ~XkbRF_PendingMatch;
720     }
721 }
722
723 static void
724 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules,struct xkb_component_names * names)
725 {
726 int             i;
727 XkbRF_RulePtr   rule;
728
729     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
730         if ((rule->flags&XkbRF_PendingMatch)==0)
731             continue;
732         XkbRF_ApplyRule(rule,names);
733     }
734 }
735
736 static void
737 XkbRF_CheckApplyRules(  XkbRF_RulesPtr          rules,
738                         XkbRF_MultiDefsPtr      mdefs,
739                         struct xkb_component_names *    names,
740                         int                     flags)
741 {
742 int             i;
743 XkbRF_RulePtr   rule;
744 int             skip;
745
746     for (rule = rules->rules, i=0; i < rules->num_rules; rule++, i++) {
747         if ((rule->flags & flags) != flags)
748             continue;
749         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
750         if (skip && !(flags & XkbRF_Option)) {
751             for ( ;(i < rules->num_rules) && (rule->number == skip);
752                   rule++, i++);
753             rule--; i--;
754         }
755     }
756 }
757
758 /***====================================================================***/
759
760 static char *
761 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
762 {
763 char    *str, *outstr, *orig, *var;
764 int     len, ndx;
765
766     orig= name;
767     str= index(name,'%');
768     if (str==NULL)
769         return name;
770     len= strlen(name);
771     while (str!=NULL) {
772         char pfx= str[1];
773         int   extra_len= 0;
774         if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
775             extra_len= 1;
776             str++;
777         }
778         else if (pfx=='(') {
779             extra_len= 2;
780             str++;
781         }
782         var = str + 1;
783         str = get_index(var + 1, &ndx);
784         if (ndx == -1) {
785             str = index(str,'%');
786             continue;
787         }
788         if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
789             len+= strlen(mdefs->layout[ndx])+extra_len;
790         else if ((*var=='m')&&mdefs->model)
791             len+= strlen(mdefs->model)+extra_len;
792         else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
793             len+= strlen(mdefs->variant[ndx])+extra_len;
794         if ((pfx=='(')&&(*str==')')) {
795             str++;
796         }
797         str= index(&str[0],'%');
798     }
799     name= (char *)malloc(len+1);
800     str= orig;
801     outstr= name;
802     while (*str!='\0') {
803         if (str[0]=='%') {
804             char pfx,sfx;
805             str++;
806             pfx= str[0];
807             sfx= '\0';
808             if ((pfx=='+')||(pfx=='|')||(pfx=='_')||(pfx=='-')) {
809                 str++;
810             }
811             else if (pfx=='(') {
812                 sfx= ')';
813                 str++;
814             }
815             else pfx= '\0';
816
817             var = str;
818             str = get_index(var + 1, &ndx);
819             if (ndx == -1) {
820                 continue;
821             }
822             if ((*var=='l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
823                 if (pfx) *outstr++= pfx;
824                 strcpy(outstr,mdefs->layout[ndx]);
825                 outstr+= strlen(mdefs->layout[ndx]);
826                 if (sfx) *outstr++= sfx;
827             }
828             else if ((*var=='m')&&(mdefs->model)) {
829                 if (pfx) *outstr++= pfx;
830                 strcpy(outstr,mdefs->model);
831                 outstr+= strlen(mdefs->model);
832                 if (sfx) *outstr++= sfx;
833             }
834             else if ((*var=='v') && mdefs->variant[ndx] && *mdefs->variant[ndx]) {
835                 if (pfx) *outstr++= pfx;
836                 strcpy(outstr,mdefs->variant[ndx]);
837                 outstr+= strlen(mdefs->variant[ndx]);
838                 if (sfx) *outstr++= sfx;
839             }
840             if ((pfx=='(')&&(*str==')'))
841                 str++;
842         }
843         else {
844             *outstr++= *str++;
845         }
846     }
847     *outstr++= '\0';
848     if (orig!=name)
849         free(orig);
850     return name;
851 }
852
853 /***====================================================================***/
854
855 Bool
856 XkbcRF_GetComponents(   XkbRF_RulesPtr          rules,
857                         XkbRF_VarDefsPtr        defs,
858                         struct xkb_component_names *    names)
859 {
860     XkbRF_MultiDefsRec mdefs;
861
862     MakeMultiDefs(&mdefs, defs);
863
864     bzero((char *)names,sizeof(struct xkb_component_names));
865     XkbRF_ClearPartialMatches(rules);
866     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
867     XkbRF_ApplyPartialMatches(rules, names);
868     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
869     XkbRF_ApplyPartialMatches(rules, names);
870     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
871     XkbRF_ApplyPartialMatches(rules, names);
872
873     if (names->keycodes)
874         names->keycodes= XkbRF_SubstituteVars(names->keycodes, &mdefs);
875     if (names->symbols) 
876         names->symbols= XkbRF_SubstituteVars(names->symbols, &mdefs);
877     if (names->types)
878         names->types= XkbRF_SubstituteVars(names->types, &mdefs);
879     if (names->compat)
880         names->compat= XkbRF_SubstituteVars(names->compat, &mdefs);
881     if (names->geometry)
882         names->geometry= XkbRF_SubstituteVars(names->geometry, &mdefs);
883     if (names->keymap)  
884         names->keymap= XkbRF_SubstituteVars(names->keymap, &mdefs);
885
886     FreeMultiDefs(&mdefs);
887     return (names->keycodes && names->symbols && names->types &&
888                 names->compat && names->geometry ) || names->keymap;
889 }
890
891 static XkbRF_RulePtr
892 XkbcRF_AddRule(XkbRF_RulesPtr   rules)
893 {
894     if (rules->sz_rules<1) {
895         rules->sz_rules= 16;
896         rules->num_rules= 0;
897         rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
898     }
899     else if (rules->num_rules>=rules->sz_rules) {
900         rules->sz_rules*= 2;
901         rules->rules= _XkbTypedRealloc(rules->rules,rules->sz_rules,
902                                                         XkbRF_RuleRec);
903     }
904     if (!rules->rules) {
905         rules->sz_rules= rules->num_rules= 0;
906 #ifdef DEBUG
907         fprintf(stderr,"Allocation failure in XkbcRF_AddRule\n");
908 #endif
909         return NULL;
910     }
911     bzero((char *)&rules->rules[rules->num_rules],sizeof(XkbRF_RuleRec));
912     return &rules->rules[rules->num_rules++];
913 }
914
915 static XkbRF_GroupPtr
916 XkbcRF_AddGroup(XkbRF_RulesPtr  rules)
917 {
918     if (rules->sz_groups<1) {
919         rules->sz_groups= 16;
920         rules->num_groups= 0;
921         rules->groups= _XkbTypedCalloc(rules->sz_groups,XkbRF_GroupRec);
922     }
923     else if (rules->num_groups >= rules->sz_groups) {
924         rules->sz_groups *= 2;
925         rules->groups= _XkbTypedRealloc(rules->groups,rules->sz_groups,
926                                                         XkbRF_GroupRec);
927     }
928     if (!rules->groups) {
929         rules->sz_groups= rules->num_groups= 0;
930         return NULL;
931     }
932
933     bzero((char *)&rules->groups[rules->num_groups],sizeof(XkbRF_GroupRec));
934     return &rules->groups[rules->num_groups++];
935 }
936
937 Bool
938 XkbcRF_LoadRules(FILE *file, XkbRF_RulesPtr rules)
939 {
940 InputLine       line;
941 RemapSpec       remap;
942 XkbRF_RuleRec   trule,*rule;
943 XkbRF_GroupRec  tgroup,*group;
944
945     if (!(rules && file))
946         return False;
947     bzero((char *)&remap,sizeof(RemapSpec));
948     bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
949     InitInputLine(&line);
950     while (GetInputLine(file,&line,True)) {
951         if (CheckLine(&line,&remap,&trule,&tgroup)) {
952             if (tgroup.number) {
953                 if ((group= XkbcRF_AddGroup(rules))!=NULL) {
954                     *group= tgroup;
955                     bzero((char *)&tgroup,sizeof(XkbRF_GroupRec));
956                 }
957             } else {
958                 if ((rule= XkbcRF_AddRule(rules))!=NULL) {
959                     *rule= trule;
960                     bzero((char *)&trule,sizeof(XkbRF_RuleRec));
961                 }
962             }
963         }
964         line.num_line= 0;
965     }
966     FreeInputLine(&line);
967     return True;
968 }
969
970 static void
971 XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
972 {
973 register int i;
974
975     for (i=0;i<var->num_desc;i++) {
976         free(var->desc[i].name);
977         free(var->desc[i].desc);
978         var->desc[i].name= var->desc[i].desc= NULL;
979     }
980     free(var->desc);
981     var->desc= NULL;
982     return;
983 }
984
985 void
986 XkbcRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
987 {
988 int             i;
989 XkbRF_RulePtr   rule;
990 XkbRF_GroupPtr  group;
991
992     if (!rules)
993         return;
994     XkbRF_ClearVarDescriptions(&rules->models);
995     XkbRF_ClearVarDescriptions(&rules->layouts);
996     XkbRF_ClearVarDescriptions(&rules->variants);
997     XkbRF_ClearVarDescriptions(&rules->options);
998     if (rules->extra) {
999         for (i = 0; i < rules->num_extra; i++) {
1000             XkbRF_ClearVarDescriptions(&rules->extra[i]);
1001         }
1002         free(rules->extra);
1003         rules->num_extra= rules->sz_extra= 0;
1004         rules->extra= NULL;
1005     }
1006     for (i=0, rule = rules->rules; i < rules->num_rules && rules; i++, rule++) {
1007         free(rule->model);
1008         free(rule->layout);
1009         free(rule->variant);
1010         free(rule->option);
1011         free(rule->keycodes);
1012         free(rule->symbols);
1013         free(rule->types);
1014         free(rule->compat);
1015         free(rule->geometry);
1016         free(rule->keymap);
1017         bzero((char *)rule,sizeof(XkbRF_RuleRec));
1018     }
1019     free(rules->rules);
1020     rules->num_rules= rules->sz_rules= 0;
1021     rules->rules= NULL;
1022
1023     for (i=0, group = rules->groups; i < rules->num_groups && group; i++, group++) {
1024         free(group->name);
1025         free(group->words);
1026     }
1027     free(rules->groups);
1028     rules->num_groups= 0;
1029     rules->groups= NULL;
1030
1031     if (freeRules)
1032         free(rules);
1033     return;
1034 }