Move STRINGIFY to utils.h and add STRINGIFY2
[platform/upstream/libxkbcommon.git] / src / xkbcomp / keycodes.c
index 8751021..b8abf36 100644 (file)
 /************************************************************
- Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
-
- Permission to use, copy, modify, and distribute this
- software and its documentation for any purpose and without
- fee is hereby granted, provided that the above copyright
- notice appear in all copies and that both that copyright
- notice and this permission notice appear in supporting
- documentation, and that the name of Silicon Graphics not be
- used in advertising or publicity pertaining to distribution
- of the software without specific prior written permission.
- Silicon Graphics makes no representation about the suitability
- of this software for any purpose. It is provided "as is"
- without any express or implied warranty.
-
- SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
- SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
- AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
- GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
- DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
- OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
- THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+ *
Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright
notice and this permission notice appear in supporting
documentation, and that the name of Silicon Graphics not be
used in advertising or publicity pertaining to distribution
of the software without specific prior written permission.
Silicon Graphics makes no representation about the suitability
of this software for any purpose. It is provided "as is"
without any express or implied warranty.
+ *
SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
  ********************************************************/
 
-#include "keycodes.h"
-#include "expr.h"
-#include "parseutils.h"
-#include "alias.h"
-
-const char *
-longText(unsigned long val)
-{
-    char buf[4];
+#include "config.h"
 
-    LongToKeyName(val, buf);
-    return XkbcKeyNameText(buf);
-}
+#include "xkbcomp-priv.h"
+#include "text.h"
+#include "expr.h"
+#include "include.h"
 
-/***====================================================================***/
+typedef struct {
+    enum merge_mode merge;
 
-void
-LongToKeyName(unsigned long val, char *name)
-{
-    name[0] = ((val >> 24) & 0xff);
-    name[1] = ((val >> 16) & 0xff);
-    name[2] = ((val >> 8) & 0xff);
-    name[3] = (val & 0xff);
-}
+    xkb_atom_t alias;
+    xkb_atom_t real;
+} AliasInfo;
 
-/***====================================================================***/
+typedef struct {
+    enum merge_mode merge;
 
-typedef struct _IndicatorNameInfo
-{
-    CommonInfo defs;
-    int ndx;
     xkb_atom_t name;
-    bool virtual;
-} IndicatorNameInfo;
+} LedNameInfo;
 
-typedef struct _KeyNamesInfo
-{
-    char *name;     /* e.g. evdev+aliases(qwerty) */
+typedef struct {
+    char *name;
     int errorCount;
-    unsigned file_id;
-    enum merge_mode merge;
-    xkb_keycode_t computedMin; /* lowest keycode stored */
-    xkb_keycode_t computedMax; /* highest keycode stored */
-    xkb_keycode_t explicitMin;
-    xkb_keycode_t explicitMax;
-    darray(unsigned long) names;
-    darray(unsigned int) files;
-    IndicatorNameInfo *leds;
-    AliasInfo *aliases;
-} KeyNamesInfo;
 
-static void HandleKeycodesFile(XkbFile *file, struct xkb_keymap *keymap,
-                               enum merge_mode merge, KeyNamesInfo *info);
+    xkb_keycode_t min_key_code;
+    xkb_keycode_t max_key_code;
+    darray(xkb_atom_t) key_names;
+    LedNameInfo led_names[XKB_MAX_LEDS];
+    unsigned int num_led_names;
+    darray(AliasInfo) aliases;
 
-static void
-ResizeKeyNameArrays(KeyNamesInfo *info, int newMax)
-{
-    if (newMax < darray_size(info->names))
-        return;
+    struct xkb_context *ctx;
+} KeyNamesInfo;
 
-    darray_resize0(info->names, newMax + 1);
-    darray_resize0(info->files, newMax + 1);
-}
+/***====================================================================***/
 
 static void
-InitIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
+InitAliasInfo(AliasInfo *info, enum merge_mode merge,
+              xkb_atom_t alias, xkb_atom_t real)
 {
-    ii->defs.defined = 0;
-    ii->defs.merge = info->merge;
-    ii->defs.file_id = info->file_id;
-    ii->defs.next = NULL;
-    ii->ndx = 0;
-    ii->name = XKB_ATOM_NONE;
-    ii->virtual = false;
+    memset(info, 0, sizeof(*info));
+    info->merge = merge;
+    info->alias = alias;
+    info->real = real;
 }
 
-static void
-ClearIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
+static LedNameInfo *
+FindLedByName(KeyNamesInfo *info, xkb_atom_t name,
+              xkb_led_index_t *idx_out)
 {
-    if (ii == info->leds)
-    {
-        ClearCommonInfo(&ii->defs);
-        info->leds = NULL;
+    for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) {
+        LedNameInfo *ledi = &info->led_names[idx];
+        if (ledi->name == name) {
+            *idx_out = idx;
+            return ledi;
+        }
     }
-}
-
-static IndicatorNameInfo *
-NextIndicatorName(KeyNamesInfo * info)
-{
-    IndicatorNameInfo *ii;
 
-    ii = uTypedAlloc(IndicatorNameInfo);
-    if (ii)
-    {
-        InitIndicatorNameInfo(ii, info);
-        info->leds = AddCommonInfo(&info->leds->defs, &ii->defs);
-    }
-    return ii;
+    return NULL;
 }
 
