xkbcomp: Lazy keysym parsing (avoid XStringToKeysym)
[profile/ivi/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 "X11/extensions/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         _XkbFree(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 *)_XkbAlloc(line->sz_line*2);
103             memcpy(line->line,line->buf,line->sz_line);
104         }
105         else {
106             line->line=(char *)_XkbRealloc((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) _XkbFree(defs->options);
584   if (defs->layout[1])  _XkbFree(defs->layout[1]);
585   if (defs->variant[1])  _XkbFree(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                         XkbComponentNamesPtr    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                         XkbComponentNamesPtr    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,XkbComponentNamesPtr 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                         XkbComponentNamesPtr    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 *)_XkbAlloc(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         _XkbFree(orig);
851     return name;
852 }
853
854 /***====================================================================***/
855
856 Bool
857 XkbcRF_GetComponents(   XkbRF_RulesPtr          rules,
858                         XkbRF_VarDefsPtr        defs,
859                         XkbComponentNamesPtr    names)
860 {
861     XkbRF_MultiDefsRec mdefs;
862
863     MakeMultiDefs(&mdefs, defs);
864
865     bzero((char *)names,sizeof(XkbComponentNamesRec));
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 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 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 Bool
972 XkbcRF_LoadRulesByName(char *base,char *locale,XkbRF_RulesPtr rules)
973 {
974 FILE *          file;
975 char            buf[PATH_MAX];
976 Bool            ok;
977
978     if ((!base)||(!rules))
979         return False;
980     if (locale) {
981         if (strlen(base)+strlen(locale)+2 > PATH_MAX)
982             return False;
983         sprintf(buf,"%s-%s", base, locale);
984     }
985     else {
986         if (strlen(base)+1 > PATH_MAX)
987             return False;
988         strcpy(buf,base);
989     }
990
991     file= fopen(buf, "r");
992     if ((!file)&&(locale)) { /* fallback if locale was specified */
993         strcpy(buf,base);
994         file= fopen(buf, "r");
995     }
996     if (!file)
997         return False;
998     ok= XkbcRF_LoadRules(file,rules);
999     fclose(file);
1000     return ok;
1001 }
1002
1003 /***====================================================================***/
1004
1005 #define HEAD_NONE       0
1006 #define HEAD_MODEL      1
1007 #define HEAD_LAYOUT     2
1008 #define HEAD_VARIANT    3
1009 #define HEAD_OPTION     4
1010 #define HEAD_EXTRA      5
1011
1012 XkbRF_VarDescPtr
1013 XkbcRF_AddVarDesc(XkbRF_DescribeVarsPtr vars)
1014 {
1015     if (vars->sz_desc<1) {
1016         vars->sz_desc= 16;
1017         vars->num_desc= 0;
1018         vars->desc= _XkbTypedCalloc(vars->sz_desc,XkbRF_VarDescRec);
1019     }
1020     else if (vars->num_desc>=vars->sz_desc) {
1021         vars->sz_desc*= 2;
1022         vars->desc= _XkbTypedRealloc(vars->desc,vars->sz_desc,XkbRF_VarDescRec);
1023     }
1024     if (!vars->desc) {
1025         vars->sz_desc= vars->num_desc= 0;
1026         PR_DEBUG("Allocation failure in XkbcRF_AddVarDesc\n");
1027         return NULL;
1028     }
1029     vars->desc[vars->num_desc].name= NULL;
1030     vars->desc[vars->num_desc].desc= NULL;
1031     return &vars->desc[vars->num_desc++];
1032 }
1033
1034 XkbRF_VarDescPtr
1035 XkbcRF_AddVarDescCopy(XkbRF_DescribeVarsPtr vars,XkbRF_VarDescPtr from)
1036 {
1037 XkbRF_VarDescPtr        nd;
1038
1039     if ((nd=XkbcRF_AddVarDesc(vars))!=NULL) {
1040         nd->name= _XkbDupString(from->name);
1041         nd->desc= _XkbDupString(from->desc);
1042     }
1043     return nd;
1044 }
1045
1046 XkbRF_DescribeVarsPtr
1047 XkbcRF_AddVarToDescribe(XkbRF_RulesPtr rules,char *name)
1048 {
1049     if (rules->sz_extra<1) {
1050         rules->num_extra= 0;
1051         rules->sz_extra= 1;
1052         rules->extra_names= _XkbTypedCalloc(rules->sz_extra,char *);
1053         rules->extra= _XkbTypedCalloc(rules->sz_extra, XkbRF_DescribeVarsRec);
1054     }
1055     else if (rules->num_extra>=rules->sz_extra) {
1056         rules->sz_extra*= 2;
1057         rules->extra_names= _XkbTypedRealloc(rules->extra_names,rules->sz_extra,
1058                                                                 char *);
1059         rules->extra=_XkbTypedRealloc(rules->extra, rules->sz_extra,
1060                                                         XkbRF_DescribeVarsRec);
1061     }
1062     if ((!rules->extra_names)||(!rules->extra)) {
1063         PR_DEBUG("allocation error in extra parts\n");
1064         rules->sz_extra= rules->num_extra= 0;
1065         rules->extra_names= NULL;
1066         rules->extra= NULL;
1067         return NULL;
1068     }
1069     rules->extra_names[rules->num_extra]= _XkbDupString(name);
1070     bzero(&rules->extra[rules->num_extra],sizeof(XkbRF_DescribeVarsRec));
1071     return &rules->extra[rules->num_extra++];
1072 }
1073
1074 Bool
1075 XkbcRF_LoadDescriptions(FILE *file,XkbRF_RulesPtr rules)
1076 {
1077 InputLine               line;
1078 XkbRF_VarDescRec        tmp;
1079 char                    *tok;
1080 int                     len,headingtype,extra_ndx = 0;
1081
1082     bzero((char *)&tmp, sizeof(XkbRF_VarDescRec));
1083     headingtype = HEAD_NONE;
1084     InitInputLine(&line);
1085     for ( ; GetInputLine(file,&line,False); line.num_line= 0) {
1086         if (line.line[0]=='!') {
1087             tok = strtok(&(line.line[1]), " \t");
1088             if (strcmp(tok,"model") == 0)
1089                 headingtype = HEAD_MODEL;
1090             else if (_XkbStrCaseCmp(tok,"layout") == 0)
1091                 headingtype = HEAD_LAYOUT;
1092             else if (_XkbStrCaseCmp(tok,"variant") == 0)
1093                 headingtype = HEAD_VARIANT;
1094             else if (_XkbStrCaseCmp(tok,"option") == 0)
1095                 headingtype = HEAD_OPTION;
1096             else {
1097                 int i;
1098                 headingtype = HEAD_EXTRA;
1099                 extra_ndx= -1;
1100                 for (i=0;(i<rules->num_extra)&&(extra_ndx<0);i++) {
1101                     if (_XkbStrCaseCmp(tok,rules->extra_names[i]))
1102                         extra_ndx= i;
1103                 }
1104                 if (extra_ndx<0) {
1105                     XkbRF_DescribeVarsPtr       var;
1106                     PR_DEBUG1("Extra heading \"%s\" encountered\n",tok);
1107                     var= XkbcRF_AddVarToDescribe(rules,tok);
1108                     if (var)
1109                          extra_ndx= var-rules->extra;
1110                     else headingtype= HEAD_NONE;
1111                 }
1112             }
1113             continue;
1114         }
1115
1116         if (headingtype == HEAD_NONE) {
1117             PR_DEBUG("Must have a heading before first line of data\n");
1118             PR_DEBUG("Illegal line of data ignored\n");
1119             continue;
1120         }
1121
1122         len = strlen(line.line);
1123         if ((tmp.name= strtok(line.line, " \t")) == NULL) {
1124             PR_DEBUG("Huh? No token on line\n");
1125             PR_DEBUG("Illegal line of data ignored\n");
1126             continue;
1127         }
1128         if (strlen(tmp.name) == len) {
1129             PR_DEBUG("No description found\n");
1130             PR_DEBUG("Illegal line of data ignored\n");
1131             continue;
1132         }
1133
1134         tok = line.line + strlen(tmp.name) + 1;
1135         while ((*tok!='\n')&&isspace(*tok))
1136                 tok++;
1137         if (*tok == '\0') {
1138             PR_DEBUG("No description found\n");
1139             PR_DEBUG("Illegal line of data ignored\n");
1140             continue;
1141         }
1142         tmp.desc= tok;
1143         switch (headingtype) {
1144             case HEAD_MODEL:
1145                 XkbcRF_AddVarDescCopy(&rules->models,&tmp);
1146                 break;
1147             case HEAD_LAYOUT:
1148                 XkbcRF_AddVarDescCopy(&rules->layouts,&tmp);
1149                 break;
1150             case HEAD_VARIANT:
1151                 XkbcRF_AddVarDescCopy(&rules->variants,&tmp);
1152                 break;
1153             case HEAD_OPTION:
1154                 XkbcRF_AddVarDescCopy(&rules->options,&tmp);
1155                 break;
1156             case HEAD_EXTRA:
1157                 XkbcRF_AddVarDescCopy(&rules->extra[extra_ndx],&tmp);
1158                 break;
1159         }
1160     }
1161     FreeInputLine(&line);
1162     if ((rules->models.num_desc==0) && (rules->layouts.num_desc==0) &&
1163         (rules->variants.num_desc==0) && (rules->options.num_desc==0) &&
1164         (rules->num_extra==0)) {
1165         return False;
1166     }
1167     return True;
1168 }
1169
1170 Bool
1171 XkbcRF_LoadDescriptionsByName(char *base,char *locale,XkbRF_RulesPtr rules)
1172 {
1173 FILE *          file;
1174 char            buf[PATH_MAX];
1175 Bool            ok;
1176
1177     if ((!base)||(!rules))
1178         return False;
1179     if (locale) {
1180         if (strlen(base)+strlen(locale)+6 > PATH_MAX)
1181             return False;
1182         sprintf(buf,"%s-%s.lst", base, locale);
1183     }
1184     else {
1185         if (strlen(base)+5 > PATH_MAX)
1186             return False;
1187         sprintf(buf,"%s.lst", base);
1188     }
1189
1190     file= fopen(buf, "r");
1191     if ((!file)&&(locale)) { /* fallback if locale was specified */
1192         sprintf(buf,"%s.lst", base);
1193
1194         file= fopen(buf, "r");
1195     }
1196     if (!file)
1197         return False;
1198     ok= XkbcRF_LoadDescriptions(file,rules);
1199     fclose(file);
1200     return ok;
1201 }
1202
1203 /***====================================================================***/
1204
1205 XkbRF_RulesPtr
1206 XkbcRF_Load(char *base,char *locale,Bool wantDesc,Bool wantRules)
1207 {
1208 XkbRF_RulesPtr  rules;
1209
1210     if ((!base)||((!wantDesc)&&(!wantRules)))
1211         return NULL;
1212     if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1213         return NULL;
1214     if (wantDesc&&(!XkbcRF_LoadDescriptionsByName(base,locale,rules))) {
1215         XkbcRF_Free(rules,True);
1216         return NULL;
1217     }
1218     if (wantRules&&(!XkbcRF_LoadRulesByName(base,locale,rules))) {
1219         XkbcRF_Free(rules,True);
1220         return NULL;
1221     }
1222     return rules;
1223 }
1224
1225 XkbRF_RulesPtr
1226 XkbcRF_Create(int szRules,int szExtra)
1227 {
1228 XkbRF_RulesPtr rules;
1229
1230     if ((rules=_XkbTypedCalloc(1,XkbRF_RulesRec))==NULL)
1231         return NULL;
1232     if (szRules>0) {
1233         rules->sz_rules= szRules;
1234         rules->rules= _XkbTypedCalloc(rules->sz_rules,XkbRF_RuleRec);
1235         if (!rules->rules) {
1236             _XkbFree(rules);
1237             return NULL;
1238         }
1239     }
1240     if (szExtra>0) {
1241         rules->sz_extra= szExtra;
1242         rules->extra= _XkbTypedCalloc(rules->sz_extra,XkbRF_DescribeVarsRec);
1243         if (!rules->extra) {
1244             if (rules->rules)
1245                 _XkbFree(rules->rules);
1246             _XkbFree(rules);
1247             return NULL;
1248         }
1249     }
1250     return rules;
1251 }
1252
1253 /***====================================================================***/
1254
1255 static void
1256 XkbRF_ClearVarDescriptions(XkbRF_DescribeVarsPtr var)
1257 {
1258 register int i;
1259
1260     for (i=0;i<var->num_desc;i++) {
1261         if (var->desc[i].name)
1262             _XkbFree(var->desc[i].name);
1263         if (var->desc[i].desc)
1264             _XkbFree(var->desc[i].desc);
1265         var->desc[i].name= var->desc[i].desc= NULL;
1266     }
1267     if (var->desc)
1268         _XkbFree(var->desc);
1269     var->desc= NULL;
1270     return;
1271 }
1272
1273 void
1274 XkbcRF_Free(XkbRF_RulesPtr rules,Bool freeRules)
1275 {
1276 int             i;
1277 XkbRF_RulePtr   rule;
1278 XkbRF_GroupPtr  group;
1279
1280     if (!rules)
1281         return;
1282     XkbRF_ClearVarDescriptions(&rules->models);
1283     XkbRF_ClearVarDescriptions(&rules->layouts);
1284     XkbRF_ClearVarDescriptions(&rules->variants);
1285     XkbRF_ClearVarDescriptions(&rules->options);
1286     if (rules->extra) {
1287         for (i = 0; i < rules->num_extra; i++) {
1288             XkbRF_ClearVarDescriptions(&rules->extra[i]);
1289         }
1290         _XkbFree(rules->extra);
1291         rules->num_extra= rules->sz_extra= 0;
1292         rules->extra= NULL;
1293     }
1294     if (rules->rules) {
1295         for (i=0,rule=rules->rules;i<rules->num_rules;i++,rule++) {
1296             if (rule->model)    _XkbFree(rule->model);
1297             if (rule->layout)   _XkbFree(rule->layout);
1298             if (rule->variant)  _XkbFree(rule->variant);
1299             if (rule->option)   _XkbFree(rule->option);
1300             if (rule->keycodes) _XkbFree(rule->keycodes);
1301             if (rule->symbols)  _XkbFree(rule->symbols);
1302             if (rule->types)    _XkbFree(rule->types);
1303             if (rule->compat)   _XkbFree(rule->compat);
1304             if (rule->geometry) _XkbFree(rule->geometry);
1305             if (rule->keymap)   _XkbFree(rule->keymap);
1306             bzero((char *)rule,sizeof(XkbRF_RuleRec));
1307         }
1308         _XkbFree(rules->rules);
1309         rules->num_rules= rules->sz_rules= 0;
1310         rules->rules= NULL;
1311     }
1312
1313     if (rules->groups) {
1314         for (i=0, group=rules->groups;i<rules->num_groups;i++,group++) {
1315             if (group->name)    _XkbFree(group->name);
1316             if (group->words)   _XkbFree(group->words);
1317         }
1318         _XkbFree(rules->groups);
1319         rules->num_groups= 0;
1320         rules->groups= NULL;
1321     }
1322     if (freeRules)
1323         _XkbFree(rules);
1324     return;
1325 }