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