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