-static IndicatorNameInfo *
-FindIndicatorByIndex(KeyNamesInfo * info, int ndx)
+static bool
+AddLedName(KeyNamesInfo *info, enum merge_mode merge, bool same_file,
+           LedNameInfo *new, xkb_led_index_t new_idx)
 {
-    IndicatorNameInfo *old;
+    xkb_led_index_t old_idx;
+    LedNameInfo *old;
+    const int verbosity = xkb_context_get_log_verbosity(info->ctx);
+    const bool report = (same_file && verbosity > 0) || verbosity > 9;
+    const bool replace = (merge == MERGE_REPLACE || merge == MERGE_OVERRIDE);
+
+    /* LED with the same name already exists. */
+    old = FindLedByName(info, new->name, &old_idx);
+    if (old) {
+        if (old_idx == new_idx) {
+            log_warn(info->ctx,
+                     "Multiple indicators named \"%s\"; "
+                     "Identical definitions ignored\n",
+                     xkb_atom_text(info->ctx, new->name));
+            return true;
+        }
 
-    for (old = info->leds; old != NULL;
-         old = (IndicatorNameInfo *) old->defs.next)
-    {
-        if (old->ndx == ndx)
-            return old;
-    }
-    return NULL;
-}
+        if (report) {
+            xkb_led_index_t use = (replace ? new_idx + 1 : old_idx + 1);
+            xkb_led_index_t ignore = (replace ? old_idx + 1 : new_idx + 1);
+            log_warn(info->ctx,
+                     "Multiple indicators named %s; Using %d, ignoring %d\n",
+                     xkb_atom_text(info->ctx, new->name), use, ignore);
+        }
 
-static IndicatorNameInfo *
-FindIndicatorByName(KeyNamesInfo * info, xkb_atom_t name)
-{
-    IndicatorNameInfo *old;
+        if (replace)
+            *old = *new;
 
-    for (old = info->leds; old != NULL;
-         old = (IndicatorNameInfo *) old->defs.next)
-    {
-        if (old->name == name)
-            return old;
+        return true;
     }
-    return NULL;
-}
 
-static bool
-AddIndicatorName(KeyNamesInfo *info, struct xkb_keymap *keymap, enum merge_mode merge,
-                 IndicatorNameInfo *new)
-{
-    IndicatorNameInfo *old;
-    bool replace;
-
-    replace = (merge == MERGE_REPLACE) || (merge == MERGE_OVERRIDE);
-    old = FindIndicatorByName(info, new->name);
-    if (old)
-    {
-        if (((old->defs.file_id == new->defs.file_id) && (warningLevel > 0))
-            || (warningLevel > 9))
-        {
-            WARN("Multiple indicators named %s\n",
-                 xkb_atom_text(keymap->ctx, new->name));
-            if (old->ndx == new->ndx)
-            {
-                if (old->virtual != new->virtual)
-                {
-                    if (replace)
-                        old->virtual = new->virtual;
-                    ACTION("Using %s instead of %s\n",
-                           (old->virtual ? "virtual" : "real"),
-                           (old->virtual ? "real" : "virtual"));
-                }
-                else
-                {
-                    ACTION("Identical definitions ignored\n");
-                }
-                return true;
-            }
-            else
-            {
-                if (replace)
-                    ACTION("Ignoring %d, using %d\n", old->ndx, new->ndx);
-                else
-                    ACTION("Using %d, ignoring %d\n", old->ndx, new->ndx);
-            }
-            if (replace)
-            {
-                if (info->leds == old)
-                    info->leds = (IndicatorNameInfo *) old->defs.next;
-                else
-                {
-                    IndicatorNameInfo *tmp;
-                    tmp = info->leds;
-                    for (; tmp != NULL;
-                         tmp = (IndicatorNameInfo *) tmp->defs.next)
-                    {
-                        if (tmp->defs.next == (CommonInfo *) old)
-                        {
-                            tmp->defs.next = old->defs.next;
-                            break;
-                        }
-                    }
-                }
-                free(old);
-            }
-        }
-    }
-    old = FindIndicatorByIndex(info, new->ndx);
-    if (old)
-    {
-        if (((old->defs.file_id == new->defs.file_id) && (warningLevel > 0))
-            || (warningLevel > 9))
-        {
-            WARN("Multiple names for indicator %d\n", new->ndx);
-            if ((old->name == new->name) && (old->virtual == new->virtual))
-                ACTION("Identical definitions ignored\n");
-            else
-            {
-                const char *oldType, *newType;
-                xkb_atom_t using, ignoring;
-                if (old->virtual)
-                    oldType = "virtual indicator";
-                else
-                    oldType = "real indicator";
-                if (new->virtual)
-                    newType = "virtual indicator";
-                else
-                    newType = "real indicator";
-                if (replace)
-                {
-                    using = new->name;
-                    ignoring = old->name;
-                }
-                else
-                {
-                    using = old->name;
-                    ignoring = new->name;
-                }
-                ACTION("Using %s %s, ignoring %s %s\n",
-                        oldType, xkb_atom_text(keymap->ctx, using),
-                        newType, xkb_atom_text(keymap->ctx, ignoring));
-            }
+    if (new_idx >= info->num_led_names)
+        info->num_led_names = new_idx + 1;
+
+    /* LED with the same index already exists. */
+    old = &info->led_names[new_idx];
+    if (old->name != XKB_ATOM_NONE) {
+        if (report) {
+            const xkb_atom_t use = (replace ? new->name : old->name);
+            const xkb_atom_t ignore = (replace ? old->name : new->name);
+            log_warn(info->ctx, "Multiple names for indicator %d; "
+                     "Using %s, ignoring %s\n", new_idx + 1,
+                     xkb_atom_text(info->ctx, use),
+                     xkb_atom_text(info->ctx, ignore));
         }
+
         if (replace)
-        {
-            old->name = new->name;
-            old->virtual = new->virtual;
-        }
+            *old = *new;
+
         return true;
     }
-    old = new;
-    new = NextIndicatorName(info);
-    if (!new)
-    {
-        WSGO("Couldn't allocate name for indicator %d\n", old->ndx);
-        ACTION("Ignored\n");
-        return false;
-    }
-    new->name = old->name;
-    new->ndx = old->ndx;
-    new->virtual = old->virtual;
+
+    *old = *new;
     return true;
 }
 
 static void
-ClearKeyNamesInfo(KeyNamesInfo * info)
+ClearKeyNamesInfo(KeyNamesInfo *info)
 {
     free(info->name);
-    info->name = NULL;
-    info->computedMax = info->explicitMax = info->explicitMin = 0;
-    info->computedMin = XKB_KEYCODE_MAX;
-    darray_free(info->names);
-    darray_free(info->files);
-    if (info->leds)
-        ClearIndicatorNameInfo(info->leds, info);
-    if (info->aliases)
-        ClearAliases(&info->aliases);
+    darray_free(info->key_names);
+    darray_free(info->aliases);
 }
 
 static void
-InitKeyNamesInfo(KeyNamesInfo * info, unsigned file_id)
+InitKeyNamesInfo(KeyNamesInfo *info, struct xkb_context *ctx)
 {
-    info->name = NULL;
-    info->leds = NULL;
-    info->aliases = NULL;
-    info->file_id = file_id;
-    darray_init(info->names);
-    darray_init(info->files);
-    ClearKeyNamesInfo(info);
-    info->errorCount = 0;
+    memset(info, 0, sizeof(*info));
+    info->ctx = ctx;
+    info->min_key_code = XKB_KEYCODE_INVALID;
+#if XKB_KEYCODE_INVALID < XKB_KEYCODE_MAX
+#error "Hey, you can't be changing stuff like that."
+#endif
 }
 
-static int
-FindKeyByLong(KeyNamesInfo * info, unsigned long name)
+static xkb_keycode_t
+FindKeyByName(KeyNamesInfo *info, xkb_atom_t name)
 {
-    uint64_t i;
+    xkb_keycode_t i;
 
-    for (i = info->computedMin; i <= info->computedMax; i++)
-        if (darray_item(info->names, i) == name)
+    for (i = info->min_key_code; i <= info->max_key_code; i++)
+        if (darray_item(info->key_names, i) == name)
             return i;
 
-    return 0;
+    return XKB_KEYCODE_INVALID;
 }
 
-/**
- * Store the name of the key as a long in the info struct under the given
- * keycode. If the same keys is referred to twice, print a warning.
- * Note that the key's name is stored as a long, the keycode is the index.
- */
 static bool
-AddKeyName(KeyNamesInfo * info,
-           xkb_keycode_t kc, char *name, enum merge_mode merge,
-           unsigned file_id, bool reportCollisions)
+AddKeyName(KeyNamesInfo *info, xkb_keycode_t kc, xkb_atom_t name,
+           enum merge_mode merge, bool same_file, bool report)
 {
-    xkb_keycode_t old;
-    unsigned long lval;
-
-    ResizeKeyNameArrays(info, kc);
-
-    if (kc < info->computedMin)
-        info->computedMin = kc;
-    if (kc > info->computedMax)
-        info->computedMax = kc;
-    lval = KeyNameToLong(name);
-
-    if (reportCollisions)
-    {
-        reportCollisions = (warningLevel > 7 ||
-                            (warningLevel > 0 &&
-                             file_id == darray_item(info->files, kc)));
-    }
-
-    if (darray_item(info->names, kc) != 0)
-    {
-        char buf[6];
-
-        LongToKeyName(darray_item(info->names, kc), buf);
-        buf[4] = '\0';
-        if (darray_item(info->names, kc) == lval && reportCollisions)
-        {
-            WARN("Multiple identical key name definitions\n");
-            ACTION("Later occurences of \"<%s> = %d\" ignored\n",
-                   buf, kc);
+    xkb_atom_t old_name;
+    xkb_keycode_t old_kc;
+    const int verbosity = xkb_context_get_log_verbosity(info->ctx);
+
+    report = report && ((same_file && verbosity > 0) || verbosity > 7);
+
+    if (kc >= darray_size(info->key_names))
+        darray_resize0(info->key_names, kc + 1);
+
+    info->min_key_code = MIN(info->min_key_code, kc);
+    info->max_key_code = MAX(info->max_key_code, kc);
+
+    /* There's already a key with this keycode. */
+    old_name = darray_item(info->key_names, kc);
+    if (old_name != XKB_ATOM_NONE) {
+        const char *lname = KeyNameText(info->ctx, old_name);
+        const char *kname = KeyNameText(info->ctx, name);
+
+        if (old_name == name) {
+            if (report)
+                log_warn(info->ctx,
+                         "Multiple identical key name definitions; "
+                         "Later occurrences of \"%s = %d\" ignored\n",
+                         lname, kc);
             return true;
         }
-        if (merge == MERGE_AUGMENT)
-        {
-            if (reportCollisions)
-            {
-                WARN("Multiple names for keycode %d\n", kc);
-                ACTION("Using <%s>, ignoring <%s>\n", buf, name);
-            }
+        else if (merge == MERGE_AUGMENT) {
+            if (report)
+                log_warn(info->ctx,
+                         "Multiple names for keycode %d; "
+                         "Using %s, ignoring %s\n", kc, lname, kname);
             return true;
         }
-        else
-        {
-            if (reportCollisions)
-            {
-                WARN("Multiple names for keycode %d\n", kc);
-                ACTION("Using <%s>, ignoring <%s>\n", name, buf);
-            }
-            darray_item(info->names, kc) = 0;
-            darray_item(info->files, kc) = 0;
+        else {
+            if (report)
+                log_warn(info->ctx,
+                         "Multiple names for keycode %d; "
+                         "Using %s, ignoring %s\n", kc, kname, lname);
+            darray_item(info->key_names, kc) = XKB_ATOM_NONE;
         }
     }
-    old = FindKeyByLong(info, lval);
-    if ((old != 0) && (old != kc))
-    {
-        if (merge == MERGE_OVERRIDE)
-        {
-            darray_item(info->names, old) = 0;
-            darray_item(info->files, old) = 0;
-            if (reportCollisions)
-            {
-                WARN("Key name <%s> assigned to multiple keys\n", name);
-                ACTION("Using %d, ignoring %d\n", kc, old);
-            }
+
+    /* There's already a key with this name. */
+    old_kc = FindKeyByName(info, name);
+    if (old_kc != XKB_KEYCODE_INVALID && old_kc != kc) {
+        const char *kname = KeyNameText(info->ctx, name);
+
+        if (merge == MERGE_OVERRIDE) {
+            darray_item(info->key_names, old_kc) = XKB_ATOM_NONE;
+            if (report)
+                log_warn(info->ctx,
+                         "Key name %s assigned to multiple keys; "
+                         "Using %d, ignoring %d\n", kname, kc, old_kc);
         }
-        else
-        {
-            if ((reportCollisions) && (warningLevel > 3))
-            {
-                WARN("Key name <%s> assigned to multiple keys\n", name);
-                ACTION("Using %d, ignoring %d\n", old, kc);
-            }
+        else {
+            if (report)
+                log_vrb(info->ctx, 3,
+                        "Key name %s assigned to multiple keys; "
+                        "Using %d, ignoring %d\n", kname, old_kc, kc);
             return true;
         }
     }
-    darray_item(info->names, kc) = lval;
-    darray_item(info->files, kc) = file_id;
+
+    darray_item(info->key_names, kc) = name;
     return true;
 }
 
 /***====================================================================***/
 
+static bool
+HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge);
+
 static void
-MergeIncludedKeycodes(KeyNamesInfo *into, struct xkb_keymap *keymap,
-                      KeyNamesInfo *from, enum merge_mode merge)
+MergeIncludedKeycodes(KeyNamesInfo *into, KeyNamesInfo *from,
+                      enum merge_mode merge)
 {
-    uint64_t i;
-    char buf[5];
-
-    if (from->errorCount > 0)
-    {
+    if (from->errorCount > 0) {
         into->errorCount += from->errorCount;
         return;
     }
-    if (into->name == NULL)
-    {
+
+    if (into->name == NULL) {
         into->name = from->name;
         from->name = NULL;
     }
 
-    ResizeKeyNameArrays(into, from->computedMax);
+    /* Merge key names. */
+    if (darray_empty(into->key_names)) {
+        into->key_names = from->key_names;
+        darray_init(from->key_names);
+        into->min_key_code = from->min_key_code;
+        into->max_key_code = from->max_key_code;
+    }
+    else {
+        if (darray_size(into->key_names) < darray_size(from->key_names))
+            darray_resize0(into->key_names, darray_size(from->key_names));
 
-    for (i = from->computedMin; i <= from->computedMax; i++)
-    {
-        if (darray_item(from->names, i) == 0)
-            continue;
-        LongToKeyName(darray_item(from->names, i), buf);
-        buf[4] = '\0';
-        if (!AddKeyName(into, i, buf, merge, from->file_id, false))
-            into->errorCount++;
+        for (unsigned i = from->min_key_code; i <= from->max_key_code; i++) {
+            xkb_atom_t name = darray_item(from->key_names, i);
+            if (name == XKB_ATOM_NONE)
+                continue;
+
+            if (!AddKeyName(into, i, name, merge, true, false))
+                into->errorCount++;
+        }
     }
-    if (from->leds)
-    {
-        IndicatorNameInfo *led, *next;
-        for (led = from->leds; led != NULL; led = next)
-        {
-            if (merge != MERGE_DEFAULT)
-                led->defs.merge = merge;
-            if (!AddIndicatorName(into, keymap, led->defs.merge, led))
+
+    /* Merge key aliases. */
+    if (darray_empty(into->aliases)) {
+        into->aliases = from->aliases;
+        darray_init(from->aliases);
+    }
+    else {
+        AliasInfo *alias;
+
+        darray_foreach(alias, from->aliases) {
+            KeyAliasDef def;
+
+            def.merge = (merge == MERGE_DEFAULT ? alias->merge : merge);
+            def.alias = alias->alias;
+            def.real = alias->real;
+
+            if (!HandleAliasDef(into, &def, def.merge))
                 into->errorCount++;
-            next = (IndicatorNameInfo *) led->defs.next;
         }
     }
-    if (!MergeAliases(&into->aliases, &from->aliases, merge))
-        into->errorCount++;
-    if (from->explicitMin != 0)
-    {
-        if ((into->explicitMin == 0)
-            || (into->explicitMin > from->explicitMin))
-            into->explicitMin = from->explicitMin;
+
+    /* Merge LED names. */
+    if (into->num_led_names == 0) {
+        memcpy(into->led_names, from->led_names,
+               sizeof(*from->led_names) * from->num_led_names);
+        into->num_led_names = from->num_led_names;
+        from->num_led_names = 0;
     }
-    if (from->explicitMax > 0)
-    {
-        if ((into->explicitMax == 0)
-            || (into->explicitMax < from->explicitMax))
-            into->explicitMax = from->explicitMax;
+    else {
+        for (xkb_led_index_t idx = 0; idx < from->num_led_names; idx++) {
+            LedNameInfo *ledi = &from->led_names[idx];
+
+            if (ledi->name == XKB_ATOM_NONE)
+                continue;
+
+            ledi->merge = (merge == MERGE_DEFAULT ? ledi->merge : merge);
+            if (!AddLedName(into, ledi->merge, false, ledi, idx))
+                into->errorCount++;
+        }
     }
 }
 
-/**
- * Handle the given include statement (e.g. "include "evdev+aliases(qwerty)").
- *
- * @param stmt The include statement from the keymap file.
- * @param keymap Unused for all but the keymap->flags.
- * @param info Struct to store the key info in.
- */
+static void
+HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge);
+
 static bool
-HandleIncludeKeycodes(IncludeStmt *stmt, struct xkb_keymap *keymap,
-                      KeyNamesInfo *info)
+HandleIncludeKeycodes(KeyNamesInfo *info, IncludeStmt *include)
 {
-    unsigned newMerge;
-    XkbFile *rtrn;
     KeyNamesInfo included;
-    bool haveSelf;
 
-    memset(&included, 0, sizeof(included));
+    InitKeyNamesInfo(&included, info->ctx);
+    included.name = include->stmt;
+    include->stmt = NULL;
 
-    haveSelf = false;
-    if ((stmt->file == NULL) && (stmt->map == NULL))
-    {
-        haveSelf = true;
-        included = *info;
-        memset(info, 0, sizeof(KeyNamesInfo));
-    }
-    else if (stmt->file && strcmp(stmt->file, "computed") == 0)
-    {
-        keymap->flags |= AutoKeyNames;
-        info->explicitMin = 0;
-        info->explicitMax = XKB_KEYCODE_MAX;
-        return (info->errorCount == 0);
-    } /* parse file, store returned info in the xkb struct */
-    else if (ProcessIncludeFile(keymap->ctx, stmt, FILE_TYPE_KEYCODES, &rtrn,
-                                &newMerge))
-    {
-        InitKeyNamesInfo(&included, rtrn->id);
-        HandleKeycodesFile(rtrn, keymap, MERGE_OVERRIDE, &included);
-        if (stmt->stmt != NULL)
-        {
-            free(included.name);
-            included.name = stmt->stmt;
-            stmt->stmt = NULL;
-        }
-        FreeXKBFile(rtrn);
-    }
-    else
-    {
-        info->errorCount += 10; /* XXX: why 10?? */
-        return false;
-    }
-    /* Do we have more than one include statement? */
-    if ((stmt->next != NULL) && (included.errorCount < 1))
-    {
-        IncludeStmt *next;
-        unsigned op;
+    for (IncludeStmt *stmt = include; stmt; stmt = stmt->next_incl) {
         KeyNamesInfo next_incl;
+        XkbFile *file;
 
-        for (next = stmt->next; next != NULL; next = next->next)
-        {
-            if ((next->file == NULL) && (next->map == NULL))
-            {
-                haveSelf = true;
-                MergeIncludedKeycodes(&included, keymap, info, next->merge);
-                ClearKeyNamesInfo(info);
-            }
-            else if (ProcessIncludeFile(keymap->ctx, next, FILE_TYPE_KEYCODES,
-                                        &rtrn, &op))
-            {
-                InitKeyNamesInfo(&next_incl, rtrn->id);
-                HandleKeycodesFile(rtrn, keymap, MERGE_OVERRIDE, &next_incl);
-                MergeIncludedKeycodes(&included, keymap, &next_incl, op);
-                ClearKeyNamesInfo(&next_incl);
-                FreeXKBFile(rtrn);
-            }
-            else
-            {
-                info->errorCount += 10; /* XXX: Why 10?? */
-                ClearKeyNamesInfo(&included);
-                return false;
-            }
+        file = ProcessIncludeFile(info->ctx, stmt, FILE_TYPE_KEYCODES);
+        if (!file) {
+            info->errorCount += 10;
+            ClearKeyNamesInfo(&included);
+            return false;
         }
+
+        InitKeyNamesInfo(&next_incl, info->ctx);
+
+        HandleKeycodesFile(&next_incl, file, MERGE_OVERRIDE);
+
+        MergeIncludedKeycodes(&included, &next_incl, stmt->merge);
+
+        ClearKeyNamesInfo(&next_incl);
+        FreeXkbFile(file);
     }
-    if (haveSelf)
-        *info = included;
-    else
-    {
-        MergeIncludedKeycodes(info, keymap, &included, newMerge);
-        ClearKeyNamesInfo(&included);
-    }
+
+    MergeIncludedKeycodes(info, &included, include->merge);
+    ClearKeyNamesInfo(&included);
+
     return (info->errorCount == 0);
 }
 
-/**
- * Parse the given statement and store the output in the info struct.
- * e.g. <ESC> = 9
- */
-static int
-HandleKeycodeDef(KeycodeDef *stmt, enum merge_mode merge, KeyNamesInfo *info)
+static bool
+HandleKeycodeDef(KeyNamesInfo *info, KeycodeDef *stmt, enum merge_mode merge)
 {
-    if ((info->explicitMin != 0 && stmt->value < info->explicitMin) ||
-        (info->explicitMax != 0 && stmt->value > info->explicitMax))
-    {
-        ERROR("Illegal keycode %lu for name <%s>\n", stmt->value, stmt->name);
-        ACTION("Must be in the range %d-%d inclusive\n",
-                info->explicitMin,
-                info->explicitMax ? info->explicitMax : XKB_KEYCODE_MAX);
-        return 0;
-    }
-    if (stmt->merge != MERGE_DEFAULT)
-    {
+    if (stmt->merge != MERGE_DEFAULT) {
         if (stmt->merge == MERGE_REPLACE)
             merge = MERGE_OVERRIDE;
         else
             merge = stmt->merge;
     }
-    return AddKeyName(info, stmt->value, stmt->name, merge, info->file_id,
-                      true);
-}
 
-#define        MIN_KEYCODE_DEF         0
-#define        MAX_KEYCODE_DEF         1
+    if (stmt->value < 0 || stmt->value > XKB_KEYCODE_MAX) {
+        log_err(info->ctx,
+                "Illegal keycode %lld: must be between 0..%u; "
+                "Key ignored\n", (long long) stmt->value, XKB_KEYCODE_MAX);
+        return false;
+    }
 
-/**
- * Handle the minimum/maximum statement of the xkb file.
- * Sets explicitMin/Max of the info struct.
- *
- * @return 1 on success, 0 otherwise.
- */
-static int
-HandleKeyNameVar(VarDef *stmt, struct xkb_keymap *keymap, KeyNamesInfo *info)
+    return AddKeyName(info, (xkb_keycode_t) stmt->value,
+                      stmt->name, merge, false, true);
+}
+
+static bool
+HandleAliasDef(KeyNamesInfo *info, KeyAliasDef *def, enum merge_mode merge)
 {
-    ExprResult tmp, field;
-    ExprDef *arrayNdx;
-    int which;
+    AliasInfo *old, new;
+
+    darray_foreach(old, info->aliases) {
+        if (old->alias == def->alias) {
+            if (def->real == old->real) {
+                log_vrb(info->ctx, 1,
+                        "Alias of %s for %s declared more than once; "
+                        "First definition ignored\n",
+                        KeyNameText(info->ctx, def->alias),
+                        KeyNameText(info->ctx, def->real));
+            }
+            else {
+                xkb_atom_t use, ignore;
 
-    if (ExprResolveLhs(keymap, stmt->name, &tmp, &field, &arrayNdx) == 0)
-        return 0;               /* internal error, already reported */
+                use = (merge == MERGE_AUGMENT ? old->real : def->real);
+                ignore = (merge == MERGE_AUGMENT ? def->real : old->real);
 
-    if (tmp.str != NULL)
-    {
-        ERROR("Unknown element %s encountered\n", tmp.str);
-        ACTION("Default for field %s ignored\n", field.str);
-        goto err_out;
-    }
-    if (strcasecmp(field.str, "minimum") == 0)
-        which = MIN_KEYCODE_DEF;
-    else if (strcasecmp(field.str, "maximum") == 0)
-        which = MAX_KEYCODE_DEF;
-    else
-    {
-        ERROR("Unknown field encountered\n");
-        ACTION("Assigment to field %s ignored\n", field.str);
-        goto err_out;
-    }
-    if (arrayNdx != NULL)
-    {
-        ERROR("The %s setting is not an array\n", field.str);
-        ACTION("Illegal array reference ignored\n");
-        goto err_out;
-    }
+                log_warn(info->ctx,
+                         "Multiple definitions for alias %s; "
+                         "Using %s, ignoring %s\n",
+                         KeyNameText(info->ctx, old->alias),
+                         KeyNameText(info->ctx, use),
+                         KeyNameText(info->ctx, ignore));
 
-    if (ExprResolveKeyCode(keymap->ctx, stmt->value, &tmp) == 0)
-    {
-        ACTION("Assignment to field %s ignored\n", field.str);
-        goto err_out;
-    }
-    if (tmp.uval > XKB_KEYCODE_MAX)
-    {
-        ERROR
-            ("Illegal keycode %d (must be in the range %d-%d inclusive)\n",
-             tmp.uval, 0, XKB_KEYCODE_MAX);
-        ACTION("Value of \"%s\" not changed\n", field.str);
-        goto err_out;
-    }
-    if (which == MIN_KEYCODE_DEF)
-    {
-        if ((info->explicitMax > 0) && (info->explicitMax < tmp.uval))
-        {
-            ERROR
-                ("Minimum key code (%d) must be <= maximum key code (%d)\n",
-                 tmp.uval, info->explicitMax);
-            ACTION("Minimum key code value not changed\n");
-            goto err_out;
-        }
-        if ((info->computedMax > 0) && (info->computedMin < tmp.uval))
-        {
-            ERROR
-                ("Minimum key code (%d) must be <= lowest defined key (%d)\n",
-                 tmp.uval, info->computedMin);
-            ACTION("Minimum key code value not changed\n");
-            goto err_out;
+                old->real = use;
+            }
+
+            old->merge = merge;
+            return true;
         }
-        info->explicitMin = tmp.uval;
     }
-    if (which == MAX_KEYCODE_DEF)
-    {
-        if ((info->explicitMin > 0) && (info->explicitMin > tmp.uval))
-        {
-            ERROR("Maximum code (%d) must be >= minimum key code (%d)\n",
-                   tmp.uval, info->explicitMin);
-            ACTION("Maximum code value not changed\n");
-            goto err_out;
-        }
-        if ((info->computedMax > 0) && (info->computedMax > tmp.uval))
-        {
-            ERROR
-                ("Maximum code (%d) must be >= highest defined key (%d)\n",
-                 tmp.uval, info->computedMax);
-            ACTION("Maximum code value not changed\n");
-            goto err_out;
-        }
-        info->explicitMax = tmp.uval;
+
+    InitAliasInfo(&new, merge, def->alias, def->real);
+    darray_append(info->aliases, new);
+    return true;
+}
+
+static bool
+HandleKeyNameVar(KeyNamesInfo *info, VarDef *stmt)
+{
+    const char *elem, *field;
+    ExprDef *arrayNdx;
+
+    if (!ExprResolveLhs(info->ctx, stmt->name, &elem, &field, &arrayNdx))
+        return false;
+
+    if (elem) {
+        log_err(info->ctx, "Unknown element %s encountered; "
+                "Default for field %s ignored\n", elem, field);
+        return false;
     }
 
-    free(field.str);
-    return 1;
+    if (!istreq(field, "minimum") && !istreq(field, "maximum")) {
+        log_err(info->ctx, "Unknown field encountered; "
+                "Assignment to field %s ignored\n", field);
+        return false;
+    }
 
-err_out:
-    free(field.str);
-    return 0;
+    /* We ignore explicit min/max statements, we always use computed. */
+    return true;
 }
 
-static int
-HandleIndicatorNameDef(IndicatorNameDef *def, struct xkb_keymap *keymap,
-                       enum merge_mode merge, KeyNamesInfo *info)
+static bool
+HandleLedNameDef(KeyNamesInfo *info, LedNameDef *def,
+                 enum merge_mode merge)
 {
-    IndicatorNameInfo ii;
-    ExprResult tmp;
+    LedNameInfo ledi;
+    xkb_atom_t name;
 
-    if ((def->ndx < 1) || (def->ndx > XkbNumIndicators))
-    {
+    if (def->ndx < 1 || def->ndx > XKB_MAX_LEDS) {
         info->errorCount++;
-        ERROR("Name specified for illegal indicator index %d\n", def->ndx);
-        ACTION("Ignored\n");
+        log_err(info->ctx,
+                "Illegal indicator index (%d) specified; must be between 1 .. %d; "
+                "Ignored\n", def->ndx, XKB_MAX_LEDS);
         return false;
     }
-    InitIndicatorNameInfo(&ii, info);
-    ii.ndx = def->ndx;
-    if (!ExprResolveString(keymap->ctx, def->name, &tmp))
-    {
+
+    if (!ExprResolveString(info->ctx, def->name, &name)) {
         char buf[20];
-        snprintf(buf, sizeof(buf), "%d", def->ndx);
+        snprintf(buf, sizeof(buf), "%u", def->ndx);
         info->errorCount++;
-        return ReportBadType("indicator", "name", buf, "string");
+        return ReportBadType(info->ctx, "indicator", "name", buf, "string");
     }
-    ii.name = xkb_atom_intern(keymap->ctx, tmp.str);
-    free(tmp.str);
-    ii.virtual = def->virtual;
-    if (!AddIndicatorName(info, keymap, merge, &ii))
-        return false;
-    return true;
+
+    ledi.merge = merge;
+    ledi.name = name;
+    return AddLedName(info, merge, true, &ledi, def->ndx - 1);
 }
 
-/**
- * Handle the xkb_keycodes section of a xkb file.
- * All information about parsed keys is stored in the info struct.
- *
- * Such a section may have include statements, in which case this function is
- * semi-recursive (it calls HandleIncludeKeycodes, which may call
- * HandleKeycodesFile again).
- *
- * @param file The input file (parsed xkb_keycodes section)
- * @param xkb Necessary to pass down, may have flags changed.
- * @param merge Merge strategy (MERGE_OVERRIDE, etc.)
- * @param info Struct to contain the fully parsed key information.
- */
 static void
-HandleKeycodesFile(XkbFile *file, struct xkb_keymap *keymap,
-                   enum merge_mode merge, KeyNamesInfo *info)
+HandleKeycodesFile(KeyNamesInfo *info, XkbFile *file, enum merge_mode merge)
 {
-    ParseCommon *stmt;
+    bool ok;
 
     free(info->name);
-    info->name = uDupString(file->name);
-    stmt = file->defs;
-    while (stmt)
-    {
-        switch (stmt->stmtType)
-        {
-        case StmtInclude:    /* e.g. include "evdev+aliases(qwerty)" */
-            if (!HandleIncludeKeycodes((IncludeStmt *) stmt, keymap, info))
-                info->errorCount++;
+    info->name = strdup_safe(file->name);
+
+    for (ParseCommon *stmt = file->defs; stmt; stmt = stmt->next) {
+        switch (stmt->type) {
+        case STMT_INCLUDE:
+            ok = HandleIncludeKeycodes(info, (IncludeStmt *) stmt);
             break;
-        case StmtKeycodeDef: /* e.g. <ESC> = 9; */
-            if (!HandleKeycodeDef((KeycodeDef *) stmt, merge, info))
-                info->errorCount++;
+        case STMT_KEYCODE:
+            ok = HandleKeycodeDef(info, (KeycodeDef *) stmt, merge);
             break;
-        case StmtKeyAliasDef: /* e.g. alias <MENU> = <COMP>; */
-            if (!HandleAliasDef((KeyAliasDef *) stmt, merge, info->file_id,
-                                &info->aliases))
-                info->errorCount++;
+        case STMT_ALIAS:
+            ok = HandleAliasDef(info, (KeyAliasDef *) stmt, merge);
             break;
-        case StmtVarDef: /* e.g. minimum, maximum */
-            if (!HandleKeyNameVar((VarDef *) stmt, keymap, info))
-                info->errorCount++;
+        case STMT_VAR:
+            ok = HandleKeyNameVar(info, (VarDef *) stmt);
             break;
-        case StmtIndicatorNameDef: /* e.g. indicator 1 = "Caps Lock"; */
-            if (!HandleIndicatorNameDef((IndicatorNameDef *) stmt, keymap,
-                                        merge, info))
-                info->errorCount++;
-            break;
-        case StmtInterpDef:
-        case StmtVModDef:
-            ERROR("Keycode files may define key and indicator names only\n");
-            ACTION("Ignoring definition of %s\n",
-                    ((stmt->stmtType ==
-                      StmtInterpDef) ? "a symbol interpretation" :
-                     "virtual modifiers"));
-            info->errorCount++;
+        case STMT_LED_NAME:
+            ok = HandleLedNameDef(info, (LedNameDef *) stmt, merge);
             break;
         default:
-            WSGO("Unexpected statement type %d in HandleKeycodesFile\n",
-                  stmt->stmtType);
+            log_err(info->ctx,
+                    "Keycode files may define key and indicator names only; "
+                    "Ignoring %s\n", stmt_type_to_string(stmt->type));
+            ok = false;
             break;
         }
-        stmt = stmt->next;
-        if (info->errorCount > 10)
-        {
-#ifdef NOISY
-            ERROR("Too many errors\n");
-#endif
-            ACTION("Abandoning keycodes file \"%s\"\n", file->topName);
+
+        if (!ok)
+            info->errorCount++;
+
+        if (info->errorCount > 10) {
+            log_err(info->ctx, "Abandoning keycodes file \"%s\"\n",
+                    file->name);
             break;
         }
     }
 }
 
-/**
- * Compile the xkb_keycodes section, parse it's output, return the results.
- *
- * @param file The parsed XKB file (may have include statements requiring
- * further parsing)
- * @param result The effective keycodes, as gathered from the file.
- * @param merge Merge strategy.
- *
- * @return true on success, false otherwise.
- */
-bool
-CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap, enum merge_mode merge)
+/***====================================================================***/
+
+static bool
+CopyKeyNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info)
 {
-    KeyNamesInfo info; /* contains all the info after parsing */
+    struct xkb_key *keys;
+    xkb_keycode_t min_key_code, max_key_code, kc;
 
-    InitKeyNamesInfo(&info, file->id);
+    min_key_code = info->min_key_code;
+    max_key_code = info->max_key_code;
+    /* If the keymap has no keys, let's just use the safest pair we know. */
+    if (min_key_code == XKB_KEYCODE_INVALID) {
+        min_key_code = 8;
+        max_key_code = 255;
+    }
 
-    HandleKeycodesFile(file, keymap, merge, &info);
+    keys = calloc(max_key_code + 1, sizeof(*keys));
+    if (!keys)
+        return false;
 
-    /* all the keys are now stored in info */
+    for (kc = min_key_code; kc <= max_key_code; kc++)
+        keys[kc].keycode = kc;
 
-    if (info.errorCount != 0)
-        goto err_info;
+    for (kc = info->min_key_code; kc <= info->max_key_code; kc++)
+        keys[kc].name = darray_item(info->key_names, kc);
 
-    if (info.explicitMin > 0) /* if "minimum" statement was present */
-        keymap->min_key_code = info.explicitMin;
-    else
-        keymap->min_key_code = info.computedMin;
-
-    if (info.explicitMax > 0) /* if "maximum" statement was present */
-        keymap->max_key_code = info.explicitMax;
-    else
-        keymap->max_key_code = info.computedMax;
-
-    if (XkbcAllocNames(keymap, XkbKeyNamesMask | XkbIndicatorNamesMask, 0)
-        == Success) {
-        uint64_t i;
-        for (i = info.computedMin; i <= info.computedMax; i++)
-            LongToKeyName(darray_item(info.names, i),
-                          darray_item(keymap->names->keys, i).name);
-        if (info.name)
-            keymap->names->keycodes = strdup(info.name);
-    } else {
-        WSGO("Cannot create struct xkb_names in CompileKeycodes\n");
-        goto err_info;
-    }
+    keymap->min_key_code = min_key_code;
+    keymap->max_key_code = max_key_code;
+    keymap->keys = keys;
+    return true;
+}
 
-    if (info.leds) {
-        IndicatorNameInfo *ii;
-        if (XkbcAllocIndicatorMaps(keymap) != Success) {
-            WSGO("Couldn't allocate IndicatorRec in CompileKeycodes\n");
-            ACTION("Physical indicators not set\n");
+static bool
+CopyKeyAliasesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info)
+{
+    AliasInfo *alias;
+    unsigned i, num_key_aliases;
+    struct xkb_key_alias *key_aliases;
+
+    /*
+     * Do some sanity checking on the aliases. We can't do it before
+     * because keys and their aliases may be added out-of-order.
+     */
+    num_key_aliases = 0;
+    darray_foreach(alias, info->aliases) {
+        /* Check that ->real is a key. */
+        if (!XkbKeyByName(keymap, alias->real, false)) {
+            log_vrb(info->ctx, 5,
+                    "Attempt to alias %s to non-existent key %s; Ignored\n",
+                    KeyNameText(info->ctx, alias->alias),
+                    KeyNameText(info->ctx, alias->real));
+            alias->real = XKB_ATOM_NONE;
+            continue;
         }
 
-        for (ii = info.leds; ii; ii = (IndicatorNameInfo *)ii->defs.next) {
-            free(keymap->names->indicators[ii->ndx - 1]);
-            keymap->names->indicators[ii->ndx - 1] =
-                xkb_atom_strdup(keymap->ctx, ii->name);
+        /* Check that ->alias is not a key. */
+        if (XkbKeyByName(keymap, alias->alias, false)) {
+            log_vrb(info->ctx, 5,
+                    "Attempt to create alias with the name of a real key; "
+                    "Alias \"%s = %s\" ignored\n",
+                    KeyNameText(info->ctx, alias->alias),
+                    KeyNameText(info->ctx, alias->real));
+            alias->real = XKB_ATOM_NONE;
+            continue;
         }
+
+        num_key_aliases++;
     }
 
-    ApplyAliases(keymap, &info.aliases);
+    /* Copy key aliases. */
+    key_aliases = NULL;
+    if (num_key_aliases > 0) {
+        key_aliases = calloc(num_key_aliases, sizeof(*key_aliases));
+        if (!key_aliases)
+            return false;
+
+        i = 0;
+        darray_foreach(alias, info->aliases) {
+            if (alias->real != XKB_ATOM_NONE) {
+                key_aliases[i].alias = alias->alias;
+                key_aliases[i].real = alias->real;
+                i++;
+            }
+        }
+    }
+
+    keymap->num_key_aliases = num_key_aliases;
+    keymap->key_aliases = key_aliases;
+    return true;
+}
+
+static bool
+CopyLedNamesToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info)
+{
+    keymap->num_leds = info->num_led_names;
+    for (xkb_led_index_t idx = 0; idx < info->num_led_names; idx++) {
+        LedNameInfo *ledi = &info->led_names[idx];
+
+        if (ledi->name == XKB_ATOM_NONE)
+            continue;
+
+        keymap->leds[idx].name = ledi->name;
+    }
+
+    return true;
+}
+
+static bool
+CopyKeyNamesInfoToKeymap(struct xkb_keymap *keymap, KeyNamesInfo *info)
+{
+    /* This function trashes keymap on error, but that's OK. */
+    if (!CopyKeyNamesToKeymap(keymap, info) ||
+        !CopyKeyAliasesToKeymap(keymap, info) ||
+        !CopyLedNamesToKeymap(keymap, info))
+        return false;
+
+    keymap->keycodes_section_name = strdup_safe(info->name);
+    XkbEscapeMapName(keymap->keycodes_section_name);
+    return true;
+}
+
+/***====================================================================***/
+
+bool
+CompileKeycodes(XkbFile *file, struct xkb_keymap *keymap,
+                enum merge_mode merge)
+{
+    KeyNamesInfo info;
+
+    InitKeyNamesInfo(&info, keymap->ctx);
+
+    HandleKeycodesFile(&info, file, merge);
+    if (info.errorCount != 0)
+        goto err_info;
+
+    if (!CopyKeyNamesInfoToKeymap(keymap, &info))
+        goto err_info;
 
     ClearKeyNamesInfo(&info);
     return true;