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