Pass around xkb_key's instead of keycodes
[platform/upstream/libxkbcommon.git] / src / xkbcomp / symbols.c
index e6f8022..d766cd0 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 "xkbcomp.h"
-#include "xkballoc.h"
-#include "xkbmisc.h"
-#include "expr.h"
-#include "parseutils.h"
-
-#include <X11/keysym.h>
-#include <stdlib.h>
+#include <limits.h>
 
-#include "expr.h"
-#include "vmod.h"
+#include "xkbcomp-priv.h"
+#include "parseutils.h"
 #include "action.h"
-#include "keycodes.h"
-#include "misc.h"
 #include "alias.h"
-
-extern uint32_t tok_ONE_LEVEL;
-extern uint32_t tok_TWO_LEVEL;
-extern uint32_t tok_KEYPAD;
+#include "keycodes.h"
+#include "vmod.h"
 
 /***====================================================================***/
 
-#define        RepeatYes       1
-#define        RepeatNo        0
-#define        RepeatUndefined ~((unsigned)0)
+/* Needed to work with the typechecker. */
+typedef darray (xkb_keysym_t) darray_xkb_keysym_t;
+typedef darray (union xkb_action) darray_xkb_action;
 
-#define        _Key_Syms       (1<<0)
-#define        _Key_Acts       (1<<1)
-#define        _Key_Repeat     (1<<2)
-#define        _Key_Behavior   (1<<3)
-#define        _Key_Type_Dflt  (1<<4)
-#define        _Key_Types      (1<<5)
-#define        _Key_GroupInfo  (1<<6)
-#define        _Key_VModMap    (1<<7)
+#define RepeatYes       1
+#define RepeatNo        0
+#define RepeatUndefined ~((unsigned) 0)
 
-typedef struct _KeyInfo
-{
+#define _Key_Syms       (1 << 0)
+#define _Key_Acts       (1 << 1)
+#define _Key_Repeat     (1 << 2)
+#define _Key_Behavior   (1 << 3)
+#define _Key_Type_Dflt  (1 << 4)
+#define _Key_Types      (1 << 5)
+#define _Key_GroupInfo  (1 << 6)
+#define _Key_VModMap    (1 << 7)
+
+typedef struct _KeyInfo {
     CommonInfo defs;
     unsigned long name; /* the 4 chars of the key name, as long */
-    unsigned char groupInfo;
     unsigned char typesDefined;
     unsigned char symsDefined;
     unsigned char actsDefined;
-    short numLevels[XkbNumKbdGroups];
-    uint32_t *syms[XkbNumKbdGroups];
-    union xkb_action *acts[XkbNumKbdGroups];
-    uint32_t types[XkbNumKbdGroups];
+    unsigned int numLevels[XkbNumKbdGroups];
+
+    /* syms[group] -> Single array for all the keysyms in the group. */
+    darray_xkb_keysym_t syms[XkbNumKbdGroups];
+    /*
+     * symsMapIndex[group][level] -> The index from which the syms for
+     * the level begin in the syms[group] array. Remember each keycode
+     * can have multiple keysyms in each level (that is, each key press
+     * can result in multiple keysyms).
+     */
+    darray(int) symsMapIndex[XkbNumKbdGroups];
+    /*
+     * symsMapNumEntries[group][level] -> How many syms are in
+     * syms[group][symsMapIndex[group][level]].
+     */
+    darray(size_t) symsMapNumEntries[XkbNumKbdGroups];
+
+    darray_xkb_action acts[XkbNumKbdGroups];
+
+    xkb_atom_t types[XkbNumKbdGroups];
     unsigned repeat;
     struct xkb_behavior behavior;
     unsigned short vmodmap;
-    unsigned long nameForOverlayKey;
-    unsigned long allowNone;
-    uint32_t dfltType;
+    xkb_atom_t dfltType;
+
+    uint8_t out_of_range_group_action;
+    uint8_t out_of_range_group_number;
 } KeyInfo;
 
 /**
  * Init the given key info to sane values.
  */
 static void
-InitKeyInfo(KeyInfo * info)
+InitKeyInfo(KeyInfo *keyi, unsigned file_id)
 {
     int i;
-    static char dflt[4] = "*";
-
-    info->defs.defined = 0;
-    info->defs.fileID = 0;
-    info->defs.merge = MergeOverride;
-    info->defs.next = NULL;
-    info->name = KeyNameToLong(dflt);
-    info->groupInfo = 0;
-    info->typesDefined = info->symsDefined = info->actsDefined = 0;
-    for (i = 0; i < XkbNumKbdGroups; i++)
-    {
-        info->numLevels[i] = 0;
-        info->types[i] = None;
-        info->syms[i] = NULL;
-        info->acts[i] = NULL;
-    }
-    info->dfltType = None;
-    info->behavior.type = XkbKB_Default;
-    info->behavior.data = 0;
-    info->vmodmap = 0;
-    info->nameForOverlayKey = 0;
-    info->repeat = RepeatUndefined;
-    info->allowNone = 0;
+    static const char dflt[4] = "*";
+
+    keyi->defs.defined = 0;
+    keyi->defs.file_id = file_id;
+    keyi->defs.merge = MERGE_OVERRIDE;
+    keyi->defs.next = NULL;
+    keyi->name = KeyNameToLong(dflt);
+    keyi->typesDefined = keyi->symsDefined = keyi->actsDefined = 0;
+
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        keyi->numLevels[i] = 0;
+        keyi->types[i] = XKB_ATOM_NONE;
+        darray_init(keyi->syms[i]);
+        darray_init(keyi->symsMapIndex[i]);
+        darray_init(keyi->symsMapNumEntries[i]);
+        darray_init(keyi->acts[i]);
+    }
+
+    keyi->dfltType = XKB_ATOM_NONE;
+    keyi->behavior.type = XkbKB_Default;
+    keyi->behavior.data = 0;
+    keyi->vmodmap = 0;
+    keyi->repeat = RepeatUndefined;
+    keyi->out_of_range_group_action = 0;
+    keyi->out_of_range_group_number = 0;
 }
 
-/**
- * Free memory associated with this key info and reset to sane values.
- */
 static void
-FreeKeyInfo(KeyInfo * info)
+FreeKeyInfo(KeyInfo *keyi)
 {
     int i;
 
-    info->defs.defined = 0;
-    info->defs.fileID = 0;
-    info->defs.merge = MergeOverride;
-    info->defs.next = NULL;
-    info->groupInfo = 0;
-    info->typesDefined = info->symsDefined = info->actsDefined = 0;
-    for (i = 0; i < XkbNumKbdGroups; i++)
-    {
-        info->numLevels[i] = 0;
-        info->types[i] = None;
-        free(info->syms[i]);
-        info->syms[i] = NULL;
-        free(info->acts[i]);
-        info->acts[i] = NULL;
-    }
-    info->dfltType = None;
-    info->behavior.type = XkbKB_Default;
-    info->behavior.data = 0;
-    info->vmodmap = 0;
-    info->nameForOverlayKey = 0;
-    info->repeat = RepeatUndefined;
-    info->allowNone = 0;
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        darray_free(keyi->syms[i]);
+        darray_free(keyi->symsMapIndex[i]);
+        darray_free(keyi->symsMapNumEntries[i]);
+        darray_free(keyi->acts[i]);
+    }
 }
 
 /**
@@ -148,113 +139,81 @@ FreeKeyInfo(KeyInfo * info)
  * If old is reset, new simply re-uses old's memory. Otherwise, the memory is
  * newly allocated and new points to the new memory areas.
  */
