f7b7404778f0d120b1927a73cdbe5e0366ea70c0
[platform/upstream/libxkbcommon.git] / src / xkbcomp / compat.c
1 /************************************************************
2  * Copyright (c) 1994 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 #include "xkbcomp-priv.h"
28 #include "text.h"
29 #include "expr.h"
30 #include "action.h"
31 #include "vmod.h"
32 #include "include.h"
33
34 /*
35  * The xkb_compat section
36  * =====================
37  * This section is the third to be processesed, after xkb_keycodes and
38  * xkb_types.
39  *
40  * Interpret statements
41  * --------------------
42  * Statements of the form:
43  *      interpret Num_Lock+Any { ... }
44  *
45  * The body of the statment may include statements of the following
46  * forms:
47  *
48  * - action statement:
49  *      action = LockMods(modifiers=NumLock);
50  *
51  * - virtual modifier statement:
52  *      virtualModifier = NumLock;
53  *
54  * - repeat statement:
55  *      repeat = True;
56  *
57  * - useModMapMods statement:
58  *      useModMapMods = level1;
59  *
60  * Indicator map statements
61  * ------------------------
62  * Statements of the form:
63  *      indicator "Shift Lock" { ... }
64  *
65  *   This statement specifies the behavior and binding of the indicator
66  *   with the given name ("Shift Lock" above). The name should have been
67  *   declared previously in the xkb_keycodes section (see Indicator name
68  *   statement), and given an index there. If it wasn't, it is created
69  *   with the next free index.
70  *   The body of the statement describes the conditions of the keyboard
71  *   state which will cause the indicator to be lit. It may include the
72  *   following statements:
73  *
74  * - modifiers statment:
75  *      modifiers = ScrollLock;
76  *
77  *   If the given modifiers are in the required state (see below), the
78  *   led is lit.
79  *
80  * - whichModifierState statment:
81  *      whichModState = Latched + Locked;
82  *
83  *   Can be any combination of:
84  *      base, latched, locked, effective
85  *      any (i.e. all of the above)
86  *      none (i.e. none of the above)
87  *      compat (this is legal, but unused)
88  *   This will cause the respective portion of the modifer state (see
89  *   struct xkb_state) to be matched against the modifiers given in the
90  *   "modifiers" statement.
91  *
92  *   Here's a simple example:
93  *      indicator "Num Lock" {
94  *          modifiers = NumLock;
95  *          whichModState = Locked;
96  *      };
97  *   Whenever the NumLock modifier is locked, the Num Lock indicator
98  *   will light up.
99  *
100  * - groups statment:
101  *      groups = All - group1;
102  *
103  *   If the given groups are in the required state (see below), the led
104  *   is lit.
105  *
106  * - whichGroupState statment:
107  *      whichGroupState = Effective;
108  *
109  *   Can be any combination of:
110  *      base, latched, locked, effective
111  *      any (i.e. all of the above)
112  *      none (i.e. none of the above)
113  *   This will cause the respective portion of the group state (see
114  *   struct xkb_state) to be matched against the groups given in the
115  *   "groups" statement.
116  *
117  *   Note: the above conditions are disjunctive, i.e. if any of them are
118  *   satisfied the led is lit.
119  *
120  * Virtual modifier statements
121  * ---------------------------
122  * Statements of the form:
123  *     virtual_modifiers LControl;
124  *
125  * Can appear in the xkb_types, xkb_compat, xkb_symbols sections.
126  * TODO
127  *
128  * Effect on keymap
129  * ----------------
130  * After all of the xkb_compat sections have been compiled, the following
131  * members of struct xkb_keymap are finalized:
132  *      darray(struct xkb_sym_interpret) sym_interpret;
133  *      struct xkb_indicator_map indicators[XKB_NUM_INDICATORS];
134  *      char *compat_section_name;
135  * TODO: virtual modifiers.
136  */
137
138 enum si_field {
139     SI_FIELD_VIRTUAL_MOD    = (1 << 0),
140     SI_FIELD_ACTION         = (1 << 1),
141     SI_FIELD_AUTO_REPEAT    = (1 << 2),
142     SI_FIELD_LEVEL_ONE_ONLY = (1 << 3),
143 };
144
145 typedef struct _SymInterpInfo {
146     enum si_field defined;
147     unsigned file_id;
148     enum merge_mode merge;
149
150     struct xkb_sym_interpret interp;
151 } SymInterpInfo;
152
153 enum led_field {
154     LED_FIELD_MODS       = (1 << 0),
155     LED_FIELD_GROUPS     = (1 << 1),
156     LED_FIELD_CTRLS      = (1 << 2),
157 };
158
159 typedef struct _LEDInfo {
160     enum led_field defined;
161     unsigned file_id;
162     enum merge_mode merge;
163
164     struct xkb_indicator_map im;
165 } LEDInfo;
166
167 typedef struct _CompatInfo {
168     char *name;
169     unsigned file_id;
170     int errorCount;
171     SymInterpInfo dflt;
172     darray(SymInterpInfo) interps;
173     LEDInfo ledDflt;
174     darray(LEDInfo) leds;
175     VModInfo vmods;
176     ActionsInfo *actions;
177     struct xkb_keymap *keymap;
178 } CompatInfo;
179
180 static const char *
181 siText(SymInterpInfo *si, CompatInfo *info)
182 {
183     static char buf[128];
184
185     if (si == &info->dflt)
186         return "default";
187
188     snprintf(buf, sizeof(buf), "%s+%s(%s)",
189              KeysymText(si->interp.sym),
190              SIMatchText(si->interp.match),
191              ModMaskText(si->interp.mods));
192     return buf;
193 }
194
195 static inline bool
196 ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field)
197 {
198     return ReportNotArray(info->keymap, "symbol interpretation", field,
199                           siText(si, info));
200 }
201
202 static inline bool
203 ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field,
204                 const char *wanted)
205 {
206     return ReportBadType(info->keymap->ctx, "symbol interpretation", field,
207                          siText(si, info), wanted);
208 }
209
210 static inline bool
211 ReportIndicatorBadType(CompatInfo *info, LEDInfo *led,
212                        const char *field, const char *wanted)
213 {
214     return ReportBadType(info->keymap->ctx, "indicator map", field,
215                          xkb_atom_text(info->keymap->ctx, led->im.name),
216                          wanted);
217 }
218
219 static inline bool
220 ReportIndicatorNotArray(CompatInfo *info, LEDInfo *led,
221                         const char *field)
222 {
223     return ReportNotArray(info->keymap, "indicator map", field,
224                           xkb_atom_text(info->keymap->ctx, led->im.name));
225 }
226
227 static void
228 InitCompatInfo(CompatInfo *info, struct xkb_keymap *keymap, unsigned file_id,
229                ActionsInfo *actions)
230 {
231     info->keymap = keymap;
232     info->name = NULL;
233     info->file_id = file_id;
234     info->errorCount = 0;
235     darray_init(info->interps);
236     info->actions = actions;
237     info->dflt.file_id = file_id;
238     info->dflt.defined = 0;
239     info->dflt.merge = MERGE_OVERRIDE;
240     info->dflt.interp.repeat = false;
241     info->dflt.interp.virtual_mod = XKB_MOD_INVALID;
242     memset(&info->dflt.interp.act, 0, sizeof(info->dflt.interp.act));
243     info->dflt.interp.act.type = ACTION_TYPE_NONE;
244     memset(&info->ledDflt, 0, sizeof(info->ledDflt));
245     info->ledDflt.file_id = file_id;
246     info->ledDflt.merge = MERGE_OVERRIDE;
247     darray_init(info->leds);
248     InitVModInfo(&info->vmods, keymap);
249 }
250
251 static void
252 ClearCompatInfo(CompatInfo *info)
253 {
254     free(info->name);
255     info->name = NULL;
256     info->dflt.defined = 0;
257     info->dflt.merge = MERGE_AUGMENT;
258     info->dflt.interp.repeat = false;
259     info->dflt.interp.virtual_mod = XKB_MOD_INVALID;
260     memset(&info->dflt.interp.act, 0, sizeof(info->dflt.interp.act));
261     info->dflt.interp.act.type = ACTION_TYPE_NONE;
262     memset(&info->ledDflt, 0, sizeof(info->ledDflt));
263     darray_free(info->interps);
264     darray_free(info->leds);
265     info->actions = NULL;
266     info->keymap = NULL;
267     ClearVModInfo(&info->vmods);
268 }
269
270 static SymInterpInfo *
271 FindMatchingInterp(CompatInfo *info, SymInterpInfo *new)
272 {
273     SymInterpInfo *old;
274
275     darray_foreach(old, info->interps)
276         if (old->interp.sym == new->interp.sym &&
277             old->interp.mods == new->interp.mods &&
278             old->interp.match == new->interp.match)
279             return old;
280
281     return NULL;
282 }
283
284 static bool
285 UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new,
286                   bool report, enum si_field *collide)
287 {
288     if (!(old->defined & field))
289         return true;
290
291     if (new->defined & field) {
292         if (report)
293             *collide |= field;
294
295         if (new->merge != MERGE_AUGMENT)
296             return true;
297     }
298
299     return false;
300 }
301
302 static bool
303 AddInterp(CompatInfo *info, SymInterpInfo *new)
304 {
305     enum si_field collide = 0;
306     SymInterpInfo *old;
307
308     old = FindMatchingInterp(info, new);
309     if (old) {
310         int verbosity = xkb_get_log_verbosity(info->keymap->ctx);
311         bool report = ((old->file_id == new->file_id && verbosity > 0) ||
312                        verbosity > 9);
313
314         if (new->merge == MERGE_REPLACE) {
315             if (report)
316                 log_warn(info->keymap->ctx,
317                          "Multiple definitions for \"%s\"; "
318                          "Earlier interpretation ignored\n",
319                          siText(new, info));
320             *old = *new;
321             return true;
322         }
323
324         if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, report,
325                               &collide)) {
326             old->interp.virtual_mod = new->interp.virtual_mod;
327             old->defined |= SI_FIELD_VIRTUAL_MOD;
328         }
329         if (UseNewInterpField(SI_FIELD_ACTION, old, new, report,
330                               &collide)) {
331             old->interp.act = new->interp.act;
332             old->defined |= SI_FIELD_ACTION;
333         }
334         if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, report,
335                               &collide)) {
336             old->interp.repeat = new->interp.repeat;
337             old->defined |= SI_FIELD_AUTO_REPEAT;
338         }
339         if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, report,
340                               &collide)) {
341             old->interp.match &= ~MATCH_LEVEL_ONE_ONLY;
342             old->interp.match |= (new->interp.match & MATCH_LEVEL_ONE_ONLY);
343             old->defined |= SI_FIELD_LEVEL_ONE_ONLY;
344         }
345
346         if (collide) {
347             log_warn(info->keymap->ctx,
348                      "Multiple interpretations of \"%s\"; "
349                      "Using %s definition for duplicate fields\n",
350                      siText(new, info),
351                      (new->merge != MERGE_AUGMENT ? "last" : "first"));
352         }
353
354         return true;
355     }
356
357     darray_append(info->interps, *new);
358     return true;
359 }
360
361
362 /***====================================================================***/
363
364 static bool
365 ResolveStateAndPredicate(ExprDef *expr, enum xkb_match_operation *pred_rtrn,
366                          xkb_mod_mask_t *mods_rtrn, CompatInfo *info)
367 {
368     if (expr == NULL) {
369         *pred_rtrn = MATCH_ANY_OR_NONE;
370         *mods_rtrn = ~0;
371         return true;
372     }
373
374     *pred_rtrn = MATCH_EXACTLY;
375     if (expr->op == EXPR_ACTION_DECL) {
376         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
377                                              expr->value.action.name);
378         if (!LookupString(symInterpretMatchMaskNames, pred_txt, pred_rtrn)) {
379             log_err(info->keymap->ctx,
380                     "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt);
381             return false;
382         }
383         expr = expr->value.action.args;
384     }
385     else if (expr->op == EXPR_IDENT) {
386         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
387                                              expr->value.str);
388         if (pred_txt && istreq(pred_txt, "any")) {
389             *pred_rtrn = MATCH_ANY;
390             *mods_rtrn = 0xff;
391             return true;
392         }
393     }
394
395     return ExprResolveModMask(info->keymap->ctx, expr, mods_rtrn);
396 }
397
398 /***====================================================================***/
399
400 static bool
401 UseNewLEDField(enum led_field field, LEDInfo *old, LEDInfo *new,
402                bool report, enum led_field *collide)
403 {
404     if (!(old->defined & field))
405         return true;
406
407     if (new->defined & field) {
408         if (report)
409             *collide |= field;
410
411         if (new->merge != MERGE_AUGMENT)
412             return true;
413     }
414
415     return false;
416 }
417
418 static bool
419 AddIndicatorMap(CompatInfo *info, LEDInfo *new)
420 {
421     LEDInfo *old;
422     enum led_field collide;
423     struct xkb_context *ctx = info->keymap->ctx;
424     int verbosity = xkb_get_log_verbosity(ctx);
425
426     darray_foreach(old, info->leds) {
427         bool report;
428
429         if (old->im.name != new->im.name)
430             continue;
431
432         if (old->im.mods.mods == new->im.mods.mods &&
433             old->im.groups == new->im.groups &&
434             old->im.ctrls == new->im.ctrls &&
435             old->im.which_mods == new->im.which_mods &&
436             old->im.which_groups == new->im.which_groups) {
437             old->defined |= new->defined;
438             return true;
439         }
440
441         report = ((old->file_id == new->file_id && verbosity > 0) ||
442                   verbosity > 9);
443
444         if (new->merge == MERGE_REPLACE) {
445             if (report)
446                 log_warn(info->keymap->ctx,
447                          "Map for indicator %s redefined; "
448                          "Earlier definition ignored\n",
449                          xkb_atom_text(ctx, old->im.name));
450             *old = *new;
451             return true;
452         }
453
454         collide = 0;
455         if (UseNewLEDField(LED_FIELD_MODS, old, new, report, &collide)) {
456             old->im.which_mods = new->im.which_mods;
457             old->im.mods = new->im.mods;
458             old->defined |= LED_FIELD_MODS;
459         }
460         if (UseNewLEDField(LED_FIELD_GROUPS, old, new, report, &collide)) {
461             old->im.which_groups = new->im.which_groups;
462             old->im.groups = new->im.groups;
463             old->defined |= LED_FIELD_GROUPS;
464         }
465         if (UseNewLEDField(LED_FIELD_CTRLS, old, new, report, &collide)) {
466             old->im.ctrls = new->im.ctrls;
467             old->defined |= LED_FIELD_CTRLS;
468         }
469
470         if (collide) {
471             log_warn(info->keymap->ctx,
472                      "Map for indicator %s redefined; "
473                      "Using %s definition for duplicate fields\n",
474                      xkb_atom_text(ctx, old->im.name),
475                      (new->merge == MERGE_AUGMENT ? "first" : "last"));
476         }
477
478         return true;
479     }
480
481     darray_append(info->leds, *new);
482     return true;
483 }
484
485 static void
486 MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from,
487                         enum merge_mode merge)
488 {
489     SymInterpInfo *si;
490     LEDInfo *led;
491
492     if (from->errorCount > 0) {
493         into->errorCount += from->errorCount;
494         return;
495     }
496
497     if (into->name == NULL) {
498         into->name = from->name;
499         from->name = NULL;
500     }
501
502     darray_foreach(si, from->interps) {
503         si->merge = (merge == MERGE_DEFAULT ? si->merge : merge);
504         if (!AddInterp(into, si))
505             into->errorCount++;
506     }
507
508     darray_foreach(led, from->leds) {
509         led->merge = (merge == MERGE_DEFAULT ? led->merge : merge);
510         if (!AddIndicatorMap(into, led))
511             into->errorCount++;
512     }
513 }
514
515 static void
516 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge);
517
518 static bool
519 HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *stmt)
520 {
521     enum merge_mode merge = MERGE_DEFAULT;
522     XkbFile *rtrn;
523     CompatInfo included, next_incl;
524
525     InitCompatInfo(&included, info->keymap, info->file_id, info->actions);
526     if (stmt->stmt) {
527         free(included.name);
528         included.name = stmt->stmt;
529         stmt->stmt = NULL;
530     }
531
532     for (; stmt; stmt = stmt->next_incl) {
533         if (!ProcessIncludeFile(info->keymap->ctx, stmt, FILE_TYPE_COMPAT,
534                                 &rtrn, &merge)) {
535             info->errorCount += 10;
536             ClearCompatInfo(&included);
537             return false;
538         }
539
540         InitCompatInfo(&next_incl, info->keymap, rtrn->id, info->actions);
541         next_incl.file_id = rtrn->id;
542         next_incl.dflt = info->dflt;
543         next_incl.dflt.file_id = rtrn->id;
544         next_incl.dflt.merge = merge;
545         next_incl.ledDflt.file_id = rtrn->id;
546         next_incl.ledDflt.merge = merge;
547
548         HandleCompatMapFile(&next_incl, rtrn, MERGE_OVERRIDE);
549
550         MergeIncludedCompatMaps(&included, &next_incl, merge);
551
552         ClearCompatInfo(&next_incl);
553         FreeXkbFile(rtrn);
554     }
555
556     MergeIncludedCompatMaps(info, &included, merge);
557     ClearCompatInfo(&included);
558
559     return (info->errorCount == 0);
560 }
561
562 static bool
563 SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field,
564                ExprDef *arrayNdx, ExprDef *value)
565 {
566     struct xkb_keymap *keymap = info->keymap;
567     xkb_mod_index_t ndx;
568
569     if (istreq(field, "action")) {
570         if (arrayNdx)
571             return ReportSINotArray(info, si, field);
572
573         if (!HandleActionDef(value, keymap, &si->interp.act, info->actions))
574             return false;
575
576         si->defined |= SI_FIELD_ACTION;
577     }
578     else if (istreq(field, "virtualmodifier") ||
579              istreq(field, "virtualmod")) {
580         if (arrayNdx)
581             return ReportSINotArray(info, si, field);
582
583         if (!ResolveVirtualModifier(value, keymap, &ndx, &info->vmods))
584             return ReportSIBadType(info, si, field, "virtual modifier");
585
586         si->interp.virtual_mod = ndx;
587         si->defined |= SI_FIELD_VIRTUAL_MOD;
588     }
589     else if (istreq(field, "repeat")) {
590         bool set;
591
592         if (arrayNdx)
593             return ReportSINotArray(info, si, field);
594
595         if (!ExprResolveBoolean(keymap->ctx, value, &set))
596             return ReportSIBadType(info, si, field, "boolean");
597
598         si->interp.repeat = set;
599
600         si->defined |= SI_FIELD_AUTO_REPEAT;
601     }
602     else if (istreq(field, "locking")) {
603         log_dbg(info->keymap->ctx,
604                 "The \"locking\" field in symbol interpretation is unsupported; "
605                 "Ignored\n");
606     }
607     else if (istreq(field, "usemodmap") ||
608              istreq(field, "usemodmapmods")) {
609         unsigned int val;
610
611         if (arrayNdx)
612             return ReportSINotArray(info, si, field);
613
614         if (!ExprResolveEnum(keymap->ctx, value, &val, useModMapValueNames))
615             return ReportSIBadType(info, si, field, "level specification");
616
617         if (val)
618             si->interp.match |= MATCH_LEVEL_ONE_ONLY;
619         else
620             si->interp.match &= ~MATCH_LEVEL_ONE_ONLY;
621
622         si->defined |= SI_FIELD_LEVEL_ONE_ONLY;
623     }
624     else {
625         return ReportBadField(keymap, "symbol interpretation", field,
626                               siText(si, info));
627     }
628
629     return true;
630 }
631
632 static bool
633 SetIndicatorMapField(CompatInfo *info, LEDInfo *led,
634                      const char *field, ExprDef *arrayNdx, ExprDef *value)
635 {
636     bool ok = true;
637     struct xkb_keymap *keymap = info->keymap;
638
639     if (istreq(field, "modifiers") || istreq(field, "mods")) {
640         if (arrayNdx)
641             return ReportIndicatorNotArray(info, led, field);
642
643         if (!ExprResolveVModMask(keymap, value, &led->im.mods.mods))
644             return ReportIndicatorBadType(info, led, field, "modifier mask");
645
646         led->defined |= LED_FIELD_MODS;
647     }
648     else if (istreq(field, "groups")) {
649         unsigned int mask;
650
651         if (arrayNdx)
652             return ReportIndicatorNotArray(info, led, field);
653
654         if (!ExprResolveMask(keymap->ctx, value, &mask, groupMaskNames))
655             return ReportIndicatorBadType(info, led, field, "group mask");
656
657         led->im.groups = mask;
658         led->defined |= LED_FIELD_GROUPS;
659     }
660     else if (istreq(field, "controls") || istreq(field, "ctrls")) {
661         unsigned int mask;
662
663         if (arrayNdx)
664             return ReportIndicatorNotArray(info, led, field);
665
666         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlMaskNames))
667             return ReportIndicatorBadType(info, led, field,
668                                           "controls mask");
669
670         led->im.ctrls = mask;
671         led->defined |= LED_FIELD_CTRLS;
672     }
673     else if (istreq(field, "allowexplicit")) {
674         log_dbg(info->keymap->ctx,
675                 "The \"allowExplicit\" field in indicator statements is unsupported; "
676                 "Ignored\n");
677     }
678     else if (istreq(field, "whichmodstate") ||
679              istreq(field, "whichmodifierstate")) {
680         unsigned int mask;
681
682         if (arrayNdx)
683             return ReportIndicatorNotArray(info, led, field);
684
685         if (!ExprResolveMask(keymap->ctx, value, &mask,
686                              modComponentMaskNames))
687             return ReportIndicatorBadType(info, led, field,
688                                           "mask of modifier state components");
689
690         led->im.which_mods = mask;
691     }
692     else if (istreq(field, "whichgroupstate")) {
693         unsigned mask;
694
695         if (arrayNdx)
696             return ReportIndicatorNotArray(info, led, field);
697
698         if (!ExprResolveMask(keymap->ctx, value, &mask,
699                              groupComponentMaskNames))
700             return ReportIndicatorBadType(info, led, field,
701                                           "mask of group state components");
702
703         led->im.which_groups = mask;
704     }
705     else if (istreq(field, "driveskbd") ||
706              istreq(field, "driveskeyboard") ||
707              istreq(field, "leddriveskbd") ||
708              istreq(field, "leddriveskeyboard") ||
709              istreq(field, "indicatordriveskbd") ||
710              istreq(field, "indicatordriveskeyboard")) {
711         log_dbg(info->keymap->ctx,
712                 "The \"%s\" field in indicator statements is unsupported; "
713                 "Ignored\n", field);
714     }
715     else if (istreq(field, "index")) {
716         /* Users should see this, it might cause unexpected behavior. */
717         log_err(info->keymap->ctx,
718                 "The \"index\" field in indicator statements is unsupported; "
719                 "Ignored\n");
720     }
721     else {
722         log_err(info->keymap->ctx,
723                 "Unknown field %s in map for %s indicator; "
724                 "Definition ignored\n",
725                 field, xkb_atom_text(keymap->ctx, led->im.name));
726         ok = false;
727     }
728
729     return ok;
730 }
731
732 static bool
733 HandleGlobalVar(CompatInfo *info, VarDef *stmt)
734 {
735     const char *elem, *field;
736     ExprDef *ndx;
737     bool ret;
738
739     if (!ExprResolveLhs(info->keymap->ctx, stmt->name, &elem, &field, &ndx))
740         ret = false;
741     else if (elem && istreq(elem, "interpret"))
742         ret = SetInterpField(info, &info->dflt, field, ndx, stmt->value);
743     else if (elem && istreq(elem, "indicator"))
744         ret = SetIndicatorMapField(info, &info->ledDflt, field, ndx,
745                                    stmt->value);
746     else
747         ret = SetActionField(info->keymap, elem, field, ndx, stmt->value,
748                              info->actions);
749     return ret;
750 }
751
752 static bool
753 HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si)
754 {
755     bool ok = true;
756     const char *elem, *field;
757     ExprDef *arrayNdx;
758
759     for (; def; def = (VarDef *) def->common.next) {
760         if (def->name && def->name->op == EXPR_FIELD_REF) {
761             log_err(info->keymap->ctx,
762                     "Cannot set a global default value from within an interpret statement; "
763                     "Move statements to the global file scope\n");
764             ok = false;
765             continue;
766         }
767
768         ok = ExprResolveLhs(info->keymap->ctx, def->name, &elem, &field,
769                             &arrayNdx);
770         if (!ok)
771             continue;
772
773         ok = SetInterpField(info, si, field, arrayNdx, def->value);
774     }
775
776     return ok;
777 }
778
779 static bool
780 HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge)
781 {
782     unsigned pred, mods;
783     SymInterpInfo si;
784
785     if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) {
786         log_err(info->keymap->ctx,
787                 "Couldn't determine matching modifiers; "
788                 "Symbol interpretation ignored\n");
789         return false;
790     }
791
792     si = info->dflt;
793
794     si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge);
795
796     if (!LookupKeysym(def->sym, &si.interp.sym)) {
797         log_err(info->keymap->ctx,
798                 "Could not resolve keysym %s; "
799                 "Symbol interpretation ignored\n",
800                 def->sym);
801         return false;
802     }
803
804     si.interp.match = pred & MATCH_OP_MASK;
805
806     si.interp.mods = mods;
807
808     if (!HandleInterpBody(info, def->def, &si)) {
809         info->errorCount++;
810         return false;
811     }
812
813     if (!AddInterp(info, &si)) {
814         info->errorCount++;
815         return false;
816     }
817
818     return true;
819 }
820
821 static bool
822 HandleIndicatorMapDef(CompatInfo *info, IndicatorMapDef *def,
823                       enum merge_mode merge)
824 {
825     LEDInfo led;
826     VarDef *var;
827     bool ok;
828
829     if (def->merge != MERGE_DEFAULT)
830         merge = def->merge;
831
832     led = info->ledDflt;
833     led.merge = merge;
834     led.im.name = def->name;
835
836     ok = true;
837     for (var = def->body; var != NULL; var = (VarDef *) var->common.next) {
838         const char *elem, *field;
839         ExprDef *arrayNdx;
840         if (!ExprResolveLhs(info->keymap->ctx, var->name, &elem, &field,
841                             &arrayNdx)) {
842             ok = false;
843             continue;
844         }
845
846         if (elem) {
847             log_err(info->keymap->ctx,
848                     "Cannot set defaults for \"%s\" element in indicator map; "
849                     "Assignment to %s.%s ignored\n", elem, elem, field);
850             ok = false;
851         }
852         else {
853             ok = SetIndicatorMapField(info, &led, field, arrayNdx,
854                                       var->value) && ok;
855         }
856     }
857
858     if (ok)
859         return AddIndicatorMap(info, &led);
860
861     return false;
862 }
863
864 static void
865 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge)
866 {
867     bool ok;
868     ParseCommon *stmt;
869
870     merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge);
871
872     free(info->name);
873     info->name = strdup_safe(file->name);
874
875     for (stmt = file->defs; stmt; stmt = stmt->next) {
876         switch (stmt->type) {
877         case STMT_INCLUDE:
878             ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt);
879             break;
880         case STMT_INTERP:
881             ok = HandleInterpDef(info, (InterpDef *) stmt, merge);
882             break;
883         case STMT_GROUP_COMPAT:
884             log_dbg(info->keymap->ctx,
885                     "The \"group\" statement in compat is unsupported; "
886                     "Ignored\n");
887             ok = true;
888             break;
889         case STMT_INDICATOR_MAP:
890             ok = HandleIndicatorMapDef(info, (IndicatorMapDef *) stmt, merge);
891             break;
892         case STMT_VAR:
893             ok = HandleGlobalVar(info, (VarDef *) stmt);
894             break;
895         case STMT_VMOD:
896             ok = HandleVModDef((VModDef *) stmt, info->keymap, merge,
897                                &info->vmods);
898             break;
899         default:
900             log_err(info->keymap->ctx,
901                     "Interpretation files may not include other types; "
902                     "Ignoring %s\n", stmt_type_to_string(stmt->type));
903             ok = false;
904             break;
905         }
906
907         if (!ok)
908             info->errorCount++;
909
910         if (info->errorCount > 10) {
911             log_err(info->keymap->ctx,
912                     "Abandoning compatibility map \"%s\"\n", file->topName);
913             break;
914         }
915     }
916 }
917
918 static void
919 CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred)
920 {
921     SymInterpInfo *si;
922
923     darray_foreach(si, info->interps) {
924         if (((si->interp.match & MATCH_OP_MASK) != pred) ||
925             (needSymbol && si->interp.sym == XKB_KEY_NoSymbol) ||
926             (!needSymbol && si->interp.sym != XKB_KEY_NoSymbol))
927             continue;
928
929         darray_append(info->keymap->sym_interpret, si->interp);
930     }
931 }
932
933 static void
934 CopyIndicatorMapDefs(CompatInfo *info)
935 {
936     LEDInfo *led;
937     xkb_led_index_t i;
938     struct xkb_indicator_map *im;
939     struct xkb_keymap *keymap = info->keymap;
940
941     darray_foreach(led, info->leds) {
942         /*
943          * Find the indicator with the given name, if it was already
944          * declared in keycodes.
945          */
946         for (i = 0; i < XKB_NUM_INDICATORS; i++)
947             if (keymap->indicators[i].name == led->im.name)
948                 break;
949
950         /* Not previously declared; create it with next free index. */
951         if (i >= XKB_NUM_INDICATORS) {
952             log_dbg(keymap->ctx,
953                     "Indicator name \"%s\" was not declared in the keycodes section; "
954                     "Adding new indicator\n",
955                     xkb_atom_text(keymap->ctx, led->im.name));
956
957             for (i = 0; i < XKB_NUM_INDICATORS; i++)
958                 if (keymap->indicators[i].name == XKB_ATOM_NONE)
959                     break;
960
961             /* Not place to put it; ignore. */
962             if (i >= XKB_NUM_INDICATORS) {
963                 log_err(keymap->ctx,
964                         "Too many indicators (maximum is %d); "
965                         "Indicator name \"%s\" ignored\n",
966                         XKB_NUM_INDICATORS,
967                         xkb_atom_text(keymap->ctx, led->im.name));
968                 continue;
969             }
970         }
971
972         im = &keymap->indicators[i];
973         *im  = led->im;
974         if (im->groups != 0 && im->which_groups == 0)
975             im->which_groups = XKB_STATE_EFFECTIVE;
976         if (im->mods.mods != 0 && im->which_mods == 0)
977             im->which_mods = XKB_STATE_EFFECTIVE;
978     }
979 }
980
981 static bool
982 CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info)
983 {
984     keymap->compat_section_name = strdup_safe(info->name);
985
986     if (!darray_empty(info->interps)) {
987         /* Most specific to least specific. */
988         CopyInterps(info, true, MATCH_EXACTLY);
989         CopyInterps(info, true, MATCH_ALL);
990         CopyInterps(info, true, MATCH_ANY);
991         CopyInterps(info, true, MATCH_ANY_OR_NONE);
992         CopyInterps(info, false, MATCH_EXACTLY);
993         CopyInterps(info, false, MATCH_ALL);
994         CopyInterps(info, false, MATCH_ANY);
995         CopyInterps(info, false, MATCH_ANY_OR_NONE);
996     }
997
998     CopyIndicatorMapDefs(info);
999
1000     return true;
1001 }
1002
1003 bool
1004 CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap,
1005                  enum merge_mode merge)
1006 {
1007     CompatInfo info;
1008     ActionsInfo *actions;
1009
1010     actions = NewActionsInfo();
1011     if (!actions)
1012         return false;
1013
1014     InitCompatInfo(&info, keymap, file->id, actions);
1015     info.dflt.merge = merge;
1016     info.ledDflt.merge = merge;
1017
1018     HandleCompatMapFile(&info, file, merge);
1019     if (info.errorCount != 0)
1020         goto err_info;
1021
1022     if (!CopyCompatToKeymap(keymap, &info))
1023         goto err_info;
1024
1025     ClearCompatInfo(&info);
1026     FreeActionsInfo(actions);
1027     return true;
1028
1029 err_info:
1030     ClearCompatInfo(&info);
1031     FreeActionsInfo(actions);
1032     return false;
1033 }