maprules.c revision 1b5d61b8
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
57typedef 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
65static void
66InitInputLine(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
75static void
76FreeInputLine(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
87static int
88InputLineAddChar(InputLine * line, int ch)
89{
90    if (line->num_line >= line->sz_line) {
91        if (line->line == line->buf) {
92            line->line = xallocarray(line->sz_line, 2);
93            memcpy(line->line, line->buf, line->sz_line);
94        }
95        else {
96            line->line = reallocarray(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
108static Bool
109GetInputLine(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
201static const char *cname[MAX_WORDS] = {
202    "model", "layout", "variant", "option",
203    "keycodes", "symbols", "types", "compat", "geometry"
204};
205
206typedef struct _RemapSpec {
207    int number;
208    int num_remap;
209    struct {
210        int word;
211        int index;
212    } remap[MAX_WORDS];
213} RemapSpec;
214
215typedef struct _FileSpec {
216    char *name[MAX_WORDS];
217    struct _FileSpec *pending;
218} FileSpec;
219
220typedef 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
231static char *
232get_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
256static void
257SetUpRemap(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
345static Bool
346MatchOneOf(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
370static Bool
371CheckLine(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
471static 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
485static void
486squeeze_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
498static Bool
499MakeMultiDefs(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
569static void
570FreeMultiDefs(XkbRF_MultiDefsPtr defs)
571{
572    free((void *) defs->options);
573    free((void *) defs->layout[1]);
574    free((void *) defs->variant[1]);
575}
576
577static void
578Apply(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
591static void
592XkbRF_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
603static Bool
604CheckGroup(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
625static int
626XkbRF_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
704static void
705XkbRF_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
715static void
716XkbRF_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
728static void
729XkbRF_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
752static char *
753XkbRF_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
857Bool
858XkbRF_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
890static XkbRF_RulePtr
891XkbRF_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 = reallocarray(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
912static XkbRF_GroupPtr
913XkbRF_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 = reallocarray(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
935Bool
936XkbRF_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
969Bool
970XkbRF_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
1002XkbRF_RulesPtr
1003XkbRF_Create(void)
1004{
1005    return calloc(1, sizeof(XkbRF_RulesRec));
1006}
1007
1008/***====================================================================***/
1009
1010void
1011XkbRF_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}
1050