Add rules rules file parsing from xkbfile
[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 "X11/extensions/XKBcommon.h"
31 #include "XKBcommonint.h"
32
33 #ifdef HAVE_DIX_CONFIG_H
34 #include <dix-config.h>
35 #elif defined(HAVE_CONFIG_H)
36 #include <config.h>
37 #endif
38
39 #include <stdio.h>
40 #include <ctype.h>
41 #include <stdlib.h>
42
43 #define X_INCLUDE_STRING_H
44 #define XOS_USE_NO_LOCKING
45 #include <X11/Xos_r.h>
46
47 #include <X11/Xproto.h>
48 #include <X11/X.h>
49 #include <X11/Xos.h>
50 #include <X11/Xfuncs.h>
51
52 #ifdef DEBUG
53 #define PR_DEBUG(s)             fprintf(stderr,s)
54 #define PR_DEBUG1(s,a)          fprintf(stderr,s,a)
55 #define PR_DEBUG2(s,a,b)        fprintf(stderr,s,a,b)
56 #else
57 #define PR_DEBUG(s)
58 #define PR_DEBUG1(s,a)
59 #define PR_DEBUG2(s,a,b)
60 #endif
61
62 /***====================================================================***/
63
64 #define DFLT_LINE_SIZE  128
65
66 typedef struct {
67         int     line_num;
68         int     sz_line;
69         int     num_line;
70         char    buf[DFLT_LINE_SIZE];
71         char *  line;
72 } InputLine;
73
74 static void
75 InitInputLine(InputLine *line)
76 {
77     line->line_num= 1;
78     line->num_line= 0;
79     line->sz_line= DFLT_LINE_SIZE;
80     line->line= line->buf;
81     return;
82 }
83
84 static void
85 FreeInputLine(InputLine *line)
86 {
87     if (line->line!=line->buf)
88         _XkbFree(line->line);
89     line->line_num= 1;
90     line->num_line= 0;
91     line->sz_line= DFLT_LINE_SIZE;
92     line->line= line->buf;
93     return;
94 }
95
96 static int
97 InputLineAddChar(InputLine *line,int ch)
98 {
99     if (line->num_line>=line->sz_line) {
100         if (line->line==line->buf) {
101             line->line= (char *)_XkbAlloc(line->sz_line*2);
102             memcpy(line->line,line->buf,line->sz_line);
103         }
104         else {
105             line->line=(char *)_XkbRealloc((char *)line->line,line->sz_line*2);
106         }
107         line->sz_line*= 2;
108     }
109     line->line[line->num_line++]= ch;
110     return ch;
111 }
112
113 #define ADD_CHAR(l,c)   ((l)->num_line<(l)->sz_line?\
114                                 (int)((l)->line[(l)->num_line++]= (c)):\
115                                 InputLineAddChar(l,c))
116
117 static Bool
118 GetInputLine(FILE *file,InputLine *line,Bool checkbang)
119 {
120 int     ch;
121 Bool    endOfFile,spacePending,slashPending,inComment;
122
123      endOfFile= False;
124      while ((!endOfFile)&&(line->num_line==0)) {
125         spacePending= slashPending= inComment= False;
126         while (((ch=getc(file))!='\n')&&(ch!=EOF)) {
127             if (ch=='\\') {
128                 if ((ch=getc(file))==EOF)
129                     break;
130                 if (ch=='\n') {
131                     inComment= False;
132                     ch= ' ';
133                     line->line_num++;
134                 }
135             }
136             if (inComment)
137                 continue;
138             if (ch=='/') {
139                 if (slashPending) {
140                     inComment= True;
141                     slashPending= False;
142                 }
143                 else {
144                     slashPending= True;
145                 }
146                 continue;
147             }
148             else if (slashPending) {
149                 if (spacePending) {
150                     ADD_CHAR(line,' ');
151                     spacePending= False;
152                 }
153                 ADD_CHAR(line,'/');
154                 slashPending= False;
155             }
156             if (isspace(ch)) {
157                 while (isspace(ch)&&(ch!='\n')&&(ch!=EOF)) {
158                     ch= getc(file);
159                 }
160                 if (ch==EOF)
161                     break;
162                 if ((ch!='\n')&&(line->num_line>0))
163                     spacePending= True;
164                 ungetc(ch,file);
165             }
166             else {
167                 if (spacePending) {
168                     ADD_CHAR(line,' ');
169                     spacePending= False;
170                 }
171                 if (checkbang && ch=='!') {
172                     if (line->num_line!=0) {
173                         PR_DEBUG("The '!' legal only at start of line\n");
174                         PR_DEBUG("Line containing '!' ignored\n");
175                         line->num_line= 0;
176                         inComment= 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
524    bzero((char *)mdefs,sizeof(XkbRF_MultiDefsRec));
525    mdefs->model = defs->model;
526    mdefs->options = _XkbDupString(defs->options);
527    if (mdefs->options) squeeze_spaces(mdefs->options); 
528
529    if (defs->layout) {
530        if (!strchr(defs->layout, ',')) {
531            mdefs->layout[0] = defs->layout;
532        } else {
533            char *p;
534            int i;
535            mdefs->layout[1] = _XkbDupString(defs->layout);
536            if (mdefs->layout[1] == NULL)
537               return False;
538            squeeze_spaces(mdefs->layout[1]);
539            p = mdefs->layout[1];
540            for (i = 2; i <= XkbNumKbdGroups; i++) {
541               if ((p = strchr(p, ','))) {
542                  *p++ = '\0';
543                  mdefs->layout[i] = p;
544               } else {
545                  break;
546               }
547            }
548            if (p && (p = strchr(p, ',')))
549               *p = '\0';
550        }
551    }
552
553    if (defs->variant) {
554        if (!strchr(defs->variant, ',')) {
555            mdefs->variant[0] = defs->variant;
556        } else {
557            char *p;
558            int i;
559            mdefs->variant[1] = _XkbDupString(defs->variant);
560            if (mdefs->variant[1] == NULL)
561               return False;
562            squeeze_spaces(mdefs->variant[1]);
563            p = mdefs->variant[1];
564            for (i = 2; i <= XkbNumKbdGroups; i++) {
565               if ((p = strchr(p, ','))) {
566                  *p++ = '\0';
567                  mdefs->variant[i] = p;
568               } else {
569                  break;
570               }
571            }
572            if (p && (p = strchr(p, ',')))
573               *p = '\0';
574        }
575    }
576    return True;
577 }
578
579 static void
580 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
581 {
582   if (defs->options) _XkbFree(defs->options);
583   if (defs->layout[1])  _XkbFree(defs->layout[1]);
584   if (defs->variant[1])  _XkbFree(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                         XkbComponentNamesPtr    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                 char *                  group_name,
617                 char *                  name)
618 {
619    int i;
620    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                         XkbComponentNamesPtr    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,XkbComponentNamesPtr 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                         XkbComponentNamesPtr    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 *)_XkbAlloc(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         _XkbFree(orig);
850     return name;
851 }
852
853 /***====================================================================***/
854
855 Bool
856 XkbcRF_GetComponents(   XkbRF_RulesPtr          rules,
857                         XkbRF_VarDefsPtr        defs,
858                         XkbComponentNamesPtr    names)
859 {
860     XkbRF_MultiDefsRec mdefs;
861
862     MakeMultiDefs(&mdefs, defs);
863
864     bzero((char *)names,sizeof(XkbComponentNamesRec));
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 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 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 Bool
971 XkbcRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
972 {
973 FILE *          file;
974 char            buf[PATH_MAX];
975 Bool            ok;
976
977     if ((!base)||(!rules))
978         return False;
979     if (locale) {
980         if (strlen(base)+strlen(locale)+2 > PATH_MAX)
981             return False;
982         sprintf(buf,"%s-%s", base, locale);
983     }
984     else {
985         if (strlen(base)+1 > PATH_MAX)
986             return False;
987         strcpy(buf,base);
988     }
989
990     file= fopen(buf, "r");
991     if ((!file)&&(locale)) { /* fallback if locale was specified */
992         strcpy(buf,base);
993         file= fopen(buf, "r");
994     }
995     if (!file)
996         return False;
997     ok= XkbcRF_LoadRules(file,rules);
998     fclose(file);
999     return ok;
1000 }
1001
1002 /***====================================================================***/
1003
1004 #define HEAD_NONE       0
1005 #define HEAD_MODEL      1
1006 #define HEAD_LAYOUT     2
1007 #define HEAD_VARIANT    3
1008 #define HEAD_OPTION     4
1009 #define HEAD_EXTRA      5
1010
1011 XkbRF_VarDescPtr
1012 XkbcRF_AddVarDesc(XkbRF_DescribeVarsPtr vars)
1013 {
1014     if (vars->sz_desc<1) {
1015         vars->sz_desc= 16;
1016         vars->num_desc= 0;
1017         vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
1018     }
1019     else if (vars->num_desc>=vars->sz_desc) {
1020         vars->sz_desc*= 2;
1021         vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
1022     }
1023     if (!vars->desc) {
1024         vars->sz_desc= vars->num_desc= 0;
1025         PR_DEBUG("Allocation failure in XkbcRF_AddVarDesc\n");
1026         return NULL;
1027     }
1028     vars->desc[vars->num_desc].name= NULL;
1029     vars->desc[vars->num_desc].desc= NULL;
1030     return &vars->desc[vars->num_desc++];
1031 }
1032
1033 XkbRF_VarDescPtr
1034 XkbcRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
1035 {
1036 XkbRF_VarDescPtr        nd;
1037
1038     if ((nd=XkbcRF_AddVarDesc(vars))!=NULL) {
1039         nd->name= _XkbDupString(from->name);
1040         nd->desc= _XkbDupString(from->desc);
1041     }
1042     return nd;
1043 }
1044
1045 XkbRF_DescribeVarsPtr 
1046 XkbcRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
1047 {
1048     if (rules->sz_extra<1) {
1049         rules->num_extra= 0;
1050         rules->sz_extra= 1;
1051         rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
1052         rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
1053     }
1054     else if (rules->num_extra>=rules->sz_extra) {
1055         rules->sz_extra*= 2;
1056         rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
1057                                                                 char *);
1058         rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
1059                                                         XkbRF_DescribeVarsRec);
1060     }
1061     if ((!rules->extra_names)||(!rules->extra)) {
1062         PR_DEBUG("allocation error in extra parts\n");
1063         rules->sz_extra= rules->num_extra= 0;
1064         rules->extra_names= NULL;
1065         rules->extra= NULL;
1066         return NULL;
1067     }
1068     rules->extra_names[rules->num_extra]= _XkbDupString(name);
1069     bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
1070     return &rules->extra[rules->num_extra++];
1071 }
1072
1073 Bool
1074 XkbcRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
1075 {
1076 InputLine               line;
1077 XkbRF_VarDescRec        tmp;
1078 char                    *tok;
1079 int                     len,headingtype,extra_ndx = 0;
1080
1081     bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
1082     headingtype = HEAD_NONE;
1083     InitInputLine(&line);
1084     for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
1085         if (line.line[0]=='!') {
1086             tok = strtok(&(line.line[1]), " \t");
1087             if (strcmp(tok,"model") == 0)
1088                 headingtype = HEAD_MODEL;
1089             else if (_XkbStrCaseCmp(tok,"layout") == 0)
1090                 headingtype = HEAD_LAYOUT;
1091             else if (_XkbStrCaseCmp(tok,"variant") == 0)
1092                 headingtype = HEAD_VARIANT;
1093             else if (_XkbStrCaseCmp(tok,"option") == 0)
1094                 headingtype = HEAD_OPTION;
1095             else {
1096                 int i;
1097                 headingtype = HEAD_EXTRA;
1098                 extra_ndx= -1;
1099                 for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
1100                     if (_XkbStrCaseCmp(tok,rules->extra_names[i]))
1101                         extra_ndx= i;
1102                 }
1103                 if (extra_ndx<0) {
1104                     XkbRF_DescribeVarsPtr       var;
1105                     PR_DEBUG1("Extra heading \"%s\" encountered\n",tok);
1106                     var= XkbcRF_AddVarToDescribe(rules,tok);
1107                     if (var)
1108                          extra_ndx= var-rules->extra;
1109                     else headingtype= HEAD_NONE;
1110                 }
1111             }
1112             continue;
1113         }
1114
1115         if (headingtype == HEAD_NONE) {
1116             PR_DEBUG("Must have a heading before first line of data\n");
1117             PR_DEBUG("Illegal line of data ignored\n");
1118             continue;
1119         }
1120
1121         len = strlen(line.line);
1122         if ((tmp.name= strtok(line.line, " \t")) == NULL) {
1123             PR_DEBUG("Huh? No token on line\n");
1124             PR_DEBUG("Illegal line of data ignored\n");
1125             continue;
1126         }
1127         if (strlen(tmp.name) == len) {
1128             PR_DEBUG("No description found\n");
1129             PR_DEBUG("Illegal line of data ignored\n");
1130             continue;
1131         }
1132
1133         tok = line.line + strlen(tmp.name) + 1;
1134         while ((*tok!='\n')&&isspace(*tok))
1135                 tok++;
1136         if (*tok == '\0') {
1137             PR_DEBUG("No description found\n");
1138             PR_DEBUG("Illegal line of data ignored\n");
1139             continue;
1140         }
1141         tmp.desc= tok;
1142         switch (headingtype) {
1143             case HEAD_MODEL:
1144                 XkbcRF_AddVarDescCopy(&rules->models,&tmp);
1145                 break;
1146             case HEAD_LAYOUT:
1147                 XkbcRF_AddVarDescCopy(&rules->layouts,&tmp);
1148                 break;
1149             case HEAD_VARIANT:
1150                 XkbcRF_AddVarDescCopy(&rules->variants,&tmp);
1151                 break;
1152             case HEAD_OPTION:
1153                 XkbcRF_AddVarDescCopy(&rules->options,&tmp);
1154                 break;
1155             case HEAD_EXTRA:
1156                 XkbcRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
1157                 break;
1158         }
1159     }
1160     FreeInputLine(&line);
1161     if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
1162         (rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
1163         (rules->num_extra==0)) {
1164         return False;
1165     }
1166     return True;
1167 }
1168
1169 Bool
1170 XkbcRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
1171 {
1172 FILE *          file;
1173 char            buf[PATH_MAX];
1174 Bool            ok;
1175
1176     if ((!base)||(!rules))
1177         return False;
1178     if (locale) {
1179         if (strlen(base)+strlen(locale)+6 > PATH_MAX)
1180             return False;
1181         sprintf(buf,"%s-%s.lst", base, locale);
1182     }
1183     else {
1184         if (strlen(base)+5 > PATH_MAX)
1185             return False;
1186         sprintf(buf,"%s.lst", base);
1187     }
1188
1189     file= fopen(buf, "r");
1190     if ((!file)&&(locale)) { /* fallback if locale was specified */
1191         sprintf(buf,"%s.lst", base);
1192
1193         file= fopen(buf, "r");
1194     }
1195     if (!file)
1196         return False;
1197     ok= XkbcRF_LoadDescriptions(file,rules);
1198     fclose(file);
1199     return ok;
1200 }
1201
1202 /***====================================================================***/
1203
1204 XkbRF_RulesPtr
1205 XkbcRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
1206 {
1207 XkbRF_RulesPtr  rules;
1208
1209     if ((!base)||((!wantDesc)&&(!wantRules)))
1210         return NULL;
1211     if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1212         return NULL;
1213     if (wantDesc&&(!XkbcRF_LoadDescriptionsByName(base,locale,rules))) {
1214         XkbcRF_Free(rules,True);
1215         return NULL;
1216     }
1217     if (wantRules&&(!XkbcRF_LoadRulesByName(base,locale,rules))) {
1218         XkbcRF_Free(rules,True);
1219         return NULL;
1220     }
1221     return rules;
1222 }
1223
1224 XkbRF_RulesPtr
1225 XkbcRF_Create(int szRules,int szExtra) 
1226 {
1227 XkbRF_RulesPtr rules;
1228
1229     if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1230         return NULL;
1231     if (szRules>0) {
1232         rules->sz_rules= szRules; 
1233         rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
1234         if (!rules->rules) {
1235             _XkbFree(rules);
1236             return NULL;
1237         }
1238     }
1239     if (szExtra>0) {
1240         rules->sz_extra= szExtra; 
1241         rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
1242         if (!rules->extra) {
1243             if (rules->rules)
1244                 _XkbFree(rules->rules);
1245             _XkbFree(rules);
1246             return NULL;
1247         }
1248     }
1249     return rules;
1250 }
1251
1252 /***====================================================================***/
1253
1254 static void
1255 XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
1256 {
1257 register int i;
1258     
1259     for (i=0;i<var->num_desc;i++) {
1260         if (var->desc[i].name)
1261             _XkbFree(var->desc[i].name);
1262         if (var->desc[i].desc)
1263             _XkbFree(var->desc[i].desc);
1264         var->desc[i].name= var->desc[i].desc= NULL;
1265     }
1266     if (var->desc)
1267         _XkbFree(var->desc);
1268     var->desc= NULL;
1269     return;
1270 }
1271
1272 void
1273 XkbcRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
1274 {
1275 int             i;
1276 XkbRF_RulePtr   rule;
1277 XkbRF_GroupPtr  group;
1278
1279     if (!rules)
1280         return;
1281     XkbRF_ClearVarDescriptions(&rules->models);
1282     XkbRF_ClearVarDescriptions(&rules->layouts);
1283     XkbRF_ClearVarDescriptions(&rules->variants);
1284     XkbRF_ClearVarDescriptions(&rules->options);
1285     if (rules->extra) {
1286         for (i = 0; i < rules->num_extra; i++) {
1287             XkbRF_ClearVarDescriptions(&rules->extra[i]);
1288         }
1289         _XkbFree(rules->extra);
1290         rules->num_extra= rules->sz_extra= 0;
1291         rules->extra= NULL;
1292     }
1293     if (rules->rules) {
1294         for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
1295             if (rule->model)    _XkbFree(rule->model);
1296             if (rule->layout)   _XkbFree(rule->layout);
1297             if (rule->variant)  _XkbFree(rule->variant);
1298             if (rule->option)   _XkbFree(rule->option);
1299             if (rule->keycodes) _XkbFree(rule->keycodes);
1300             if (rule->symbols)  _XkbFree(rule->symbols);
1301             if (rule->types)    _XkbFree(rule->types);
1302             if (rule->compat)   _XkbFree(rule->compat);
1303             if (rule->geometry) _XkbFree(rule->geometry);
1304             if (rule->keymap)   _XkbFree(rule->keymap);
1305             bzero((char *)rule,sizeof(XkbRF_RuleRec));
1306         }
1307         _XkbFree(rules->rules);
1308         rules->num_rules= rules->sz_rules= 0;
1309         rules->rules= NULL;
1310     }
1311
1312     if (rules->groups) {
1313         for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
1314             if (group->name)    _XkbFree(group->name);
1315             if (group->words)   _XkbFree(group->words);
1316         }
1317         _XkbFree(rules->groups);
1318         rules->num_groups= 0;
1319         rules->groups= NULL;
1320     }
1321     if (freeRules)
1322         _XkbFree(rules);
1323     return;
1324 }