Home | History | Annotate | Download | only in xkbcomp
      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 (at) 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 enum si_field {
     58     SI_FIELD_VIRTUAL_MOD = (1 << 0),
     59     SI_FIELD_ACTION = (1 << 1),
     60     SI_FIELD_AUTO_REPEAT = (1 << 2),
     61     SI_FIELD_LEVEL_ONE_ONLY = (1 << 3),
     62 };
     63 
     64 typedef struct {
     65     enum si_field defined;
     66     enum merge_mode merge;
     67 
     68     struct xkb_sym_interpret interp;
     69 } SymInterpInfo;
     70 
     71 enum led_field {
     72     LED_FIELD_MODS = (1 << 0),
     73     LED_FIELD_GROUPS = (1 << 1),
     74     LED_FIELD_CTRLS = (1 << 2),
     75 };
     76 
     77 typedef struct {
     78     enum led_field defined;
     79     enum merge_mode merge;
     80 
     81     struct xkb_led led;
     82 } LedInfo;
     83 
     84 typedef struct {
     85     char *name;
     86     int errorCount;
     87     SymInterpInfo default_interp;
     88     darray(SymInterpInfo) interps;
     89     LedInfo default_led;
     90     LedInfo leds[XKB_MAX_LEDS];
     91     unsigned int num_leds;
     92     ActionsInfo *actions;
     93     struct xkb_mod_set mods;
     94 
     95     struct xkb_context *ctx;
     96 } CompatInfo;
     97 
     98 static const char *
     99 siText(SymInterpInfo *si, CompatInfo *info)
    100 {
    101     char *buf = xkb_context_get_buffer(info->ctx, 128);
    102 
    103     if (si == &info->default_interp)
    104         return "default";
    105 
    106     snprintf(buf, 128, "%s+%s(%s)",
    107              KeysymText(info->ctx, si->interp.sym),
    108              SIMatchText(si->interp.match),
    109              ModMaskText(info->ctx, &info->mods, si->interp.mods));
    110 
    111     return buf;
    112 }
    113 
    114 static inline bool
    115 ReportSINotArray(CompatInfo *info, SymInterpInfo *si, const char *field)
    116 {
    117     return ReportNotArray(info->ctx, "symbol interpretation", field,
    118                           siText(si, info));
    119 }
    120 
    121 static inline bool
    122 ReportSIBadType(CompatInfo *info, SymInterpInfo *si, const char *field,
    123                 const char *wanted)
    124 {
    125     return ReportBadType(info->ctx, "symbol interpretation", field,
    126                          siText(si, info), wanted);
    127 }
    128 
    129 static inline bool
    130 ReportLedBadType(CompatInfo *info, LedInfo *ledi, const char *field,
    131                  const char *wanted)
    132 {
    133     return ReportBadType(info->ctx, "indicator map", field,
    134                          xkb_atom_text(info->ctx, ledi->led.name),
    135                          wanted);
    136 }
    137 
    138 static inline bool
    139 ReportLedNotArray(CompatInfo *info, LedInfo *ledi, const char *field)
    140 {
    141     return ReportNotArray(info->ctx, "indicator map", field,
    142                           xkb_atom_text(info->ctx, ledi->led.name));
    143 }
    144 
    145 static void
    146 InitCompatInfo(CompatInfo *info, struct xkb_context *ctx,
    147                ActionsInfo *actions, const struct xkb_mod_set *mods)
    148 {
    149     memset(info, 0, sizeof(*info));
    150     info->ctx = ctx;
    151     info->actions = actions;
    152     info->mods = *mods;
    153     info->default_interp.merge = MERGE_OVERRIDE;
    154     info->default_interp.interp.virtual_mod = XKB_MOD_INVALID;
    155     info->default_led.merge = MERGE_OVERRIDE;
    156 }
    157 
    158 static void
    159 ClearCompatInfo(CompatInfo *info)
    160 {
    161     free(info->name);
    162     darray_free(info->interps);
    163 }
    164 
    165 static SymInterpInfo *
    166 FindMatchingInterp(CompatInfo *info, SymInterpInfo *new)
    167 {
    168     SymInterpInfo *old;
    169 
    170     darray_foreach(old, info->interps)
    171         if (old->interp.sym == new->interp.sym &&
    172             old->interp.mods == new->interp.mods &&
    173             old->interp.match == new->interp.match)
    174             return old;
    175 
    176     return NULL;
    177 }
    178 
    179 static bool
    180 UseNewInterpField(enum si_field field, SymInterpInfo *old, SymInterpInfo *new,
    181                   bool report, enum si_field *collide)
    182 {
    183     if (!(old->defined & field))
    184         return true;
    185 
    186     if (new->defined & field) {
    187         if (report)
    188             *collide |= field;
    189 
    190         if (new->merge != MERGE_AUGMENT)
    191             return true;
    192     }
    193 
    194     return false;
    195 }
    196 
    197 static bool
    198 AddInterp(CompatInfo *info, SymInterpInfo *new, bool same_file)
    199 {
    200     SymInterpInfo *old = FindMatchingInterp(info, new);
    201     if (old) {
    202         const int verbosity = xkb_context_get_log_verbosity(info->ctx);
    203         const bool report = (same_file && verbosity > 0) || verbosity > 9;
    204         enum si_field collide = 0;
    205 
    206         if (new->merge == MERGE_REPLACE) {
    207             if (report)
    208                 log_warn(info->ctx,
    209                          "Multiple definitions for \"%s\"; "
    210                          "Earlier interpretation ignored\n",
    211                          siText(new, info));
    212             *old = *new;
    213             return true;
    214         }
    215 
    216         if (UseNewInterpField(SI_FIELD_VIRTUAL_MOD, old, new, report,
    217                               &collide)) {
    218             old->interp.virtual_mod = new->interp.virtual_mod;
    219             old->defined |= SI_FIELD_VIRTUAL_MOD;
    220         }
    221         if (UseNewInterpField(SI_FIELD_ACTION, old, new, report,
    222                               &collide)) {
    223             old->interp.action = new->interp.action;
    224             old->defined |= SI_FIELD_ACTION;
    225         }
    226         if (UseNewInterpField(SI_FIELD_AUTO_REPEAT, old, new, report,
    227                               &collide)) {
    228             old->interp.repeat = new->interp.repeat;
    229             old->defined |= SI_FIELD_AUTO_REPEAT;
    230         }
    231         if (UseNewInterpField(SI_FIELD_LEVEL_ONE_ONLY, old, new, report,
    232                               &collide)) {
    233             old->interp.level_one_only = new->interp.level_one_only;
    234             old->defined |= SI_FIELD_LEVEL_ONE_ONLY;
    235         }
    236 
    237         if (collide) {
    238             log_warn(info->ctx,
    239                      "Multiple interpretations of \"%s\"; "
    240                      "Using %s definition for duplicate fields\n",
    241                      siText(new, info),
    242                      (new->merge != MERGE_AUGMENT ? "last" : "first"));
    243         }
    244 
    245         return true;
    246     }
    247 
    248     darray_append(info->interps, *new);
    249     return true;
    250 }
    251 
    252 /***====================================================================***/
    253 
    254 static bool
    255 ResolveStateAndPredicate(ExprDef *expr, enum xkb_match_operation *pred_rtrn,
    256                          xkb_mod_mask_t *mods_rtrn, CompatInfo *info)
    257 {
    258     if (expr == NULL) {
    259         *pred_rtrn = MATCH_ANY_OR_NONE;
    260         *mods_rtrn = MOD_REAL_MASK_ALL;
    261         return true;
    262     }
    263 
    264     *pred_rtrn = MATCH_EXACTLY;
    265     if (expr->expr.op == EXPR_ACTION_DECL) {
    266         const char *pred_txt = xkb_atom_text(info->ctx, expr->action.name);
    267         if (!LookupString(symInterpretMatchMaskNames, pred_txt, pred_rtrn)) {
    268             log_err(info->ctx,
    269                     "Illegal modifier predicate \"%s\"; Ignored\n", pred_txt);
    270             return false;
    271         }
    272         expr = expr->action.args;
    273     }
    274     else if (expr->expr.op == EXPR_IDENT) {
    275         const char *pred_txt = xkb_atom_text(info->ctx, expr->ident.ident);
    276         if (pred_txt && istreq(pred_txt, "any")) {
    277             *pred_rtrn = MATCH_ANY;
    278             *mods_rtrn = MOD_REAL_MASK_ALL;
    279             return true;
    280         }
    281     }
    282 
    283     return ExprResolveModMask(info->ctx, expr, MOD_REAL, &info->mods,
    284                               mods_rtrn);
    285 }
    286 
    287 /***====================================================================***/
    288 
    289 static bool
    290 UseNewLEDField(enum led_field field, LedInfo *old, LedInfo *new,
    291                bool report, enum led_field *collide)
    292 {
    293     if (!(old->defined & field))
    294         return true;
    295 
    296     if (new->defined & field) {
    297         if (report)
    298             *collide |= field;
    299 
    300         if (new->merge != MERGE_AUGMENT)
    301             return true;
    302     }
    303 
    304     return false;
    305 }
    306 
    307 static bool
    308 AddLedMap(CompatInfo *info, LedInfo *new, bool same_file)
    309 {
    310     enum led_field collide;
    311     const int verbosity = xkb_context_get_log_verbosity(info->ctx);
    312     const bool report = (same_file && verbosity > 0) || verbosity > 9;
    313 
    314     for (xkb_led_index_t i = 0; i < info->num_leds; i++) {
    315         LedInfo *old = &info->leds[i];
    316 
    317         if (old->led.name != new->led.name)
    318             continue;
    319 
    320         if (old->led.mods.mods == new->led.mods.mods &&
    321             old->led.groups == new->led.groups &&
    322             old->led.ctrls == new->led.ctrls &&
    323             old->led.which_mods == new->led.which_mods &&
    324             old->led.which_groups == new->led.which_groups) {
    325             old->defined |= new->defined;
    326             return true;
    327         }
    328 
    329         if (new->merge == MERGE_REPLACE) {
    330             if (report)
    331                 log_warn(info->ctx,
    332                          "Map for indicator %s redefined; "
    333                          "Earlier definition ignored\n",
    334                          xkb_atom_text(info->ctx, old->led.name));
    335             *old = *new;
    336             return true;
    337         }
    338 
    339         collide = 0;
    340         if (UseNewLEDField(LED_FIELD_MODS, old, new, report, &collide)) {
    341             old->led.which_mods = new->led.which_mods;
    342             old->led.mods = new->led.mods;
    343             old->defined |= LED_FIELD_MODS;
    344         }
    345         if (UseNewLEDField(LED_FIELD_GROUPS, old, new, report, &collide)) {
    346             old->led.which_groups = new->led.which_groups;
    347             old->led.groups = new->led.groups;
    348             old->defined |= LED_FIELD_GROUPS;
    349         }
    350         if (UseNewLEDField(LED_FIELD_CTRLS, old, new, report, &collide)) {
    351             old->led.ctrls = new->led.ctrls;
    352             old->defined |= LED_FIELD_CTRLS;
    353         }
    354 
    355         if (collide) {
    356             log_warn(info->ctx,
    357                      "Map for indicator %s redefined; "
    358                      "Using %s definition for duplicate fields\n",
    359                      xkb_atom_text(info->ctx, old->led.name),
    360                      (new->merge == MERGE_AUGMENT ? "first" : "last"));
    361         }
    362 
    363         return true;
    364     }
    365 
    366     if (info->num_leds >= XKB_MAX_LEDS) {
    367         log_err(info->ctx,
    368                 "Too many LEDs defined (maximum %d)\n",
    369                 XKB_MAX_LEDS);
    370         return false;
    371     }
    372     info->leds[info->num_leds++] = *new;
    373     return true;
    374 }
    375 
    376 static void
    377 MergeIncludedCompatMaps(CompatInfo *into, CompatInfo *from,
    378                         enum merge_mode merge)
    379 {
    380     SymInterpInfo *si;
    381 
    382     if (from->errorCount > 0) {
    383         into->errorCount += from->errorCount;
    384         return;
    385     }
    386 
    387     into->mods = from->mods;
    388 
    389     if (into->name == NULL) {
    390         into->name = from->name;
    391         from->name = NULL;
    392     }
    393 
    394     if (darray_empty(into->interps)) {
    395         into->interps = from->interps;
    396         darray_init(from->interps);
    397     }
    398     else {
    399         darray_foreach(si, from->interps) {
    400             si->merge = (merge == MERGE_DEFAULT ? si->merge : merge);
    401             if (!AddInterp(into, si, false))
    402                 into->errorCount++;
    403         }
    404     }
    405 
    406     if (into->num_leds == 0) {
    407         memcpy(into->leds, from->leds, sizeof(*from->leds) * from->num_leds);
    408         into->num_leds = from->num_leds;
    409         from->num_leds = 0;
    410     }
    411     else {
    412         for (xkb_led_index_t i = 0; i < from->num_leds; i++) {
    413             LedInfo *ledi = &from->leds[i];
    414             ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge);
    415             if (!AddLedMap(into, ledi, false))
    416                 into->errorCount++;
    417         }
    418     }
    419 }
    420 
    421 static void
    422 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge);
    423 
    424 static bool
    425 HandleIncludeCompatMap(CompatInfo *info, IncludeStmt *include)
    426 {
    427     CompatInfo included;
    428 
    429     InitCompatInfo(&included, info->ctx, info->actions, &info->mods);
    430     included.name = include->stmt;
    431     include->stmt = NULL;
    432 
    433     for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) {
    434         CompatInfo next_incl;
    435         XkbFile *file;
    436 
    437         file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_COMPAT);
    438         if (!file) {
    439             info->errorCount += 10;
    440             ClearCompatInfo(&included);
    441             return false;
    442         }
    443 
    444         InitCompatInfo(&next_incl, info->ctx, info->actions, &included.mods);
    445         next_incl.default_interp = info->default_interp;
    446         next_incl.default_interp.merge = stmt->merge;
    447         next_incl.default_led = info->default_led;
    448         next_incl.default_led.merge = stmt->merge;
    449 
    450         HandleCompatMapFile(&next_incl, file, MERGE_OVERRIDE);
    451 
    452         MergeIncludedCompatMaps(&included, &next_incl, stmt->merge);
    453 
    454         ClearCompatInfo(&next_incl);
    455         FreeXkbFile(file);
    456     }
    457 
    458     MergeIncludedCompatMaps(info, &included, include->merge);
    459     ClearCompatInfo(&included);
    460 
    461     return (info->errorCount == 0);
    462 }
    463 
    464 static bool
    465 SetInterpField(CompatInfo *info, SymInterpInfo *si, const char *field,
    466                ExprDef *arrayNdx, ExprDef *value)
    467 {
    468     xkb_mod_index_t ndx;
    469 
    470     if (istreq(field, "action")) {
    471         if (arrayNdx)
    472             return ReportSINotArray(info, si, field);
    473 
    474         if (!HandleActionDef(info->ctx, info->actions, &info->mods,
    475                              value, &si->interp.action))
    476             return false;
    477 
    478         si->defined |= SI_FIELD_ACTION;
    479     }
    480     else if (istreq(field, "virtualmodifier") ||
    481              istreq(field, "virtualmod")) {
    482         if (arrayNdx)
    483             return ReportSINotArray(info, si, field);
    484 
    485         if (!ExprResolveMod(info->ctx, value, MOD_VIRT, &info->mods, &ndx))
    486             return ReportSIBadType(info, si, field, "virtual modifier");
    487 
    488         si->interp.virtual_mod = ndx;
    489         si->defined |= SI_FIELD_VIRTUAL_MOD;
    490     }
    491     else if (istreq(field, "repeat")) {
    492         bool set;
    493 
    494         if (arrayNdx)
    495             return ReportSINotArray(info, si, field);
    496 
    497         if (!ExprResolveBoolean(info->ctx, value, &set))
    498             return ReportSIBadType(info, si, field, "boolean");
    499 
    500         si->interp.repeat = set;
    501 
    502         si->defined |= SI_FIELD_AUTO_REPEAT;
    503     }
    504     else if (istreq(field, "locking")) {
    505         log_dbg(info->ctx,
    506                 "The \"locking\" field in symbol interpretation is unsupported; "
    507                 "Ignored\n");
    508     }
    509     else if (istreq(field, "usemodmap") ||
    510              istreq(field, "usemodmapmods")) {
    511         unsigned int val;
    512 
    513         if (arrayNdx)
    514             return ReportSINotArray(info, si, field);
    515 
    516         if (!ExprResolveEnum(info->ctx, value, &val, useModMapValueNames))
    517             return ReportSIBadType(info, si, field, "level specification");
    518 
    519         si->interp.level_one_only = val;
    520         si->defined |= SI_FIELD_LEVEL_ONE_ONLY;
    521     }
    522     else {
    523         return ReportBadField(info->ctx, "symbol interpretation", field,
    524                               siText(si, info));
    525     }
    526 
    527     return true;
    528 }
    529 
    530 static bool
    531 SetLedMapField(CompatInfo *info, LedInfo *ledi, const char *field,
    532                ExprDef *arrayNdx, ExprDef *value)
    533 {
    534     bool ok = true;
    535 
    536     if (istreq(field, "modifiers") || istreq(field, "mods")) {
    537         if (arrayNdx)
    538             return ReportLedNotArray(info, ledi, field);
    539 
    540         if (!ExprResolveModMask(info->ctx, value, MOD_BOTH,
    541                                 &info->mods, &ledi->led.mods.mods))
    542             return ReportLedBadType(info, ledi, field, "modifier mask");
    543 
    544         ledi->defined |= LED_FIELD_MODS;
    545     }
    546     else if (istreq(field, "groups")) {
    547         unsigned int mask;
    548 
    549         if (arrayNdx)
    550             return ReportLedNotArray(info, ledi, field);
    551 
    552         if (!ExprResolveMask(info->ctx, value, &mask, groupMaskNames))
    553             return ReportLedBadType(info, ledi, field, "group mask");
    554 
    555         ledi->led.groups = mask;
    556         ledi->defined |= LED_FIELD_GROUPS;
    557     }
    558     else if (istreq(field, "controls") || istreq(field, "ctrls")) {
    559         unsigned int mask;
    560 
    561         if (arrayNdx)
    562             return ReportLedNotArray(info, ledi, field);
    563 
    564         if (!ExprResolveMask(info->ctx, value, &mask, ctrlMaskNames))
    565             return ReportLedBadType(info, ledi, field, "controls mask");
    566 
    567         ledi->led.ctrls = mask;
    568         ledi->defined |= LED_FIELD_CTRLS;
    569     }
    570     else if (istreq(field, "allowexplicit")) {
    571         log_dbg(info->ctx,
    572                 "The \"allowExplicit\" field in indicator statements is unsupported; "
    573                 "Ignored\n");
    574     }
    575     else if (istreq(field, "whichmodstate") ||
    576              istreq(field, "whichmodifierstate")) {
    577         unsigned int mask;
    578 
    579         if (arrayNdx)
    580             return ReportLedNotArray(info, ledi, field);
    581 
    582         if (!ExprResolveMask(info->ctx, value, &mask,
    583                              modComponentMaskNames))
    584             return ReportLedBadType(info, ledi, field,
    585                                     "mask of modifier state components");
    586 
    587         ledi->led.which_mods = mask;
    588     }
    589     else if (istreq(field, "whichgroupstate")) {
    590         unsigned mask;
    591 
    592         if (arrayNdx)
    593             return ReportLedNotArray(info, ledi, field);
    594 
    595         if (!ExprResolveMask(info->ctx, value, &mask,
    596                              groupComponentMaskNames))
    597             return ReportLedBadType(info, ledi, field,
    598                                     "mask of group state components");
    599 
    600         ledi->led.which_groups = mask;
    601     }
    602     else if (istreq(field, "driveskbd") ||
    603              istreq(field, "driveskeyboard") ||
    604              istreq(field, "leddriveskbd") ||
    605              istreq(field, "leddriveskeyboard") ||
    606              istreq(field, "indicatordriveskbd") ||
    607              istreq(field, "indicatordriveskeyboard")) {
    608         log_dbg(info->ctx,
    609                 "The \"%s\" field in indicator statements is unsupported; "
    610                 "Ignored\n", field);
    611     }
    612     else if (istreq(field, "index")) {
    613         /* Users should see this, it might cause unexpected behavior. */
    614         log_err(info->ctx,
    615                 "The \"index\" field in indicator statements is unsupported; "
    616                 "Ignored\n");
    617     }
    618     else {
    619         log_err(info->ctx,
    620                 "Unknown field %s in map for %s indicator; "
    621                 "Definition ignored\n",
    622                 field, xkb_atom_text(info->ctx, ledi->led.name));
    623         ok = false;
    624     }
    625 
    626     return ok;
    627 }
    628 
    629 static bool
    630 HandleGlobalVar(CompatInfo *info, VarDef *stmt)
    631 {
    632     const char *elem, *field;
    633     ExprDef *ndx;
    634     bool ret;
    635 
    636     if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &ndx))
    637         ret = false;
    638     else if (elem && istreq(elem, "interpret"))
    639         ret = SetInterpField(info, &info->default_interp, field, ndx,
    640                              stmt->value);
    641     else if (elem && istreq(elem, "indicator"))
    642         ret = SetLedMapField(info, &info->default_led, field, ndx,
    643                              stmt->value);
    644     else
    645         ret = SetActionField(info->ctx, info->actions, &info->mods,
    646                              elem, field, ndx, stmt->value);
    647     return ret;
    648 }
    649 
    650 static bool
    651 HandleInterpBody(CompatInfo *info, VarDef *def, SymInterpInfo *si)
    652 {
    653     bool ok = true;
    654     const char *elem, *field;
    655     ExprDef *arrayNdx;
    656 
    657     for (; def; def = (VarDef *) def->common.next) {
    658         if (def->name && def->name->expr.op == EXPR_FIELD_REF) {
    659             log_err(info->ctx,
    660                     "Cannot set a global default value from within an interpret statement; "
    661                     "Move statements to the global file scope\n");
    662             ok = false;
    663             continue;
    664         }
    665 
    666         ok = ExprResolveLhs(info->ctx, def->name, &elem, &field, &arrayNdx);
    667         if (!ok)
    668             continue;
    669 
    670         ok = SetInterpField(info, si, field, arrayNdx, def->value);
    671     }
    672 
    673     return ok;
    674 }
    675 
    676 static bool
    677 HandleInterpDef(CompatInfo *info, InterpDef *def, enum merge_mode merge)
    678 {
    679     enum xkb_match_operation pred;
    680     xkb_mod_mask_t mods;
    681     SymInterpInfo si;
    682 
    683     if (!ResolveStateAndPredicate(def->match, &pred, &mods, info)) {
    684         log_err(info->ctx,
    685                 "Couldn't determine matching modifiers; "
    686                 "Symbol interpretation ignored\n");
    687         return false;
    688     }
    689 
    690     si = info->default_interp;
    691     si.merge = merge = (def->merge == MERGE_DEFAULT ? merge : def->merge);
    692     si.interp.sym = def->sym;
    693     si.interp.match = pred;
    694     si.interp.mods = mods;
    695 
    696     if (!HandleInterpBody(info, def->def, &si)) {
    697         info->errorCount++;
    698         return false;
    699     }
    700 
    701     if (!AddInterp(info, &si, true)) {
    702         info->errorCount++;
    703         return false;
    704     }
    705 
    706     return true;
    707 }
    708 
    709 static bool
    710 HandleLedMapDef(CompatInfo *info, LedMapDef *def, enum merge_mode merge)
    711 {
    712     LedInfo ledi;
    713     VarDef *var;
    714     bool ok;
    715 
    716     if (def->merge != MERGE_DEFAULT)
    717         merge = def->merge;
    718 
    719     ledi = info->default_led;
    720     ledi.merge = merge;
    721     ledi.led.name = def->name;
    722 
    723     ok = true;
    724     for (var = def->body; var != NULL; var = (VarDef *) var->common.next) {
    725         const char *elem, *field;
    726         ExprDef *arrayNdx;
    727         if (!ExprResolveLhs(info->ctx, var->name, &elem, &field, &arrayNdx)) {
    728             ok = false;
    729             continue;
    730         }
    731 
    732         if (elem) {
    733             log_err(info->ctx,
    734                     "Cannot set defaults for \"%s\" element in indicator map; "
    735                     "Assignment to %s.%s ignored\n", elem, elem, field);
    736             ok = false;
    737         }
    738         else {
    739             ok = SetLedMapField(info, &ledi, field, arrayNdx, var->value) && ok;
    740         }
    741     }
    742 
    743     if (ok)
    744         return AddLedMap(info, &ledi, true);
    745 
    746     return false;
    747 }
    748 
    749 static void
    750 HandleCompatMapFile(CompatInfo *info, XkbFile *file, enum merge_mode merge)
    751 {
    752     bool ok;
    753 
    754     merge = (merge == MERGE_DEFAULT ? MERGE_AUGMENT : merge);
    755 
    756     free(info->name);
    757     info->name = strdup_safe(file->name);
    758 
    759     for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) {
    760         switch (stmt->type) {
    761         case STMT_INCLUDE:
    762             ok = HandleIncludeCompatMap(info, (IncludeStmt *) stmt);
    763             break;
    764         case STMT_INTERP:
    765             ok = HandleInterpDef(info, (InterpDef *) stmt, merge);
    766             break;
    767         case STMT_GROUP_COMPAT:
    768             log_dbg(info->ctx,
    769                     "The \"group\" statement in compat is unsupported; "
    770                     "Ignored\n");
    771             ok = true;
    772             break;
    773         case STMT_LED_MAP:
    774             ok = HandleLedMapDef(info, (LedMapDef *) stmt, merge);
    775             break;
    776         case STMT_VAR:
    777             ok = HandleGlobalVar(info, (VarDef *) stmt);
    778             break;
    779         case STMT_VMOD:
    780             ok = HandleVModDef(info->ctx, &info->mods, (VModDef *) stmt, merge);
    781             break;
    782         default:
    783             log_err(info->ctx,
    784                     "Compat files may not include other types; "
    785                     "Ignoring %s\n", stmt_type_to_string(stmt->type));
    786             ok = false;
    787             break;
    788         }
    789 
    790         if (!ok)
    791             info->errorCount++;
    792 
    793         if (info->errorCount > 10) {
    794             log_err(info->ctx,
    795                     "Abandoning compatibility map \"%s\"\n", file->topName);
    796             break;
    797         }
    798     }
    799 }
    800 
    801 /* Temporary struct for CopyInterps. */
    802 struct collect {
    803     darray(struct xkb_sym_interpret) sym_interprets;
    804 };
    805 
    806 static void
    807 CopyInterps(CompatInfo *info, bool needSymbol, enum xkb_match_operation pred,
    808             struct collect *collect)
    809 {
    810     SymInterpInfo *si;
    811 
    812     darray_foreach(si, info->interps)
    813         if (si->interp.match == pred &&
    814             (si->interp.sym != XKB_KEY_NoSymbol) == needSymbol)
    815             darray_append(collect->sym_interprets, si->interp);
    816 }
    817 
    818 static void
    819 CopyLedMapDefsToKeymap(struct xkb_keymap *keymap, CompatInfo *info)
    820 {
    821     for (xkb_led_index_t idx = 0; idx < info->num_leds; idx++) {
    822         LedInfo *ledi = &info->leds[idx];
    823         xkb_led_index_t i;
    824         struct xkb_led *led;
    825 
    826         /*
    827          * Find the LED with the given name, if it was already declared
    828          * in keycodes.
    829          */
    830         xkb_leds_enumerate(i, led, keymap)
    831             if (led->name == ledi->led.name)
    832                 break;
    833 
    834         /* Not previously declared; create it with next free index. */
    835         if (i >= keymap->num_leds) {
    836             log_dbg(keymap->ctx,
    837                     "Indicator name \"%s\" was not declared in the keycodes section; "
    838                     "Adding new indicator\n",
    839                     xkb_atom_text(keymap->ctx, ledi->led.name));
    840 
    841             xkb_leds_enumerate(i, led, keymap)
    842                 if (led->name == XKB_ATOM_NONE)
    843                     break;
    844 
    845             if (i >= keymap->num_leds) {
    846                 /* Not place to put it; ignore. */
    847                 if (i >= XKB_MAX_LEDS) {
    848                     log_err(keymap->ctx,
    849                             "Too many indicators (maximum is %d); "
    850                             "Indicator name \"%s\" ignored\n",
    851                             XKB_MAX_LEDS,
    852                             xkb_atom_text(keymap->ctx, ledi->led.name));
    853                     continue;
    854                 }
    855 
    856                 /* Add a new LED. */
    857                 led = &keymap->leds[keymap->num_leds++];
    858             }
    859         }
    860 
    861         *led = ledi->led;
    862         if (led->groups != 0 && led->which_groups == 0)
    863             led->which_groups = XKB_STATE_LAYOUT_EFFECTIVE;
    864         if (led->mods.mods != 0 && led->which_mods == 0)
    865             led->which_mods = XKB_STATE_MODS_EFFECTIVE;
    866     }
    867 }
    868 
    869 static bool
    870 CopyCompatToKeymap(struct xkb_keymap *keymap, CompatInfo *info)
    871 {
    872     keymap->compat_section_name = strdup_safe(info->name);
    873     XkbEscapeMapName(keymap->compat_section_name);
    874 
    875     keymap->mods = info->mods;
    876 
    877     if (!darray_empty(info->interps)) {
    878         struct collect collect;
    879         darray_init(collect.sym_interprets);
    880 
    881         /* Most specific to least specific. */
    882         CopyInterps(info, true, MATCH_EXACTLY, &collect);
    883         CopyInterps(info, true, MATCH_ALL, &collect);
    884         CopyInterps(info, true, MATCH_NONE, &collect);
    885         CopyInterps(info, true, MATCH_ANY, &collect);
    886         CopyInterps(info, true, MATCH_ANY_OR_NONE, &collect);
    887         CopyInterps(info, false, MATCH_EXACTLY, &collect);
    888         CopyInterps(info, false, MATCH_ALL, &collect);
    889         CopyInterps(info, false, MATCH_NONE, &collect);
    890         CopyInterps(info, false, MATCH_ANY, &collect);
    891         CopyInterps(info, false, MATCH_ANY_OR_NONE, &collect);
    892 
    893         darray_steal(collect.sym_interprets,
    894                      &keymap->sym_interprets, &keymap->num_sym_interprets);
    895     }
    896 
    897     CopyLedMapDefsToKeymap(keymap, info);
    898 
    899     return true;
    900 }
    901 
    902 bool
    903 CompileCompatMap(XkbFile *file, struct xkb_keymap *keymap,
    904                  enum merge_mode merge)
    905 {
    906     CompatInfo info;
    907     ActionsInfo *actions;
    908 
    909     actions = NewActionsInfo();
    910     if (!actions)
    911         return false;
    912 
    913     InitCompatInfo(&info, keymap->ctx, actions, &keymap->mods);
    914     info.default_interp.merge = merge;
    915     info.default_led.merge = merge;
    916 
    917     HandleCompatMapFile(&info, file, merge);
    918     if (info.errorCount != 0)
    919         goto err_info;
    920 
    921     if (!CopyCompatToKeymap(keymap, &info))
    922         goto err_info;
    923 
    924     ClearCompatInfo(&info);
    925     FreeActionsInfo(actions);
    926     return true;
    927 
    928 err_info:
    929     ClearCompatInfo(&info);
    930     FreeActionsInfo(actions);
    931     return false;
    932 }
    933