-static Bool
-CopyKeyInfo(KeyInfo * old, KeyInfo * new, Bool clearOld)
+static bool
+CopyKeyInfo(KeyInfo * old, KeyInfo * new, bool clearOld)
 {
     int i;
 
     *new = *old;
     new->defs.next = NULL;
-    if (clearOld)
-    {
-        for (i = 0; i < XkbNumKbdGroups; i++)
-        {
+
+    if (clearOld) {
+        for (i = 0; i < XkbNumKbdGroups; i++) {
             old->numLevels[i] = 0;
-            old->syms[i] = NULL;
-            old->acts[i] = NULL;
+            darray_init(old->symsMapIndex[i]);
+            darray_init(old->symsMapNumEntries[i]);
+            darray_init(old->syms[i]);
+            darray_init(old->acts[i]);
         }
     }
-    else
-    {
-        int width;
-        for (i = 0; i < XkbNumKbdGroups; i++)
-        {
-            width = new->numLevels[i];
-            if (old->syms[i] != NULL)
-            {
-                new->syms[i] = uTypedCalloc(width, uint32_t);
-                if (!new->syms[i])
-                {
-                    new->syms[i] = NULL;
-                    new->numLevels[i] = 0;
-                    return False;
-                }
-                memcpy(new->syms[i], old->syms[i], width * sizeof(uint32_t));
-            }
-            if (old->acts[i] != NULL)
-            {
-                new->acts[i] = uTypedCalloc(width, union xkb_action);
-                if (!new->acts[i])
-                {
-                    new->acts[i] = NULL;
-                    return False;
-                }
-                memcpy(new->acts[i], old->acts[i],
-                       width * sizeof(union xkb_action));
-            }
+    else {
+        for (i = 0; i < XkbNumKbdGroups; i++) {
+            darray_copy(new->syms[i], old->syms[i]);
+            darray_copy(new->symsMapIndex[i], old->symsMapIndex[i]);
+            darray_copy(new->symsMapNumEntries[i], old->symsMapNumEntries[i]);
+            darray_copy(new->acts[i], old->acts[i]);
         }
     }
-    return True;
+
+    return true;
 }
 
 /***====================================================================***/
 
-typedef struct _ModMapEntry
-{
+typedef struct _ModMapEntry {
     CommonInfo defs;
-    Bool haveSymbol;
+    bool haveSymbol;
     int modifier;
-    union
-    {
+    union {
         unsigned long keyName;
-        uint32_t keySym;
+        xkb_keysym_t keySym;
     } u;
 } ModMapEntry;
 
-#define        SYMBOLS_INIT_SIZE       110
-#define        SYMBOLS_CHUNK           20
-typedef struct _SymbolsInfo
-{
+typedef struct _SymbolsInfo {
     char *name;         /* e.g. pc+us+inet(evdev) */
     int errorCount;
-    unsigned fileID;
-    unsigned merge;
+    unsigned file_id;
+    enum merge_mode merge;
     unsigned explicit_group;
-    unsigned groupInfo;
-    unsigned szKeys;
-    unsigned nKeys;
-    KeyInfo *keys;
+    darray(KeyInfo) keys;
     KeyInfo dflt;
     VModInfo vmods;
     ActionInfo *action;
-    uint32_t groupNames[XkbNumKbdGroups];
+    xkb_atom_t groupNames[XkbNumKbdGroups];
 
     ModMapEntry *modMap;
     AliasInfo *aliases;
 } SymbolsInfo;
 
 static void
-InitSymbolsInfo(SymbolsInfo * info, struct xkb_desc * xkb)
+InitSymbolsInfo(SymbolsInfo * info, struct xkb_keymap *keymap,
+                unsigned file_id)
 {
     int i;
 
-    tok_ONE_LEVEL = xkb_intern_atom("ONE_LEVEL");
-    tok_TWO_LEVEL = xkb_intern_atom("TWO_LEVEL");
-    tok_KEYPAD = xkb_intern_atom("KEYPAD");
     info->name = NULL;
     info->explicit_group = 0;
     info->errorCount = 0;
-    info->fileID = 0;
-    info->merge = MergeOverride;
-    info->groupInfo = 0;
-    info->szKeys = SYMBOLS_INIT_SIZE;
-    info->nKeys = 0;
-    info->keys = uTypedCalloc(SYMBOLS_INIT_SIZE, KeyInfo);
+    info->file_id = file_id;
+    info->merge = MERGE_OVERRIDE;
+    darray_init(info->keys);
+    darray_growalloc(info->keys, 110);
     info->modMap = NULL;
     for (i = 0; i < XkbNumKbdGroups; i++)
-        info->groupNames[i] = None;
-    InitKeyInfo(&info->dflt);
-    InitVModInfo(&info->vmods, xkb);
+        info->groupNames[i] = XKB_ATOM_NONE;
+    InitKeyInfo(&info->dflt, file_id);
+    InitVModInfo(&info->vmods, keymap);
     info->action = NULL;
     info->aliases = NULL;
 }
@@ -262,15 +221,13 @@ InitSymbolsInfo(SymbolsInfo * info, struct xkb_desc * xkb)
 static void
 FreeSymbolsInfo(SymbolsInfo * info)
 {
-    int i;
+    KeyInfo *keyi;
 
     free(info->name);
-    if (info->keys)
-    {
-        for (i = 0; i < info->nKeys; i++)
-            FreeKeyInfo(&info->keys[i]);
-        free(info->keys);
+    darray_foreach(keyi, info->keys) {
+        FreeKeyInfo(keyi);
     }
+    darray_free(info->keys);
     if (info->modMap)
         ClearCommonInfo(&info->modMap->defs);
     if (info->aliases)
@@ -278,458 +235,477 @@ FreeSymbolsInfo(SymbolsInfo * info)
     memset(info, 0, sizeof(SymbolsInfo));
 }
 
-static Bool
-ResizeKeyGroup(KeyInfo * key,
-               unsigned group, unsigned atLeastSize, Bool forceActions)
+static bool
+ResizeKeyGroup(KeyInfo *keyi, unsigned int group, unsigned int numLevels,
+               unsigned sizeSyms, bool forceActions)
 {
-    Bool tooSmall;
-    unsigned newWidth;
+    int i;
 
-    tooSmall = (key->numLevels[group] < atLeastSize);
-    if (tooSmall)
-        newWidth = atLeastSize;
-    else
-        newWidth = key->numLevels[group];
+    if (darray_size(keyi->syms[group]) < sizeSyms)
+        darray_resize0(keyi->syms[group], sizeSyms);
 
-    if ((key->syms[group] == NULL) || tooSmall)
-    {
-        key->syms[group] = uTypedRecalloc(key->syms[group],
-                                          key->numLevels[group], newWidth,
-                                          uint32_t);
-        if (!key->syms[group])
-            return False;
-    }
-    if (((forceActions) && (tooSmall || (key->acts[group] == NULL))) ||
-        (tooSmall && (key->acts[group] != NULL)))
-    {
-        key->acts[group] = uTypedRecalloc(key->acts[group],
-                                          key->numLevels[group], newWidth,
-                                          union xkb_action);
-        if (!key->acts[group])
-            return False;
-    }
-    key->numLevels[group] = newWidth;
-    return True;
+    if (darray_empty(keyi->symsMapIndex[group]) ||
+        keyi->numLevels[group] < numLevels) {
+        darray_resize(keyi->symsMapIndex[group], numLevels);
+        for (i = keyi->numLevels[group]; i < numLevels; i++)
+            darray_item(keyi->symsMapIndex[group], i) = -1;
+    }
+
+    if (darray_empty(keyi->symsMapNumEntries[group]) ||
+        keyi->numLevels[group] < numLevels)
+        darray_resize0(keyi->symsMapNumEntries[group], numLevels);
+
+    if ((forceActions && (keyi->numLevels[group] < numLevels ||
+                          darray_empty(keyi->acts[group]))) ||
+        (keyi->numLevels[group] < numLevels && !darray_empty(keyi->acts[group])))
+        darray_resize0(keyi->acts[group], numLevels);
+
+    if (keyi->numLevels[group] < numLevels)
+        keyi->numLevels[group] = numLevels;
+
+    return true;
 }
 
-static Bool
+enum key_group_selector {
+    NONE = 0,
+    FROM = (1 << 0),
+    TO = (1 << 1),
+};
+
+static bool
 MergeKeyGroups(SymbolsInfo * info,
                KeyInfo * into, KeyInfo * from, unsigned group)
 {
-    uint32_t *resultSyms;
-    union xkb_action *resultActs;
-    int resultWidth;
+    darray_xkb_keysym_t resultSyms;
+    enum key_group_selector using = NONE;
+    darray_xkb_action resultActs;
+    unsigned int resultWidth;
+    unsigned int resultSize = 0;
+    int cur_idx = 0;
     int i;
-    Bool report, clobber;
+    bool report, clobber;
+
+    clobber = (from->defs.merge != MERGE_AUGMENT);
 
-    clobber = (from->defs.merge != MergeAugment);
     report = (warningLevel > 9) ||
-        ((into->defs.fileID == from->defs.fileID) && (warningLevel > 0));
-    if (into->numLevels[group] >= from->numLevels[group])
-    {
-        resultSyms = into->syms[group];
+             ((into->defs.file_id == from->defs.file_id) && (warningLevel > 0));
+
+    darray_init(resultSyms);
+
+    if (into->numLevels[group] >= from->numLevels[group]) {
         resultActs = into->acts[group];
         resultWidth = into->numLevels[group];
     }
-    else
-    {
-        resultSyms = from->syms[group];
+    else {
         resultActs = from->acts[group];
         resultWidth = from->numLevels[group];
+        darray_resize(into->symsMapIndex[group],
+                      from->numLevels[group]);
+        darray_resize0(into->symsMapNumEntries[group],
+                       from->numLevels[group]);
+
+        for (i = into->numLevels[group]; i < from->numLevels[group]; i++)
+            darray_item(into->symsMapIndex[group], i) = -1;
     }
-    if (resultSyms == NULL)
-    {
-        resultSyms = uTypedCalloc(resultWidth, uint32_t);
-        if (!resultSyms)
-        {
-            WSGO("Could not allocate symbols for group merge\n");
-            ACTION("Group %d of key %s not merged\n", group,
-                    longText(into->name));
-            return False;
-        }
-    }
-    if ((resultActs == NULL) && (into->acts[group] || from->acts[group]))
-    {
-        resultActs = uTypedCalloc(resultWidth, union xkb_action);
-        if (!resultActs)
-        {
-            WSGO("Could not allocate actions for group merge\n");
-            ACTION("Group %d of key %s not merged\n", group,
-                    longText(into->name));
-            return False;
-        }
-    }
-    for (i = 0; i < resultWidth; i++)
-    {
-        uint32_t fromSym, toSym;
-        if (from->syms[group] && (i < from->numLevels[group]))
-            fromSym = from->syms[group][i];
-        else
-            fromSym = NoSymbol;
-        if (into->syms[group] && (i < into->numLevels[group]))
-            toSym = into->syms[group][i];
-        else
-            toSym = NoSymbol;
-        if ((fromSym == NoSymbol) || (fromSym == toSym))
-            resultSyms[i] = toSym;
-        else if (toSym == NoSymbol)
-            resultSyms[i] = fromSym;
-        else
-        {
-            uint32_t use, ignore;
-            if (clobber)
-            {
-                use = fromSym;
-                ignore = toSym;
-            }
-            else
-            {
-                use = toSym;
-                ignore = fromSym;
-            }
-            if (report)
-            {
-                WARN
-                    ("Multiple symbols for level %d/group %d on key %s\n",
-                     i + 1, group + 1, longText(into->name));
-                ACTION("Using %s, ignoring %s\n",
-                        XkbcKeysymText(use), XkbcKeysymText(ignore));
-            }
-            resultSyms[i] = use;
-        }
-        if (resultActs != NULL)
-        {
-            union xkb_action *fromAct, *toAct;
-            fromAct = (from->acts[group] ? &from->acts[group][i] : NULL);
-            toAct = (into->acts[group] ? &into->acts[group][i] : NULL);
+
+    if (darray_empty(resultActs) && (!darray_empty(into->acts[group]) ||
+                                     !darray_empty(from->acts[group]))) {
+        darray_resize0(resultActs, resultWidth);
+        for (i = 0; i < resultWidth; i++) {
+            union xkb_action *fromAct = NULL, *toAct = NULL;
+
+            if (!darray_empty(from->acts[group]))
+                fromAct = &darray_item(from->acts[group], i);
+
+            if (!darray_empty(into->acts[group]))
+                toAct = &darray_item(into->acts[group], i);
+
             if (((fromAct == NULL) || (fromAct->type == XkbSA_NoAction))
-                && (toAct != NULL))
-            {
-                resultActs[i] = *toAct;
+                && (toAct != NULL)) {
+                darray_item(resultActs, i) = *toAct;
             }
             else if (((toAct == NULL) || (toAct->type == XkbSA_NoAction))
-                     && (fromAct != NULL))
-            {
-                resultActs[i] = *fromAct;
+                     && (fromAct != NULL)) {
+                darray_item(resultActs, i) = *fromAct;
             }
-            else
-            {
+            else {
                 union xkb_action *use, *ignore;
-                if (clobber)
-                {
+                if (clobber) {
                     use = fromAct;
                     ignore = toAct;
                 }
-                else
-                {
+                else {
                     use = toAct;
                     ignore = fromAct;
                 }
-                if (report)
-                {
+                if (report) {
                     WARN
                         ("Multiple actions for level %d/group %d on key %s\n",
-                         i + 1, group + 1, longText(into->name));
+                        i + 1, group + 1, longText(into->name));
                     ACTION("Using %s, ignoring %s\n",
-                            XkbcActionTypeText(use->type),
-                            XkbcActionTypeText(ignore->type));
+                           XkbcActionTypeText(use->type),
+                           XkbcActionTypeText(ignore->type));
                 }
                 if (use)
-                    resultActs[i] = *use;
+                    darray_item(resultActs, i) = *use;
             }
         }
     }
-    if (resultSyms != into->syms[group])
-        free(into->syms[group]);
-    if (resultSyms != from->syms[group])
-        free(from->syms[group]);
-    if (resultActs != into->acts[group])
-        free(into->acts[group]);
-    if (resultActs != from->acts[group])
-        free(from->acts[group]);
+
+    for (i = 0; i < resultWidth; i++) {
+        unsigned int fromSize = 0;
+        unsigned toSize = 0;
+
+        if (!darray_empty(from->symsMapNumEntries[group]) &&
+            i < from->numLevels[group])
+            fromSize = darray_item(from->symsMapNumEntries[group], i);
+
+        if (!darray_empty(into->symsMapNumEntries[group]) &&
+            i < into->numLevels[group])
+            toSize = darray_item(into->symsMapNumEntries[group], i);
+
+        if (fromSize == 0) {
+            resultSize += toSize;
+            using |= TO;
+        }
+        else if (toSize == 0 || clobber) {
+            resultSize += fromSize;
+            using |= FROM;
+        }
+        else {
+            resultSize += toSize;
+            using |= TO;
+        }
+    }
+
+    if (resultSize == 0)
+        goto out;
+
+    if (using == FROM) {
+        resultSyms = from->syms[group];
+        darray_free(into->symsMapNumEntries[group]);
+        darray_free(into->symsMapIndex[group]);
+        into->symsMapNumEntries[group] = from->symsMapNumEntries[group];
+        into->symsMapIndex[group] = from->symsMapIndex[group];
+        darray_init(from->symsMapNumEntries[group]);
+        darray_init(from->symsMapIndex[group]);
+        goto out;
+    }
+    else if (using == TO) {
+        resultSyms = into->syms[group];
+        goto out;
+    }
+
+    darray_resize0(resultSyms, resultSize);
+
+    for (i = 0; i < resultWidth; i++) {
+        enum key_group_selector use = NONE;
+        unsigned int fromSize = 0;
+        unsigned int toSize = 0;
+
+        if (i < from->numLevels[group])
+            fromSize = darray_item(from->symsMapNumEntries[group], i);
+
+        if (i < into->numLevels[group])
+            toSize = darray_item(into->symsMapNumEntries[group], i);
+
+        if (fromSize == 0 && toSize == 0) {
+            darray_item(into->symsMapIndex[group], i) = -1;
+            darray_item(into->symsMapNumEntries[group], i) = 0;
+            continue;
+        }
+
+        if (fromSize == 0)
+            use = TO;
+        else if (toSize == 0 || clobber)
+            use = FROM;
+        else
+            use = TO;
+
+        if (toSize && fromSize && report) {
+            INFO("Multiple symbols for group %d, level %d on key %s\n",
+                 group + 1, i + 1, longText(into->name));
+            ACTION("Using %s, ignoring %s\n",
+                   (use == FROM ? "from" : "to"),
+                   (use == FROM ? "to" : "from"));
+        }
+
+        if (use == FROM) {
+            memcpy(darray_mem(resultSyms, cur_idx),
+                   darray_mem(from->syms[group],
+                              darray_item(from->symsMapIndex[group], i)),
+                   darray_item(from->symsMapNumEntries[group],
+                               i) * sizeof(xkb_keysym_t));
+            darray_item(into->symsMapIndex[group], i) = cur_idx;
+            darray_item(into->symsMapNumEntries[group], i) =
+                darray_item(from->symsMapNumEntries[group], i);
+        }
+        else {
+            memcpy(darray_mem(resultSyms, cur_idx),
+                   darray_mem(into->syms[group],
+                              darray_item(into->symsMapIndex[group], i)),
+                   darray_item(into->symsMapNumEntries[group],
+                               i) * sizeof(xkb_keysym_t));
+            darray_item(into->symsMapIndex[group], i) = cur_idx;
+        }
+        cur_idx += darray_item(into->symsMapNumEntries[group], i);
+    }
+
+out:
+    if (!darray_same(resultActs, into->acts[group]))
+        darray_free(into->acts[group]);
+    if (!darray_same(resultActs, from->acts[group]))
+        darray_free(from->acts[group]);
     into->numLevels[group] = resultWidth;
+    if (!darray_same(resultSyms, into->syms[group]))
+        darray_free(into->syms[group]);
     into->syms[group] = resultSyms;
-    from->syms[group] = NULL;
+    if (!darray_same(resultSyms, from->syms[group]))
+        darray_free(from->syms[group]);
+    darray_init(from->syms[group]);
+    darray_free(from->symsMapIndex[group]);
+    darray_free(from->symsMapNumEntries[group]);
     into->acts[group] = resultActs;
-    from->acts[group] = NULL;
-    into->symsDefined |= (1 << group);
+    darray_init(from->acts[group]);
+    if (!darray_empty(into->syms[group]))
+        into->symsDefined |= (1 << group);
     from->symsDefined &= ~(1 << group);
     into->actsDefined |= (1 << group);
     from->actsDefined &= ~(1 << group);
-    return True;
+
+    return true;
 }
 
-static Bool
-MergeKeys(SymbolsInfo * info, KeyInfo * into, KeyInfo * from)
+static bool
+MergeKeys(SymbolsInfo *info, struct xkb_keymap *keymap,
+          KeyInfo *into, KeyInfo *from)
 {
     int i;
     unsigned collide = 0;
-    Bool report;
+    bool report;
 
-    if (from->defs.merge == MergeReplace)
-    {
-        for (i = 0; i < XkbNumKbdGroups; i++)
-        {
-            if (into->numLevels[i] != 0)
-            {
-                free(into->syms[i]);
-                free(into->acts[i]);
+    if (from->defs.merge == MERGE_REPLACE) {
+        for (i = 0; i < XkbNumKbdGroups; i++) {
+            if (into->numLevels[i] != 0) {
+                darray_free(into->syms[i]);
+                darray_free(into->acts[i]);
             }
         }
         *into = *from;
         memset(from, 0, sizeof(KeyInfo));
-        return True;
+        return true;
     }
     report = ((warningLevel > 9) ||
-              ((into->defs.fileID == from->defs.fileID)
+              ((into->defs.file_id == from->defs.file_id)
                && (warningLevel > 0)));
-    for (i = 0; i < XkbNumKbdGroups; i++)
-    {
-        if (from->numLevels[i] > 0)
-        {
-            if (into->numLevels[i] == 0)
-            {
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        if (from->numLevels[i] > 0) {
+            if (into->numLevels[i] == 0) {
                 into->numLevels[i] = from->numLevels[i];
                 into->syms[i] = from->syms[i];
+                into->symsMapIndex[i] = from->symsMapIndex[i];
+                into->symsMapNumEntries[i] = from->symsMapNumEntries[i];
                 into->acts[i] = from->acts[i];
                 into->symsDefined |= (1 << i);
-                from->syms[i] = NULL;
-                from->acts[i] = NULL;
+                darray_init(from->syms[i]);
+                darray_init(from->symsMapIndex[i]);
+                darray_init(from->symsMapNumEntries[i]);
+                darray_init(from->acts[i]);
                 from->numLevels[i] = 0;
                 from->symsDefined &= ~(1 << i);
-                if (into->syms[i])
+                if (!darray_empty(into->syms[i]))
                     into->defs.defined |= _Key_Syms;
-                if (into->acts[i])
+                if (!darray_empty(into->acts[i]))
                     into->defs.defined |= _Key_Acts;
             }
-            else
-            {
-                if (report)
-                {
-                    if (into->syms[i])
+            else {
+                if (report) {
+                    if (!darray_empty(into->syms[i]))
                         collide |= _Key_Syms;
-                    if (into->acts[i])
+                    if (!darray_empty(into->acts[i]))
                         collide |= _Key_Acts;
                 }
                 MergeKeyGroups(info, into, from, (unsigned) i);
             }
         }
-        if (from->types[i] != None)
-        {
-            if ((into->types[i] != None) && (report) &&
-                (into->types[i] != from->types[i]))
-            {
-                uint32_t use, ignore;
+        if (from->types[i] != XKB_ATOM_NONE) {
+            if ((into->types[i] != XKB_ATOM_NONE) && report &&
+                (into->types[i] != from->types[i])) {
+                xkb_atom_t use, ignore;
                 collide |= _Key_Types;
-                if (from->defs.merge != MergeAugment)
-                {
+                if (from->defs.merge != MERGE_AUGMENT) {
                     use = from->types[i];
                     ignore = into->types[i];
                 }
-                else
-                {
+                else {
                     use = into->types[i];
                     ignore = from->types[i];
                 }
                 WARN
                     ("Multiple definitions for group %d type of key %s\n",
-                     i, longText(into->name));
+                    i, longText(into->name));
                 ACTION("Using %s, ignoring %s\n",
-                        XkbcAtomText(use),
-                        XkbcAtomText(ignore));
+                       xkb_atom_text(keymap->ctx, use),
+                       xkb_atom_text(keymap->ctx, ignore));
             }
-            if ((from->defs.merge != MergeAugment)
-                || (into->types[i] == None))
-            {
+            if ((from->defs.merge != MERGE_AUGMENT)
+                || (into->types[i] == XKB_ATOM_NONE)) {
                 into->types[i] = from->types[i];
             }
         }
     }
-    if (UseNewField(_Key_Behavior, &into->defs, &from->defs, &collide))
-    {
+    if (UseNewField(_Key_Behavior, &into->defs, &from->defs, &collide)) {
         into->behavior = from->behavior;
-        into->nameForOverlayKey = from->nameForOverlayKey;
         into->defs.defined |= _Key_Behavior;
     }
-    if (UseNewField(_Key_VModMap, &into->defs, &from->defs, &collide))
-    {
+    if (UseNewField(_Key_VModMap, &into->defs, &from->defs, &collide)) {
         into->vmodmap = from->vmodmap;
         into->defs.defined |= _Key_VModMap;
     }
-    if (UseNewField(_Key_Repeat, &into->defs, &from->defs, &collide))
-    {
+    if (UseNewField(_Key_Repeat, &into->defs, &from->defs, &collide)) {
         into->repeat = from->repeat;
         into->defs.defined |= _Key_Repeat;
     }
-    if (UseNewField(_Key_Type_Dflt, &into->defs, &from->defs, &collide))
-    {
+    if (UseNewField(_Key_Type_Dflt, &into->defs, &from->defs, &collide)) {
         into->dfltType = from->dfltType;
         into->defs.defined |= _Key_Type_Dflt;
     }
-    if (UseNewField(_Key_GroupInfo, &into->defs, &from->defs, &collide))
-    {
-        into->groupInfo = from->groupInfo;
+    if (UseNewField(_Key_GroupInfo, &into->defs, &from->defs, &collide)) {
+        into->out_of_range_group_action = from->out_of_range_group_action;
+        into->out_of_range_group_number = from->out_of_range_group_number;
         into->defs.defined |= _Key_GroupInfo;
     }
-    if (collide)
-    {
+    if (collide) {
         WARN("Symbol map for key %s redefined\n",
-              longText(into->name));
+             longText(into->name));
         ACTION("Using %s definition for conflicting fields\n",
-                (from->defs.merge == MergeAugment ? "first" : "last"));
+               (from->defs.merge == MERGE_AUGMENT ? "first" : "last"));
     }
-    return True;
+    return true;
 }
 
-static Bool
-AddKeySymbols(SymbolsInfo * info, KeyInfo * key, struct xkb_desc * xkb)
+static bool
+AddKeySymbols(SymbolsInfo *info, KeyInfo *keyi, struct xkb_keymap *keymap)
 {
-    int i;
     unsigned long real_name;
+    KeyInfo *iter, *new;
 
-    for (i = 0; i < info->nKeys; i++)
-    {
-        if (info->keys[i].name == key->name)
-            return MergeKeys(info, &info->keys[i], key);
-    }
-    if (FindKeyNameForAlias(xkb, key->name, &real_name))
-    {
-        for (i = 0; i < info->nKeys; i++)
-        {
-            if (info->keys[i].name == real_name)
-                return MergeKeys(info, &info->keys[i], key);
-        }
-    }
-    if (info->nKeys >= info->szKeys)
-    {
-        info->szKeys += SYMBOLS_CHUNK;
-        info->keys =
-            uTypedRecalloc(info->keys, info->nKeys, info->szKeys, KeyInfo);
-        if (!info->keys)
-        {
-            WSGO("Could not allocate key symbols descriptions\n");
-            ACTION("Some key symbols definitions may be lost\n");
-            return False;
-        }
-    }
-    return CopyKeyInfo(key, &info->keys[info->nKeys++], True);
+    darray_foreach(iter, info->keys)
+        if (iter->name == keyi->name)
+            return MergeKeys(info, keymap, iter, keyi);
+
+    if (FindKeyNameForAlias(keymap, keyi->name, &real_name))
+        darray_foreach(iter, info->keys)
+            if (iter->name == real_name)
+                return MergeKeys(info, keymap, iter, keyi);
+
+    darray_resize0(info->keys, darray_size(info->keys) + 1);
+    new = &darray_item(info->keys, darray_size(info->keys) - 1);
+    return CopyKeyInfo(keyi, new, true);
 }
 
-static Bool
+static bool
 AddModMapEntry(SymbolsInfo * info, ModMapEntry * new)
 {
     ModMapEntry *mm;
-    Bool clobber;
+    bool clobber;
 
-    clobber = (new->defs.merge != MergeAugment);
-    for (mm = info->modMap; mm != NULL; mm = (ModMapEntry *) mm->defs.next)
-    {
+    clobber = (new->defs.merge != MERGE_AUGMENT);
+    for (mm = info->modMap; mm != NULL; mm = (ModMapEntry *) mm->defs.next) {
         if (new->haveSymbol && mm->haveSymbol
-            && (new->u.keySym == mm->u.keySym))
-        {
+            && (new->u.keySym == mm->u.keySym)) {
             unsigned use, ignore;
-            if (mm->modifier != new->modifier)
-            {
-                if (clobber)
-                {
+            if (mm->modifier != new->modifier) {
+                if (clobber) {
                     use = new->modifier;
                     ignore = mm->modifier;
                 }
-                else
-                {
+                else {
                     use = mm->modifier;
                     ignore = new->modifier;
                 }
                 ERROR
                     ("%s added to symbol map for multiple modifiers\n",
-                     XkbcKeysymText(new->u.keySym));
+                    XkbcKeysymText(new->u.keySym));
                 ACTION("Using %s, ignoring %s.\n",
-                        XkbcModIndexText(use),
-                        XkbcModIndexText(ignore));
+                       XkbcModIndexText(use),
+                       XkbcModIndexText(ignore));
                 mm->modifier = use;
             }
-            return True;
+            return true;
         }
         if ((!new->haveSymbol) && (!mm->haveSymbol) &&
-            (new->u.keyName == mm->u.keyName))
-        {
+            (new->u.keyName == mm->u.keyName)) {
             unsigned use, ignore;
-            if (mm->modifier != new->modifier)
-            {
-                if (clobber)
-                {
+            if (mm->modifier != new->modifier) {
+                if (clobber) {
                     use = new->modifier;
                     ignore = mm->modifier;
                 }
-                else
-                {
+                else {
                     use = mm->modifier;
                     ignore = new->modifier;
                 }
                 ERROR("Key %s added to map for multiple modifiers\n",
-                       longText(new->u.keyName));
+                      longText(new->u.keyName));
                 ACTION("Using %s, ignoring %s.\n",
-                        XkbcModIndexText(use),
-                        XkbcModIndexText(ignore));
+                       XkbcModIndexText(use),
+                       XkbcModIndexText(ignore));
                 mm->modifier = use;
             }
-            return True;
+            return true;
         }
     }
     mm = uTypedAlloc(ModMapEntry);
-    if (mm == NULL)
-    {
+    if (mm == NULL) {
         WSGO("Could not allocate modifier map entry\n");
         ACTION("Modifier map for %s will be incomplete\n",
-                XkbcModIndexText(new->modifier));
-        return False;
+               XkbcModIndexText(new->modifier));
+        return false;
     }
     *mm = *new;
     mm->defs.next = &info->modMap->defs;
     info->modMap = mm;
-    return True;
+    return true;
 }
 
 /***====================================================================***/
 
 static void
-MergeIncludedSymbols(SymbolsInfo * into, SymbolsInfo * from,
-                     unsigned merge, struct xkb_desc * xkb)
+MergeIncludedSymbols(SymbolsInfo *into, SymbolsInfo *from,
+                     enum merge_mode merge, struct xkb_keymap *keymap)
 {
-    int i;
-    KeyInfo *key;
+    unsigned int i;
+    KeyInfo *keyi;
 
-    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;
     }
-    for (i = 0; i < XkbNumKbdGroups; i++)
-    {
-        if (from->groupNames[i] != None)
-        {
-            if ((merge != MergeAugment) || (into->groupNames[i] == None))
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        if (from->groupNames[i] != XKB_ATOM_NONE) {
+            if ((merge != MERGE_AUGMENT) ||
+                (into->groupNames[i] == XKB_ATOM_NONE))
                 into->groupNames[i] = from->groupNames[i];
         }
     }
-    for (i = 0, key = from->keys; i < from->nKeys; i++, key++)
-    {
-        if (merge != MergeDefault)
-            key->defs.merge = merge;
-        if (!AddKeySymbols(into, key, xkb))
+
+    darray_foreach(keyi, from->keys) {
+        if (merge != MERGE_DEFAULT)
+            keyi->defs.merge = merge;
+
+        if (!AddKeySymbols(into, keyi, keymap))
             into->errorCount++;
     }
-    if (from->modMap != NULL)
-    {
+
+    if (from->modMap != NULL) {
         ModMapEntry *mm, *next;
-        for (mm = from->modMap; mm != NULL; mm = next)
-        {
-            if (merge != MergeDefault)
+        for (mm = from->modMap; mm != NULL; mm = next) {
+            if (merge != MERGE_DEFAULT)
                 mm->defs.merge = merge;
             if (!AddModMapEntry(into, mm))
                 into->errorCount++;
@@ -742,107 +718,98 @@ MergeIncludedSymbols(SymbolsInfo * into, SymbolsInfo * from,
         into->errorCount++;
 }
 
-typedef void (*FileHandler) (XkbFile * /* rtrn */ ,
-                             struct xkb_desc * /* xkb */ ,
-                             unsigned /* merge */ ,
-                             SymbolsInfo *      /* included */
-    );
+static void
+HandleSymbolsFile(XkbFile *file, struct xkb_keymap *keymap,
+                  enum merge_mode merge,
+                  SymbolsInfo *info);
 
-static Bool
-HandleIncludeSymbols(IncludeStmt * stmt,
-                     struct xkb_desc * xkb, SymbolsInfo * info, FileHandler hndlr)
+static bool
+HandleIncludeSymbols(IncludeStmt *stmt, struct xkb_keymap *keymap,
+                     SymbolsInfo *info)
 {
-    unsigned newMerge;
+    enum merge_mode newMerge;
     XkbFile *rtrn;
     SymbolsInfo included;
-    Bool haveSelf;
+    bool haveSelf;
 
-    haveSelf = False;
-    if ((stmt->file == NULL) && (stmt->map == NULL))
-    {
-        haveSelf = True;
+    haveSelf = false;
+    if ((stmt->file == NULL) && (stmt->map == NULL)) {
+        haveSelf = true;
         included = *info;
         memset(info, 0, sizeof(SymbolsInfo));
     }
-    else if (ProcessIncludeFile(stmt, XkmSymbolsIndex, &rtrn, &newMerge))
-    {
-        InitSymbolsInfo(&included, xkb);
-        included.fileID = included.dflt.defs.fileID = rtrn->id;
-        included.merge = included.dflt.defs.merge = MergeOverride;
-        if (stmt->modifier)
-        {
+    else if (ProcessIncludeFile(keymap->ctx, stmt, FILE_TYPE_SYMBOLS, &rtrn,
+                                &newMerge)) {
+        InitSymbolsInfo(&included, keymap, rtrn->id);
+        included.merge = included.dflt.defs.merge = MERGE_OVERRIDE;
+        if (stmt->modifier) {
             included.explicit_group = atoi(stmt->modifier) - 1;
         }
-        else
-        {
+        else {
             included.explicit_group = info->explicit_group;
         }
-        (*hndlr) (rtrn, xkb, MergeOverride, &included);
-        if (stmt->stmt != NULL)
-        {
+        HandleSymbolsFile(rtrn, keymap, MERGE_OVERRIDE, &included);
+        if (stmt->stmt != NULL) {
             free(included.name);
             included.name = stmt->stmt;
             stmt->stmt = NULL;
         }
+        FreeXKBFile(rtrn);
     }
-    else
-    {
+    else {
         info->errorCount += 10;
-        return False;
+        return false;
     }
-    if ((stmt->next != NULL) && (included.errorCount < 1))
-    {
+    if ((stmt->next != NULL) && (included.errorCount < 1)) {
         IncludeStmt *next;
         unsigned op;
         SymbolsInfo next_incl;
 
-        for (next = stmt->next; next != NULL; next = next->next)
-        {
-            if ((next->file == NULL) && (next->map == NULL))
-            {
-                haveSelf = True;
-                MergeIncludedSymbols(&included, info, next->merge, xkb);
+        for (next = stmt->next; next != NULL; next = next->next) {
+            if ((next->file == NULL) && (next->map == NULL)) {
+                haveSelf = true;
+                MergeIncludedSymbols(&included, info, next->merge, keymap);
                 FreeSymbolsInfo(info);
             }
-            else if (ProcessIncludeFile(next, XkmSymbolsIndex, &rtrn, &op))
-            {
-                InitSymbolsInfo(&next_incl, xkb);
-                next_incl.fileID = next_incl.dflt.defs.fileID = rtrn->id;
-                next_incl.merge = next_incl.dflt.defs.merge = MergeOverride;
-                if (next->modifier)
-                {
+            else if (ProcessIncludeFile(keymap->ctx, next, FILE_TYPE_SYMBOLS,
+                                        &rtrn, &op)) {
+                InitSymbolsInfo(&next_incl, keymap, rtrn->id);
+                next_incl.merge = next_incl.dflt.defs.merge = MERGE_OVERRIDE;
+                if (next->modifier) {
                     next_incl.explicit_group = atoi(next->modifier) - 1;
                 }
-                else
-                {
+                else {
                     next_incl.explicit_group = info->explicit_group;
                 }
-                (*hndlr) (rtrn, xkb, MergeOverride, &next_incl);
-                MergeIncludedSymbols(&included, &next_incl, op, xkb);
+                HandleSymbolsFile(rtrn, keymap, MERGE_OVERRIDE, &next_incl);
+                MergeIncludedSymbols(&included, &next_incl, op, keymap);
                 FreeSymbolsInfo(&next_incl);
+                FreeXKBFile(rtrn);
             }
-            else
-            {
+            else {
                 info->errorCount += 10;
-                return False;
+                FreeSymbolsInfo(&included);
+                return false;
             }
         }
     }
+    else if (stmt->next) {
+        info->errorCount += included.errorCount;
+    }
     if (haveSelf)
         *info = included;
-    else
-    {
-        MergeIncludedSymbols(info, &included, newMerge, xkb);
+    else {
+        MergeIncludedSymbols(info, &included, newMerge, keymap);
         FreeSymbolsInfo(&included);
     }
     return (info->errorCount == 0);
 }
 
-#define        SYMBOLS 1
-#define        ACTIONS 2
+#define SYMBOLS 1
+#define ACTIONS 2
 
-static Bool
-GetGroupIndex(KeyInfo * key,
+static bool
+GetGroupIndex(KeyInfo *keyi, struct xkb_keymap *keymap,
               ExprDef * arrayNdx, unsigned what, unsigned *ndx_rtrn)
 {
     const char *name;
@@ -853,574 +820,405 @@ GetGroupIndex(KeyInfo * key,
     else
         name = "actions";
 
-    if (arrayNdx == NULL)
-    {
+    if (arrayNdx == NULL) {
         int i;
         unsigned defined;
         if (what == SYMBOLS)
-            defined = key->symsDefined;
+            defined = keyi->symsDefined;
         else
-            defined = key->actsDefined;
+            defined = keyi->actsDefined;
 
-        for (i = 0; i < XkbNumKbdGroups; i++)
-        {
-            if ((defined & (1 << i)) == 0)
-            {
+        for (i = 0; i < XkbNumKbdGroups; i++) {
+            if ((defined & (1 << i)) == 0) {
                 *ndx_rtrn = i;
-                return True;
+                return true;
             }
         }
         ERROR("Too many groups of %s for key %s (max %d)\n", name,
-               longText(key->name), XkbNumKbdGroups + 1);
+              longText(keyi->name), XkbNumKbdGroups + 1);
         ACTION("Ignoring %s defined for extra groups\n", name);
-        return False;
+        return false;
     }
-    if (!ExprResolveGroup(arrayNdx, &tmp))
-    {
+    if (!ExprResolveGroup(keymap->ctx, arrayNdx, &tmp)) {
         ERROR("Illegal group index for %s of key %s\n", name,
-               longText(key->name));
+              longText(keyi->name));
         ACTION("Definition with non-integer array index ignored\n");
-        return False;
+        return false;
     }
     *ndx_rtrn = tmp.uval - 1;
-    return True;
+    return true;
 }
 
-static Bool
-AddSymbolsToKey(KeyInfo * key,
-                struct xkb_desc * xkb,
-                char *field,
-                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+static bool
+AddSymbolsToKey(KeyInfo *keyi, struct xkb_keymap *keymap,
+                ExprDef *arrayNdx, ExprDef *value, SymbolsInfo *info)
 {
-    unsigned ndx, nSyms;
-    int i;
-
-    if (!GetGroupIndex(key, arrayNdx, SYMBOLS, &ndx))
-        return False;
-    if (value == NULL)
-    {
-        key->symsDefined |= (1 << ndx);
-        return True;
-    }
-    if (value->op != ExprKeysymList)
-    {
-        ERROR("Expected a list of symbols, found %s\n",
-               exprOpText(value->op));
-        ACTION("Ignoring symbols for group %d of %s\n", ndx,
-                longText(key->name));
-        return False;
-    }
-    if (key->syms[ndx] != NULL)
-    {
-        WSGO("Symbols for key %s, group %d already defined\n",
-              longText(key->name), ndx);
-        return False;
-    }
-    nSyms = value->value.list.nSyms;
-    if (((key->numLevels[ndx] < nSyms) || (key->syms[ndx] == NULL)) &&
-        (!ResizeKeyGroup(key, ndx, nSyms, False)))
-    {
-        WSGO("Could not resize group %d of key %s\n", ndx,
-              longText(key->name));
+    unsigned ndx, nSyms, nLevels;
+    unsigned int i;
+    long j;
+
+    if (!GetGroupIndex(keyi, keymap, arrayNdx, SYMBOLS, &ndx))
+        return false;
+    if (value == NULL) {
+        keyi->symsDefined |= (1 << ndx);
+        return true;
+    }
+    if (value->op != ExprKeysymList) {
+        ERROR("Expected a list of symbols, found %s\n", exprOpText(value->op));
+        ACTION("Ignoring symbols for group %d of %s\n", ndx + 1,
+               longText(keyi->name));
+        return false;
+    }
+    if (!darray_empty(keyi->syms[ndx])) {
+        ERROR("Symbols for key %s, group %d already defined\n",
+               longText(keyi->name), ndx + 1);
+        ACTION("Ignoring duplicate definition\n");
+        return false;
+    }
+    nSyms = darray_size(value->value.list.syms);
+    nLevels = darray_size(value->value.list.symsMapIndex);
+    if ((keyi->numLevels[ndx] < nSyms || darray_empty(keyi->syms[ndx])) &&
+        (!ResizeKeyGroup(keyi, ndx, nLevels, nSyms, false))) {
+        WSGO("Could not resize group %d of key %s to contain %d levels\n",
+             ndx + 1, longText(keyi->name), nSyms);
         ACTION("Symbols lost\n");
-        return False;
-    }
-    key->symsDefined |= (1 << ndx);
-    for (i = 0; i < nSyms; i++) {
-        if (!LookupKeysym(value->value.list.syms[i], &key->syms[ndx][i])) {
-            WSGO("Could not resolve keysym %s\n", value->value.list.syms[i]);
-            key->syms[ndx][i] = NoSymbol;
+        return false;
+    }
+    keyi->symsDefined |= (1 << ndx);
+    for (i = 0; i < nLevels; i++) {
+        darray_item(keyi->symsMapIndex[ndx], i) =
+            darray_item(value->value.list.symsMapIndex, i);
+        darray_item(keyi->symsMapNumEntries[ndx], i) =
+            darray_item(value->value.list.symsNumEntries, i);
+
+        for (j = 0; j < darray_item(keyi->symsMapNumEntries[ndx], i); j++) {
+            /* FIXME: What's abort() doing here? */
+            if (darray_item(keyi->symsMapIndex[ndx], i) + j >= nSyms)
+                abort();
+            if (!LookupKeysym(darray_item(value->value.list.syms,
+                                          darray_item(value->value.list.symsMapIndex,
+                                                      i) + j),
+                              &darray_item(keyi->syms[ndx],
+                                           darray_item(keyi->symsMapIndex[ndx],
+                                                       i) + j))) {
+                WARN(
+                    "Could not resolve keysym %s for key %s, group %d (%s), level %d\n",
+                    darray_item(value->value.list.syms, i),
+                    longText(keyi->name),
+                    ndx + 1,
+                    xkb_atom_text(keymap->ctx, info->groupNames[ndx]), nSyms);
+                while (--j >= 0)
+                    darray_item(keyi->syms[ndx],
+                                darray_item(keyi->symsMapIndex[ndx],
+                                            i) + j) = XKB_KEY_NoSymbol;
+                darray_item(keyi->symsMapIndex[ndx], i) = -1;
+                darray_item(keyi->symsMapNumEntries[ndx], i) = 0;
+                break;
+            }
+            if (darray_item(keyi->symsMapNumEntries[ndx], i) == 1 &&
+                darray_item(keyi->syms[ndx],
+                            darray_item(keyi->symsMapIndex[ndx],
+                                        i) + j) == XKB_KEY_NoSymbol) {
+                darray_item(keyi->symsMapIndex[ndx], i) = -1;
+                darray_item(keyi->symsMapNumEntries[ndx], i) = 0;
+            }
         }
     }
-    for (i = key->numLevels[ndx] - 1;
-         (i >= 0) && (key->syms[ndx][i] == NoSymbol); i--)
-    {
-        key->numLevels[ndx]--;
-    }
-    return True;
+    for (j = keyi->numLevels[ndx] - 1;
+         j >= 0 && darray_item(keyi->symsMapNumEntries[ndx], j) == 0; j--)
+        keyi->numLevels[ndx]--;
+    return true;
 }
 
-static Bool
-AddActionsToKey(KeyInfo * key,
-                struct xkb_desc * xkb,
-                char *field,
-                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+static bool
+AddActionsToKey(KeyInfo *keyi, struct xkb_keymap *keymap, ExprDef *arrayNdx,
+                ExprDef *value, SymbolsInfo *info)
 {
-    int i;
+    unsigned int i;
     unsigned ndx, nActs;
     ExprDef *act;
     struct xkb_any_action *toAct;
 
-    if (!GetGroupIndex(key, arrayNdx, ACTIONS, &ndx))
-        return False;
+    if (!GetGroupIndex(keyi, keymap, arrayNdx, ACTIONS, &ndx))
+        return false;
 
-    if (value == NULL)
-    {
-        key->actsDefined |= (1 << ndx);
-        return True;
+    if (value == NULL) {
+        keyi->actsDefined |= (1 << ndx);
+        return true;
     }
-    if (value->op != ExprActionList)
-    {
+    if (value->op != ExprActionList) {
         WSGO("Bad expression type (%d) for action list value\n", value->op);
         ACTION("Ignoring actions for group %d of %s\n", ndx,
-                longText(key->name));
-        return False;
+               longText(keyi->name));
+        return false;
     }
-    if (key->acts[ndx] != NULL)
-    {
+    if (!darray_empty(keyi->acts[ndx])) {
         WSGO("Actions for key %s, group %d already defined\n",
-              longText(key->name), ndx);
-        return False;
+             longText(keyi->name), ndx);
+        return false;
     }
-    for (nActs = 0, act = value->value.child; act != NULL; nActs++)
-    {
+    for (nActs = 0, act = value->value.child; act != NULL; nActs++) {
         act = (ExprDef *) act->common.next;
     }
-    if (nActs < 1)
-    {
+    if (nActs < 1) {
         WSGO("Action list but not actions in AddActionsToKey\n");
-        return False;
+        return false;
     }
-    if (((key->numLevels[ndx] < nActs) || (key->acts[ndx] == NULL)) &&
-        (!ResizeKeyGroup(key, ndx, nActs, True)))
-    {
+    if ((keyi->numLevels[ndx] < nActs || darray_empty(keyi->acts[ndx])) &&
+        !ResizeKeyGroup(keyi, ndx, nActs, nActs, true)) {
         WSGO("Could not resize group %d of key %s\n", ndx,
-              longText(key->name));
+              longText(keyi->name));
         ACTION("Actions lost\n");
-        return False;
+        return false;
     }
-    key->actsDefined |= (1 << ndx);
+    keyi->actsDefined |= (1 << ndx);
 
-    toAct = (struct xkb_any_action *) key->acts[ndx];
+    toAct = (struct xkb_any_action *) darray_mem(keyi->acts[ndx], 0);
     act = value->value.child;
-    for (i = 0; i < nActs; i++, toAct++)
-    {
-        if (!HandleActionDef(act, xkb, toAct, MergeOverride, info->action))
-        {
+    for (i = 0; i < nActs; i++, toAct++) {
+        if (!HandleActionDef(act, keymap, toAct, info->action)) {
             ERROR("Illegal action definition for %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Action for group %d/level %d ignored\n", ndx + 1, i + 1);
         }
         act = (ExprDef *) act->common.next;
     }
-    return True;
+    return true;
 }
 
-static int
-SetAllowNone(KeyInfo * key, ExprDef * arrayNdx, ExprDef * value)
-{
-    ExprResult tmp;
-    unsigned radio_groups = 0;
-
-    if (arrayNdx == NULL)
-    {
-        radio_groups = XkbAllRadioGroupsMask;
-    }
-    else
-    {
-        if (!ExprResolveRadioGroup(arrayNdx, &tmp))
-        {
-            ERROR("Illegal index in group name definition\n");
-            ACTION("Definition with non-integer array index ignored\n");
-            return False;
-        }
-        if ((tmp.uval < 1) || (tmp.uval > XkbMaxRadioGroups))
-        {
-            ERROR("Illegal radio group specified (must be 1..%d)\n",
-                   XkbMaxRadioGroups + 1);
-            ACTION("Value of \"allow none\" for group %d ignored\n",
-                    tmp.uval);
-            return False;
-        }
-        radio_groups |= (1 << (tmp.uval - 1));
-    }
-    if (!ExprResolveBoolean(value, &tmp))
-    {
-        ERROR("Illegal \"allow none\" value for %s\n",
-               longText(key->name));
-        ACTION("Non-boolean value ignored\n");
-        return False;
-    }
-    if (tmp.uval)
-        key->allowNone |= radio_groups;
-    else
-        key->allowNone &= ~radio_groups;
-    return True;
-}
-
-
-static LookupEntry lockingEntries[] = {
-    {"true", XkbKB_Lock},
-    {"yes", XkbKB_Lock},
-    {"on", XkbKB_Lock},
-    {"false", XkbKB_Default},
-    {"no", XkbKB_Default},
-    {"off", XkbKB_Default},
-    {"permanent", XkbKB_Lock | XkbKB_Permanent},
-    {NULL, 0}
+static const LookupEntry lockingEntries[] = {
+    { "true", XkbKB_Lock },
+    { "yes", XkbKB_Lock },
+    { "on", XkbKB_Lock },
+    { "false", XkbKB_Default },
+    { "no", XkbKB_Default },
+    { "off", XkbKB_Default },
+    { "permanent", XkbKB_Lock | XkbKB_Permanent },
+    { NULL, 0 }
 };
 
-static LookupEntry repeatEntries[] = {
-    {"true", RepeatYes},
-    {"yes", RepeatYes},
-    {"on", RepeatYes},
-    {"false", RepeatNo},
-    {"no", RepeatNo},
-    {"off", RepeatNo},
-    {"default", RepeatUndefined},
-    {NULL, 0}
+static const LookupEntry repeatEntries[] = {
+    { "true", RepeatYes },
+    { "yes", RepeatYes },
+    { "on", RepeatYes },
+    { "false", RepeatNo },
+    { "no", RepeatNo },
+    { "off", RepeatNo },
+    { "default", RepeatUndefined },
+    { NULL, 0 }
 };
 
-static Bool
-SetSymbolsField(KeyInfo * key,
-                struct xkb_desc * xkb,
-                char *field,
-                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+static bool
+SetSymbolsField(KeyInfo *keyi, struct xkb_keymap *keymap, char *field,
+                ExprDef *arrayNdx, ExprDef *value, SymbolsInfo *info)
 {
-    Bool ok = True;
+    bool ok = true;
     ExprResult tmp;
 
-    if (uStrCaseCmp(field, "type") == 0)
-    {
+    if (strcasecmp(field, "type") == 0) {
         ExprResult ndx;
-        if ((!ExprResolveString(value, &tmp))
-            && (warningLevel > 0))
-        {
+        if ((!ExprResolveString(keymap->ctx, value, &tmp))
+            && (warningLevel > 0)) {
             WARN("The type field of a key symbol map must be a string\n");
             ACTION("Ignoring illegal type definition\n");
         }
-        if (arrayNdx == NULL)
-        {
-            key->dfltType = xkb_intern_atom(tmp.str);
-            key->defs.defined |= _Key_Type_Dflt;
+        if (arrayNdx == NULL) {
+            keyi->dfltType = xkb_atom_intern(keymap->ctx, tmp.str);
+            keyi->defs.defined |= _Key_Type_Dflt;
         }
-        else if (!ExprResolveGroup(arrayNdx, &ndx))
-        {
+        else if (!ExprResolveGroup(keymap->ctx, arrayNdx, &ndx)) {
             ERROR("Illegal group index for type of key %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Definition with non-integer array index ignored\n");
             free(tmp.str);
-            return False;
+            return false;
         }
-        else
-        {
-            key->types[ndx.uval - 1] = xkb_intern_atom(tmp.str);
-            key->typesDefined |= (1 << (ndx.uval - 1));
+        else {
+            keyi->types[ndx.uval - 1] = xkb_atom_intern(keymap->ctx, tmp.str);
+            keyi->typesDefined |= (1 << (ndx.uval - 1));
         }
         free(tmp.str);
     }
-    else if (uStrCaseCmp(field, "symbols") == 0)
-        return AddSymbolsToKey(key, xkb, field, arrayNdx, value, info);
-    else if (uStrCaseCmp(field, "actions") == 0)
-        return AddActionsToKey(key, xkb, field, arrayNdx, value, info);
-    else if ((uStrCaseCmp(field, "vmods") == 0) ||
-             (uStrCaseCmp(field, "virtualmods") == 0) ||
-             (uStrCaseCmp(field, "virtualmodifiers") == 0))
-    {
-        ok = ExprResolveVModMask(value, &tmp, xkb);
-        if (ok)
-        {
-            key->vmodmap = (tmp.uval >> 8);
-            key->defs.defined |= _Key_VModMap;
+    else if (strcasecmp(field, "symbols") == 0)
+        return AddSymbolsToKey(keyi, keymap, arrayNdx, value, info);
+    else if (strcasecmp(field, "actions") == 0)
+        return AddActionsToKey(keyi, keymap, arrayNdx, value, info);
+    else if ((strcasecmp(field, "vmods") == 0) ||
+             (strcasecmp(field, "virtualmods") == 0) ||
+             (strcasecmp(field, "virtualmodifiers") == 0)) {
+        ok = ExprResolveVModMask(value, &tmp, keymap);
+        if (ok) {
+            keyi->vmodmap = (tmp.uval >> 8);
+            keyi->defs.defined |= _Key_VModMap;
         }
-        else
-        {
+        else {
             ERROR("Expected a virtual modifier mask, found %s\n",
-                   exprOpText(value->op));
+                  exprOpText(value->op));
             ACTION("Ignoring virtual modifiers definition for key %s\n",
-                    longText(key->name));
+                   longText(keyi->name));
         }
     }
-    else if ((uStrCaseCmp(field, "locking") == 0)
-             || (uStrCaseCmp(field, "lock") == 0)
-             || (uStrCaseCmp(field, "locks") == 0))
-    {
-        ok = ExprResolveEnum(value, &tmp, lockingEntries);
+    else if ((strcasecmp(field, "locking") == 0) ||
+             (strcasecmp(field, "lock") == 0) ||
+             (strcasecmp(field, "locks") == 0)) {
+        ok = ExprResolveEnum(keymap->ctx, value, &tmp, lockingEntries);
         if (ok)
-            key->behavior.type = tmp.uval;
-        key->defs.defined |= _Key_Behavior;
+            keyi->behavior.type = tmp.uval;
+        keyi->defs.defined |= _Key_Behavior;
     }
-    else if ((uStrCaseCmp(field, "radiogroup") == 0) ||
-             (uStrCaseCmp(field, "permanentradiogroup") == 0))
-    {
-        Bool permanent = False;
-        if (uStrCaseCmp(field, "permanentradiogroup") == 0)
-            permanent = True;
-        if (ExprResolveString(value, &tmp)) {
-            ok = (strcmp(tmp.str, "none") == 0);
-            free(tmp.str);
-            if (ok)
-                tmp.uval = 0;
-        }
-        else {
-            ok = ExprResolveInteger(value, &tmp);
-        }
-        if (!ok)
-        {
-            ERROR("Illegal radio group specification for %s\n",
-                  longText(key->name));
-            ACTION("Non-integer radio group ignored\n");
-            return False;
-        }
-        if (tmp.uval == 0)
-        {
-            key->behavior.type = XkbKB_Default;
-            key->behavior.data = 0;
-            return ok;
-        }
-        if ((tmp.uval < 1) || (tmp.uval > XkbMaxRadioGroups))
-        {
-            ERROR
-                ("Radio group specification for %s out of range (1..32)\n",
-                 longText(key->name));
-            ACTION("Illegal radio group %d ignored\n", tmp.uval);
-            return False;
-        }
-        key->behavior.type =
-            XkbKB_RadioGroup | (permanent ? XkbKB_Permanent : 0);
-        key->behavior.data = tmp.uval - 1;
-        if (key->allowNone & (1 << (tmp.uval - 1)))
-            key->behavior.data |= XkbKB_RGAllowNone;
-        key->defs.defined |= _Key_Behavior;
-    }
-    else if (uStrCaseEqual(field, "allownone"))
-    {
-        ok = SetAllowNone(key, arrayNdx, value);
+    else if ((strcasecmp(field, "radiogroup") == 0) ||
+             (strcasecmp(field, "permanentradiogroup") == 0) ||
+             (strcasecmp(field, "allownone") == 0)) {
+        ERROR("Radio groups not supported\n");
+        ACTION("Ignoring radio group specification for key %s\n",
+               longText(keyi->name));
+        return false;
     }
     else if (uStrCasePrefix("overlay", field) ||
-             uStrCasePrefix("permanentoverlay", field))
-    {
-        Bool permanent = False;
-        char *which;
-        int overlayNdx;
-        if (uStrCasePrefix("permanent", field))
-        {
-            permanent = True;
-            which = &field[sizeof("permanentoverlay") - 1];
-        }
-        else
-        {
-            which = &field[sizeof("overlay") - 1];
-        }
-        if (sscanf(which, "%d", &overlayNdx) == 1)
-        {
-            if (((overlayNdx < 1) || (overlayNdx > 2)) && (warningLevel > 0))
-            {
-                ERROR("Illegal overlay %d specified for %s\n",
-                       overlayNdx, longText(key->name));
-                ACTION("Ignored\n");
-                return False;
-            }
-        }
-        else if (*which == '\0')
-            overlayNdx = 1;
-        else if (warningLevel > 0)
-        {
-            ERROR("Illegal overlay \"%s\" specified for %s\n",
-                   which, longText(key->name));
-            ACTION("Ignored\n");
-            return False;
-        }
-        ok = ExprResolveKeyName(value, &tmp);
-        if (!ok)
-        {
-            ERROR("Illegal overlay key specification for %s\n",
-                   longText(key->name));
-            ACTION("Overlay key must be specified by name\n");
-            return False;
-        }
-        if (overlayNdx == 1)
-            key->behavior.type = XkbKB_Overlay1;
-        else
-            key->behavior.type = XkbKB_Overlay2;
-        if (permanent)
-            key->behavior.type |= XkbKB_Permanent;
-
-        key->behavior.data = 0;
-        key->nameForOverlayKey = KeyNameToLong(tmp.keyName.name);
-        key->defs.defined |= _Key_Behavior;
-    }
-    else if ((uStrCaseCmp(field, "repeating") == 0) ||
-             (uStrCaseCmp(field, "repeats") == 0) ||
-             (uStrCaseCmp(field, "repeat") == 0))
-    {
-        ok = ExprResolveEnum(value, &tmp, repeatEntries);
-        if (!ok)
-        {
+             uStrCasePrefix("permanentoverlay", field)) {
+        ERROR("Overlays not supported\n");
+        ACTION("Ignoring overlay specification for key %s\n",
+               longText(keyi->name));
+    }
+    else if ((strcasecmp(field, "repeating") == 0) ||
+             (strcasecmp(field, "repeats") == 0) ||
+             (strcasecmp(field, "repeat") == 0)) {
+        ok = ExprResolveEnum(keymap->ctx, value, &tmp, repeatEntries);
+        if (!ok) {
             ERROR("Illegal repeat setting for %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Non-boolean repeat setting ignored\n");
-            return False;
+            return false;
         }
-        key->repeat = tmp.uval;
-        key->defs.defined |= _Key_Repeat;
+        keyi->repeat = tmp.uval;
+        keyi->defs.defined |= _Key_Repeat;
     }
-    else if ((uStrCaseCmp(field, "groupswrap") == 0) ||
-             (uStrCaseCmp(field, "wrapgroups") == 0))
-    {
-        ok = ExprResolveBoolean(value, &tmp);
-        if (!ok)
-        {
+    else if ((strcasecmp(field, "groupswrap") == 0) ||
+             (strcasecmp(field, "wrapgroups") == 0)) {
+        ok = ExprResolveBoolean(keymap->ctx, value, &tmp);
+        if (!ok) {
             ERROR("Illegal groupsWrap setting for %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Non-boolean value ignored\n");
-            return False;
+            return false;
         }
         if (tmp.uval)
-            key->groupInfo = XkbWrapIntoRange;
+            keyi->out_of_range_group_action = XkbWrapIntoRange;
         else
-            key->groupInfo = XkbClampIntoRange;
-        key->defs.defined |= _Key_GroupInfo;
+            keyi->out_of_range_group_action = XkbClampIntoRange;
+        keyi->defs.defined |= _Key_GroupInfo;
     }
-    else if ((uStrCaseCmp(field, "groupsclamp") == 0) ||
-             (uStrCaseCmp(field, "clampgroups") == 0))
-    {
-        ok = ExprResolveBoolean(value, &tmp);
-        if (!ok)
-        {
+    else if ((strcasecmp(field, "groupsclamp") == 0) ||
+             (strcasecmp(field, "clampgroups") == 0)) {
+        ok = ExprResolveBoolean(keymap->ctx, value, &tmp);
+        if (!ok) {
             ERROR("Illegal groupsClamp setting for %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Non-boolean value ignored\n");
-            return False;
+            return false;
         }
         if (tmp.uval)
-            key->groupInfo = XkbClampIntoRange;
+            keyi->out_of_range_group_action = XkbClampIntoRange;
         else
-            key->groupInfo = XkbWrapIntoRange;
-        key->defs.defined |= _Key_GroupInfo;
+            keyi->out_of_range_group_action = XkbWrapIntoRange;
+        keyi->defs.defined |= _Key_GroupInfo;
     }
-    else if ((uStrCaseCmp(field, "groupsredirect") == 0) ||
-             (uStrCaseCmp(field, "redirectgroups") == 0))
-    {
-        if (!ExprResolveGroup(value, &tmp))
-        {
+    else if ((strcasecmp(field, "groupsredirect") == 0) ||
+             (strcasecmp(field, "redirectgroups") == 0)) {
+        if (!ExprResolveGroup(keymap->ctx, value, &tmp)) {
             ERROR("Illegal group index for redirect of key %s\n",
-                   longText(key->name));
+                  longText(keyi->name));
             ACTION("Definition with non-integer group ignored\n");
-            return False;
+            return false;
         }
-        key->groupInfo =
-            XkbSetGroupInfo(0, XkbRedirectIntoRange, tmp.uval - 1);
-        key->defs.defined |= _Key_GroupInfo;
+        keyi->out_of_range_group_action = XkbRedirectIntoRange;
+        keyi->out_of_range_group_number = tmp.uval - 1;
+        keyi->defs.defined |= _Key_GroupInfo;
     }
-    else
-    {
+    else {
         ERROR("Unknown field %s in a symbol interpretation\n", field);
         ACTION("Definition ignored\n");
-        ok = False;
+        ok = false;
     }
     return ok;
 }
 
 static int
-SetGroupName(SymbolsInfo * info, ExprDef * arrayNdx, ExprDef * value)
+SetGroupName(SymbolsInfo *info, struct xkb_keymap *keymap, ExprDef *arrayNdx,
+             ExprDef *value)
 {
     ExprResult tmp, name;
 
-    if ((arrayNdx == NULL) && (warningLevel > 0))
-    {
+    if ((arrayNdx == NULL) && (warningLevel > 0)) {
         WARN("You must specify an index when specifying a group name\n");
         ACTION("Group name definition without array subscript ignored\n");
-        return False;
+        return false;
     }
-    if (!ExprResolveGroup(arrayNdx, &tmp))
-    {
+    if (!ExprResolveGroup(keymap->ctx, arrayNdx, &tmp)) {
         ERROR("Illegal index in group name definition\n");
         ACTION("Definition with non-integer array index ignored\n");
-        return False;
+        return false;
     }
-    if (!ExprResolveString(value, &name))
-    {
+    if (!ExprResolveString(keymap->ctx, value, &name)) {
         ERROR("Group name must be a string\n");
         ACTION("Illegal name for group %d ignored\n", tmp.uval);
-        return False;
+        return false;
     }
     info->groupNames[tmp.uval - 1 + info->explicit_group] =
-        xkb_intern_atom(name.str);
+        xkb_atom_intern(keymap->ctx, name.str);
     free(name.str);
 
-    return True;
+    return true;
 }
 
 static int
-HandleSymbolsVar(VarDef * stmt, struct xkb_desc * xkb, SymbolsInfo * info)
+HandleSymbolsVar(VarDef *stmt, struct xkb_keymap *keymap, SymbolsInfo *info)
 {
-    ExprResult elem, field, tmp;
+    ExprResult elem, field;
     ExprDef *arrayNdx;
-    Bool ret;
+    bool ret;
 
-    if (ExprResolveLhs(stmt->name, &elem, &field, &arrayNdx) == 0)
+    if (ExprResolveLhs(keymap, stmt->name, &elem, &field, &arrayNdx) == 0)
         return 0;               /* internal error, already reported */
-    if (elem.str && (uStrCaseCmp(elem.str, "key") == 0))
-    {
-        ret = SetSymbolsField(&info->dflt, xkb, field.str, arrayNdx,
+    if (elem.str && (strcasecmp(elem.str, "key") == 0)) {
+        ret = SetSymbolsField(&info->dflt, keymap, field.str, arrayNdx,
                               stmt->value, info);
     }
-    else if ((elem.str == NULL) && ((uStrCaseCmp(field.str, "name") == 0) ||
-                                    (uStrCaseCmp(field.str, "groupname") ==
-                                     0)))
-    {
-        ret = SetGroupName(info, arrayNdx, stmt->value);
+    else if ((elem.str == NULL) && ((strcasecmp(field.str, "name") == 0) ||
+                                    (strcasecmp(field.str, "groupname") ==
+                                     0))) {
+        ret = SetGroupName(info, keymap, arrayNdx, stmt->value);
     }
     else if ((elem.str == NULL)
-             && ((uStrCaseCmp(field.str, "groupswrap") == 0)
-                 || (uStrCaseCmp(field.str, "wrapgroups") == 0)))
-    {
-        if (!ExprResolveBoolean(stmt->value, &tmp))
-        {
-            ERROR("Illegal setting for global groupsWrap\n");
-            ACTION("Non-boolean value ignored\n");
-            ret = False;
-        }
-        else {
-            if (tmp.uval)
-                info->groupInfo = XkbWrapIntoRange;
-            else
-                info->groupInfo = XkbClampIntoRange;
-            ret = True;
-        }
+             && ((strcasecmp(field.str, "groupswrap") == 0) ||
+                 (strcasecmp(field.str, "wrapgroups") == 0))) {
+        ERROR("Global \"groupswrap\" not supported\n");
+        ACTION("Ignored\n");
+        ret = true;
     }
     else if ((elem.str == NULL)
-             && ((uStrCaseCmp(field.str, "groupsclamp") == 0)
-                 || (uStrCaseCmp(field.str, "clampgroups") == 0)))
-    {
-        if (!ExprResolveBoolean(stmt->value, &tmp))
-        {
-            ERROR("Illegal setting for global groupsClamp\n");
-            ACTION("Non-boolean value ignored\n");
-            return False;
-        }
-        else {
-            if (tmp.uval)
-                info->groupInfo = XkbClampIntoRange;
-            else
-                info->groupInfo = XkbWrapIntoRange;
-            ret = True;
-        }
+             && ((strcasecmp(field.str, "groupsclamp") == 0) ||
+                 (strcasecmp(field.str, "clampgroups") == 0))) {
+        ERROR("Global \"groupsclamp\" not supported\n");
+        ACTION("Ignored\n");
+        ret = true;
     }
     else if ((elem.str == NULL)
-             && ((uStrCaseCmp(field.str, "groupsredirect") == 0)
-                 || (uStrCaseCmp(field.str, "redirectgroups") == 0)))
-    {
-        if (!ExprResolveGroup(stmt->value, &tmp))
-        {
-            ERROR("Illegal group index for global groupsRedirect\n");
-            ACTION("Definition with non-integer group ignored\n");
-            ret = False;
-        }
-        else {
-            info->groupInfo = XkbSetGroupInfo(0, XkbRedirectIntoRange,
-                                              tmp.uval);
-            ret = True;
-        }
-    }
-    else if ((elem.str == NULL) && (uStrCaseCmp(field.str, "allownone") == 0))
-    {
-        ret = SetAllowNone(&info->dflt, arrayNdx, stmt->value);
+             && ((strcasecmp(field.str, "groupsredirect") == 0) ||
+                 (strcasecmp(field.str, "redirectgroups") == 0))) {
+        ERROR("Global \"groupsredirect\" not supported\n");
+        ACTION("Ignored\n");
+        ret = true;
+    }
+    else if ((elem.str == NULL) &&
+             (strcasecmp(field.str, "allownone") == 0)) {
+        ERROR("Radio groups not supported\n");
+        ACTION("Ignoring \"allownone\" specification\n");
+        ret = true;
     }
     else {
-        ret = SetActionField(xkb, elem.str, field.str, arrayNdx, stmt->value,
-                             &info->action);
+        ret = SetActionField(keymap, elem.str, field.str, arrayNdx,
+                             stmt->value, &info->action);
     }
 
     free(elem.str);
@@ -1428,25 +1226,21 @@ HandleSymbolsVar(VarDef * stmt, struct xkb_desc * xkb, SymbolsInfo * info)
     return ret;
 }
 
-static Bool
-HandleSymbolsBody(VarDef * def,
-                  struct xkb_desc * xkb, KeyInfo * key, SymbolsInfo * info)
+static bool
+HandleSymbolsBody(VarDef *def, struct xkb_keymap *keymap, KeyInfo *keyi,
+                  SymbolsInfo *info)
 {
-    Bool ok = True;
+    bool ok = true;
     ExprResult tmp, field;
     ExprDef *arrayNdx;
 
-    for (; def != NULL; def = (VarDef *) def->common.next)
-    {
-        if ((def->name) && (def->name->type == ExprFieldRef))
-        {
-            ok = HandleSymbolsVar(def, xkb, info);
+    for (; def != NULL; def = (VarDef *) def->common.next) {
+        if ((def->name) && (def->name->type == ExprFieldRef)) {
+            ok = HandleSymbolsVar(def, keymap, info);
             continue;
         }
-        else
-        {
-            if (def->name == NULL)
-            {
+        else {
+            if (def->name == NULL) {
                 if ((def->value == NULL)
                     || (def->value->op == ExprKeysymList))
                     field.str = strdup("symbols");
@@ -1454,12 +1248,12 @@ HandleSymbolsBody(VarDef * def,
                     field.str = strdup("actions");
                 arrayNdx = NULL;
             }
-            else
-            {
-                ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
+            else {
+                ok = ExprResolveLhs(keymap, def->name, &tmp, &field,
+                                    &arrayNdx);
             }
             if (ok)
-                ok = SetSymbolsField(key, xkb, field.str, arrayNdx,
+                ok = SetSymbolsField(keyi, keymap, field.str, arrayNdx,
                                      def->value, info);
             free(field.str);
         }
@@ -1467,109 +1261,100 @@ HandleSymbolsBody(VarDef * def,
     return ok;
 }
 
-static Bool
-SetExplicitGroup(SymbolsInfo * info, KeyInfo * key)
+static bool
+SetExplicitGroup(SymbolsInfo *info, KeyInfo *keyi)
 {
     unsigned group = info->explicit_group;
 
     if (group == 0)
-        return True;
+        return true;
 
-    if ((key->typesDefined | key->symsDefined | key->actsDefined) & ~1)
-    {
+    if ((keyi->typesDefined | keyi->symsDefined | keyi->actsDefined) & ~1) {
         int i;
         WARN("For the map %s an explicit group specified\n", info->name);
         WARN("but key %s has more than one group defined\n",
-              longText(key->name));
+             longText(keyi->name));
         ACTION("All groups except first one will be ignored\n");
-        for (i = 1; i < XkbNumKbdGroups; i++)
-        {
-            key->numLevels[i] = 0;
-            free(key->syms[i]);
-            key->syms[i] = NULL;
-            free(key->acts[i]);
-            key->acts[i] = NULL;
-            key->types[i] = 0;
-        }
-    }
-    key->typesDefined = key->symsDefined = key->actsDefined = 1 << group;
-
-    key->numLevels[group] = key->numLevels[0];
-    key->numLevels[0] = 0;
-    key->syms[group] = key->syms[0];
-    key->syms[0] = NULL;
-    key->acts[group] = key->acts[0];
-    key->acts[0] = NULL;
-    key->types[group] = key->types[0];
-    key->types[0] = 0;
-    return True;
+        for (i = 1; i < XkbNumKbdGroups; i++) {
+            keyi->numLevels[i] = 0;
+            darray_free(keyi->syms[i]);
+            darray_free(keyi->acts[i]);
+            keyi->types[i] = 0;
+        }
+    }
+    keyi->typesDefined = keyi->symsDefined = keyi->actsDefined = 1 << group;
+
+    keyi->numLevels[group] = keyi->numLevels[0];
+    keyi->numLevels[0] = 0;
+    keyi->syms[group] = keyi->syms[0];
+    darray_init(keyi->syms[0]);
+    keyi->symsMapIndex[group] = keyi->symsMapIndex[0];
+    darray_init(keyi->symsMapIndex[0]);
+    keyi->symsMapNumEntries[group] = keyi->symsMapNumEntries[0];
+    darray_init(keyi->symsMapNumEntries[0]);
+    keyi->acts[group] = keyi->acts[0];
+    darray_init(keyi->acts[0]);
+    keyi->types[group] = keyi->types[0];
+    keyi->types[0] = 0;
+    return true;
 }
 
 static int
-HandleSymbolsDef(SymbolsDef * stmt,
-                 struct xkb_desc * xkb, unsigned merge, SymbolsInfo * info)
+HandleSymbolsDef(SymbolsDef *stmt, struct xkb_keymap *keymap,
+                 SymbolsInfo *info)
 {
-    KeyInfo key;
+    KeyInfo keyi;
 
-    InitKeyInfo(&key);
-    CopyKeyInfo(&info->dflt, &key, False);
-    key.defs.merge = stmt->merge;
-    key.name = KeyNameToLong(stmt->keyName);
-    if (!HandleSymbolsBody((VarDef *) stmt->symbols, xkb, &key, info))
-    {
+    InitKeyInfo(&keyi, info->file_id);
+    CopyKeyInfo(&info->dflt, &keyi, false);
+    keyi.defs.merge = stmt->merge;
+    keyi.name = KeyNameToLong(stmt->keyName);
+    if (!HandleSymbolsBody((VarDef *) stmt->symbols, keymap, &keyi, info)) {
         info->errorCount++;
-        return False;
+        return false;
     }
 
-    if (!SetExplicitGroup(info, &key))
-    {
+    if (!SetExplicitGroup(info, &keyi)) {
         info->errorCount++;
-        return False;
+        return false;
     }
 
-    if (!AddKeySymbols(info, &key, xkb))
-    {
+    if (!AddKeySymbols(info, &keyi, keymap)) {
         info->errorCount++;
-        return False;
+        return false;
     }
-    return True;
+    return true;
 }
 
-static Bool
-HandleModMapDef(ModMapDef * def,
-                struct xkb_desc * xkb, unsigned merge, SymbolsInfo * info)
+static bool
+HandleModMapDef(ModMapDef *def, struct xkb_keymap *keymap, SymbolsInfo *info)
 {
     ExprDef *key;
     ModMapEntry tmp;
     ExprResult rtrn;
-    Bool ok;
+    bool ok;
 
-    if (!LookupModIndex(NULL, def->modifier, TypeInt, &rtrn))
-    {
+    if (!LookupModIndex(keymap->ctx, NULL, def->modifier, TypeInt, &rtrn)) {
         ERROR("Illegal modifier map definition\n");
         ACTION("Ignoring map for non-modifier \"%s\"\n",
-                XkbcAtomText(def->modifier));
-        return False;
+               xkb_atom_text(keymap->ctx, def->modifier));
+        return false;
     }
-    ok = True;
+    ok = true;
     tmp.modifier = rtrn.uval;
-    for (key = def->keys; key != NULL; key = (ExprDef *) key->common.next)
-    {
-        if ((key->op == ExprValue) && (key->type == TypeKeyName))
-        {
-            tmp.haveSymbol = False;
+    for (key = def->keys; key != NULL; key = (ExprDef *) key->common.next) {
+        if ((key->op == ExprValue) && (key->type == TypeKeyName)) {
+            tmp.haveSymbol = false;
             tmp.u.keyName = KeyNameToLong(key->value.keyName);
         }
-        else if (ExprResolveKeySym(key, &rtrn))
-        {
-            tmp.haveSymbol = True;
+        else if (ExprResolveKeySym(keymap->ctx, key, &rtrn)) {
+            tmp.haveSymbol = true;
             tmp.u.keySym = rtrn.uval;
         }
-        else
-        {
+        else {
             ERROR("Modmap entries may contain only key names or keysyms\n");
             ACTION("Illegal definition for %s modifier ignored\n",
-                    XkbcModIndexText(tmp.modifier));
+                   XkbcModIndexText(tmp.modifier));
             continue;
         }
 
@@ -1579,32 +1364,31 @@ HandleModMapDef(ModMapDef * def,
 }
 
 static void
-HandleSymbolsFile(XkbFile * file,
-                  struct xkb_desc * xkb, unsigned merge, SymbolsInfo * info)
+HandleSymbolsFile(XkbFile *file, struct xkb_keymap *keymap,
+                  enum merge_mode merge, SymbolsInfo *info)
 {
     ParseCommon *stmt;
 
-    info->name = _XkbDupString(file->name);
+    free(info->name);
+    info->name = uDupString(file->name);
     stmt = file->defs;
     while (stmt)
     {
-        switch (stmt->stmtType)
-        {
+        switch (stmt->stmtType) {
         case StmtInclude:
-            if (!HandleIncludeSymbols((IncludeStmt *) stmt, xkb, info,
-                                      HandleSymbolsFile))
+            if (!HandleIncludeSymbols((IncludeStmt *) stmt, keymap, info))
                 info->errorCount++;
             break;
         case StmtSymbolsDef:
-            if (!HandleSymbolsDef((SymbolsDef *) stmt, xkb, merge, info))
+            if (!HandleSymbolsDef((SymbolsDef *) stmt, keymap, info))
                 info->errorCount++;
             break;
         case StmtVarDef:
-            if (!HandleSymbolsVar((VarDef *) stmt, xkb, info))
+            if (!HandleSymbolsVar((VarDef *) stmt, keymap, info))
                 info->errorCount++;
             break;
         case StmtVModDef:
-            if (!HandleVModDef((VModDef *) stmt, xkb, merge, &info->vmods))
+            if (!HandleVModDef((VModDef *) stmt, keymap, merge, &info->vmods))
                 info->errorCount++;
             break;
         case StmtInterpDef:
@@ -1618,17 +1402,16 @@ HandleSymbolsFile(XkbFile * file,
             info->errorCount++;
             break;
         case StmtModMapDef:
-            if (!HandleModMapDef((ModMapDef *) stmt, xkb, merge, info))
+            if (!HandleModMapDef((ModMapDef *) stmt, keymap, info))
                 info->errorCount++;
             break;
         default:
             WSGO("Unexpected statement type %d in HandleSymbolsFile\n",
-                  stmt->stmtType);
+                 stmt->stmtType);
             break;
         }
         stmt = stmt->next;
-        if (info->errorCount > 10)
-        {
+        if (info->errorCount > 10) {
 #ifdef NOISY
             ERROR("Too many errors\n");
 #endif
@@ -1638,59 +1421,78 @@ HandleSymbolsFile(XkbFile * file,
     }
 }
 
-static Bool
-FindKeyForSymbol(struct xkb_desc * xkb, uint32_t sym, xkb_keycode_t *kc_rtrn)
+/**
+ * Given a keysym @sym, return a key which generates it, or NULL.
+ * This is used for example in a modifier map definition, such as:
+ *      modifier_map Lock           { Caps_Lock };
+ * where we want to add the Lock modifier to the modmap of the key
+ * which matches the keysym Caps_Lock.
+ * Since there can be many keys which generates the keysym, the key
+ * is chosen first by lowest group in which the keysym appears, than
+ * by lowest level and than by lowest key code.
+ */
+static struct xkb_key *
+FindKeyForSymbol(struct xkb_keymap *keymap, xkb_keysym_t sym)
 {
-    int i, j;
-    Bool gotOne;
+    struct xkb_key *key, *ret = NULL;
+    unsigned int group, level, min_group = UINT_MAX, min_level = UINT_MAX;
+
+    xkb_foreach_key(key, keymap) {
+        for (group = 0; group < key->num_groups; group++) {
+            for (level = 0; level < XkbKeyGroupWidth(keymap, key, group);
+                 level++) {
+                if (XkbKeyNumSyms(key, group, level) != 1 ||
+                    (XkbKeySymEntry(key, group, level))[0] != sym)
+                    continue;
 
-    j = 0;
-    do
-    {
-        gotOne = False;
-        for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++)
-        {
-            if (j < (int) XkbKeyNumSyms(xkb, i))
-            {
-                gotOne = True;
-                if (XkbKeySym(xkb, i, j) == sym)
-                {
-                    *kc_rtrn = i;
-                    return True;
+                /*
+                 * If the keysym was found in a group or level > 0, we must
+                 * keep looking since we might find a key in which the keysym
+                 * is in a lower group or level.
+                 */
+                if (group < min_group ||
+                    (group == min_group && level < min_level)) {
+                    ret = key;
+                    if (group == 0 && level == 0) {
+                        return ret;
+                    }
+                    else {
+                        min_group = group;
+                        min_level = level;
+                    }
                 }
             }
         }
-        j++;
     }
-    while (gotOne);
-    return False;
+
+    return ret;
 }
 
 /**
- * Find the given name in the xkb->map->types and return its index.
+ * Find the given name in the keymap->map->types and return its index.
  *
- * @param name The atom to search for.
+ * @param atom The atom to search for.
  * @param type_rtrn Set to the index of the name if found.
  *
- * @return True if found, False otherwise.
+ * @return true if found, false otherwise.
  */
-static Bool
-FindNamedType(struct xkb_desc * xkb, uint32_t name, unsigned *type_rtrn)
+static bool
+FindNamedType(struct xkb_keymap *keymap, xkb_atom_t atom, unsigned *type_rtrn)
 {
-    unsigned n;
+    unsigned n = 0;
+    const char *name = xkb_atom_text(keymap->ctx, atom);
+    struct xkb_key_type *type;
 
-    if (xkb && xkb->map && xkb->map->types)
-    {
-        for (n = 0; n < xkb->map->num_types; n++)
-        {
-            if (xkb->map->types[n].name == (uint32_t) name)
-            {
+    if (keymap) {
+        darray_foreach(type, keymap->types) {
+            if (strcmp(type->name, name) == 0) {
                 *type_rtrn = n;
-                return True;
+                return true;
             }
+            n++;
         }
     }
-    return False;
+    return false;
 }
 
 /**
@@ -1707,48 +1509,52 @@ FindNamedType(struct xkb_desc * xkb, uint32_t name, unsigned *type_rtrn)
  * @param syms The keysyms for the given key (must be size width).
  * @param typeNameRtrn Set to the Atom of the type name.
  *
- * @returns True if a type could be found, False otherwise.
+ * @returns true if a type could be found, false otherwise.
+ *
+ * FIXME: I need to take the KeyInfo so I can look at symsMapIndex and
+ *        all that fun stuff rather than just assuming there's always one
+ *        symbol per level.
  */
-static Bool
-FindAutomaticType(int width, uint32_t * syms, uint32_t * typeNameRtrn,
-                  Bool * autoType)
+static bool
+FindAutomaticType(struct xkb_keymap *keymap, int width,
+                  const xkb_keysym_t *syms, xkb_atom_t *typeNameRtrn,
+                  bool *autoType)
 {
-    *autoType = False;
-    if ((width == 1) || (width == 0))
-    {
-        *typeNameRtrn = xkb_intern_atom("ONE_LEVEL");
-        *autoType = True;
+    *autoType = false;
+    if ((width == 1) || (width == 0)) {
+        *typeNameRtrn = xkb_atom_intern(keymap->ctx, "ONE_LEVEL");
+        *autoType = true;
     }
-    else if (width == 2)
-    {
-        if (syms && XkbcKSIsLower(syms[0]) && XkbcKSIsUpper(syms[1]))
-        {
-            *typeNameRtrn = xkb_intern_atom("ALPHABETIC");
+    else if (width == 2) {
+        if (syms && xkb_keysym_is_lower(syms[0]) &&
+            xkb_keysym_is_upper(syms[1])) {
+            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "ALPHABETIC");
         }
-        else if (syms && (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1])))
-        {
-            *typeNameRtrn = xkb_intern_atom("KEYPAD");
-            *autoType = True;
+        else if (syms && (xkb_keysym_is_keypad(syms[0]) ||
+                          xkb_keysym_is_keypad(syms[1]))) {
+            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "KEYPAD");
+            *autoType = true;
         }
-        else
-        {
-            *typeNameRtrn = xkb_intern_atom("TWO_LEVEL");
-            *autoType = True;
+        else {
+            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "TWO_LEVEL");
+            *autoType = true;
         }
     }
-    else if (width <= 4)
-    {
-        if (syms && XkbcKSIsLower(syms[0]) && XkbcKSIsUpper(syms[1]))
-            if (XkbcKSIsLower(syms[2]) && XkbcKSIsUpper(syms[3]))
+    else if (width <= 4) {
+        if (syms && xkb_keysym_is_lower(syms[0]) &&
+            xkb_keysym_is_upper(syms[1]))
+            if (xkb_keysym_is_lower(syms[2]) && xkb_keysym_is_upper(syms[3]))
                 *typeNameRtrn =
-                    xkb_intern_atom("FOUR_LEVEL_ALPHABETIC");
+                    xkb_atom_intern(keymap->ctx, "FOUR_LEVEL_ALPHABETIC");
             else
-                *typeNameRtrn = xkb_intern_atom("FOUR_LEVEL_SEMIALPHABETIC");
+                *typeNameRtrn = xkb_atom_intern(keymap->ctx,
+                                                "FOUR_LEVEL_SEMIALPHABETIC");
 
-        else if (syms && (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1])))
-            *typeNameRtrn = xkb_intern_atom("FOUR_LEVEL_KEYPAD");
+        else if (syms && (xkb_keysym_is_keypad(syms[0]) ||
+                          xkb_keysym_is_keypad(syms[1])))
+            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "FOUR_LEVEL_KEYPAD");
         else
-            *typeNameRtrn = xkb_intern_atom("FOUR_LEVEL");
+            *typeNameRtrn = xkb_atom_intern(keymap->ctx, "FOUR_LEVEL");
         /* XXX: why not set autoType here? */
     }
     return ((width >= 0) && (width <= 4));
@@ -1759,15 +1565,14 @@ FindAutomaticType(int width, uint32_t * syms, uint32_t * typeNameRtrn,
  * groups, and reduce to one group if all groups are identical anyway.
  */
 static void
-PrepareKeyDef(KeyInfo * key)
+PrepareKeyDef(KeyInfo *keyi)
 {
     int i, j, width, defined, lastGroup;
-    Bool identical;
+    bool identical;
 
-    defined = key->symsDefined | key->actsDefined | key->typesDefined;
+    defined = keyi->symsDefined | keyi->actsDefined | keyi->typesDefined;
     /* get highest group number */
-    for (i = XkbNumKbdGroups - 1; i >= 0; i--)
-    {
+    for (i = XkbNumKbdGroups - 1; i >= 0; i--) {
         if (defined & (1 << i))
             break;
     }
@@ -1779,83 +1584,89 @@ PrepareKeyDef(KeyInfo * key)
     /* If there are empty groups between non-empty ones fill them with data */
     /* from the first group. */
     /* We can make a wrong assumption here. But leaving gaps is worse. */
-    for (i = lastGroup; i > 0; i--)
-    {
+    for (i = lastGroup; i > 0; i--) {
         if (defined & (1 << i))
             continue;
-        width = key->numLevels[0];
-        if (key->typesDefined & 1)
-        {
-            for (j = 0; j < width; j++)
-            {
-                key->types[i] = key->types[0];
+        width = keyi->numLevels[0];
+        if (keyi->typesDefined & 1) {
+            for (j = 0; j < width; j++) {
+                keyi->types[i] = keyi->types[0];
             }
-            key->typesDefined |= 1 << i;
+            keyi->typesDefined |= 1 << i;
         }
-        if ((key->actsDefined & 1) && key->acts[0])
-        {
-            key->acts[i] = uTypedCalloc(width, union xkb_action);
-            if (key->acts[i] == NULL)
-                continue;
-            memcpy(key->acts[i], key->acts[0],
-                   width * sizeof(union xkb_action));
-            key->actsDefined |= 1 << i;
-        }
-        if ((key->symsDefined & 1) && key->syms[0])
-        {
-            key->syms[i] = uTypedCalloc(width, uint32_t);
-            if (key->syms[i] == NULL)
-                continue;
-            memcpy(key->syms[i], key->syms[0], width * sizeof(uint32_t));
-            key->symsDefined |= 1 << i;
+        if ((keyi->actsDefined & 1) && !darray_empty(keyi->acts[0])) {
+            darray_copy(keyi->acts[i], keyi->acts[0]);
+            keyi->actsDefined |= 1 << i;
+        }
+        if ((keyi->symsDefined & 1) && !darray_empty(keyi->syms[0])) {
+            darray_copy(keyi->syms[i], keyi->syms[0]);
+            darray_copy(keyi->symsMapIndex[i], keyi->symsMapIndex[0]);
+            darray_copy(keyi->symsMapNumEntries[i],
+                        keyi->symsMapNumEntries[0]);
+            keyi->symsDefined |= 1 << i;
         }
-        if (defined & 1)
-        {
-            key->numLevels[i] = key->numLevels[0];
+        if (defined & 1) {
+            keyi->numLevels[i] = keyi->numLevels[0];
         }
     }
     /* If all groups are completely identical remove them all */
     /* exept the first one. */
-    identical = True;
-    for (i = lastGroup; i > 0; i--)
-    {
-        if ((key->numLevels[i] != key->numLevels[0]) ||
-            (key->types[i] != key->types[0]))
-        {
-            identical = False;
+    identical = true;
+    for (i = lastGroup; i > 0; i--) {
+        if ((keyi->numLevels[i] != keyi->numLevels[0]) ||
+            (keyi->types[i] != keyi->types[0])) {
+            identical = false;
             break;
         }
-        if ((key->syms[i] != key->syms[0]) &&
-            (key->syms[i] == NULL || key->syms[0] == NULL ||
-             memcmp(key->syms[i], key->syms[0],
-                    sizeof(uint32_t) * key->numLevels[0])))
-        {
-            identical = False;
+        if (!darray_same(keyi->syms[i], keyi->syms[0]) &&
+            (darray_empty(keyi->syms[i]) || darray_empty(keyi->syms[0]) ||
+             darray_size(keyi->syms[i]) != darray_size(keyi->syms[0]) ||
+             memcmp(darray_mem(keyi->syms[i], 0),
+                    darray_mem(keyi->syms[0], 0),
+                   sizeof(xkb_keysym_t) * darray_size(keyi->syms[0])))) {
+            identical = false;
             break;
         }
-        if ((key->acts[i] != key->acts[0]) &&
-            (key->acts[i] == NULL || key->acts[0] == NULL ||
-             memcmp(key->acts[i], key->acts[0],
-                    sizeof(union xkb_action) * key->numLevels[0])))
-        {
-            identical = False;
+        if (!darray_same(keyi->symsMapIndex[i], keyi->symsMapIndex[0]) &&
+            (darray_empty(keyi->symsMapIndex[i]) ||
+             darray_empty(keyi->symsMapIndex[0]) ||
+             memcmp(darray_mem(keyi->symsMapIndex[i], 0),
+                    darray_mem(keyi->symsMapIndex[0], 0),
+                    keyi->numLevels[0] * sizeof(int)))) {
+            identical = false;
+            continue;
+        }
+        if (!darray_same(keyi->symsMapNumEntries[i],
+                         keyi->symsMapNumEntries[0]) &&
+            (darray_empty(keyi->symsMapNumEntries[i]) ||
+             darray_empty(keyi->symsMapNumEntries[0]) ||
+             memcmp(darray_mem(keyi->symsMapNumEntries[i], 0),
+                    darray_mem(keyi->symsMapNumEntries[0], 0),
+                    keyi->numLevels[0] * sizeof(size_t)))) {
+            identical = false;
+            continue;
+        }
+        if (!darray_same(keyi->acts[i], keyi->acts[0]) &&
+            (darray_empty(keyi->acts[i]) || darray_empty(keyi->acts[0]) ||
+             memcmp(darray_mem(keyi->acts[i], 0),
+                    darray_mem(keyi->acts[0], 0),
+                    keyi->numLevels[0] * sizeof(union xkb_action)))) {
+            identical = false;
             break;
         }
     }
-    if (identical)
-    {
-        for (i = lastGroup; i > 0; i--)
-        {
-            key->numLevels[i] = 0;
-            free(key->syms[i]);
-            key->syms[i] = NULL;
-            free(key->acts[i]);
-            key->acts[i] = NULL;
-            key->types[i] = 0;
-        }
-        key->symsDefined &= 1;
-        key->actsDefined &= 1;
-        key->typesDefined &= 1;
+    if (identical) {
+        for (i = lastGroup; i > 0; i--) {
+            keyi->numLevels[i] = 0;
+            darray_free(keyi->syms[i]);
+            darray_free(keyi->symsMapIndex[i]);
+            darray_free(keyi->symsMapNumEntries[i]);
+            darray_free(keyi->acts[i]);
+            keyi->types[i] = 0;
+        }
+        keyi->symsDefined &= 1;
+        keyi->actsDefined &= 1;
+        keyi->typesDefined &= 1;
     }
 }
 
@@ -1864,135 +1675,114 @@ PrepareKeyDef(KeyInfo * key)
  *
  * This function recurses.
  */
-static Bool
-CopySymbolsDef(struct xkb_desc * xkb, KeyInfo *key, int start_from)
+static bool
+CopySymbolsDef(struct xkb_keymap *keymap, KeyInfo *keyi, int start_from)
 {
-    int i;
-    xkb_keycode_t okc, kc;
+    unsigned int i;
+    xkb_keycode_t kc;
+    struct xkb_key *key;
+    unsigned int sizeSyms = 0;
     unsigned width, tmp, nGroups;
     struct xkb_key_type * type;
-    Bool haveActions, autoType, useAlias;
-    uint32_t *outSyms;
-    union xkb_action *outActs;
+    bool haveActions, autoType, useAlias;
     unsigned types[XkbNumKbdGroups];
+    union xkb_action *outActs;
+    unsigned int symIndex = 0;
 
     useAlias = (start_from == 0);
 
-    /* get the keycode for the key. */
-    if (!FindNamedKey(xkb, key->name, &kc, useAlias, CreateKeyNames(xkb),
-                      start_from))
-    {
-        if ((start_from == 0) && (warningLevel >= 5))
-        {
-            WARN("Key %s not found in %s keycodes\n",
-                  longText(key->name),
-                  XkbcAtomText(xkb->names->keycodes));
+    key = FindNamedKey(keymap, keyi->name, useAlias,
+                       CreateKeyNames(keymap), start_from);
+    if (!key) {
+        if (start_from == 0 && warningLevel >= 5) {
+            WARN("Key %s not found in keycodes\n", longText(keyi->name));
             ACTION("Symbols ignored\n");
         }
-        return False;
+        return false;
     }
+    kc = XkbKeyGetKeycode(keymap, key);
 
-    haveActions = False;
-    for (i = width = nGroups = 0; i < XkbNumKbdGroups; i++)
-    {
+    haveActions = false;
+    for (i = width = nGroups = 0; i < XkbNumKbdGroups; i++) {
         if (((i + 1) > nGroups)
-            && (((key->symsDefined | key->actsDefined) & (1 << i))
-                || (key->typesDefined) & (1 << i)))
+            && (((keyi->symsDefined | keyi->actsDefined) & (1 << i))
+                || (keyi->typesDefined) & (1 << i)))
             nGroups = i + 1;
-        if (key->acts[i])
-            haveActions = True;
-        autoType = False;
+        if (!darray_empty(keyi->acts[i]))
+            haveActions = true;
+        autoType = false;
         /* Assign the type to the key, if it is missing. */
-        if (key->types[i] == None)
-        {
-            if (key->dfltType != None)
-                key->types[i] = key->dfltType;
-            else if (FindAutomaticType(key->numLevels[i], key->syms[i],
-                                       &key->types[i], &autoType))
-            {
-            }
-            else
-            {
-                if (warningLevel >= 5)
-                {
+        if (keyi->types[i] == XKB_ATOM_NONE) {
+            if (keyi->dfltType != XKB_ATOM_NONE)
+                keyi->types[i] = keyi->dfltType;
+            else if (FindAutomaticType(keymap, keyi->numLevels[i],
+                                       darray_mem(keyi->syms[i], 0),
+                                       &keyi->types[i], &autoType)) { }
+            else {
+                if (warningLevel >= 5) {
                     WARN("No automatic type for %d symbols\n",
-                          (unsigned int) key->numLevels[i]);
+                          (unsigned int) keyi->numLevels[i]);
                     ACTION("Using %s for the %s key (keycode %d)\n",
-                            XkbcAtomText(key->types[i]),
-                            longText(key->name), kc);
+                            xkb_atom_text(keymap->ctx, keyi->types[i]),
+                            longText(keyi->name), kc);
                 }
             }
         }
-        if (FindNamedType(xkb, key->types[i], &types[i]))
-        {
-            if (!autoType || key->numLevels[i] > 2)
-                xkb->server->explicit[kc] |= (1 << i);
+        if (FindNamedType(keymap, keyi->types[i], &types[i])) {
+            if (!autoType || keyi->numLevels[i] > 2)
+                key->explicit |= (1 << i);
         }
-        else
-        {
-            if (warningLevel >= 3)
-            {
+        else {
+            if (warningLevel >= 3) {
                 WARN("Type \"%s\" is not defined\n",
-                      XkbcAtomText(key->types[i]));
+                     xkb_atom_text(keymap->ctx, keyi->types[i]));
                 ACTION("Using TWO_LEVEL for the %s key (keycode %d)\n",
-                        longText(key->name), kc);
+                       longText(keyi->name), kc);
             }
             types[i] = XkbTwoLevelIndex;
         }
-        /* if the type specifies less syms than the key has, shrink the key */
-        type = &xkb->map->types[types[i]];
-        if (type->num_levels < key->numLevels[i])
-        {
-            if (warningLevel > 0)
-            {
-                WARN
-                    ("Type \"%s\" has %d levels, but %s has %d symbols\n",
-                     XkbcAtomText(type->name),
-                     (unsigned int) type->num_levels,
-                     longText(key->name),
-                     (unsigned int) key->numLevels[i]);
+        /* if the type specifies fewer levels than the key has, shrink the key */
+        type = &darray_item(keymap->types, types[i]);
+        if (type->num_levels < keyi->numLevels[i]) {
+            if (warningLevel > 0) {
+                WARN("Type \"%s\" has %d levels, but %s has %d symbols\n",
+                     type->name, type->num_levels,
+                     xkb_atom_text(keymap->ctx, keyi->name), keyi->numLevels[i]);
                 ACTION("Ignoring extra symbols\n");
             }
-            key->numLevels[i] = type->num_levels;
+            keyi->numLevels[i] = type->num_levels;
         }
-        if (key->numLevels[i] > width)
-            width = key->numLevels[i];
+        if (keyi->numLevels[i] > width)
+            width = keyi->numLevels[i];
         if (type->num_levels > width)
             width = type->num_levels;
+        sizeSyms += darray_size(keyi->syms[i]);
     }
 
-    /* width is now the largest width found */
+    darray_resize0(key->syms, sizeSyms);
 
-    i = width * nGroups;
-    outSyms = XkbcResizeKeySyms(xkb, kc, i);
-    if (outSyms == NULL)
-    {
-        WSGO("Could not enlarge symbols for %s (keycode %d)\n",
-              longText(key->name), kc);
-        return False;
-    }
-    if (haveActions)
-    {
-        outActs = XkbcResizeKeyActions(xkb, kc, i);
-        if (outActs == NULL)
-        {
+    if (haveActions) {
+        outActs = XkbcResizeKeyActions(keymap, key, width * nGroups);
+        if (outActs == NULL) {
             WSGO("Could not enlarge actions for %s (key %d)\n",
-                  longText(key->name), kc);
-            return False;
+                 longText(keyi->name), kc);
+            return false;
         }
-        xkb->server->explicit[kc] |= XkbExplicitInterpretMask;
+        key->explicit |= XkbExplicitInterpretMask;
     }
     else
         outActs = NULL;
-    if (key->defs.defined & _Key_GroupInfo)
-        i = key->groupInfo;
-    else
-        i = xkb->map->key_sym_map[kc].group_info;
 
-    xkb->map->key_sym_map[kc].group_info = XkbSetNumGroups(i, nGroups);
-    xkb->map->key_sym_map[kc].width = width;
-    for (i = 0; i < nGroups; i++)
-    {
+    key->num_groups = nGroups;
+    if (keyi->defs.defined & _Key_GroupInfo) {
+        key->out_of_range_group_number = keyi->out_of_range_group_number;
+        key->out_of_range_group_action = keyi->out_of_range_group_action;
+    }
+    key->width = width;
+    key->sym_index = calloc(nGroups * width, sizeof(*key->sym_index));
+    key->num_syms = calloc(nGroups * width, sizeof(*key->num_syms));
+
+    for (i = 0; i < nGroups; i++) {
         /* assign kt_index[i] to the index of the type in map->types.
          * kt_index[i] may have been set by a previous run (if we have two
          * layouts specified). Let's not overwrite it with the ONE_LEVEL
@@ -2000,221 +1790,170 @@ CopySymbolsDef(struct xkb_desc * xkb, KeyInfo *key, int start_from)
          *
          * FIXME: There should be a better fix for this.
          */
-        if (key->numLevels[i])
-            xkb->map->key_sym_map[kc].kt_index[i] = types[i];
-        if (key->syms[i] != NULL)
-        {
+        if (keyi->numLevels[i])
+            key->kt_index[i] = types[i];
+        if (!darray_empty(keyi->syms[i])) {
             /* fill key to "width" symbols*/
-            for (tmp = 0; tmp < width; tmp++)
-            {
-                if (tmp < key->numLevels[i])
-                    outSyms[tmp] = key->syms[i][tmp];
-                else
-                    outSyms[tmp] = NoSymbol;
-                if ((outActs != NULL) && (key->acts[i] != NULL))
-                {
-                    if (tmp < key->numLevels[i])
-                        outActs[tmp] = key->acts[i][tmp];
+            for (tmp = 0; tmp < width; tmp++) {
+                if (tmp < keyi->numLevels[i] &&
+                    darray_item(keyi->symsMapNumEntries[i], tmp) != 0) {
+                    memcpy(darray_mem(key->syms, symIndex),
+                           darray_mem(keyi->syms[i],
+                                      darray_item(keyi->symsMapIndex[i], tmp)),
+                           darray_item(keyi->symsMapNumEntries[i],
+                                       tmp) * sizeof(xkb_keysym_t));
+                    key->sym_index[(i * width) + tmp] = symIndex;
+                    key->num_syms[(i * width) + tmp] =
+                        darray_item(keyi->symsMapNumEntries[i], tmp);
+                    symIndex += key->num_syms[(i * width) + tmp];
+                }
+                else {
+                    key->sym_index[(i * width) + tmp] = -1;
+                    key->num_syms[(i * width) + tmp] = 0;
+                }
+                if (outActs != NULL && !darray_empty(keyi->acts[i])) {
+                    if (tmp < keyi->numLevels[i])
+                        outActs[tmp] = darray_item(keyi->acts[i], tmp);
                     else
                         outActs[tmp].type = XkbSA_NoAction;
                 }
             }
         }
-        outSyms += width;
-        if (outActs)
-            outActs += width;
     }
-    switch (key->behavior.type & XkbKB_OpMask)
-    {
+    switch (keyi->behavior.type & XkbKB_OpMask) {
     case XkbKB_Default:
         break;
-    case XkbKB_Overlay1:
-    case XkbKB_Overlay2:
-        /* find key by name! */
-        if (!FindNamedKey(xkb, key->nameForOverlayKey, &okc, True,
-                          CreateKeyNames(xkb), 0))
-        {
-            if (warningLevel >= 1)
-            {
-                WARN("Key %s not found in %s keycodes\n",
-                      longText(key->nameForOverlayKey),
-                      XkbcAtomText(xkb->names->keycodes));
-                ACTION("Not treating %s as an overlay key \n",
-                        longText(key->name));
-            }
-            break;
-        }
-        key->behavior.data = okc;
+
     default:
-        xkb->server->behaviors[kc] = key->behavior;
-        xkb->server->explicit[kc] |= XkbExplicitBehaviorMask;
+        key->behavior = keyi->behavior;
+        key->explicit |= XkbExplicitBehaviorMask;
         break;
     }
-    if (key->defs.defined & _Key_VModMap)
-    {
-        xkb->server->vmodmap[kc] = key->vmodmap;
-        xkb->server->explicit[kc] |= XkbExplicitVModMapMask;
+    if (keyi->defs.defined & _Key_VModMap) {
+        key->vmodmap = keyi->vmodmap;
+        key->explicit |= XkbExplicitVModMapMask;
     }
-    if (key->repeat != RepeatUndefined)
-    {
-        if (key->repeat == RepeatYes)
-            xkb->ctrls->per_key_repeat[kc / 8] |= (1 << (kc % 8));
-        else
-            xkb->ctrls->per_key_repeat[kc / 8] &= ~(1 << (kc % 8));
-        xkb->server->explicit[kc] |= XkbExplicitAutoRepeatMask;
+    if (keyi->repeat != RepeatUndefined) {
+        key->repeats = keyi->repeat == RepeatYes;
+        key->explicit |= XkbExplicitAutoRepeatMask;
     }
 
-    if (nGroups > xkb->ctrls->num_groups)
-       xkb->ctrls->num_groups = nGroups;
-
     /* do the same thing for the next key */
-    CopySymbolsDef(xkb, key, kc + 1);
-    return True;
+    CopySymbolsDef(keymap, keyi, kc + 1);
+    return true;
 }
 
-static Bool
-CopyModMapDef(struct xkb_desc * xkb, ModMapEntry *entry)
+static bool
+CopyModMapDef(struct xkb_keymap *keymap, ModMapEntry *entry)
 {
-    xkb_keycode_t kc;
+    struct xkb_key *key;
+
+    if (!entry->haveSymbol) {
+        key = FindNamedKey(keymap, entry->u.keyName, true,
+                           CreateKeyNames(keymap), 0);
+        if (!key) {
+            if (warningLevel >= 5) {
+                WARN("Key %s not found in keycodes\n",
+                     longText(entry->u.keyName));
+                ACTION("Modifier map entry for %s not updated\n",
+                       XkbcModIndexText(entry->modifier));
+            }
+            return false;
+        }
+    }
+    else {
+        key = FindKeyForSymbol(keymap, entry->u.keySym);
+        if (!key) {
+            if (warningLevel > 5) {
+                WARN("Key \"%s\" not found in symbol map\n",
+                     XkbcKeysymText(entry->u.keySym));
+                ACTION("Modifier map entry for %s not updated\n",
+                       XkbcModIndexText(entry->modifier));
+            }
+            return false;
+        }
+    }
 
-    if ((!entry->haveSymbol)
-        &&
-        (!FindNamedKey
-         (xkb, entry->u.keyName, &kc, True, CreateKeyNames(xkb), 0)))
-    {
-        if (warningLevel >= 5)
-        {
-            WARN("Key %s not found in %s keycodes\n",
-                  longText(entry->u.keyName),
-                  XkbcAtomText(xkb->names->keycodes));
-            ACTION("Modifier map entry for %s not updated\n",
-                    XkbcModIndexText(entry->modifier));
-        }
-        return False;
-    }
-    else if (entry->haveSymbol
-             && (!FindKeyForSymbol(xkb, entry->u.keySym, &kc)))
-    {
-        if (warningLevel > 5)
-        {
-            WARN("Key \"%s\" not found in %s symbol map\n",
-                  XkbcKeysymText(entry->u.keySym),
-                  XkbcAtomText(xkb->names->symbols));
-            ACTION("Modifier map entry for %s not updated\n",
-                    XkbcModIndexText(entry->modifier));
-        }
-        return False;
-    }
-    xkb->map->modmap[kc] |= (1 << entry->modifier);
-    return True;
+    key->modmap |= (1 << entry->modifier);
+    return true;
 }
 
 /**
  * Handle the xkb_symbols section of an xkb file.
  *
  * @param file The parsed xkb_symbols section of the xkb file.
- * @param xkb Handle to the keyboard description to store the symbols in.
- * @param merge Merge strategy (e.g. MergeOverride).
+ * @param keymap Handle to the keyboard description to store the symbols in.
+ * @param merge Merge strategy (e.g. MERGE_OVERRIDE).
  */
-Bool
-CompileSymbols(XkbFile *file, struct xkb_desc * xkb, unsigned merge)
+bool
+CompileSymbols(XkbFile *file, struct xkb_keymap *keymap,
+               enum merge_mode merge)
 {
     int i;
+    struct xkb_key *key;
     SymbolsInfo info;
+    KeyInfo *keyi;
 
-    InitSymbolsInfo(&info, xkb);
-    info.dflt.defs.fileID = file->id;
+    InitSymbolsInfo(&info, keymap, file->id);
     info.dflt.defs.merge = merge;
-    HandleSymbolsFile(file, xkb, merge, &info);
 
-    if (info.nKeys == 0) {
-        FreeSymbolsInfo(&info);
-        return True;
-    }
+    HandleSymbolsFile(file, keymap, merge, &info);
 
-    if (info.errorCount == 0)
-    {
-        KeyInfo *key;
-
-        /* alloc memory in the xkb struct */
-        if (XkbcAllocNames(xkb, XkbSymbolsNameMask | XkbGroupNamesMask, 0, 0)
-            != Success)
-        {
-            WSGO("Can not allocate names in CompileSymbols\n");
-            ACTION("Symbols not added\n");
-            return False;
-        }
-        if (XkbcAllocClientMap(xkb, XkbKeySymsMask | XkbModifierMapMask, 0)
-            != Success)
-        {
-            WSGO("Could not allocate client map in CompileSymbols\n");
-            ACTION("Symbols not added\n");
-            return False;
-        }
-        if (XkbcAllocServerMap(xkb, XkbAllServerInfoMask, 32) != Success)
-        {
-            WSGO("Could not allocate server map in CompileSymbols\n");
-            ACTION("Symbols not added\n");
-            return False;
-        }
-        if (XkbcAllocControls(xkb, XkbPerKeyRepeatMask) != Success)
-        {
-            WSGO("Could not allocate controls in CompileSymbols\n");
-            ACTION("Symbols not added\n");
-            return False;
-        }
-
-        /* now copy info into xkb. */
-        xkb->names->symbols = xkb_intern_atom(info.name);
-        if (info.aliases)
-            ApplyAliases(xkb, False, &info.aliases);
-        for (i = 0; i < XkbNumKbdGroups; i++)
-        {
-            if (info.groupNames[i] != None)
-                xkb->names->groups[i] = info.groupNames[i];
-        }
-        /* sanitize keys */
-        for (key = info.keys, i = 0; i < info.nKeys; i++, key++)
-        {
-            PrepareKeyDef(key);
-        }
-        /* copy! */
-        for (key = info.keys, i = 0; i < info.nKeys; i++, key++)
-        {
-            if (!CopySymbolsDef(xkb, key, 0))
-                info.errorCount++;
+    if (darray_empty(info.keys))
+        goto err_info;
+
+    if (info.errorCount != 0)
+        goto err_info;
+
+    darray_resize0(keymap->acts, darray_size(keymap->acts) + 32 + 1);
+
+    if (info.name)
+        keymap->symbols_section_name = strdup(info.name);
+
+    /* now copy info into xkb. */
+    ApplyAliases(keymap, &info.aliases);
+
+    for (i = 0; i < XkbNumKbdGroups; i++) {
+        if (info.groupNames[i] != XKB_ATOM_NONE) {
+            free(keymap->group_names[i]);
+            keymap->group_names[i] = xkb_atom_strdup(keymap->ctx,
+                                                     info.groupNames[i]);
         }
-        if (warningLevel > 3)
-        {
-            for (i = xkb->min_key_code; i <= xkb->max_key_code; i++)
-            {
-                if (xkb->names->keys[i].name[0] == '\0')
-                    continue;
-                if (XkbKeyNumGroups(xkb, i) < 1)
-                {
-                    char buf[5];
-                    memcpy(buf, xkb->names->keys[i].name, 4);
-                    buf[4] = '\0';
-                    WARN
-                        ("No symbols defined for <%s> (keycode %d)\n",
-                         buf, i);
-                }
-            }
+    }
+
+    /* sanitize keys */
+    darray_foreach(keyi, info.keys)
+        PrepareKeyDef(keyi);
+
+    /* copy! */
+    darray_foreach(keyi, info.keys)
+        if (!CopySymbolsDef(keymap, keyi, 0))
+            info.errorCount++;
+
+    if (warningLevel > 3) {
+        xkb_foreach_key(key, keymap) {
+            if (key->name[0] == '\0')
+                continue;
+
+            if (key->num_groups < 1)
+                WARN("No symbols defined for <%.4s> (keycode %d)\n",
+                     key->name, XkbKeyGetKeycode(keymap, key));
         }
-        if (info.modMap)
-        {
-            ModMapEntry *mm, *next;
-            for (mm = info.modMap; mm != NULL; mm = next)
-            {
-                if (!CopyModMapDef(xkb, mm))
-                    info.errorCount++;
-                next = (ModMapEntry *) mm->defs.next;
-            }
+    }
+
+    if (info.modMap) {
+        ModMapEntry *mm, *next;
+        for (mm = info.modMap; mm != NULL; mm = next) {
+            if (!CopyModMapDef(keymap, mm))
+                info.errorCount++;
+            next = (ModMapEntry *) mm->defs.next;
         }
-        FreeSymbolsInfo(&info);
-        return True;
     }
 
     FreeSymbolsInfo(&info);
-    return False;
+    return true;
+
+err_info:
+    FreeSymbolsInfo(&info);
+    return false;
 }