c6900eccb53247bc471386b463c8267cec102bd3
[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     char *model;
222     char *layout[XkbNumKbdGroups + 1];
223     char *variant[XkbNumKbdGroups + 1];
224     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(char *wanted, char *vals_defined)
347 {
348     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, 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
502     memset((char *) mdefs, 0, sizeof(XkbRF_MultiDefsRec));
503     mdefs->model = defs->model;
504     mdefs->options = Xstrdup(defs->options);
505     if (mdefs->options)
506         squeeze_spaces(mdefs->options);
507
508     if (defs->layout) {
509         if (!strchr(defs->layout, ',')) {
510             mdefs->layout[0] = defs->layout;
511         }
512         else {
513             char *p;
514             int i;
515
516             mdefs->layout[1] = Xstrdup(defs->layout);
517             if (mdefs->layout[1] == NULL)
518                 return FALSE;
519             squeeze_spaces(mdefs->layout[1]);
520             p = mdefs->layout[1];
521             for (i = 2; i <= XkbNumKbdGroups; i++) {
522                 if ((p = strchr(p, ','))) {
523                     *p++ = '\0';
524                     mdefs->layout[i] = p;
525                 }
526                 else {
527                     break;
528                 }
529             }
530             if (p && (p = strchr(p, ',')))
531                 *p = '\0';
532         }
533     }
534
535     if (defs->variant) {
536         if (!strchr(defs->variant, ',')) {
537             mdefs->variant[0] = defs->variant;
538         }
539         else {
540             char *p;
541             int i;
542
543             mdefs->variant[1] = Xstrdup(defs->variant);
544             if (mdefs->variant[1] == NULL)
545                 return FALSE;
546             squeeze_spaces(mdefs->variant[1]);
547             p = mdefs->variant[1];
548             for (i = 2; i <= XkbNumKbdGroups; i++) {
549                 if ((p = strchr(p, ','))) {
550                     *p++ = '\0';
551                     mdefs->variant[i] = p;
552                 }
553                 else {
554                     break;
555                 }
556             }
557             if (p && (p = strchr(p, ',')))
558                 *p = '\0';
559         }
560     }
561     return TRUE;
562 }
563
564 static void
565 FreeMultiDefs(XkbRF_MultiDefsPtr defs)
566 {
567     free(defs->options);
568     free(defs->layout[1]);
569     free(defs->variant[1]);
570 }
571
572 static void
573 Apply(char *src, char **dst)
574 {
575     if (src) {
576         if (*src == '+' || *src == '!') {
577             *dst = _Concat(*dst, src);
578         }
579         else {
580             if (*dst == NULL)
581                 *dst = Xstrdup(src);
582         }
583     }
584 }
585
586 static void
587 XkbRF_ApplyRule(XkbRF_RulePtr rule, XkbComponentNamesPtr names)
588 {
589     rule->flags &= ~XkbRF_PendingMatch; /* clear the flag because it's applied */
590
591     Apply(rule->keycodes, &names->keycodes);
592     Apply(rule->symbols, &names->symbols);
593     Apply(rule->types, &names->types);
594     Apply(rule->compat, &names->compat);
595     Apply(rule->geometry, &names->geometry);
596 }
597
598 static Bool
599 CheckGroup(XkbRF_RulesPtr rules, char *group_name, char *name)
600 {
601     int i;
602     char *p;
603     XkbRF_GroupPtr group;
604
605     for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
606         if (!strcmp(group->name, group_name)) {
607             break;
608         }
609     }
610     if (i == rules->num_groups)
611         return FALSE;
612     for (i = 0, p = group->words; i < group->number; i++, p += strlen(p) + 1) {
613         if (!strcmp(p, name)) {
614             return TRUE;
615         }
616     }
617     return FALSE;
618 }
619
620 static int
621 XkbRF_CheckApplyRule(XkbRF_RulePtr rule,
622                      XkbRF_MultiDefsPtr mdefs,
623                      XkbComponentNamesPtr names, XkbRF_RulesPtr rules)
624 {
625     Bool pending = FALSE;
626
627     if (rule->model != NULL) {
628         if (mdefs->model == NULL)
629             return 0;
630         if (strcmp(rule->model, "*") == 0) {
631             pending = TRUE;
632         }
633         else {
634             if (rule->model[0] == '$') {
635                 if (!CheckGroup(rules, rule->model, mdefs->model))
636                     return 0;
637             }
638             else {
639                 if (strcmp(rule->model, mdefs->model) != 0)
640                     return 0;
641             }
642         }
643     }
644     if (rule->option != NULL) {
645         if (mdefs->options == NULL)
646             return 0;
647         if ((!MatchOneOf(rule->option, mdefs->options)))
648             return 0;
649     }
650
651     if (rule->layout != NULL) {
652         if (mdefs->layout[rule->layout_num] == NULL ||
653             *mdefs->layout[rule->layout_num] == '\0')
654             return 0;
655         if (strcmp(rule->layout, "*") == 0) {
656             pending = TRUE;
657         }
658         else {
659             if (rule->layout[0] == '$') {
660                 if (!CheckGroup(rules, rule->layout,
661                                 mdefs->layout[rule->layout_num]))
662                     return 0;
663             }
664             else {
665                 if (strcmp(rule->layout, mdefs->layout[rule->layout_num]) != 0)
666                     return 0;
667             }
668         }
669     }
670     if (rule->variant != NULL) {
671         if (mdefs->variant[rule->variant_num] == NULL ||
672             *mdefs->variant[rule->variant_num] == '\0')
673             return 0;
674         if (strcmp(rule->variant, "*") == 0) {
675             pending = TRUE;
676         }
677         else {
678             if (rule->variant[0] == '$') {
679                 if (!CheckGroup(rules, rule->variant,
680                                 mdefs->variant[rule->variant_num]))
681                     return 0;
682             }
683             else {
684                 if (strcmp(rule->variant,
685                            mdefs->variant[rule->variant_num]) != 0)
686                     return 0;
687             }
688         }
689     }
690     if (pending) {
691         rule->flags |= XkbRF_PendingMatch;
692         return rule->number;
693     }
694     /* exact match, apply it now */
695     XkbRF_ApplyRule(rule, names);
696     return rule->number;
697 }
698
699 static void
700 XkbRF_ClearPartialMatches(XkbRF_RulesPtr rules)
701 {
702     register int i;
703     XkbRF_RulePtr rule;
704
705     for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
706         rule->flags &= ~XkbRF_PendingMatch;
707     }
708 }
709
710 static void
711 XkbRF_ApplyPartialMatches(XkbRF_RulesPtr rules, XkbComponentNamesPtr names)
712 {
713     int i;
714     XkbRF_RulePtr rule;
715
716     for (rule = rules->rules, i = 0; i < rules->num_rules; i++, rule++) {
717         if ((rule->flags & XkbRF_PendingMatch) == 0)
718             continue;
719         XkbRF_ApplyRule(rule, names);
720     }
721 }
722
723 static void
724 XkbRF_CheckApplyRules(XkbRF_RulesPtr rules,
725                       XkbRF_MultiDefsPtr mdefs,
726                       XkbComponentNamesPtr names, int flags)
727 {
728     int i;
729     XkbRF_RulePtr rule;
730     int skip;
731
732     for (rule = rules->rules, i = 0; i < rules->num_rules; rule++, i++) {
733         if ((rule->flags & flags) != flags)
734             continue;
735         skip = XkbRF_CheckApplyRule(rule, mdefs, names, rules);
736         if (skip && !(flags & XkbRF_Option)) {
737             for (; (i < rules->num_rules) && (rule->number == skip);
738                  rule++, i++);
739             rule--;
740             i--;
741         }
742     }
743 }
744
745 /***====================================================================***/
746
747 static char *
748 XkbRF_SubstituteVars(char *name, XkbRF_MultiDefsPtr mdefs)
749 {
750     char *str, *outstr, *orig, *var;
751     int len, ndx;
752
753     orig = name;
754     str = index(name, '%');
755     if (str == NULL)
756         return name;
757     len = strlen(name);
758     while (str != NULL) {
759         char pfx = str[1];
760         int extra_len = 0;
761
762         if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
763             extra_len = 1;
764             str++;
765         }
766         else if (pfx == '(') {
767             extra_len = 2;
768             str++;
769         }
770         var = str + 1;
771         str = get_index(var + 1, &ndx);
772         if (ndx == -1) {
773             str = index(str, '%');
774             continue;
775         }
776         if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx])
777             len += strlen(mdefs->layout[ndx]) + extra_len;
778         else if ((*var == 'm') && mdefs->model)
779             len += strlen(mdefs->model) + extra_len;
780         else if ((*var == 'v') && mdefs->variant[ndx] && *mdefs->variant[ndx])
781             len += strlen(mdefs->variant[ndx]) + extra_len;
782         if ((pfx == '(') && (*str == ')')) {
783             str++;
784         }
785         str = index(&str[0], '%');
786     }
787     name = malloc(len + 1);
788     str = orig;
789     outstr = name;
790     while (*str != '\0') {
791         if (str[0] == '%') {
792             char pfx, sfx;
793
794             str++;
795             pfx = str[0];
796             sfx = '\0';
797             if ((pfx == '+') || (pfx == '|') || (pfx == '_') || (pfx == '-')) {
798                 str++;
799             }
800             else if (pfx == '(') {
801                 sfx = ')';
802                 str++;
803             }
804             else
805                 pfx = '\0';
806
807             var = str;
808             str = get_index(var + 1, &ndx);
809             if (ndx == -1) {
810                 continue;
811             }
812             if ((*var == 'l') && mdefs->layout[ndx] && *mdefs->layout[ndx]) {
813                 if (pfx)
814                     *outstr++ = pfx;
815                 strcpy(outstr, mdefs->layout[ndx]);
816                 outstr += strlen(mdefs->layout[ndx]);
817                 if (sfx)
818                     *outstr++ = sfx;
819             }
820             else if ((*var == 'm') && (mdefs->model)) {
821                 if (pfx)
822                     *outstr++ = pfx;
823                 strcpy(outstr, mdefs->model);
824                 outstr += strlen(mdefs->model);
825                 if (sfx)
826                     *outstr++ = sfx;
827             }
828             else if ((*var == 'v') && mdefs->variant[ndx] &&
829                      *mdefs->variant[ndx]) {
830                 if (pfx)
831                     *outstr++ = pfx;
832                 strcpy(outstr, mdefs->variant[ndx]);
833                 outstr += strlen(mdefs->variant[ndx]);
834                 if (sfx)
835                     *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 XkbRF_GetComponents(XkbRF_RulesPtr rules,
854                     XkbRF_VarDefsPtr defs, XkbComponentNamesPtr names)
855 {
856     XkbRF_MultiDefsRec mdefs;
857
858     MakeMultiDefs(&mdefs, defs);
859
860     memset((char *) names, 0, sizeof(XkbComponentNamesRec));
861     XkbRF_ClearPartialMatches(rules);
862     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Normal);
863     XkbRF_ApplyPartialMatches(rules, names);
864     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Append);
865     XkbRF_ApplyPartialMatches(rules, names);
866     XkbRF_CheckApplyRules(rules, &mdefs, names, XkbRF_Option);
867
868     if (names->keycodes)
869         names->keycodes = XkbRF_SubstituteVars(names->keycodes, &mdefs);
870     if (names->symbols)
871         names->symbols = XkbRF_SubstituteVars(names->symbols, &mdefs);
872     if (names->types)
873         names->types = XkbRF_SubstituteVars(names->types, &mdefs);
874     if (names->compat)
875         names->compat = XkbRF_SubstituteVars(names->compat, &mdefs);
876     if (names->geometry)
877         names->geometry = XkbRF_SubstituteVars(names->geometry, &mdefs);
878
879     FreeMultiDefs(&mdefs);
880     return (names->keycodes && names->symbols && names->types &&
881             names->compat && names->geometry);
882 }
883
884 static XkbRF_RulePtr
885 XkbRF_AddRule(XkbRF_RulesPtr rules)
886 {
887     if (rules->sz_rules < 1) {
888         rules->sz_rules = 16;
889         rules->num_rules = 0;
890         rules->rules = calloc(rules->sz_rules, sizeof(XkbRF_RuleRec));
891     }
892     else if (rules->num_rules >= rules->sz_rules) {
893         rules->sz_rules *= 2;
894         rules->rules = realloc(rules->rules,
895                                rules->sz_rules * sizeof(XkbRF_RuleRec));
896     }
897     if (!rules->rules) {
898         rules->sz_rules = rules->num_rules = 0;
899         DebugF("Allocation failure in XkbRF_AddRule\n");
900         return NULL;
901     }
902     memset((char *) &rules->rules[rules->num_rules], 0, sizeof(XkbRF_RuleRec));
903     return &rules->rules[rules->num_rules++];
904 }
905
906 static XkbRF_GroupPtr
907 XkbRF_AddGroup(XkbRF_RulesPtr rules)
908 {
909     if (rules->sz_groups < 1) {
910         rules->sz_groups = 16;
911         rules->num_groups = 0;
912         rules->groups = calloc(rules->sz_groups, sizeof(XkbRF_GroupRec));
913     }
914     else if (rules->num_groups >= rules->sz_groups) {
915         rules->sz_groups *= 2;
916         rules->groups = realloc(rules->groups,
917                                 rules->sz_groups * sizeof(XkbRF_GroupRec));
918     }
919     if (!rules->groups) {
920         rules->sz_groups = rules->num_groups = 0;
921         return NULL;
922     }
923
924     memset((char *) &rules->groups[rules->num_groups], 0,
925            sizeof(XkbRF_GroupRec));
926     return &rules->groups[rules->num_groups++];
927 }
928
929 Bool
930 XkbRF_LoadRules(FILE * file, XkbRF_RulesPtr rules)
931 {
932     InputLine line;
933     RemapSpec remap;
934     XkbRF_RuleRec trule, *rule;
935     XkbRF_GroupRec tgroup, *group;
936
937     if (!(rules && file))
938         return FALSE;
939     memset((char *) &remap, 0, sizeof(RemapSpec));
940     memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
941     InitInputLine(&line);
942     while (GetInputLine(file, &line, TRUE)) {
943         if (CheckLine(&line, &remap, &trule, &tgroup)) {
944             if (tgroup.number) {
945                 if ((group = XkbRF_AddGroup(rules)) != NULL) {
946                     *group = tgroup;
947                     memset((char *) &tgroup, 0, sizeof(XkbRF_GroupRec));
948                 }
949             }
950             else {
951                 if ((rule = XkbRF_AddRule(rules)) != NULL) {
952                     *rule = trule;
953                     memset((char *) &trule, 0, sizeof(XkbRF_RuleRec));
954                 }
955             }
956         }
957         line.num_line = 0;
958     }
959     FreeInputLine(&line);
960     return TRUE;
961 }
962
963 Bool
964 XkbRF_LoadRulesByName(char *base, char *locale, XkbRF_RulesPtr rules)
965 {
966     FILE *file;
967     char buf[PATH_MAX];
968     Bool ok;
969
970     if ((!base) || (!rules))
971         return FALSE;
972     if (locale) {
973         if (snprintf(buf, PATH_MAX, "%s-%s", base, locale) >= PATH_MAX)
974             return FALSE;
975     }
976     else {
977         if (strlen(base) + 1 > PATH_MAX)
978             return FALSE;
979         strcpy(buf, base);
980     }
981
982     file = fopen(buf, "r");
983     if ((!file) && (locale)) {  /* fallback if locale was specified */
984         strcpy(buf, base);
985         file = fopen(buf, "r");
986     }
987     if (!file)
988         return FALSE;
989     ok = XkbRF_LoadRules(file, rules);
990     fclose(file);
991     return ok;
992 }
993
994 /***====================================================================***/
995
996 XkbRF_RulesPtr
997 XkbRF_Create(void)
998 {
999     return calloc(1, sizeof(XkbRF_RulesRec));
1000 }
1001
1002 /***====================================================================***/
1003
1004 void
1005 XkbRF_Free(XkbRF_RulesPtr rules, Bool freeRules)
1006 {
1007     int i;
1008     XkbRF_RulePtr rule;
1009     XkbRF_GroupPtr group;
1010
1011     if (!rules)
1012         return;
1013     if (rules->rules) {
1014         for (i = 0, rule = rules->rules; i < rules->num_rules; i++, rule++) {
1015             free(rule->model);
1016             free(rule->layout);
1017             free(rule->variant);
1018             free(rule->option);
1019             free(rule->keycodes);
1020             free(rule->symbols);
1021             free(rule->types);
1022             free(rule->compat);
1023             free(rule->geometry);
1024             memset((char *) rule, 0, sizeof(XkbRF_RuleRec));
1025         }
1026         free(rules->rules);
1027         rules->num_rules = rules->sz_rules = 0;
1028         rules->rules = NULL;
1029     }
1030
1031     if (rules->groups) {
1032         for (i = 0, group = rules->groups; i < rules->num_groups; i++, group++) {
1033             free(group->name);
1034             free(group->words);
1035         }
1036         free(rules->groups);
1037         rules->num_groups = 0;
1038         rules->groups = NULL;
1039     }
1040     if (freeRules)
1041         free(rules);
1042     return;
1043 }