Split the mods, layout, leds parts of xkb_state_components
[platform/upstream/libxkbcommon.git] / src / keymap-dump.c
index faa8d43..4306a7d 100644 (file)
@@ -1,27 +1,27 @@
 /************************************************************
- 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.
+ *
  ********************************************************/
 
 /*
  * Author: Daniel Stone <daniel@fooishbar.org>
  */
 
-#include <config.h>
-
-#include <stdio.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-#include "xkb-priv.h"
+#include "keymap.h"
 #include "text.h"
 
-#define        VMOD_HIDE_VALUE 0
-#define        VMOD_SHOW_VALUE 1
-#define        VMOD_COMMENT_VALUE 2
+#define VMOD_HIDE_VALUE    0
+#define VMOD_SHOW_VALUE    1
+#define VMOD_COMMENT_VALUE 2
 
-#define BUF_CHUNK_SIZE 4096
+#define BUF_CHUNK_SIZE     4096
+
+struct buf {
+    char *buf;
+    size_t size;
+    size_t alloc;
+};
 
 static bool
-do_realloc(char **buf, size_t *size, size_t offset, size_t at_least)
+do_realloc(struct buf *buf, size_t at_least)
 {
     char *new;
 
-    *size += BUF_CHUNK_SIZE;
+    buf->alloc += BUF_CHUNK_SIZE;
     if (at_least >= BUF_CHUNK_SIZE)
-        *size += at_least;
+        buf->alloc += at_least;
 
-    new = realloc(*buf, *size);
+    new = realloc(buf->buf, buf->alloc);
     if (!new)
         return false;
-    *buf = new;
-
-    memset(*buf + offset, 0, *size - offset);
 
+    buf->buf = new;
     return true;
 }
 
-/* This whole thing should be a function, but you can't call vsnprintf
- * multiple times. */
-#define check_write_buf(keymap, buf, size, offset, ...) \
-do { \
-    size_t _printed; \
-    bool _ret = true; \
-    \
-    /* Concatenate the strings, and check whether or not the output was \
-     * truncated. */ \
-    while ((_printed = snprintf(*buf + *offset, *size - *offset, \
-                                __VA_ARGS__)) >= \
-           (*size - *offset)) {\
-        /* If it was truncated, embiggen the string and roll from the top. */ \
-        if (!do_realloc(buf, size, *offset, _printed)) { \
-            fprintf(stderr, \
-                    "xkbcommon: failed to allocate %zu bytes for keymap\n", \
-                    *size); \
-            free(*buf); \
-            *buf = NULL; \
-            _ret = false; \
-            break; \
-        } \
-    } \
-    if (_ret == true) \
-        *offset += _printed; \
-} while (0)
-
-#define write_buf(keymap, buf, size, offset, ...) \
-do { \
-    check_write_buf(keymap, buf, size, offset, __VA_ARGS__); \
-    if (*buf == NULL) \
-        return false; \
-} while (0)
-
-static bool
-write_vmods(struct xkb_keymap *keymap, char **buf, size_t *size, size_t *offset)
+ATTR_PRINTF(2, 3) static bool
+check_write_buf(struct buf *buf, const char *fmt, ...)
 {
-    int num_vmods = 0;
-    int i;
-
-    for (i = 0; i < XkbNumVirtualMods; i++) {
-       if (!keymap->names->vmods[i])
-            continue;
-        if (num_vmods == 0)
-            write_buf(keymap, buf, size, offset, "\t\tvirtual_modifiers ");
-        else
-            write_buf(keymap, buf, size, offset, ",");
-        write_buf(keymap, buf, size, offset, "%s", keymap->names->vmods[i]);
-        num_vmods++;
-    }
-
-    if (num_vmods > 0)
-       write_buf(keymap, buf, size, offset, ";\n\n");
+    va_list args;
+    int printed;
+    size_t available;
 
-    return true;
-}
+    available = buf->alloc - buf->size;
+    va_start(args, fmt);
+    printed = vsnprintf(buf->buf + buf->size, available, fmt, args);
+    va_end(args);
 
-#define GET_TEXT_BUF_SIZE 512
-
-#define append_get_text(...) do { \
-    int _size = snprintf(ret, GET_TEXT_BUF_SIZE, __VA_ARGS__); \
-    if (_size >= GET_TEXT_BUF_SIZE) \
-        return NULL; \
-} while(0)
-
-/* FIXME: Merge with src/xkbcomp/expr.c::modIndexNames. */
-static const char *core_mod_names[] = {
-    "Shift",
-    "Lock",
-    "Control",
-    "Mod1",
-    "Mod2",
-    "Mod3",
-    "Mod4",
-    "Mod5",
-};
+    if (printed < 0)
+        goto err;
 
-static const char *
-get_mod_index_text(uint8_t real_mod)
-{
-    return core_mod_names[real_mod];
-}
+    if (printed >= available)
+        if (!do_realloc(buf, printed))
+            goto err;
 
-static char *
-get_mod_mask_text(struct xkb_keymap *keymap, uint8_t real_mods, uint32_t vmods)
-{
-    static char ret[GET_TEXT_BUF_SIZE], ret2[GET_TEXT_BUF_SIZE];
-    int i;
-
-    memset(ret, 0, GET_TEXT_BUF_SIZE);
+    /* The buffer has enough space now. */
 
-    if (real_mods == 0 && vmods == 0) {
-        strcpy(ret, "none");
-        return ret;
-    }
-
-    /* This is so broken.  If we have a real modmask of 0xff and a
-     * vmodmask, we'll get, e.g., all+RightControl.  But, it's what xkbfile
-     * does, so ... */
-    if (real_mods == 0xff) {
-        strcpy(ret, "all");
-    }
-    else if (real_mods) {
-        for (i = 0; i < XkbNumModifiers; i++) {
-            if (!(real_mods & (1 << i)))
-                continue;
-            if (ret[0] != '\0') {
-                strcpy(ret2, ret);
-                append_get_text("%s+%s", ret2, core_mod_names[i]);
-            }
-            else {
-                append_get_text("%s", core_mod_names[i]);
-            }
-        }
-    }
+    available = buf->alloc - buf->size;
+    va_start(args, fmt);
+    printed = vsnprintf(buf->buf + buf->size, available, fmt, args);
+    va_end(args);
 
-    if (vmods == 0)
-        return ret;
+    if (printed >= available || printed < 0)
+        goto err;
 
-    for (i = 0; i < XkbNumVirtualMods; i++) {
-        if (!(vmods & (1 << i)))
-            continue;
-        if (ret[0] != '\0') {
-            strcpy(ret2, ret);
-            append_get_text("%s+%s", ret2, keymap->names->vmods[i]);
-        }
-        else {
-            append_get_text("%s", keymap->names->vmods[i]);
-        }
-    }
+    buf->size += printed;
+    return true;
 
-    return ret;
+err:
+    free(buf->buf);
+    buf->buf = NULL;
+    return false;
 }
 
