56828954304c5e92232a307b4dc448c605ddf940
[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  *   conjunction 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  * Led map statements
153  * ------------------------
154  * Statements of the form:
155  *      indicator "Shift Lock" { ... }
156  *
157  *   This statement specifies the behavior and binding of the LED (a.k.a
158  *   indicator) with the given name ("Shift Lock" above). The name should
159  *   have been declared previously in the xkb_keycodes section (see Led
160  *   name 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 LED to be lit. It may include the following
164  *   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 LED will light
190  *   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_led) leds;
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     enum merge_mode merge;
240
241     struct xkb_sym_interpret interp;
242 } SymInterpInfo;
243
244 enum led_field {
245     LED_FIELD_MODS       = (1 << 0),
246     LED_FIELD_GROUPS     = (1 << 1),
247     LED_FIELD_CTRLS      = (1 << 2),
248 };
249
250 typedef struct {
251     enum led_field defined;
252     enum merge_mode merge;
253
254     struct xkb_led led;
255 } LedInfo;
256
257 typedef struct {
258     char *name;
259     int errorCount;
260     SymInterpInfo default_interp;
261     darray(SymInterpInfo) interps;
262     LedInfo default_led;
263     darray(LedInfo) leds;
264     ActionsInfo *actions;
265     struct xkb_keymap *keymap;
266 } CompatInfo;
267
268 static const char *
269 siText(SymInterpInfo *si, CompatInfo *info)
270 {
271     char *buf = xkb_context_get_buffer(info->keymap->ctx, 128);
272
273     if (si == &info->default_interp)
274         return "default";
275
276     snprintf(buf, 128, "%s+%s(%s)",
277              KeysymText(info->keymap->ctx, si->interp.sym),
278              SIMatchText(si->interp.match),
279              ModMaskText(info->keymap, si->interp.mods));
280
281     return buf;
282 }
283
284 static inline bool
285 ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field)
286 {
287     return ReportNotArray(info->keymap->ctx, "symbol interpretation", field,
288                           siText(si, info));
289 }
290
291 static inline bool
292 ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field,
293                 const char *wanted)
294 {
295     return ReportBadType(info->keymap->ctx, "symbol interpretation", field,
296                          siText(si, info), wanted);
297 }
298
299 static inline bool
300 ReportLedBadType(CompatInfo *info, LedInfo *ledi, const char *field,
301                  const char *wanted)
302 {
303     return ReportBadType(info->keymap->ctx, "indicator map", field,
304                          xkb_atom_text(info->keymap->ctx, ledi->led.name),
305                          wanted);
306 }
307
308 static inline bool
309 ReportLedNotArray(CompatInfo *info, LedInfo *ledi, const char *field)
310 {
311     return ReportNotArray(info->keymap->ctx, "indicator map", field,
312                           xkb_atom_text(info->keymap->ctx, ledi->led.name));
313 }
314
315 static void
316 InitCompatInfo(CompatInfo *info, struct xkb_keymap *keymap,
317                ActionsInfo *actions)
318 {
319     memset(info, 0, sizeof(*info));
320     info->keymap = keymap;
321     info->actions = actions;
322     info->default_interp.merge = MERGE_OVERRIDE;
323     info->default_interp.interp.virtual_mod = XKB_MOD_INVALID;
324     info->default_led.merge = MERGE_OVERRIDE;
325 }
326
327 static void
328 ClearCompatInfo(CompatInfo *info)
329 {
330     free(info->name);
331     darray_free(info->interps);
332     darray_free(info->leds);
333 }
334
335 static SymInterpInfo *
336 FindMatchingInterp(CompatInfo *info, SymInterpInfo *new)
337 {
338     SymInterpInfo *old;
339
340     darray_foreach(old, info->interps)
341         if (old->interp.sym == new->interp.sym &&
342             old->interp.mods == new->interp.mods &&
343             old->interp.match == new->interp.match)
344             return old;
345
346     return NULL;
347 }
348
349 static bool
350 UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new,
351                   bool report, enum si_field *collide)
352 {
353     if (!(old->defined & field))
354         return true;
355
356     if (new->defined & field) {
357         if (report)
358             *collide |= field;
359
360         if (new->merge != MERGE_AUGMENT)
361             return true;
362     }
363
364     return false;
365 }
366
367 static bool
368 AddInterp(CompatInfo *info, SymInterpInfo *new, bool same_file)
369 {
370     SymInterpInfo *old = FindMatchingInterp(info, new);
371     if (old) {
372         const int verbosity = xkb_context_get_log_verbosity(info->keymap->ctx);
373         const bool report = (same_file && verbosity > 0) || verbosity > 9;
374         enum si_field collide = 0;
375
376         if (new->merge == MERGE_REPLACE) {
377             if (report)
378                 log_warn(info->keymap->ctx,
379                          "Multiple definitions for \"%s\"; "
380                          "Earlier interpretation ignored\n",
381                          siText(new, info));
382             *old = *new;
383             return true;
384         }
385
386         if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, report,
387                               &collide)) {
388             old->interp.virtual_mod = new->interp.virtual_mod;
389             old->defined |= SI_FIELD_VIRTUAL_MOD;
390         }
391         if (UseNewInterpField(SI_FIELD_ACTION, old, new, report,
392                               &collide)) {
393             old->interp.action = new->interp.action;
394             old->defined |= SI_FIELD_ACTION;
395         }
396         if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, report,
397                               &collide)) {
398             old->interp.repeat = new->interp.repeat;
399             old->defined |= SI_FIELD_AUTO_REPEAT;
400         }
401         if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, report,
402                               &collide)) {
403             old->interp.level_one_only = new->interp.level_one_only;
404             old->defined |= SI_FIELD_LEVEL_ONE_ONLY;
405         }
406
407         if (collide) {
408             log_warn(info->keymap->ctx,
409                      "Multiple interpretations of \"%s\"; "
410                      "Using %s definition for duplicate fields\n",
411                      siText(new, info),
412                      (new->merge != MERGE_AUGMENT ? "last" : "first"));
413         }
414
415         return true;
416     }
417
418     darray_append(info->interps, *new);
419     return true;
420 }
421
422 /***====================================================================***/
423
424 static bool
425 ResolveStateAndPredicate(ExprDef *expr, enum xkb_match_operation *pred_rtrn,
426                          xkb_mod_mask_t *mods_rtrn, CompatInfo *info)
427 {
428     if (expr == NULL) {
429         *pred_rtrn = MATCH_ANY_OR_NONE;
430         *mods_rtrn = MOD_REAL_MASK_ALL;
431         return true;
432     }
433
434     *pred_rtrn = MATCH_EXACTLY;
435     if (expr->op == EXPR_ACTION_DECL) {
436         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
437                                              expr->value.action.name);
438         if (!LookupString(symInterpretMatchMaskNames, pred_txt, pred_rtrn)) {
439             log_err(info->keymap->ctx,
440                     "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt);
441             return false;
442         }
443         expr = expr->value.action.args;
444     }
445     else if (expr->op == EXPR_IDENT) {
446         const char *pred_txt = xkb_atom_text(info->keymap->ctx,
447                                              expr->value.str);
448         if (pred_txt && istreq(pred_txt, "any")) {
449             *pred_rtrn = MATCH_ANY;
450             *mods_rtrn = MOD_REAL_MASK_ALL;
451             return true;
452         }
453     }
454
455     return ExprResolveModMask(info->keymap, expr, MOD_REAL, mods_rtrn);
456 }
457
458 /***====================================================================***/
459
460 static bool
461 UseNewLEDField(enum led_field field, LedInfo *old, LedInfo *new,
462                bool report, enum led_field *collide)
463 {
464     if (!(old->defined & field))
465         return true;
466
467     if (new->defined & field) {
468         if (report)
469             *collide |= field;
470
471         if (new->merge != MERGE_AUGMENT)
472             return true;
473     }
474
475     return false;
476 }
477
478 static bool
479 AddLedMap(CompatInfo *info, LedInfo *new, bool same_file)
480 {
481     LedInfo *old;
482     enum led_field collide;
483     struct xkb_context *ctx = info->keymap->ctx;
484     const int verbosity = xkb_context_get_log_verbosity(ctx);
485     const bool report = (same_file && verbosity > 0) || verbosity > 9;
486
487     darray_foreach(old, info->leds) {
488         if (old->led.name != new->led.name)
489             continue;
490
491         if (old->led.mods.mods == new->led.mods.mods &&
492             old->led.groups == new->led.groups &&
493             old->led.ctrls == new->led.ctrls &&
494             old->led.which_mods == new->led.which_mods &&
495             old->led.which_groups == new->led.which_groups) {
496             old->defined |= new->defined;
497             return true;
498         }
499
500         if (new->merge == MERGE_REPLACE) {
501             if (report)
502                 log_warn(info->keymap->ctx,
503                          "Map for indicator %s redefined; "
504                          "Earlier definition ignored\n",
505                          xkb_atom_text(ctx, old->led.name));
506             *old = *new;
507             return true;
508         }
509
510         collide = 0;
511         if (UseNewLEDField(LED_FIELD_MODS, old, new, report, &collide)) {
512             old->led.which_mods = new->led.which_mods;
513             old->led.mods = new->led.mods;
514             old->defined |= LED_FIELD_MODS;
515         }
516         if (UseNewLEDField(LED_FIELD_GROUPS, old, new, report, &collide)) {
517             old->led.which_groups = new->led.which_groups;
518             old->led.groups = new->led.groups;
519             old->defined |= LED_FIELD_GROUPS;
520         }
521         if (UseNewLEDField(LED_FIELD_CTRLS, old, new, report, &collide)) {
522             old->led.ctrls = new->led.ctrls;
523             old->defined |= LED_FIELD_CTRLS;
524         }
525
526         if (collide) {
527             log_warn(info->keymap->ctx,
528                      "Map for indicator %s redefined; "
529                      "Using %s definition for duplicate fields\n",
530                      xkb_atom_text(ctx, old->led.name),
531                      (new->merge == MERGE_AUGMENT ? "first" : "last"));
532         }
533
534         return true;
535     }
536
537     darray_append(info->leds, *new);
538     return true;
539 }
540
541 static void
542 MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from,
543                         enum merge_mode merge)
544 {
545     SymInterpInfo *si;
546     LedInfo *ledi;
547
548     if (from->errorCount > 0) {
549         into->errorCount += from->errorCount;
550         return;
551     }
552
553     if (into->name == NULL) {
554         into->name = from->name;
555         from->name = NULL;
556     }
557
558     darray_foreach(si, from->interps) {
559         si->merge = (merge == MERGE_DEFAULT ? si->merge : merge);
560         if (!AddInterp(into, si, false))
561             into->errorCount++;
562     }
563
564     darray_foreach(ledi, from->leds) {
565         ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge);
566         if (!AddLedMap(into, ledi, false))
567             into->errorCount++;
568     }
569 }
570
571 static void
572 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge);
573
574 static bool
575 HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include)
576 {
577     CompatInfo included;
578
579     InitCompatInfo(&included, info->keymap, info->actions);
580     included.name = include->stmt;
581     include->stmt = NULL;
582
583     for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) {
584         CompatInfo next_incl;
585         XkbFile *file;
586
587         file = ProcessIncludeFile(info->keymap->ctx, stmt, FILE_TYPE_COMPAT);
588         if (!file) {
589             info->errorCount += 10;
590             ClearCompatInfo(&included);
591             return false;
592         }
593
594         InitCompatInfo(&next_incl, info->keymap, info->actions);
595         next_incl.default_interp = info->default_interp;
596         next_incl.default_interp.merge = stmt->merge;
597         next_incl.default_led = info->default_led;
598         next_incl.default_led.merge = stmt->merge;
599
600         HandleCompatMapFile(&next_incl, file, MERGE_OVERRIDE);
601
602         MergeIncludedCompatMaps(&included, &next_incl, stmt->merge);
603
604         ClearCompatInfo(&next_incl);
605         FreeXkbFile(file);
606     }
607
608     MergeIncludedCompatMaps(info, &included, include->merge);
609     ClearCompatInfo(&included);
610
611     return (info->errorCount == 0);
612 }
613
614 static bool
615 SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field,
616                ExprDef *arrayNdx, ExprDef *value)
617 {
618     struct xkb_keymap *keymap = info->keymap;
619     xkb_mod_index_t ndx;
620
621     if (istreq(field, "action")) {
622         if (arrayNdx)
623             return ReportSINotArray(info, si, field);
624
625         if (!HandleActionDef(value, keymap, &si->interp.action, info->actions))
626             return false;
627
628         si->defined |= SI_FIELD_ACTION;
629     }
630     else if (istreq(field, "virtualmodifier") ||
631              istreq(field, "virtualmod")) {
632         if (arrayNdx)
633             return ReportSINotArray(info, si, field);
634
635         if (!ExprResolveMod(keymap, value, MOD_VIRT, &ndx))
636             return ReportSIBadType(info, si, field, "virtual modifier");
637
638         si->interp.virtual_mod = ndx;
639         si->defined |= SI_FIELD_VIRTUAL_MOD;
640     }
641     else if (istreq(field, "repeat")) {
642         bool set;
643
644         if (arrayNdx)
645             return ReportSINotArray(info, si, field);
646
647         if (!ExprResolveBoolean(keymap->ctx, value, &set))
648             return ReportSIBadType(info, si, field, "boolean");
649
650         si->interp.repeat = set;
651
652         si->defined |= SI_FIELD_AUTO_REPEAT;
653     }
654     else if (istreq(field, "locking")) {
655         log_dbg(info->keymap->ctx,
656                 "The \"locking\" field in symbol interpretation is unsupported; "
657                 "Ignored\n");
658     }
659     else if (istreq(field, "usemodmap") ||
660              istreq(field, "usemodmapmods")) {
661         unsigned int val;
662
663         if (arrayNdx)
664             return ReportSINotArray(info, si, field);
665
666         if (!ExprResolveEnum(keymap->ctx, value, &val, useModMapValueNames))
667             return ReportSIBadType(info, si, field, "level specification");
668
669         si->interp.level_one_only = !!val;
670         si->defined |= SI_FIELD_LEVEL_ONE_ONLY;
671     }
672     else {
673         return ReportBadField(keymap->ctx, "symbol interpretation", field,
674                               siText(si, info));
675     }
676
677     return true;
678 }
679
680 static bool
681 SetLedMapField(CompatInfo *info, LedInfo *ledi, const char *field,
682                ExprDef *arrayNdx, ExprDef *value)
683 {
684     bool ok = true;
685     struct xkb_keymap *keymap = info->keymap;
686
687     if (istreq(field, "modifiers") || istreq(field, "mods")) {
688         if (arrayNdx)
689             return ReportLedNotArray(info, ledi, field);
690
691         if (!ExprResolveModMask(keymap, value, MOD_BOTH, &ledi->led.mods.mods))
692             return ReportLedBadType(info, ledi, field, "modifier mask");
693
694         ledi->defined |= LED_FIELD_MODS;
695     }
696     else if (istreq(field, "groups")) {
697         unsigned int mask;
698
699         if (arrayNdx)
700             return ReportLedNotArray(info, ledi, field);
701
702         if (!ExprResolveMask(keymap->ctx, value, &mask, groupMaskNames))
703             return ReportLedBadType(info, ledi, field, "group mask");
704
705         ledi->led.groups = mask;
706         ledi->defined |= LED_FIELD_GROUPS;
707     }
708     else if (istreq(field, "controls") || istreq(field, "ctrls")) {
709         unsigned int mask;
710
711         if (arrayNdx)
712             return ReportLedNotArray(info, ledi, field);
713
714         if (!ExprResolveMask(keymap->ctx, value, &mask, ctrlMaskNames))
715             return ReportLedBadType(info, ledi, field, "controls mask");
716
717         ledi->led.ctrls = mask;
718         ledi->defined |= LED_FIELD_CTRLS;
719     }
720     else if (istreq(field, "allowexplicit")) {
721         log_dbg(info->keymap->ctx,
722                 "The \"allowExplicit\" field in indicator statements is unsupported; "
723                 "Ignored\n");
724     }
725     else if (istreq(field, "whichmodstate") ||
726              istreq(field, "whichmodifierstate")) {
727         unsigned int mask;
728
729         if (arrayNdx)
730             return ReportLedNotArray(info, ledi, field);
731
732         if (!ExprResolveMask(keymap->ctx, value, &mask,
733                              modComponentMaskNames))
734             return ReportLedBadType(info, ledi, field,
735                                     "mask of modifier state components");
736
737         ledi->led.which_mods = mask;
738     }
739     else if (istreq(field, "whichgroupstate")) {
740         unsigned mask;
741
742         if (arrayNdx)
743             return ReportLedNotArray(info, ledi, field);
744
745         if (!ExprResolveMask(keymap->ctx, value, &mask,
746                              groupComponentMaskNames))
747             return ReportLedBadType(info, ledi, field,
748                                     "mask of group state components");
749
750         ledi->led.which_groups = mask;
751     }
752     else if (istreq(field, "driveskbd") ||
753              istreq(field, "driveskeyboard") ||
754              istreq(field, "leddriveskbd") ||
755              istreq(field, "leddriveskeyboard") ||
756              istreq(field, "indicatordriveskbd") ||
757              istreq(field, "indicatordriveskeyboard")) {
758         log_dbg(info->keymap->ctx,
759                 "The \"%s\" field in indicator statements is unsupported; "
760                 "Ignored\n", field);
761     }
762     else if (istreq(field, "index")) {
763         /* Users should see this, it might cause unexpected behavior. */
764         log_err(info->keymap->ctx,
765                 "The \"index\" field in indicator statements is unsupported; "
766                 "Ignored\n");
767     }
768     else {
769         log_err(info->keymap->ctx,
770                 "Unknown field %s in map for %s indicator; "
771                 "Definition ignored\n",
772                 field, xkb_atom_text(keymap->ctx, ledi->led.name));
773         ok = false;
774     }
775
776     return ok;
777 }
778
779 static bool
780 HandleGlobalVar(CompatInfo *info, VarDef *stmt)
781 {
782     const char *elem, *field;
783     ExprDef *ndx;
784     bool ret;
785
786     if (!ExprResolveLhs(info->keymap->ctx, stmt->name, &elem, &field, &ndx))
787         ret = false;
788     else if (elem && istreq(elem, "interpret"))
789         ret = SetInterpField(info, &info->default_interp, field, ndx,
790                              stmt->value);
791     else if (elem && istreq(elem, "indicator"))
792         ret = SetLedMapField(info, &info->default_led, field, ndx,
793                              stmt->value);
794     else
795         ret = SetActionField(info->keymap, elem, field, ndx, stmt->value,
796                              info->actions);
797     return ret;
798 }
799
800 static bool
801 HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si)
802 {
803     bool ok = true;
804     const char *elem, *field;
805     ExprDef *arrayNdx;
806
807     for (; def; def = (VarDef *) def->common.next) {
808         if (def->name && def->name->op == EXPR_FIELD_REF) {
809             log_err(info->keymap->ctx,
810                     "Cannot set a global default value from within an interpret statement; "
811                     "Move statements to the global file scope\n");
812             ok = false;
813             continue;
814         }
815
816         ok = ExprResolveLhs(info->keymap->ctx, def->name, &elem, &field,
817                             &arrayNdx);
818         if (!ok)
819             continue;
820
821         ok = SetInterpField(info, si, field, arrayNdx, def->value);
822     }
823
824     return ok;
825 }
826
827 static bool
828 HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge)
829 {
830     enum xkb_match_operation pred;
831     xkb_mod_mask_t mods;
832     SymInterpInfo si;
833
834     if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) {
835         log_err(info->keymap->ctx,
836                 "Couldn't determine matching modifiers; "
837                 "Symbol interpretation ignored\n");
838         return false;
839     }
840
841     si = info->default_interp;
842     si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge);
843
844     if (!LookupKeysym(def->sym, &si.interp.sym)) {
845         log_err(info->keymap->ctx,
846                 "Could not resolve keysym %s; "
847                 "Symbol interpretation ignored\n",
848                 def->sym);
849         return false;
850     }
851
852     si.interp.match = pred;
853     si.interp.mods = mods;
854
855     if (!HandleInterpBody(info, def->def, &si)) {
856         info->errorCount++;
857         return false;
858     }
859
860     if (!AddInterp(info, &si, true)) {
861         info->errorCount++;
862         return false;
863     }
864
865     return true;
866 }
867
868 static bool
869 HandleLedMapDef(CompatInfo *info, LedMapDef *def, enum merge_mode merge)
870 {
871     LedInfo ledi;
872     VarDef *var;
873     bool ok;
874
875     if (def->merge != MERGE_DEFAULT)
876         merge = def->merge;
877
878     ledi = info->default_led;
879     ledi.merge = merge;
880     ledi.led.name = def->name;
881
882     ok = true;
883     for (var = def->body; var != NULL; var = (VarDef *) var->common.next) {
884         const char *elem, *field;
885         ExprDef *arrayNdx;
886         if (!ExprResolveLhs(info->keymap->ctx, var->name, &elem, &field,
887                             &arrayNdx)) {
888             ok = false;
889             continue;
890         }
891
892         if (elem) {
893             log_err(info->keymap->ctx,
894                     "Cannot set defaults for \"%s\" element in indicator map; "
895                     "Assignment to %s.%s ignored\n", elem, elem, field);
896             ok = false;
897         }
898         else {
899             ok = SetLedMapField(info, &ledi, field, arrayNdx, var->value) && ok;
900         }
901     }
902
903     if (ok)
904         return AddLedMap(info, &ledi, true);
905
906     return false;
907 }
908
909 static void
910 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge)
911 {
912     bool ok;
913
914     merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge);
915
916     free(info->name);
917     info->name = strdup_safe(file->name);
918
919     for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) {
920         switch (stmt->type) {
921         case STMT_INCLUDE:
922             ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt);
923             break;
924         case STMT_INTERP:
925             ok = HandleInterpDef(info, (InterpDef *) stmt, merge);
926             break;
927         case STMT_GROUP_COMPAT:
928             log_dbg(info->keymap->ctx,
929                     "The \"group\" statement in compat is unsupported; "
930                     "Ignored\n");
931             ok = true;
932             break;
933         case STMT_LED_MAP:
934             ok = HandleLedMapDef(info, (LedMapDef *) stmt, merge);
935             break;
936         case STMT_VAR:
937             ok = HandleGlobalVar(info, (VarDef *) stmt);
938             break;
939         case STMT_VMOD:
940             ok = HandleVModDef(info->keymap, (VModDef *) stmt);
941             break;
942         default:
943             log_err(info->keymap->ctx,
944                     "Interpretation files may not include other types; "
945                     "Ignoring %s\n", stmt_type_to_string(stmt->type));
946             ok = false;
947             break;
948         }
949
950         if (!ok)
951             info->errorCount++;
952
953         if (info->errorCount > 10) {
954             log_err(info->keymap->ctx,
955                     "Abandoning compatibility map \"%s\"\n", file->topName);
956             break;
957         }
958     }
959 }
960
961 static void
962 CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred)
963 {
964     SymInterpInfo *si;
965
966     darray_foreach(si, info->interps)
967         if (si->interp.match == pred &&
968             (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol)
969             darray_append(info->keymap->sym_interprets, si->interp);
970 }
971
972 static void
973 CopyLedMapDefs(CompatInfo *info)
974 {
975     LedInfo *ledi;
976     xkb_led_index_t i;
977     struct xkb_led *led;
978     struct xkb_keymap *keymap = info->keymap;
979
980     darray_foreach(ledi, info->leds) {
981         /*
982          * Find the LED with the given name, if it was already declared
983          * in keycodes.
984          */
985         darray_enumerate(i, led, keymap->leds)
986             if (led->name == ledi->led.name)
987                 break;
988
989         /* Not previously declared; create it with next free index. */
990         if (i >= darray_size(keymap->leds)) {
991             log_dbg(keymap->ctx,
992                     "Indicator name \"%s\" was not declared in the keycodes section; "
993                     "Adding new indicator\n",
994                     xkb_atom_text(keymap->ctx, ledi->led.name));
995
996             darray_enumerate(i, led, keymap->leds)
997                 if (led->name == XKB_ATOM_NONE)
998                     break;
999
1000             if (i >= darray_size(keymap->leds)) {
1001                 /* Not place to put it; ignore. */
1002                 if (i >= XKB_MAX_LEDS) {
1003                     log_err(keymap->ctx,
1004                             "Too many indicators (maximum is %d); "
1005                             "Indicator name \"%s\" ignored\n",
1006                             XKB_MAX_LEDS,
1007                             xkb_atom_text(keymap->ctx, ledi->led.name));
1008                     continue;
1009                 }
1010                 /* Add a new LED. */
1011                 darray_resize(keymap->leds, i + 1);
1012                 led = &darray_item(keymap->leds, i);
1013             }
1014         }
1015
1016         *led = ledi->led;
1017         if (led->groups != 0 && led->which_groups == 0)
1018             led->which_groups = XKB_STATE_LAYOUT_EFFECTIVE;
1019         if (led->mods.mods != 0 && led->which_mods == 0)
1020             led->which_mods = XKB_STATE_MODS_EFFECTIVE;
1021     }
1022 }
1023
1024 static bool
1025 CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info)
1026 {
1027     keymap->compat_section_name = strdup_safe(info->name);
1028
1029     if (!darray_empty(info->interps)) {
1030         /* Most specific to least specific. */
1031         CopyInterps(info, true, MATCH_EXACTLY);
1032         CopyInterps(info, true, MATCH_ALL);
1033         CopyInterps(info, true, MATCH_NONE);
1034         CopyInterps(info, true, MATCH_ANY);
1035         CopyInterps(info, true, MATCH_ANY_OR_NONE);
1036         CopyInterps(info, false, MATCH_EXACTLY);
1037         CopyInterps(info, false, MATCH_ALL);
1038         CopyInterps(info, false, MATCH_NONE);
1039         CopyInterps(info, false, MATCH_ANY);
1040         CopyInterps(info, false, MATCH_ANY_OR_NONE);
1041     }
1042
1043     CopyLedMapDefs(info);
1044
1045     return true;
1046 }
1047
1048 bool
1049 CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap,
1050                  enum merge_mode merge)
1051 {
1052     CompatInfo info;
1053     ActionsInfo *actions;
1054
1055     actions = NewActionsInfo();
1056     if (!actions)
1057         return false;
1058
1059     InitCompatInfo(&info, keymap, actions);
1060     info.default_interp.merge = merge;
1061     info.default_led.merge = merge;
1062
1063     HandleCompatMapFile(&info, file, merge);
1064     if (info.errorCount != 0)
1065         goto err_info;
1066
1067     if (!CopyCompatToKeymap(keymap, &info))
1068         goto err_info;
1069
1070     ClearCompatInfo(&info);
1071     FreeActionsInfo(actions);
1072     return true;
1073
1074 err_info:
1075     ClearCompatInfo(&info);
1076     FreeActionsInfo(actions);
1077     return false;
1078 }