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