-static char *
-get_indicator_state_text(uint8_t which)
-{
-    int i;
-    static char ret[GET_TEXT_BUF_SIZE];
-    /* FIXME: Merge with ... something ... in xkbcomp? */
-    static const char *state_names[] = {
-        "base",
-        "latched",
-        "locked",
-        "effective",
-        "compat"
-    };
-
-    memset(ret, 0, GET_TEXT_BUF_SIZE);
-
-    which &= XkbIM_UseAnyMods;
-
-    if (which == 0) {
-        strcpy(ret, "0");
-        return NULL;
-    }
-
-    for (i = 0; which != 0; i++) {
-        if (!(which & (1 << i)))
-            continue;
-        which &= ~(1 << i);
-
-        if (ret[0] != '\0')
-            append_get_text("%s+%s", ret, state_names[i]);
-        else
-            append_get_text("%s", state_names[i]);
-    }
-
-    return ret;
-}
+#define write_buf(buf, ...) do { \
+    if (!check_write_buf(buf, __VA_ARGS__)) \
+        return false; \
+} while (0)
 
-static char *
-get_control_mask_text(uint32_t control_mask)
+static bool
+write_vmods(struct xkb_keymap *keymap, struct buf *buf)
 {
-    int i;
-    static char ret[GET_TEXT_BUF_SIZE];
-    /* FIXME: Merge with ... something ... in xkbcomp. */
-    static const char *ctrl_names[] = {
-        "repeatKeys",
-        "slowKeys",
-        "bounceKeys",
-        "stickyKeys",
-        "mouseKeys",
-        "mouseKeysAccel",
-        "accessXKeys",
-        "accessXTimeout",
-        "accessXFeedback",
-        "audibleBell",
-        "overlay1",
-        "overlay2",
-        "ignoreGroupLock"
-    };
-
-    memset(ret, 0, GET_TEXT_BUF_SIZE);
-
-    control_mask &= XkbAllBooleanCtrlsMask;
-
-    if (control_mask == 0) {
-        strcpy(ret, "none");
-        return ret;
-    }
-    else if (control_mask == XkbAllBooleanCtrlsMask) {
-        strcpy(ret, "all");
-        return ret;
-    }
+    const struct xkb_mod *mod;
+    xkb_mod_index_t num_vmods = 0;
 
-    for (i = 0; control_mask; i++) {
-        if (!(control_mask & (1 << i)))
+    darray_foreach(mod, keymap->mods) {
+        if (mod->type != MOD_VIRT)
             continue;
-        control_mask &= ~(1 << i);
 
-        if (ret[0] != '\0')
-            append_get_text("%s+%s", ret, ctrl_names[i]);
+        if (num_vmods == 0)
+            write_buf(buf, "\t\tvirtual_modifiers ");
         else
-            append_get_text("%s", ctrl_names[i]);
+            write_buf(buf, ",");
+        write_buf(buf, "%s", xkb_atom_text(keymap->ctx, mod->name));
+        num_vmods++;
     }
 
-    return ret;
+    if (num_vmods > 0)
+        write_buf(buf, ";\n\n");
+
+    return true;
 }
 
 static bool
-write_keycodes(struct xkb_keymap *keymap, char **buf, size_t *size,
-               size_t *offset)
+write_keycodes(struct xkb_keymap *keymap, struct buf *buf)
 {
-    xkb_keycode_t key;
+    struct xkb_key *key;
     struct xkb_key_alias *alias;
-    int i;
-
-    write_buf(keymap, buf, size, offset, "\txkb_keycodes {\n");
-    write_buf(keymap, buf, size, offset, "\t\tminimum = %d;\n",
-              keymap->min_key_code);
-    write_buf(keymap, buf, size, offset, "\t\tmaximum = %d;\n",
-              keymap->max_key_code);
+    xkb_led_index_t i;
+    const struct xkb_indicator_map *im;
 
-    for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
-        const char *alternate = "";
+    if (keymap->keycodes_section_name)
+        write_buf(buf, "\txkb_keycodes \"%s\" {\n",
+                  keymap->keycodes_section_name);
+    else
+        write_buf(buf, "\txkb_keycodes {\n");
 
-       if (darray_item(keymap->names->keys, key).name[0] == '\0')
+    xkb_foreach_key(key, keymap) {
+        if (key->name == XKB_ATOM_NONE)
             continue;
-       if (XkbcFindKeycodeByName(keymap,
-                                  darray_item(keymap->names->keys, key).name,
-                                  true) != key)
-           alternate = "alternate ";
-        write_buf(keymap, buf, size, offset, "\t\t%s%6s = %d;\n", alternate,
-                 XkbcKeyNameText(darray_item(keymap->names->keys, key).name),
-                                  key);
-    }
 
-    for (i = 0; i < XkbNumIndicators; i++) {
-        if (!keymap->names->indicators[i])
-            continue;
-       write_buf(keymap, buf, size, offset, "\t\tindicator %d = \"%s\";\n",
-                  i + 1, keymap->names->indicators[i]);
+        write_buf(buf, "\t\t%-20s = %d;\n",
+                  KeyNameText(keymap->ctx, key->name), key->keycode);
     }
 
+    darray_enumerate(i, im, keymap->indicators)
+        if (im->name != XKB_ATOM_NONE)
+            write_buf(buf, "\t\tindicator %d = \"%s\";\n",
+                      i + 1, xkb_atom_text(keymap->ctx, im->name));
+
 
-    darray_foreach(alias, keymap->names->key_aliases)
-        write_buf(keymap, buf, size, offset, "\t\talias %6s = %6s;\n",
-                  XkbcKeyNameText(alias->alias),
-                  XkbcKeyNameText(alias->real));
+    darray_foreach(alias, keymap->key_aliases)
+        write_buf(buf, "\t\talias %-14s = %s;\n",
+                  KeyNameText(keymap->ctx, alias->alias),
+                  KeyNameText(keymap->ctx, alias->real));
 
-    write_buf(keymap, buf, size, offset, "\t};\n\n");
+    write_buf(buf, "\t};\n\n");
     return true;
 }
 
 static bool
-write_types(struct xkb_keymap *keymap, char **buf, size_t *size,
-            size_t *offset)
+write_types(struct xkb_keymap *keymap, struct buf *buf)
 {
-    int n;
+    unsigned int i, j;
+    xkb_level_index_t n;
     struct xkb_key_type *type;
+    struct xkb_kt_map_entry *entry;
 
-    write_buf(keymap, buf, size, offset, "\txkb_types {\n\n");
-    write_vmods(keymap, buf, size, offset);
+    if (keymap->types_section_name)
+        write_buf(buf, "\txkb_types \"%s\" {\n\n",
+                  keymap->types_section_name);
+    else
+        write_buf(buf, "\txkb_types {\n\n");
 
-    darray_foreach(type, keymap->map->types) {
-       write_buf(keymap, buf, size, offset, "\t\ttype \"%s\" {\n",
-                 type->name);
-       write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
-                 get_mod_mask_text(keymap, type->mods.real_mods,
-                                    type->mods.vmods));
+    write_vmods(keymap, buf);
 
-       for (n = 0; n < darray_size(type->map); n++) {
-            struct xkb_kt_map_entry *entry = &darray_item(type->map, n);
-            char *str;
+    for (i = 0; i < keymap->num_types; i++) {
+        type = &keymap->types[i];
 
-            str = get_mod_mask_text(keymap, entry->mods.real_mods,
-                                    entry->mods.vmods);
-           write_buf(keymap, buf, size, offset, "\t\t\tmap[%s]= Level%d;\n",
+        write_buf(buf, "\t\ttype \"%s\" {\n",
+                  xkb_atom_text(keymap->ctx, type->name));
+        write_buf(buf, "\t\t\tmodifiers= %s;\n",
+                  ModMaskText(keymap, type->mods.mods));
+
+        for (j = 0; j < type->num_entries; j++) {
+            const char *str;
+            entry = &type->map[j];
+
+            /*
+             * Printing level 1 entries is redundant, it's the default,
+             * unless there's preserve info.
+             */
+            if (entry->level == 0 && entry->preserve.mods == 0)
+                continue;
+
+            str = ModMaskText(keymap, entry->mods.mods);
+            write_buf(buf, "\t\t\tmap[%s]= Level%d;\n",
                       str, entry->level + 1);
 
-            if (!type->preserve || (!type->preserve[n].real_mods &&
-                                    !type->preserve[n].vmods))
+            if (entry->preserve.mods == 0)
                 continue;
-            write_buf(keymap, buf, size, offset, "\t\t\tpreserve[%s]= ", str);
-            write_buf(keymap, buf, size, offset, "%s;\n",
-                      get_mod_mask_text(keymap, type->preserve[n].real_mods,
-                                        type->preserve[n].vmods));
-       }
-
-       if (type->level_names) {
-           for (n = 0; n < type->num_levels; n++) {
-               if (!type->level_names[n])
-                   continue;
-               write_buf(keymap, buf, size, offset,
-                          "\t\t\tlevel_name[Level%d]= \"%s\";\n", n + 1,
-                          type->level_names[n]);
-           }
-       }
-       write_buf(keymap, buf, size, offset, "\t\t};\n");
+
+            write_buf(buf, "\t\t\tpreserve[%s]= ", str);
+            write_buf(buf, "%s;\n",
+                      ModMaskText(keymap, entry->preserve.mods));
+        }
+
+        if (type->level_names) {
+            for (n = 0; n < type->num_levels; n++) {
+                if (!type->level_names[n])
+                    continue;
+                write_buf(buf, "\t\t\tlevel_name[Level%d]= \"%s\";\n", n + 1,
+                          xkb_atom_text(keymap->ctx, type->level_names[n]));
+            }
+        }
+        write_buf(buf, "\t\t};\n");
     }
 
-    write_buf(keymap, buf, size, offset, "\t};\n\n");
+    write_buf(buf, "\t};\n\n");
     return true;
 }
 
 static bool
-write_indicator_map(struct xkb_keymap *keymap, char **buf, size_t *size,
-                    size_t *offset, int num)
+write_indicator_map(struct xkb_keymap *keymap, struct buf *buf,
+                    const struct xkb_indicator_map *led)
 {
-    struct xkb_indicator_map *led = &keymap->indicators->maps[num];
-
-    write_buf(keymap, buf, size, offset, "\t\tindicator \"%s\" {\n",
-              keymap->names->indicators[num]);
+    write_buf(buf, "\t\tindicator \"%s\" {\n",
+              xkb_atom_text(keymap->ctx, led->name));
 
     if (led->which_groups) {
-       if (led->which_groups != XkbIM_UseEffective) {
-           write_buf(keymap, buf, size, offset, "\t\t\twhichGroupState= %s;\n",
-                     get_indicator_state_text(led->which_groups));
-       }
-       write_buf(keymap, buf, size, offset, "\t\t\tgroups= 0x%02x;\n",
+        if (led->which_groups != XKB_STATE_LAYOUT_EFFECTIVE) {
+            write_buf(buf, "\t\t\twhichGroupState= %s;\n",
+                      IndicatorStateText(keymap->ctx, led->which_groups));
+        }
+        write_buf(buf, "\t\t\tgroups= 0x%02x;\n",
                   led->groups);
     }
 
     if (led->which_mods) {
-       if (led->which_mods != XkbIM_UseEffective) {
-           write_buf(keymap, buf, size, offset, "\t\t\twhichModState= %s;\n",
-                      get_indicator_state_text(led->which_mods));
-       }
-       write_buf(keymap, buf, size, offset, "\t\t\tmodifiers= %s;\n",
-                  get_mod_mask_text(keymap, led->mods.real_mods,
-                  led->mods.vmods));
+        if (led->which_mods != XKB_STATE_MODS_EFFECTIVE) {
+            write_buf(buf, "\t\t\twhichModState= %s;\n",
+                      IndicatorStateText(keymap->ctx, led->which_mods));
+        }
+        write_buf(buf, "\t\t\tmodifiers= %s;\n",
+                  ModMaskText(keymap, led->mods.mods));
     }
 
     if (led->ctrls) {
-       write_buf(keymap, buf, size, offset, "\t\t\tcontrols= %s;\n",
-                 get_control_mask_text(led->ctrls));
+        write_buf(buf, "\t\t\tcontrols= %s;\n",
+                  ControlMaskText(keymap->ctx, led->ctrls));
     }
 
-    write_buf(keymap, buf, size, offset, "\t\t};\n");
+    write_buf(buf, "\t\t};\n");
     return true;
 }
 
-static char *
-get_interp_match_text(uint8_t type)
-{
-    static char ret[16];
-
-    switch (type & XkbSI_OpMask) {
-        case XkbSI_NoneOf:
-            sprintf(ret, "NoneOf");
-            break;
-        case XkbSI_AnyOfOrNone:
-            sprintf(ret, "AnyOfOrNone");
-            break;
-        case XkbSI_AnyOf:
-            sprintf(ret, "AnyOf");
-            break;
-        case XkbSI_AllOf:
-            sprintf(ret, "AllOf");
-            break;
-        case XkbSI_Exactly:
-            sprintf(ret, "Exactly");
-            break;
-        default:
-            sprintf(ret, "0x%x", type & XkbSI_OpMask);
-            break;
-    }
-
-    return ret;
-}
-
 static bool
-write_action(struct xkb_keymap *keymap, char **buf, size_t *size,
-             size_t *offset, union xkb_action *action, const char *prefix,
-             const char *suffix)
+write_action(struct xkb_keymap *keymap, struct buf *buf,
+             const union xkb_action *action,
+             const char *prefix, const char *suffix)
 {
-    const char *type = NULL;
+    const char *type;
     const char *args = NULL;
 
     if (!prefix)
@@ -478,147 +294,127 @@ write_action(struct xkb_keymap *keymap, char **buf, size_t *size,
     if (!suffix)
         suffix = "";
 
-    switch (action->any.type) {
-    case XkbSA_SetMods:
-        if (!type)
-            type = "SetMods";
-    case XkbSA_LatchMods:
-        if (!type)
-            type = "LatchMods";
-    case XkbSA_LockMods:
-        if (!type)
-            type = "LockMods";
-        if (action->mods.flags & XkbSA_UseModMapMods)
+    type = ActionTypeText(action->type);
+
+    switch (action->type) {
+    case ACTION_TYPE_MOD_SET:
+    case ACTION_TYPE_MOD_LATCH:
+    case ACTION_TYPE_MOD_LOCK:
+        if (action->mods.flags & ACTION_MODS_LOOKUP_MODMAP)
             args = "modMapMods";
         else
-            args = get_mod_mask_text(keymap, action->mods.real_mods,
-                                     action->mods.vmods);
-        write_buf(keymap, buf, size, offset, "%s%s(modifiers=%s%s%s)%s",
-                  prefix, type, args,
-                  (action->any.type != XkbSA_LockGroup &&
-                   action->mods.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
-                  (action->any.type != XkbSA_LockGroup &&
-                   action->mods.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
+            args = ModMaskText(keymap, action->mods.mods.mods);
+        write_buf(buf, "%s%s(modifiers=%s%s%s)%s", prefix, type, args,
+                  (action->type != ACTION_TYPE_MOD_LOCK &&
+                   (action->mods.flags & ACTION_LOCK_CLEAR)) ?
+                   ",clearLocks" : "",
+                  (action->type != ACTION_TYPE_MOD_LOCK &&
+                   (action->mods.flags & ACTION_LATCH_TO_LOCK)) ?
+                   ",latchToLock" : "",
                   suffix);
         break;
-    case XkbSA_SetGroup:
-        if (!type)
-            type = "SetGroup";
-    case XkbSA_LatchGroup:
-        if (!type)
-            type = "LatchGroup";
-    case XkbSA_LockGroup:
-        if (!type)
-            type = "LockGroup";
-        write_buf(keymap, buf, size, offset, "%s%s(group=%s%d%s%s)%s",
-                  prefix, type,
-                  (!(action->group.flags & XkbSA_GroupAbsolute) &&
+
+    case ACTION_TYPE_GROUP_SET:
+    case ACTION_TYPE_GROUP_LATCH:
+    case ACTION_TYPE_GROUP_LOCK:
+        write_buf(buf, "%s%s(group=%s%d%s%s)%s", prefix, type,
+                  (!(action->group.flags & ACTION_ABSOLUTE_SWITCH) &&
                    action->group.group > 0) ? "+" : "",
-                  (action->group.flags & XkbSA_GroupAbsolute) ?
-                   action->group.group + 1 : action->group.group,
-                  (action->any.type != XkbSA_LockGroup &&
-                   action->group.flags & XkbSA_ClearLocks) ? ",clearLocks" : "",
-                  (action->any.type != XkbSA_LockGroup &&
-                   action->group.flags & XkbSA_LatchToLock) ? ",latchToLock" : "",
+                  (action->group.flags & ACTION_ABSOLUTE_SWITCH) ?
+                  action->group.group + 1 : action->group.group,
+                  (action->type != ACTION_TYPE_GROUP_LOCK &&
+                   (action->group.flags & ACTION_LOCK_CLEAR)) ?
+                  ",clearLocks" : "",
+                  (action->type != ACTION_TYPE_GROUP_LOCK &&
+                   (action->group.flags & ACTION_LATCH_TO_LOCK)) ?
+                  ",latchToLock" : "",
                   suffix);
         break;
-    case XkbSA_Terminate:
-        write_buf(keymap, buf, size, offset, "%sTerminate()%s", prefix, suffix);
+
+    case ACTION_TYPE_TERMINATE:
+        write_buf(buf, "%s%s()%s", prefix, type, suffix);
         break;
-    case XkbSA_MovePtr:
-        write_buf(keymap, buf, size, offset, "%sMovePtr(x=%s%d,y=%s%d%s)%s",
-                  prefix,
-                  (!(action->ptr.flags & XkbSA_MoveAbsoluteX) &&
+
+    case ACTION_TYPE_PTR_MOVE:
+        write_buf(buf, "%s%s(x=%s%d,y=%s%d%s)%s", prefix, type,
+                  (!(action->ptr.flags & ACTION_ABSOLUTE_X) &&
                    action->ptr.x >= 0) ? "+" : "",
                   action->ptr.x,
-                  (!(action->ptr.flags & XkbSA_MoveAbsoluteY) &&
+                  (!(action->ptr.flags & ACTION_ABSOLUTE_Y) &&
                    action->ptr.y >= 0) ? "+" : "",
                   action->ptr.y,
-                  (action->ptr.flags & XkbSA_NoAcceleration) ? ",!accel" : "",
+                  (action->ptr.flags & ACTION_NO_ACCEL) ? ",!accel" : "",
                   suffix);
         break;
-    case XkbSA_PtrBtn:
-        if (!type)
-            type = "PtrBtn";
-    case XkbSA_LockPtrBtn:
-        if (!type) {
-            type = "LockPtrBtn";
-            switch (action->btn.flags & (XkbSA_LockNoUnlock | XkbSA_LockNoLock)) {
-            case XkbSA_LockNoUnlock:
-                args = ",affect=lock";
-                break;
-            case XkbSA_LockNoLock:
-                args = ",affect=unlock";
-                break;
-            case XkbSA_LockNoLock | XkbSA_LockNoUnlock:
-                args = ",affect=neither";
-                break;
-            default:
-                args = ",affect=both";
-                break;
-            }
-        }
-        else {
-            args = NULL;
+
+    case ACTION_TYPE_PTR_LOCK:
+        switch (action->btn.flags &
+                 (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK)) {
+        case ACTION_LOCK_NO_UNLOCK:
+            args = ",affect=lock";
+            break;
+
+        case ACTION_LOCK_NO_LOCK:
+            args = ",affect=unlock";
+            break;
+
+        case ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK:
+            args = ",affect=neither";
+            break;
+
+        default:
+            args = ",affect=both";
+            break;
         }
-        write_buf(keymap, buf, size, offset, "%s%s(button=", prefix, type);
+    case ACTION_TYPE_PTR_BUTTON:
+        write_buf(buf, "%s%s(button=", prefix, type);
         if (action->btn.button > 0 && action->btn.button <= 5)
-            write_buf(keymap, buf, size, offset, "%d", action->btn.button);
+            write_buf(buf, "%d", action->btn.button);
         else
-            write_buf(keymap, buf, size, offset, "default");
+            write_buf(buf, "default");
         if (action->btn.count)
-            write_buf(keymap, buf, size, offset, ",count=%d",
-                      action->btn.count);
+            write_buf(buf, ",count=%d", action->btn.count);
         if (args)
-            write_buf(keymap, buf, size, offset, "%s", args);
-        write_buf(keymap, buf, size, offset, ")%s", suffix);
+            write_buf(buf, "%s", args);
+        write_buf(buf, ")%s", suffix);
         break;
-    case XkbSA_SetPtrDflt:
-        write_buf(keymap, buf, size, offset, "%sSetPtrDflt(", prefix);
-        if (action->dflt.affect == XkbSA_AffectDfltBtn)
-            write_buf(keymap, buf, size, offset, "affect=button,button=%s%d",
-                      (!(action->dflt.flags & XkbSA_DfltBtnAbsolute) &&
-                       action->dflt.value >= 0) ? "+" : "",
-                      action->dflt.value);
-        write_buf(keymap, buf, size, offset, ")%s", suffix);
+
+    case ACTION_TYPE_PTR_DEFAULT:
+        write_buf(buf, "%s%s(", prefix, type);
+        write_buf(buf, "affect=button,button=%s%d",
+                  (!(action->dflt.flags & ACTION_ABSOLUTE_SWITCH) &&
+                   action->dflt.value >= 0) ? "+" : "",
+                  action->dflt.value);
+        write_buf(buf, ")%s", suffix);
         break;
-    case XkbSA_SwitchScreen:
-        write_buf(keymap, buf, size, offset,
-                  "%sSwitchScreen(screen=%s%d,%ssame)%s", prefix,
-                  (!(action->screen.flags & XkbSA_SwitchAbsolute) &&
+
+    case ACTION_TYPE_SWITCH_VT:
+        write_buf(buf, "%s%s(screen=%s%d,%ssame)%s", prefix, type,
+                  (!(action->screen.flags & ACTION_ABSOLUTE_SWITCH) &&
                    action->screen.screen >= 0) ? "+" : "",
                   action->screen.screen,
-                  (action->screen.flags & XkbSA_SwitchApplication) ? "!" : "",
+                  (action->screen.flags & ACTION_SAME_SCREEN) ? "!" : "",
                   suffix);
         break;
-    /* Deprecated actions below here */
-    case XkbSA_SetControls:
-        if (!type)
-            type = "SetControls";
-    case XkbSA_LockControls:
-        if (!type)
-            type = "LockControls";
-        write_buf(keymap, buf, size, offset, "%s%s(controls=%s)%s",
-                  prefix, type, get_control_mask_text(action->ctrls.ctrls),
-                  suffix);
+
+    case ACTION_TYPE_CTRL_SET:
+    case ACTION_TYPE_CTRL_LOCK:
+        write_buf(buf, "%s%s(controls=%s)%s", prefix, type,
+                  ControlMaskText(keymap->ctx, action->ctrls.ctrls), suffix);
         break;
-    case XkbSA_ISOLock:
-    case XkbSA_ActionMessage:
-    case XkbSA_RedirectKey:
-    case XkbSA_DeviceBtn:
-    case XkbSA_LockDeviceBtn:
-    case XkbSA_NoAction:
-        /* XXX TODO */
-        write_buf(keymap, buf, size, offset, "%sNoAction()%s", prefix, suffix);
+
+    case ACTION_TYPE_NONE:
+        write_buf(buf, "%sNoAction()%s", prefix, suffix);
         break;
-    case XkbSA_XFree86Private:
+
     default:
-        write_buf(keymap, buf, size, offset,
-                  "%sPrivate(type=0x%02x,data[0]=0x%02x,data[1]=0x%02x,data[2]=0x%02x,data[3]=0x%02x,data[4]=0x%02x,data[5]=0x%02x,data[6]=0x%02x)%s",
-                  prefix, action->any.type, action->any.data[0],
-                  action->any.data[1], action->any.data[2],
-                  action->any.data[3], action->any.data[4],
-                  action->any.data[5], action->any.data[6], suffix);
+        write_buf(buf,
+                  "%s%s(type=0x%02x,data[0]=0x%02x,data[1]=0x%02x,data[2]=0x%02x,data[3]=0x%02x,data[4]=0x%02x,data[5]=0x%02x,data[6]=0x%02x)%s",
+                  prefix, type, action->type, action->priv.data[0],
+                  action->priv.data[1], action->priv.data[2],
+                  action->priv.data[3], action->priv.data[4],
+                  action->priv.data[5], action->priv.data[6],
+                  suffix);
         break;
     }
 
@@ -626,21 +422,23 @@ write_action(struct xkb_keymap *keymap, char **buf, size_t *size,
 }
 
 static bool
-write_compat(struct xkb_keymap *keymap, char **buf, size_t *size,
-             size_t *offset)
+write_compat(struct xkb_keymap *keymap, struct buf *buf)
 {
-    int i;
     struct xkb_sym_interpret *interp;
+    const struct xkb_indicator_map *led;
 
-    write_buf(keymap, buf, size, offset, "\txkb_compatibility {\n\n");
+    if (keymap->compat_section_name)
+        write_buf(buf, "\txkb_compatibility \"%s\" {\n\n",
+                  keymap->compat_section_name);
+    else
+        write_buf(buf, "\txkb_compatibility {\n\n");
 
-    write_vmods(keymap, buf, size, offset);
+    write_vmods(keymap, buf);
 
-    write_buf(keymap, buf, size, offset, "\t\tinterpret.useModMapMods= AnyLevel;\n");
-    write_buf(keymap, buf, size, offset, "\t\tinterpret.repeat= false;\n");
-    write_buf(keymap, buf, size, offset, "\t\tinterpret.locking= false;\n");
+    write_buf(buf, "\t\tinterpret.useModMapMods= AnyLevel;\n");
+    write_buf(buf, "\t\tinterpret.repeat= False;\n");
 
-    darray_foreach(interp, keymap->compat->sym_interpret) {
+    darray_foreach(interp, keymap->sym_interprets) {
         char keysym_name[64];
 
         if (interp->sym == XKB_KEY_NoSymbol)
@@ -648,85 +446,66 @@ write_compat(struct xkb_keymap *keymap, char **buf, size_t *size,
         else
             xkb_keysym_get_name(interp->sym, keysym_name, sizeof(keysym_name));
 
-        write_buf(keymap, buf, size, offset, "\t\tinterpret %s+%s(%s) {\n",
+        write_buf(buf, "\t\tinterpret %s+%s(%s) {\n",
                   keysym_name,
-                 get_interp_match_text(interp->match),
-                 get_mod_mask_text(keymap, interp->mods, 0));
-
-       if (interp->virtual_mod != XkbNoModifier) {
-           write_buf(keymap, buf, size, offset, "\t\t\tvirtualModifier= %s;\n",
-                      keymap->names->vmods[interp->virtual_mod]);
-       }
-
-       if (interp->match & XkbSI_LevelOneOnly)
-           write_buf(keymap, buf, size, offset, "\t\t\tuseModMapMods=level1;\n");
-       if (interp->flags & XkbSI_LockingKey)
-           write_buf(keymap, buf, size, offset, "\t\t\tlocking= true;\n");
-       if (interp->flags & XkbSI_AutoRepeat)
-           write_buf(keymap, buf, size, offset, "\t\t\trepeat= true;\n");
-
-       write_action(keymap, buf, size, offset, &interp->act,
-                     "\t\t\taction= ", ";\n");
-       write_buf(keymap, buf, size, offset, "\t\t};\n");
-    }
+                  SIMatchText(interp->match),
+                  ModMaskText(keymap, interp->mods));
 
-    for (i = 0; i < XkbNumKbdGroups; i++) {
-       struct xkb_mods *gc;
+        if (interp->virtual_mod != XKB_MOD_INVALID)
+            write_buf(buf, "\t\t\tvirtualModifier= %s;\n",
+                      ModIndexText(keymap, interp->virtual_mod));
 
-       gc = &keymap->compat->groups[i];
-       if (gc->real_mods == 0 && gc->vmods ==0)
-           continue;
-       write_buf(keymap, buf, size, offset,
-                  "\t\tgroup %d = %s;\n", i + 1,
-                  get_mod_mask_text(keymap, gc->real_mods, gc->vmods));
-    }
+        if (interp->level_one_only)
+            write_buf(buf, "\t\t\tuseModMapMods=level1;\n");
+        if (interp->repeat)
+            write_buf(buf, "\t\t\trepeat= True;\n");
 
-    for (i = 0; i < XkbNumIndicators; i++) {
-       struct xkb_indicator_map *map = &keymap->indicators->maps[i];
-        if (map->flags == 0 && map->which_groups == 0 &&
-            map->groups == 0 && map->which_mods == 0 &&
-            map->mods.real_mods == 0 && map->mods.vmods == 0 &&
-            map->ctrls == 0)
-            continue;
-        write_indicator_map(keymap, buf, size, offset, i);
+        write_action(keymap, buf, &interp->action, "\t\t\taction= ", ";\n");
+        write_buf(buf, "\t\t};\n");
     }
 
-    write_buf(keymap, buf, size, offset, "\t};\n\n");
+    darray_foreach(led, keymap->indicators)
+        if (led->which_groups || led->groups || led->which_mods ||
+            led->mods.mods || led->ctrls)
+            write_indicator_map(keymap, buf, led);
+
+    write_buf(buf, "\t};\n\n");
 
     return true;
 }
 
 static bool
-write_keysyms(struct xkb_keymap *keymap, char **buf, size_t *size,
-              size_t *offset, xkb_keycode_t key, unsigned int group)
+write_keysyms(struct xkb_keymap *keymap, struct buf *buf,
+              struct xkb_key *key, xkb_layout_index_t group)
 {
     const xkb_keysym_t *syms;
-    int num_syms, level;
+    int num_syms;
+    xkb_level_index_t level;
 #define OUT_BUF_LEN 128
     char out_buf[OUT_BUF_LEN];
 
-    for (level = 0; level < XkbKeyGroupWidth(keymap, key, group); level++) {
+    for (level = 0; level < XkbKeyGroupWidth(key, group); level++) {
         if (level != 0)
-            write_buf(keymap, buf, size, offset, ", ");
-        num_syms = xkb_key_get_syms_by_level(keymap, key, group, level,
-                                             &syms);
+            write_buf(buf, ", ");
+        num_syms = xkb_keymap_key_get_syms_by_level(keymap, key->keycode,
+                                                    group, level, &syms);
         if (num_syms == 0) {
-            write_buf(keymap, buf, size, offset, "%15s", "NoSymbol");
+            write_buf(buf, "%15s", "NoSymbol");
         }
         else if (num_syms == 1) {
             xkb_keysym_get_name(syms[0], out_buf, OUT_BUF_LEN);
-            write_buf(keymap, buf, size, offset, "%15s", out_buf);
+            write_buf(buf, "%15s", out_buf);
         }
         else {
             int s;
-            write_buf(keymap, buf, size, offset, "{ ");
+            write_buf(buf, "{ ");
             for (s = 0; s < num_syms; s++) {
                 if (s != 0)
-                    write_buf(keymap, buf, size, offset, ", ");
+                    write_buf(buf, ", ");
                 xkb_keysym_get_name(syms[s], out_buf, OUT_BUF_LEN);
-                write_buf(keymap, buf, size, offset, "%15s", out_buf);
+                write_buf(buf, "%s", out_buf);
             }
-            write_buf(keymap, buf, size, offset, " }");
+            write_buf(buf, " }");
         }
     }
 #undef OUT_BUF_LEN
@@ -735,192 +514,180 @@ write_keysyms(struct xkb_keymap *keymap, char **buf, size_t *size,
 }
 
 static bool
-write_symbols(struct xkb_keymap *keymap, char **buf, size_t *size,
-              size_t *offset)
+write_symbols(struct xkb_keymap *keymap, struct buf *buf)
 {
-    struct xkb_client_map *map = keymap->map;
-    struct xkb_server_map *srv = keymap->server;
-    xkb_keycode_t key;
-    int group, tmp;
+    struct xkb_key *key;
+    xkb_layout_index_t group, tmp;
+    xkb_atom_t *group_name;
     bool showActions;
 
-    write_buf(keymap, buf, size, offset, "\txkb_symbols {\n\n");
+    if (keymap->symbols_section_name)
+        write_buf(buf, "\txkb_symbols \"%s\" {\n\n",
+                  keymap->symbols_section_name);
+    else
+        write_buf(buf, "\txkb_symbols {\n\n");
 
-    for (tmp = group = 0; group < XkbNumKbdGroups; group++) {
-       if (!keymap->names->groups[group])
+    tmp = 0;
+    darray_enumerate(group, group_name, keymap->group_names) {
+        if (!*group_name)
             continue;
-        write_buf(keymap, buf, size, offset,
+        write_buf(buf,
                   "\t\tname[group%d]=\"%s\";\n", group + 1,
-                  keymap->names->groups[group]);
-       tmp++;
+                  xkb_atom_text(keymap->ctx, *group_name));
+        tmp++;
     }
     if (tmp > 0)
-       write_buf(keymap, buf, size, offset, "\n");
-
-    for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
-       bool simple = true;
-
-       if (xkb_key_num_groups(keymap, key) == 0)
-           continue;
-       if (XkbcFindKeycodeByName(keymap,
-                                  darray_item(keymap->names->keys, key).name,
-                                  true) != key)
-           continue;
-
-       write_buf(keymap, buf, size, offset, "\t\tkey %6s {",
-                 XkbcKeyNameText(darray_item(keymap->names->keys, key).name));
-       if (srv->explicit) {
-            if ((srv->explicit[key] & XkbExplicitKeyTypesMask)) {
-                bool multi_type = false;
-                int type = XkbKeyTypeIndex(keymap, key, 0);
-
-                simple = false;
-
-                for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
-                    if (XkbKeyTypeIndex(keymap, key, group) != type) {
-                        multi_type = true;
-                        break;
-                    }
-                }
-                if (multi_type) {
-                    for (group = 0;
-                         group < xkb_key_num_groups(keymap, key);
-                         group++) {
-                        if (!(srv->explicit[key] & (1 << group)))
-                            continue;
-                        type = XkbKeyTypeIndex(keymap, key, group);
-                        write_buf(keymap, buf, size, offset,
-                                  "\n\t\t\ttype[Group%d]= \"%s\",",
-                                  group + 1,
-                                  darray_item(map->types, type).name);
-                    }
-                }
-                else {
-                    write_buf(keymap, buf, size, offset,
-                              "\n\t\t\ttype= \"%s\",",
-                              darray_item(map->types, type).name);
+        write_buf(buf, "\n");
+
+    xkb_foreach_key(key, keymap) {
+        bool simple = true;
+        bool explicit_types = false;
+        bool multi_type = false;
+
+        if (key->num_groups == 0)
+            continue;
+
+        write_buf(buf, "\t\tkey %-20s {", KeyNameText(keymap->ctx, key->name));
+
+        for (group = 0; group < key->num_groups; group++) {
+            if (key->groups[group].explicit_type)
+                explicit_types = true;
+
+            if (group != 0 && key->groups[group].type != key->groups[0].type)
+                multi_type = true;
+        }
+
+        if (explicit_types) {
+            const struct xkb_key_type *type;
+            simple = false;
+
+            if (multi_type) {
+                for (group = 0; group < key->num_groups; group++) {
+                    if (!key->groups[group].explicit_type)
+                        continue;
+
+                    type = key->groups[group].type;
+                    write_buf(buf, "\n\t\t\ttype[group%u]= \"%s\",",
+                              group + 1,
+                              xkb_atom_text(keymap->ctx, type->name));
                 }
             }
-           if (keymap->ctrls && (srv->explicit[key] & XkbExplicitAutoRepeatMask)) {
-               if (keymap->ctrls->per_key_repeat[key / 8] & (1 << (key % 8)))
-                    write_buf(keymap, buf, size, offset,
-                               "\n\t\t\trepeat= Yes,");
-               else
-                    write_buf(keymap, buf, size, offset,
-                              "\n\t\t\trepeat= No,");
-               simple = false;
-           }
-           if (keymap->server->vmodmap[key] &&
-               (srv->explicit[key] & XkbExplicitVModMapMask)) {
-               write_buf(keymap, buf, size, offset, "\n\t\t\tvirtualMods= %s,",
-                          get_mod_mask_text(keymap, 0, keymap->server->vmodmap[key]));
-           }
-       }
-
-       switch (XkbOutOfRangeGroupAction(XkbKeyGroupInfo(keymap, key))) {
-           case XkbClampIntoRange:
-               write_buf(keymap, buf, size, offset, "\n\t\t\tgroupsClamp,");
-               break;
-           case XkbRedirectIntoRange:
-               write_buf(keymap, buf, size, offset,
-                          "\n\t\t\tgroupsRedirect= Group%d,",
-                         XkbOutOfRangeGroupNumber(XkbKeyGroupInfo(keymap, key)) + 1);
-               break;
-       }
-
-       if (srv->explicit == NULL ||
-            (srv->explicit[key] & XkbExplicitInterpretMask))
-           showActions = XkbKeyHasActions(keymap, key);
-       else
-            showActions = false;
-
-       if (xkb_key_num_groups(keymap, key) > 1 || showActions)
-           simple = false;
-
-       if (simple) {
-           write_buf(keymap, buf, size, offset, "\t[ ");
-            if (!write_keysyms(keymap, buf, size, offset, key, 0))
+            else {
+                type = key->groups[0].type;
+                write_buf(buf, "\n\t\t\ttype= \"%s\",",
+                          xkb_atom_text(keymap->ctx, type->name));
+            }
+        }
+
+        if (key->explicit & EXPLICIT_REPEAT) {
+            if (key->repeats)
+                write_buf(buf, "\n\t\t\trepeat= Yes,");
+            else
+                write_buf(buf, "\n\t\t\trepeat= No,");
+            simple = false;
+        }
+
+        if (key->vmodmap && (key->explicit & EXPLICIT_VMODMAP))
+            write_buf(buf, "\n\t\t\tvirtualMods= %s,",
+                      ModMaskText(keymap, key->vmodmap));
+
+        switch (key->out_of_range_group_action) {
+        case RANGE_SATURATE:
+            write_buf(buf, "\n\t\t\tgroupsClamp,");
+            break;
+
+        case RANGE_REDIRECT:
+            write_buf(buf, "\n\t\t\tgroupsRedirect= Group%u,",
+                      key->out_of_range_group_number + 1);
+            break;
+
+        default:
+            break;
+        }
+
+        showActions = !!(key->explicit & EXPLICIT_INTERP);
+
+        if (key->num_groups > 1 || showActions)
+            simple = false;
+
+        if (simple) {
+            write_buf(buf, "\t[ ");
+            if (!write_keysyms(keymap, buf, key, 0))
                 return false;
-            write_buf(keymap, buf, size, offset, " ] };\n");
-       }
-       else {
-           union xkb_action *acts;
-           int level;
-
-           acts = XkbKeyActionsPtr(keymap, key);
-           for (group = 0; group < xkb_key_num_groups(keymap, key); group++) {
-               if (group != 0)
-                   write_buf(keymap, buf, size, offset, ",");
-               write_buf(keymap, buf, size, offset,
-                          "\n\t\t\tsymbols[Group%d]= [ ", group + 1);
-                if (!write_keysyms(keymap, buf, size, offset, key, group))
+            write_buf(buf, " ] };\n");
+        }
+        else {
+            xkb_level_index_t level;
+
+            for (group = 0; group < key->num_groups; group++) {
+                if (group != 0)
+                    write_buf(buf, ",");
+                write_buf(buf, "\n\t\t\tsymbols[Group%u]= [ ", group + 1);
+                if (!write_keysyms(keymap, buf, key, group))
                     return false;
-               write_buf(keymap, buf, size, offset, " ]");
-               if (showActions) {
-                   write_buf(keymap, buf, size, offset,
-                              ",\n\t\t\tactions[Group%d]= [ ", group + 1);
-                   for (level = 0;
-                         level < XkbKeyGroupWidth(keymap, key, group);
-                         level++) {
-                       if (level != 0)
-                           write_buf(keymap, buf, size, offset, ", ");
-                       write_action(keymap, buf, size, offset, &acts[level],
+                write_buf(buf, " ]");
+                if (showActions) {
+                    write_buf(buf, ",\n\t\t\tactions[Group%u]= [ ",
+                              group + 1);
+                    for (level = 0;
+                         level < XkbKeyGroupWidth(key, group); level++) {
+                        if (level != 0)
+                            write_buf(buf, ", ");
+                        write_action(keymap, buf,
+                                     &key->groups[group].levels[level].action,
                                      NULL, NULL);
-                   }
-                   write_buf(keymap, buf, size, offset, " ]");
-                   acts += XkbKeyGroupsWidth(keymap, key);
-               }
-           }
-           write_buf(keymap, buf, size, offset, "\n\t\t};\n");
-       }
+                    }
+                    write_buf(buf, " ]");
+                }
+            }
+            write_buf(buf, "\n\t\t};\n");
+        }
     }
-    if (map && map->modmap) {
-       for (key = keymap->min_key_code; key <= keymap->max_key_code; key++) {
-            int mod;
-            char name[5];
 
-            if (map->modmap[key] == 0)
-                continue;
+    xkb_foreach_key(key, keymap) {
+        xkb_mod_index_t i;
+        const struct xkb_mod *mod;
 
-            for (mod = 0; mod < XkbNumModifiers; mod++) {
-                if (!(map->modmap[key] & (1 << mod)))
-                    continue;
+        if (key->modmap == 0)
+            continue;
 
-                memcpy(name, darray_item(keymap->names->keys, key).name, 4);
-                name[4]= '\0';
+        darray_enumerate(i, mod, keymap->mods) {
+            if (!(key->modmap & (1 << i)))
+                continue;
 
-                write_buf(keymap, buf, size, offset,
-                          "\t\tmodifier_map %s { <%s> };\n",
-                          get_mod_index_text(mod), name);
-            }
-       }
+            write_buf(buf, "\t\tmodifier_map %s { %s };\n",
+                      xkb_atom_text(keymap->ctx, mod->name),
+                      KeyNameText(keymap->ctx, key->name));
+        }
     }
 
-    write_buf(keymap, buf, size, offset, "\t};\n\n");
+    write_buf(buf, "\t};\n\n");
     return true;
 }
 
-_X_EXPORT char *
-xkb_map_get_as_string(struct xkb_keymap *keymap)
+XKB_EXPORT char *
+xkb_keymap_get_as_string(struct xkb_keymap *keymap,
+                         enum xkb_keymap_format format)
 {
-    char *ret = NULL;
-    size_t size = 0;
-    size_t offset = 0;
+    bool ok;
+    struct buf buf = { NULL, 0, 0 };
 
-    check_write_buf(keymap, &ret, &size, &offset, "xkb_keymap {\n");
-    if (ret == NULL)
-        return NULL;
-    if (!write_keycodes(keymap, &ret, &size, &offset))
-        return NULL;
-    if (!write_types(keymap, &ret, &size, &offset))
-        return NULL;
-    if (!write_compat(keymap, &ret, &size, &offset))
-        return NULL;
-    if (!write_symbols(keymap, &ret, &size, &offset))
-        return NULL;
-    check_write_buf(keymap, &ret, &size, &offset, "};\n");
-    if (ret == NULL)
+    if (format == XKB_KEYMAP_USE_ORIGINAL_FORMAT)
+        format = keymap->format;
+
+    if (format != XKB_KEYMAP_FORMAT_TEXT_V1) {
+        log_err(keymap->ctx,
+                "Trying to get a keymap as a string in an unsupported format (%d)\n",
+                format);
         return NULL;
+    }
+
+    ok = (check_write_buf(&buf, "xkb_keymap {\n") &&
+          write_keycodes(keymap, &buf) &&
+          write_types(keymap, &buf) &&
+          write_compat(keymap, &buf) &&
+          write_symbols(keymap, &buf) &&
+          check_write_buf(&buf, "};\n"));
 
-    return ret;
+    return (ok ? buf.buf : NULL);
 }