Structured log messages with a message registry
[platform/upstream/libxkbcommon.git] / src / xkbcomp / action.c
index e236ea1..1410aab 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 <X11/X.h>
-
-#include "xkbcomp.h"
-#include "xkbmisc.h"
+/*
+ * Copyright © 2012 Intel Corporation
+ * Copyright © 2012 Ran Benita <ran234@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Author: Daniel Stone <daniel@fooishbar.org>
+ *         Ran Benita <ran234@gmail.com>
+ */
+
+#include "config.h"
+
+#include "xkbcomp-priv.h"
+#include "text.h"
 #include "expr.h"
-
-#include "keycodes.h"
-#include "vmod.h"
-#include "misc.h"
 #include "action.h"
-#include "misc.h"
-
-static bool actionsInitialized;
-static ExprDef constTrue;
-static ExprDef constFalse;
 
-/***====================================================================***/
+static const ExprBoolean constTrue = {
+    .expr = {
+        .common = { .type = STMT_EXPR, .next = NULL },
+        .op = EXPR_VALUE,
+        .value_type = EXPR_TYPE_BOOLEAN,
+    },
+    .set = true,
+};
 
-static const LookupEntry actionStrings[] = {
-    { "noaction",          XkbSA_NoAction       },
-    { "setmods",           XkbSA_SetMods        },
-    { "latchmods",         XkbSA_LatchMods      },
-    { "lockmods",          XkbSA_LockMods       },
-    { "setgroup",          XkbSA_SetGroup       },
-    { "latchgroup",        XkbSA_LatchGroup     },
-    { "lockgroup",         XkbSA_LockGroup      },
-    { "moveptr",           XkbSA_MovePtr        },
-    { "movepointer",       XkbSA_MovePtr        },
-    { "ptrbtn",            XkbSA_PtrBtn         },
-    { "pointerbutton",     XkbSA_PtrBtn         },
-    { "lockptrbtn",        XkbSA_LockPtrBtn     },
-    { "lockpointerbutton", XkbSA_LockPtrBtn     },
-    { "lockptrbutton",     XkbSA_LockPtrBtn     },
-    { "lockpointerbtn",    XkbSA_LockPtrBtn     },
-    { "setptrdflt",        XkbSA_SetPtrDflt     },
-    { "setpointerdefault", XkbSA_SetPtrDflt     },
-    { "isolock",           XkbSA_ISOLock        },
-    { "terminate",         XkbSA_Terminate      },
-    { "terminateserver",   XkbSA_Terminate      },
-    { "switchscreen",      XkbSA_SwitchScreen   },
-    { "setcontrols",       XkbSA_SetControls    },
-    { "lockcontrols",      XkbSA_LockControls   },
-    { "actionmessage",     XkbSA_ActionMessage  },
-    { "messageaction",     XkbSA_ActionMessage  },
-    { "message",           XkbSA_ActionMessage  },
-    { "redirect",          XkbSA_RedirectKey    },
-    { "redirectkey",       XkbSA_RedirectKey    },
-    { "devbtn",            XkbSA_DeviceBtn      },
-    { "devicebtn",         XkbSA_DeviceBtn      },
-    { "devbutton",         XkbSA_DeviceBtn      },
-    { "devicebutton",      XkbSA_DeviceBtn      },
-    { "lockdevbtn",        XkbSA_DeviceBtn      },
-    { "lockdevicebtn",     XkbSA_LockDeviceBtn  },
-    { "lockdevbutton",     XkbSA_LockDeviceBtn  },
-    { "lockdevicebutton",  XkbSA_LockDeviceBtn  },
-    { "devval",            XkbSA_DeviceValuator },
-    { "deviceval",         XkbSA_DeviceValuator },
-    { "devvaluator",       XkbSA_DeviceValuator },
-    { "devicevaluator",    XkbSA_DeviceValuator },
-    { "private",           PrivateAction        },
-    { NULL,                0                    }
+static const ExprBoolean constFalse = {
+    .expr = {
+        .common = { .type = STMT_EXPR, .next = NULL },
+        .op = EXPR_VALUE,
+        .value_type = EXPR_TYPE_BOOLEAN,
+    },
+    .set = false,
 };
 
-static const LookupEntry fieldStrings[] = {
-    { "clearLocks",       F_ClearLocks  },
-    { "latchToLock",      F_LatchToLock },
-    { "genKeyEvent",      F_GenKeyEvent },
-    { "generateKeyEvent", F_GenKeyEvent },
-    { "report",           F_Report      },
-    { "default",          F_Default     },
-    { "affect",           F_Affect      },
-    { "increment",        F_Increment   },
-    { "modifiers",        F_Modifiers   },
-    { "mods",             F_Modifiers   },
-    { "group",            F_Group       },
-    { "x",                F_X           },
-    { "y",                F_Y           },
-    { "accel",            F_Accel       },
-    { "accelerate",       F_Accel       },
-    { "repeat",           F_Accel       },
-    { "button",           F_Button      },
-    { "value",            F_Value       },
-    { "controls",         F_Controls    },
-    { "ctrls",            F_Controls    },
-    { "type",             F_Type        },
-    { "count",            F_Count       },
-    { "screen",           F_Screen      },
-    { "same",             F_Same        },
-    { "sameServer",       F_Same        },
-    { "data",             F_Data        },
-    { "device",           F_Device      },
-    { "dev",              F_Device      },
-    { "key",              F_Keycode     },
-    { "keycode",          F_Keycode     },
-    { "kc",               F_Keycode     },
-    { "clearmods",        F_ModsToClear },
-    { "clearmodifiers",   F_ModsToClear },
-    { NULL,               0             }
+enum action_field {
+    ACTION_FIELD_CLEAR_LOCKS,
+    ACTION_FIELD_LATCH_TO_LOCK,
+    ACTION_FIELD_GEN_KEY_EVENT,
+    ACTION_FIELD_REPORT,
+    ACTION_FIELD_DEFAULT,
+    ACTION_FIELD_AFFECT,
+    ACTION_FIELD_INCREMENT,
+    ACTION_FIELD_MODIFIERS,
+    ACTION_FIELD_GROUP,
+    ACTION_FIELD_X,
+    ACTION_FIELD_Y,
+    ACTION_FIELD_ACCEL,
+    ACTION_FIELD_BUTTON,
+    ACTION_FIELD_VALUE,
+    ACTION_FIELD_CONTROLS,
+    ACTION_FIELD_TYPE,
+    ACTION_FIELD_COUNT,
+    ACTION_FIELD_SCREEN,
+    ACTION_FIELD_SAME,
+    ACTION_FIELD_DATA,
+    ACTION_FIELD_DEVICE,
+    ACTION_FIELD_KEYCODE,
+    ACTION_FIELD_MODS_TO_CLEAR,
 };
 
-static bool
-stringToValue(const LookupEntry tab[], const char *string,
-              unsigned *value_rtrn)
+ActionsInfo *
+NewActionsInfo(void)
 {
-    const LookupEntry *entry;
+    enum xkb_action_type type;
+    ActionsInfo *info;
 
-    if (!string)
-        return false;
+    info = calloc(1, sizeof(*info));
+    if (!info)
+        return NULL;
 
-    for (entry = tab; entry->name != NULL; entry++) {
-        if (strcasecmp(entry->name, string) == 0) {
-            *value_rtrn = entry->result;
-            return true;
-        }
-    }
+    for (type = 0; type < _ACTION_TYPE_NUM_ENTRIES; type++)
+        info->actions[type].type = type;
 
-    return false;
-}
+    /* Apply some "factory defaults". */
 
-static const char *
-valueToString(const LookupEntry tab[], unsigned value)
-{
-    const LookupEntry *entry;
+    /* Increment default button. */
+    info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.flags = 0;
+    info->actions[ACTION_TYPE_PTR_DEFAULT].dflt.value = 1;
+    info->actions[ACTION_TYPE_PTR_MOVE].ptr.flags = ACTION_ACCEL;
+    info->actions[ACTION_TYPE_SWITCH_VT].screen.flags = ACTION_SAME_SCREEN;
 
-    for (entry = tab; entry->name != NULL; entry++)
-        if (entry->result == value)
-            return entry->name;
+    return info;
+}
 
-    return "unknown";
+void
+FreeActionsInfo(ActionsInfo *info)
+{
+    free(info);
 }
 
+static const LookupEntry fieldStrings[] = {
+    { "clearLocks",       ACTION_FIELD_CLEAR_LOCKS   },
+    { "latchToLock",      ACTION_FIELD_LATCH_TO_LOCK },
+    { "genKeyEvent",      ACTION_FIELD_GEN_KEY_EVENT },
+    { "generateKeyEvent", ACTION_FIELD_GEN_KEY_EVENT },
+    { "report",           ACTION_FIELD_REPORT        },
+    { "default",          ACTION_FIELD_DEFAULT       },
+    { "affect",           ACTION_FIELD_AFFECT        },
+    { "increment",        ACTION_FIELD_INCREMENT     },
+    { "modifiers",        ACTION_FIELD_MODIFIERS     },
+    { "mods",             ACTION_FIELD_MODIFIERS     },
+    { "group",            ACTION_FIELD_GROUP         },
+    { "x",                ACTION_FIELD_X             },
+    { "y",                ACTION_FIELD_Y             },
+    { "accel",            ACTION_FIELD_ACCEL         },
+    { "accelerate",       ACTION_FIELD_ACCEL         },
+    { "repeat",           ACTION_FIELD_ACCEL         },
+    { "button",           ACTION_FIELD_BUTTON        },
+    { "value",            ACTION_FIELD_VALUE         },
+    { "controls",         ACTION_FIELD_CONTROLS      },
+    { "ctrls",            ACTION_FIELD_CONTROLS      },
+    { "type",             ACTION_FIELD_TYPE          },
+    { "count",            ACTION_FIELD_COUNT         },
+    { "screen",           ACTION_FIELD_SCREEN        },
+    { "same",             ACTION_FIELD_SAME          },
+    { "sameServer",       ACTION_FIELD_SAME          },
+    { "data",             ACTION_FIELD_DATA          },
+    { "device",           ACTION_FIELD_DEVICE        },
+    { "dev",              ACTION_FIELD_DEVICE        },
+    { "key",              ACTION_FIELD_KEYCODE       },
+    { "keycode",          ACTION_FIELD_KEYCODE       },
+    { "kc",               ACTION_FIELD_KEYCODE       },
+    { "clearmods",        ACTION_FIELD_MODS_TO_CLEAR },
+    { "clearmodifiers",   ACTION_FIELD_MODS_TO_CLEAR },
+    { NULL,               0                          }
+};
+
 static bool
-stringToAction(const char *str, unsigned *type_rtrn)
+stringToAction(const char *str, enum xkb_action_type *type_rtrn)
 {
-    return stringToValue(actionStrings, str, type_rtrn);
+    return LookupString(actionTypeNames, str, type_rtrn);
 }
 
 static bool
-stringToField(const char *str, unsigned *field_rtrn)
+stringToField(const char *str, enum action_field *field_rtrn)
 {
-    return stringToValue(fieldStrings, str, field_rtrn);
+    return LookupString(fieldStrings, str, field_rtrn);
 }
 
 static const char *
-fieldText(unsigned field)
+fieldText(enum action_field field)
 {
-    return valueToString(fieldStrings, field);
+    return LookupValue(fieldStrings, field);
 }
 
 /***====================================================================***/
 
-static bool
-ReportMismatch(unsigned action, unsigned field, const char *type)
+static inline bool
+ReportMismatch(struct xkb_context *ctx, xkb_message_code_t code,
+               enum xkb_action_type action, enum action_field field,
+               const char *type)
 {
-    ERROR("Value of %s field must be of type %s\n", fieldText(field), type);
-    ACTION("Action %s definition ignored\n", XkbcActionTypeText(action));
+    log_err_with_code(ctx, code,
+            "Value of %s field must be of type %s; "
+            "Action %s definition ignored\n",
+            fieldText(field), type, ActionTypeText(action));
     return false;
 }
 
-static bool
-ReportIllegal(unsigned action, unsigned field)
+static inline bool
+ReportIllegal(struct xkb_context *ctx, enum xkb_action_type action,
+              enum action_field field)
 {
-    ERROR("Field %s is not defined for an action of type %s\n",
-           fieldText(field), XkbcActionTypeText(action));
-    ACTION("Action definition ignored\n");
+    log_err(ctx,
+            "Field %s is not defined for an action of type %s; "
+            "Action definition ignored\n",
+            fieldText(field), ActionTypeText(action));
     return false;
 }
 
-static bool
-ReportActionNotArray(unsigned action, unsigned field)
+static inline bool
+ReportActionNotArray(struct xkb_context *ctx, enum xkb_action_type action,
+                     enum action_field field)
 {
-    ERROR("The %s field in the %s action is not an array\n",
-           fieldText(field), XkbcActionTypeText(action));
-    ACTION("Action definition ignored\n");
+    log_err(ctx,
+            "The %s field in the %s action is not an array; "
+            "Action definition ignored\n",
+            fieldText(field), ActionTypeText(action));
     return false;
 }
 
 static bool
-ReportNotFound(unsigned action, unsigned field, const char *what,
-               const char *bad)
-{
-    ERROR("%s named %s not found\n", what, bad);
-    ACTION("Ignoring the %s field of an %s action\n", fieldText(field),
-            XkbcActionTypeText(action));
-    return false;
-}
+HandleNoAction(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+               union xkb_action *action, enum action_field field,
+               const ExprDef *array_ndx, const ExprDef *value)
 
-static bool
-HandleNoAction(struct xkb_keymap * xkb,
-               struct xkb_any_action * action,
-               unsigned field, ExprDef * array_ndx, ExprDef * value)
 {
-    return ReportIllegal(action->type, field);
+    return true;
 }
 
 static bool
-CheckLatchLockFlags(unsigned action,
-                    unsigned field, ExprDef * value, unsigned *flags_inout)
+CheckBooleanFlag(struct xkb_context *ctx, enum xkb_action_type action,
+                 enum action_field field, enum xkb_action_flags flag,
+                 const ExprDef *array_ndx, const ExprDef *value,
+                 enum xkb_action_flags *flags_inout)
 {
-    unsigned tmp;
-    ExprResult result;
+    bool set;
 
-    if (field == F_ClearLocks)
-        tmp = XkbSA_ClearLocks;
-    else if (field == F_LatchToLock)
-        tmp = XkbSA_LatchToLock;
-    else
-        return false;           /* WSGO! */
-    if (!ExprResolveBoolean(value, &result))
-        return ReportMismatch(action, field, "boolean");
-    if (result.uval)
-        *flags_inout |= tmp;
+    if (array_ndx)
+        return ReportActionNotArray(ctx, action, field);
+
+    if (!ExprResolveBoolean(ctx, value, &set))
+        return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+                              action, field, "boolean");
+
+    if (set)
+        *flags_inout |= flag;
     else
-        *flags_inout &= ~tmp;
+        *flags_inout &= ~flag;
+
     return true;
 }
 
 static bool
-CheckModifierField(struct xkb_keymap * xkb,
-                   unsigned action,
-                   ExprDef * value,
-                   unsigned *flags_inout, unsigned *mods_rtrn)
+CheckModifierField(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                   enum xkb_action_type action, const ExprDef *array_ndx,
+                   const ExprDef *value, enum xkb_action_flags *flags_inout,
+                   xkb_mod_mask_t *mods_rtrn)
 {
-    ExprResult rtrn;
+    if (array_ndx)
+        return ReportActionNotArray(ctx, action, ACTION_FIELD_MODIFIERS);
 
-    if (value->op == ExprIdent)
-    {
+    if (value->expr.op == EXPR_IDENT) {
         const char *valStr;
-        valStr = XkbcAtomText(value->value.str);
-        if (valStr && ((strcasecmp(valStr, "usemodmapmods") == 0) ||
-                       (strcasecmp(valStr, "modmapmods") == 0)))
-        {
-
+        valStr = xkb_atom_text(ctx, value->ident.ident);
+        if (valStr && (istreq(valStr, "usemodmapmods") ||
+                       istreq(valStr, "modmapmods"))) {
             *mods_rtrn = 0;
-            *flags_inout |= XkbSA_UseModMapMods;
+            *flags_inout |= ACTION_MODS_LOOKUP_MODMAP;
             return true;
         }
     }
-    if (!ExprResolveVModMask(value, &rtrn, xkb))
-        return ReportMismatch(action, F_Modifiers, "modifier mask");
-    *mods_rtrn = rtrn.uval;
-    *flags_inout &= ~XkbSA_UseModMapMods;
+
+    if (!ExprResolveModMask(ctx, value, MOD_BOTH, mods, mods_rtrn))
+        return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action,
+                              ACTION_FIELD_MODIFIERS, "modifier mask");
+
+    *flags_inout &= ~ACTION_MODS_LOOKUP_MODMAP;
     return true;
 }
 
+static const LookupEntry lockWhich[] = {
+    { "both", 0 },
+    { "lock", ACTION_LOCK_NO_UNLOCK },
+    { "neither", (ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK) },
+    { "unlock", ACTION_LOCK_NO_LOCK },
+    { NULL, 0 }
+};
+
 static bool
-HandleSetLatchMods(struct xkb_keymap * xkb,
-                   struct xkb_any_action * action,
-                   unsigned field, ExprDef * array_ndx, ExprDef * value)
+CheckAffectField(struct xkb_context *ctx, enum xkb_action_type action,
+                 const ExprDef *array_ndx, const ExprDef *value,
+                 enum xkb_action_flags *flags_inout)
 {
-    struct xkb_mod_action *act;
-    unsigned rtrn;
-    unsigned t1, t2;
-
-    act = (struct xkb_mod_action *) action;
-    if (array_ndx != NULL)
-    {
-        switch (field)
-        {
-        case F_ClearLocks:
-        case F_LatchToLock:
-        case F_Modifiers:
-            return ReportActionNotArray(action->type, field);
-        }
-    }
-    switch (field)
-    {
-    case F_ClearLocks:
-    case F_LatchToLock:
-        rtrn = act->flags;
-        if (CheckLatchLockFlags(action->type, field, value, &rtrn))
-        {
-            act->flags = rtrn;
-            return true;
-        }
-        return false;
-    case F_Modifiers:
-        t1 = act->flags;
-        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
-        {
-            act->flags = t1;
-            act->real_mods = act->mask = (t2 & 0xff);
-            act->vmods = (t2 >> 8) & 0xffff;
-            return true;
-        }
-        return false;
-    }
-    return ReportIllegal(action->type, field);
+    enum xkb_action_flags flags;
+
+    if (array_ndx)
+        return ReportActionNotArray(ctx, action, ACTION_FIELD_AFFECT);
+
+    if (!ExprResolveEnum(ctx, value, &flags, lockWhich))
+        return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+                              action, ACTION_FIELD_AFFECT,
+                              "lock, unlock, both, neither");
+
+    *flags_inout &= ~(ACTION_LOCK_NO_LOCK | ACTION_LOCK_NO_UNLOCK);
+    *flags_inout |= flags;
+    return true;
 }
 
 static bool
-HandleLockMods(struct xkb_keymap * xkb,
-               struct xkb_any_action * action,
-               unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    struct xkb_mod_action *act;
-    unsigned t1, t2;
-
-    act = (struct xkb_mod_action *) action;
-    if ((array_ndx != NULL) && (field == F_Modifiers))
-        return ReportActionNotArray(action->type, field);
-    switch (field)
-    {
-    case F_Modifiers:
-        t1 = act->flags;
-        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
-        {
-            act->flags = t1;
-            act->real_mods = act->mask = (t2 & 0xff);
-            act->vmods = (t2 >> 8) & 0xffff;
-            return true;
-        }
-        return false;
-    }
-    return ReportIllegal(action->type, field);
+HandleSetLatchLockMods(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                       union xkb_action *action, enum action_field field,
+                       const ExprDef *array_ndx, const ExprDef *value)
+{
+    struct xkb_mod_action *act = &action->mods;
+    const enum xkb_action_type type = action->type;
+
+    if (field == ACTION_FIELD_MODIFIERS)
+        return CheckModifierField(ctx, mods, action->type, array_ndx, value,
+                                  &act->flags, &act->mods.mods);
+    if ((type == ACTION_TYPE_MOD_SET || type == ACTION_TYPE_MOD_LATCH) &&
+        field == ACTION_FIELD_CLEAR_LOCKS)
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_LOCK_CLEAR, array_ndx, value,
+                                &act->flags);
+    if (type == ACTION_TYPE_MOD_LATCH &&
+        field == ACTION_FIELD_LATCH_TO_LOCK)
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_LATCH_TO_LOCK, array_ndx, value,
+                                &act->flags);
+    if (type == ACTION_TYPE_MOD_LOCK &&
+        field == ACTION_FIELD_AFFECT)
+        return CheckAffectField(ctx, action->type, array_ndx, value,
+                                &act->flags);
+
+    return ReportIllegal(ctx, action->type, field);
 }
 
 static bool
-CheckGroupField(unsigned action,
-                ExprDef * value, unsigned *flags_inout, int *grp_rtrn)
+CheckGroupField(struct xkb_context *ctx, enum xkb_action_type action,
+                const ExprDef *array_ndx, const ExprDef *value,
+                enum xkb_action_flags *flags_inout, int32_t *group_rtrn)
 {
-    ExprDef *spec;
-    ExprResult rtrn;
+    const ExprDef *spec;
+    xkb_layout_index_t idx;
+    enum xkb_action_flags flags = *flags_inout;
+
+    if (array_ndx)
+        return ReportActionNotArray(ctx, action, ACTION_FIELD_GROUP);
 
-    if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
-    {
-        *flags_inout &= ~XkbSA_GroupAbsolute;
-        spec = value->value.child;
+    if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) {
+        flags &= ~ACTION_ABSOLUTE_SWITCH;
+        spec = value->unary.child;
     }
-    else
-    {
-        *flags_inout |= XkbSA_GroupAbsolute;
+    else {
+        flags |= ACTION_ABSOLUTE_SWITCH;
         spec = value;
     }
 
-    if (!ExprResolveGroup(spec, &rtrn))
-        return ReportMismatch(action, F_Group, "integer (range 1..8)");
-    if (value->op == OpNegate)
-        *grp_rtrn = -rtrn.ival;
-    else if (value->op == OpUnaryPlus)
-        *grp_rtrn = rtrn.ival;
-    else
-        *grp_rtrn = rtrn.ival - 1;
-    return true;
-}
+    if (!ExprResolveGroup(ctx, spec, &idx))
+        return ReportMismatch(ctx, XKB_ERROR_UNSUPPORTED_GROUP_INDEX, action,
+                              ACTION_FIELD_GROUP, "integer (range 1..8)");
 
-static bool
-HandleSetLatchGroup(struct xkb_keymap * xkb,
-                    struct xkb_any_action * action,
-                    unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    struct xkb_group_action *act;
-    unsigned rtrn;
-    unsigned t1;
-    int t2;
-
-    act = (struct xkb_group_action *) action;
-    if (array_ndx != NULL)
-    {
-        switch (field)
-        {
-        case F_ClearLocks:
-        case F_LatchToLock:
-        case F_Group:
-            return ReportActionNotArray(action->type, field);
-        }
+    /* +n, -n are relative, n is absolute. */
+    if (value->expr.op == EXPR_NEGATE || value->expr.op == EXPR_UNARY_PLUS) {
+        *group_rtrn = (int32_t) idx;
+        if (value->expr.op == EXPR_NEGATE)
+            *group_rtrn = -*group_rtrn;
     }
-    switch (field)
-    {
-    case F_ClearLocks:
-    case F_LatchToLock:
-        rtrn = act->flags;
-        if (CheckLatchLockFlags(action->type, field, value, &rtrn))
-        {
-            act->flags = rtrn;
-            return true;
-        }
-        return false;
-    case F_Group:
-        t1 = act->flags;
-        if (CheckGroupField(action->type, value, &t1, &t2))
-        {
-            act->flags = t1;
-           act->group = t2;
-            return true;
-        }
-        return false;
+    else {
+        *group_rtrn = (int32_t) (idx - 1);
     }
-    return ReportIllegal(action->type, field);
+    *flags_inout = flags;
+    return true;
 }
 
 static bool
-HandleLockGroup(struct xkb_keymap * xkb,
-                struct xkb_any_action * action,
-                unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    struct xkb_group_action *act;
-    unsigned t1;
-    int t2;
-
-    act = (struct xkb_group_action *) action;
-    if ((array_ndx != NULL) && (field == F_Group))
-        return ReportActionNotArray(action->type, field);
-    if (field == F_Group)
-    {
-        t1 = act->flags;
-        if (CheckGroupField(action->type, value, &t1, &t2))
-        {
-            act->flags = t1;
-           act->group = t2;
-            return true;
-        }
-        return false;
-    }
-    return ReportIllegal(action->type, field);
+HandleSetLatchLockGroup(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                        union xkb_action *action, enum action_field field,
+                        const ExprDef *array_ndx, const ExprDef *value)
+{
+    struct xkb_group_action *act = &action->group;
+    const enum xkb_action_type type = action->type;
+
+    if (field == ACTION_FIELD_GROUP)
+        return CheckGroupField(ctx, action->type, array_ndx, value,
+                               &act->flags, &act->group);
+    if ((type == ACTION_TYPE_GROUP_SET || type == ACTION_TYPE_GROUP_LATCH) &&
+        field == ACTION_FIELD_CLEAR_LOCKS)
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_LOCK_CLEAR, array_ndx, value,
+                                &act->flags);
+    if (type == ACTION_TYPE_GROUP_LATCH &&
+        field == ACTION_FIELD_LATCH_TO_LOCK)
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_LATCH_TO_LOCK, array_ndx, value,
+                                &act->flags);
+
+    return ReportIllegal(ctx, action->type, field);
 }
 
 static bool
-HandleMovePtr(struct xkb_keymap * xkb,
-              struct xkb_any_action * action,
-              unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-    struct xkb_pointer_action *act;
-    bool absolute;
-
-    act = (struct xkb_pointer_action *) action;
-    if ((array_ndx != NULL) && ((field == F_X) || (field == F_Y)))
-        return ReportActionNotArray(action->type, field);
-
-    if ((field == F_X) || (field == F_Y))
-    {
-        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
-            absolute = false;
-        else
-            absolute = true;
-        if (!ExprResolveInteger(value, &rtrn))
-            return ReportMismatch(action->type, field, "integer");
-        if (field == F_X)
-        {
+HandleMovePtr(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+              union xkb_action *action, enum action_field field,
+              const ExprDef *array_ndx, const ExprDef *value)
+{
+    struct xkb_pointer_action *act = &action->ptr;
+
+    if (field == ACTION_FIELD_X || field == ACTION_FIELD_Y) {
+        int val;
+        const bool absolute = (value->expr.op != EXPR_NEGATE &&
+                               value->expr.op != EXPR_UNARY_PLUS);
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (!ExprResolveInteger(ctx, value, &val))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "integer");
+
+        if (val < INT16_MIN || val > INT16_MAX) {
+            log_err(ctx,
+                    "The %s field in the %s action must be in range %d..%d; "
+                    "Action definition ignored\n",
+                    fieldText(field), ActionTypeText(action->type),
+                    INT16_MIN, INT16_MAX);
+            return false;
+        }
+
+        if (field == ACTION_FIELD_X) {
             if (absolute)
-                act->flags |= XkbSA_MoveAbsoluteX;
-            act->x = rtrn.ival;
+                act->flags |= ACTION_ABSOLUTE_X;
+            act->x = (int16_t) val;
         }
-        else
-        {
+        else {
             if (absolute)
-                act->flags |= XkbSA_MoveAbsoluteY;
-            act->y = rtrn.ival;
+                act->flags |= ACTION_ABSOLUTE_Y;
+            act->y = (int16_t) val;
         }
+
         return true;
     }
-    else if (field == F_Accel)
-    {
-        if (!ExprResolveBoolean(value, &rtrn))
-            return ReportMismatch(action->type, field, "boolean");
-        if (rtrn.uval)
-            act->flags &= ~XkbSA_NoAcceleration;
-        else
-            act->flags |= XkbSA_NoAcceleration;
+    else if (field == ACTION_FIELD_ACCEL) {
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_ACCEL, array_ndx, value, &act->flags);
     }
-    return ReportIllegal(action->type, field);
-}
 
-static const LookupEntry lockWhich[] = {
-    {"both", 0},
-    {"lock", XkbSA_LockNoUnlock},
-    {"neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock)},
-    {"unlock", XkbSA_LockNoLock},
-    {NULL, 0}
-};
+    return ReportIllegal(ctx, action->type, field);
+}
 
 static bool
-HandlePtrBtn(struct xkb_keymap * xkb,
-             struct xkb_any_action * action,
-             unsigned field, ExprDef * array_ndx, ExprDef * value)
+HandlePtrBtn(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+             union xkb_action *action, enum action_field field,
+             const ExprDef *array_ndx, const ExprDef *value)
 {
-    ExprResult rtrn;
-    struct xkb_pointer_button_action *act;
-
-    act = (struct xkb_pointer_button_action *) action;
-    if (field == F_Button)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveButton(value, &rtrn))
-            return ReportMismatch(action->type, field,
-                                  "integer (range 1..5)");
-        if ((rtrn.ival < 0) || (rtrn.ival > 5))
-        {
-            ERROR("Button must specify default or be in the range 1..5\n");
-            ACTION("Illegal button value %d ignored\n", rtrn.ival);
+    struct xkb_pointer_button_action *act = &action->btn;
+
+    if (field == ACTION_FIELD_BUTTON) {
+        int btn;
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (!ExprResolveButton(ctx, value, &btn))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "integer (range 1..5)");
+
+        if (btn < 0 || btn > 5) {
+            log_err(ctx,
+                    "Button must specify default or be in the range 1..5; "
+                    "Illegal button value %d ignored\n", btn);
             return false;
         }
-        act->button = rtrn.ival;
+
+        act->button = btn;
         return true;
     }
-    else if ((action->type == XkbSA_LockPtrBtn) && (field == F_Affect))
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveEnum(value, &rtrn, lockWhich))
-            return ReportMismatch(action->type, field, "lock or unlock");
-        act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
-        act->flags |= rtrn.ival;
-        return true;
+    else if (action->type == ACTION_TYPE_PTR_LOCK &&
+             field == ACTION_FIELD_AFFECT) {
+        return CheckAffectField(ctx, action->type, array_ndx, value,
+                                &act->flags);
     }
-    else if (field == F_Count)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveButton(value, &rtrn))
-            return ReportMismatch(action->type, field, "integer");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("The count field must have a value in the range 0..255\n");
-            ACTION("Illegal count %d ignored\n", rtrn.ival);
+    else if (field == ACTION_FIELD_COUNT) {
+        int val;
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (!ExprResolveInteger(ctx, value, &val))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "integer");
+
+        if (val < 0 || val > 255) {
+            log_err(ctx,
+                    "The count field must have a value in the range 0..255; "
+                    "Illegal count %d ignored\n", val);
             return false;
         }
-        act->count = rtrn.ival;
+
+        act->count = (uint8_t) val;
         return true;
     }
-    return ReportIllegal(action->type, field);
+
+    return ReportIllegal(ctx, action->type, field);
 }
 
 static const LookupEntry ptrDflts[] = {
-    {"dfltbtn", XkbSA_AffectDfltBtn},
-    {"defaultbutton", XkbSA_AffectDfltBtn},
-    {"button", XkbSA_AffectDfltBtn},
-    {NULL, 0}
+    { "dfltbtn", 1 },
+    { "defaultbutton", 1 },
+    { "button", 1 },
+    { NULL, 0 }
 };
 
 static bool
-HandleSetPtrDflt(struct xkb_keymap * xkb,
-                 struct xkb_any_action * action,
-                 unsigned field, ExprDef * array_ndx, ExprDef * value)
+HandleSetPtrDflt(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                 union xkb_action *action, enum action_field field,
+                 const ExprDef *array_ndx, const ExprDef *value)
 {
-    ExprResult rtrn;
-    struct xkb_pointer_default_action *act;
-
-    act = (struct xkb_pointer_default_action *) action;
-    if (field == F_Affect)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveEnum(value, &rtrn, ptrDflts))
-            return ReportMismatch(action->type, field, "pointer component");
-        act->affect = rtrn.uval;
+    struct xkb_pointer_default_action *act = &action->dflt;
+
+    if (field == ACTION_FIELD_AFFECT) {
+        unsigned int val;
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (!ExprResolveEnum(ctx, value, &val, ptrDflts))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "pointer component");
         return true;
     }
-    else if ((field == F_Button) || (field == F_Value))
-    {
-        ExprDef *btn;
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
-        {
-            act->flags &= ~XkbSA_DfltBtnAbsolute;
-            btn = value->value.child;
+    else if (field == ACTION_FIELD_BUTTON || field == ACTION_FIELD_VALUE) {
+        const ExprDef *button;
+        int btn;
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (value->expr.op == EXPR_NEGATE ||
+            value->expr.op == EXPR_UNARY_PLUS) {
+            act->flags &= ~ACTION_ABSOLUTE_SWITCH;
+            button = value->unary.child;
         }
-        else
-        {
-            act->flags |= XkbSA_DfltBtnAbsolute;
-            btn = value;
+        else {
+            act->flags |= ACTION_ABSOLUTE_SWITCH;
+            button = value;
         }
 
-        if (!ExprResolveButton(btn, &rtrn))
-            return ReportMismatch(action->type, field,
-                                  "integer (range 1..5)");
-        if ((rtrn.ival < 0) || (rtrn.ival > 5))
-        {
-            ERROR("New default button value must be in the range 1..5\n");
-            ACTION("Illegal default button value %d ignored\n", rtrn.ival);
+        if (!ExprResolveButton(ctx, button, &btn))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "integer (range 1..5)");
+
+        if (btn < 0 || btn > 5) {
+            log_err(ctx,
+                    "New default button value must be in the range 1..5; "
+                    "Illegal default button value %d ignored\n", btn);
             return false;
         }
-        if (rtrn.ival == 0)
-        {
-            ERROR("Cannot set default pointer button to \"default\"\n");
-            ACTION("Illegal default button setting ignored\n");
+        if (btn == 0) {
+            log_err(ctx,
+                    "Cannot set default pointer button to \"default\"; "
+                    "Illegal default button setting ignored\n");
             return false;
         }
-        if (value->op == OpNegate)
-           act->value = -rtrn.ival;
-        else
-           act->value = rtrn.ival;
-        return true;
-    }
-    return ReportIllegal(action->type, field);
-}
 
-static const LookupEntry isoNames[] = {
-    {"mods", XkbSA_ISONoAffectMods},
-    {"modifiers", XkbSA_ISONoAffectMods},
-    {"group", XkbSA_ISONoAffectGroup},
-    {"groups", XkbSA_ISONoAffectGroup},
-    {"ptr", XkbSA_ISONoAffectPtr},
-    {"pointer", XkbSA_ISONoAffectPtr},
-    {"ctrls", XkbSA_ISONoAffectCtrls},
-    {"controls", XkbSA_ISONoAffectCtrls},
-    {"all", ~((unsigned) 0)},
-    {"none", 0},
-    {NULL, 0},
-};
-
-static bool
-HandleISOLock(struct xkb_keymap * xkb,
-              struct xkb_any_action * action,
-              unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-    struct xkb_iso_action *act;
-    unsigned flags, mods;
-    int group;
-
-    act = (struct xkb_iso_action *) action;
-    switch (field)
-    {
-    case F_Modifiers:
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        flags = act->flags;
-        if (CheckModifierField(xkb, action->type, value, &flags, &mods))
-        {
-            act->flags = flags & (~XkbSA_ISODfltIsGroup);
-            act->real_mods = mods & 0xff;
-            act->vmods = (mods >> 8) & 0xff;
-            return true;
-        }
-        return false;
-    case F_Group:
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        flags = act->flags;
-        if (CheckGroupField(action->type, value, &flags, &group))
-        {
-            act->flags = flags | XkbSA_ISODfltIsGroup;
-            act->group = group;
-            return true;
-        }
-        return false;
-    case F_Affect:
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveMask(value, &rtrn, isoNames))
-            return ReportMismatch(action->type, field, "keyboard component");
-        act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
+        act->value = (value->expr.op == EXPR_NEGATE ? -btn: btn);
         return true;
     }
-    return ReportIllegal(action->type, field);
+
+    return ReportIllegal(ctx, action->type, field);
 }
 
 static bool
-HandleSwitchScreen(struct xkb_keymap * xkb,
-                   struct xkb_any_action * action,
-                   unsigned field, ExprDef * array_ndx, ExprDef * value)
+HandleSwitchScreen(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                   union xkb_action *action, enum action_field field,
+                   const ExprDef *array_ndx, const ExprDef *value)
 {
-    ExprResult rtrn;
-    struct xkb_switch_screen_action *act;
-
-    act = (struct xkb_switch_screen_action *) action;
-    if (field == F_Screen)
-    {
-        ExprDef *scrn;
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
-        {
-            act->flags &= ~XkbSA_SwitchAbsolute;
-            scrn = value->value.child;
+    struct xkb_switch_screen_action *act = &action->screen;
+
+    if (field == ACTION_FIELD_SCREEN) {
+        const ExprDef *scrn;
+        int val;
+
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (value->expr.op == EXPR_NEGATE ||
+            value->expr.op == EXPR_UNARY_PLUS) {
+            act->flags &= ~ACTION_ABSOLUTE_SWITCH;
+            scrn = value->unary.child;
         }
-        else
-        {
-            act->flags |= XkbSA_SwitchAbsolute;
+        else {
+            act->flags |= ACTION_ABSOLUTE_SWITCH;
             scrn = value;
         }
 
-        if (!ExprResolveInteger(scrn, &rtrn))
-            return ReportMismatch(action->type, field, "integer (0..255)");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("Screen index must be in the range 1..255\n");
-            ACTION("Illegal screen value %d ignored\n", rtrn.ival);
+        if (!ExprResolveInteger(ctx, scrn, &val))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "integer (0..255)");
+
+        if (val < 0 || val > 255) {
+            log_err(ctx,
+                    "Screen index must be in the range 1..255; "
+                    "Illegal screen value %d ignored\n", val);
             return false;
         }
-        if (value->op == OpNegate)
-           act->screen = -rtrn.ival;
-        else
-           act->screen = rtrn.ival;
+
+        act->screen = (value->expr.op == EXPR_NEGATE ? -val : val);
         return true;
     }
-    else if (field == F_Same)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveBoolean(value, &rtrn))
-            return ReportMismatch(action->type, field, "boolean");
-        if (rtrn.uval)
-            act->flags &= ~XkbSA_SwitchApplication;
-        else
-            act->flags |= XkbSA_SwitchApplication;
-        return true;
+    else if (field == ACTION_FIELD_SAME) {
+        return CheckBooleanFlag(ctx, action->type, field,
+                                ACTION_SAME_SCREEN, array_ndx, value,
+                                &act->flags);
     }
-    return ReportIllegal(action->type, field);
-}
 
-const LookupEntry ctrlNames[] = {
-    {"repeatkeys", XkbRepeatKeysMask},
-    {"repeat", XkbRepeatKeysMask},
-    {"autorepeat", XkbRepeatKeysMask},
-    {"slowkeys", XkbSlowKeysMask},
-    {"bouncekeys", XkbBounceKeysMask},
-    {"stickykeys", XkbStickyKeysMask},
-    {"mousekeys", XkbMouseKeysMask},
-    {"mousekeysaccel", XkbMouseKeysAccelMask},
-    {"accessxkeys", XkbAccessXKeysMask},
-    {"accessxtimeout", XkbAccessXTimeoutMask},
-    {"accessxfeedback", XkbAccessXFeedbackMask},
-    {"audiblebell", XkbAudibleBellMask},
-    {"ignoregrouplock", XkbIgnoreGroupLockMask},
-    {"all", XkbAllBooleanCtrlsMask},
-    {"overlay1", 0},
-    {"overlay2", 0},
-    {"none", 0},
-    {NULL, 0}
-};
+    return ReportIllegal(ctx, action->type, field);
+}
 
 static bool
-HandleSetLockControls(struct xkb_keymap * xkb,
-                      struct xkb_any_action * action,
-                      unsigned field, ExprDef * array_ndx, ExprDef * value)
+HandleSetLockControls(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+                      union xkb_action *action, enum action_field field,
+                      const ExprDef *array_ndx, const ExprDef *value)
 {
-    ExprResult rtrn;
-    struct xkb_controls_action *act;
-
-    act = (struct xkb_controls_action *) action;
-    if (field == F_Controls)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveMask(value, &rtrn, ctrlNames))
-            return ReportMismatch(action->type, field, "controls mask");
-        act->ctrls = rtrn.uval;
-        return true;
-    }
-    return ReportIllegal(action->type, field);
-}
+    struct xkb_controls_action *act = &action->ctrls;
 
-static const LookupEntry evNames[] = {
-    {"press", XkbSA_MessageOnPress},
-    {"keypress", XkbSA_MessageOnPress},
-    {"release", XkbSA_MessageOnRelease},
-    {"keyrelease", XkbSA_MessageOnRelease},
-    {"all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease},
-    {"none", 0},
-    {NULL, 0}
-};
+    if (field == ACTION_FIELD_CONTROLS) {
+        enum xkb_action_controls mask;
 
-static bool
-HandleActionMessage(struct xkb_keymap * xkb,
-                    struct xkb_any_action * action,
-                    unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-    struct xkb_message_action *act;
-
-    act = (struct xkb_message_action *) action;
-    switch (field)
-    {
-    case F_Report:
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveMask(value, &rtrn, evNames))
-            return ReportMismatch(action->type, field, "key event mask");
-        act->flags &= ~(XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
-        act->flags =
-            rtrn.uval & (XkbSA_MessageOnPress | XkbSA_MessageOnRelease);
-        return true;
-    case F_GenKeyEvent:
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveBoolean(value, &rtrn))
-            return ReportMismatch(action->type, field, "boolean");
-        if (rtrn.uval)
-            act->flags |= XkbSA_MessageGenKeyEvent;
-        else
-            act->flags &= ~XkbSA_MessageGenKeyEvent;
-        return true;
-    case F_Data:
-        if (array_ndx == NULL)
-        {
-            if (!ExprResolveString(value, &rtrn))
-                return ReportMismatch(action->type, field, "string");
-            else
-            {
-                int len = strlen(rtrn.str);
-                if ((len < 1) || (len > 6))
-                {
-                    WARN("An action message can hold only 6 bytes\n");
-                    ACTION("Extra %d bytes ignored\n", len - 6);
-                }
-                strncpy((char *) act->message, rtrn.str, 6);
-            }
-            return true;
-        }
-        else
-        {
-            unsigned ndx;
-            if (!ExprResolveInteger(array_ndx, &rtrn))
-            {
-                ERROR("Array subscript must be integer\n");
-                ACTION("Illegal subscript ignored\n");
-                return false;
-            }
-            ndx = rtrn.uval;
-            if (ndx > 5)
-            {
-                ERROR("An action message is at most 6 bytes long\n");
-                ACTION("Attempt to use data[%d] ignored\n", ndx);
-                return false;
-            }
-            if (!ExprResolveInteger(value, &rtrn))
-                return ReportMismatch(action->type, field, "integer");
-            if ((rtrn.ival < 0) || (rtrn.ival > 255))
-            {
-                ERROR("Message data must be in the range 0..255\n");
-                ACTION("Illegal datum %d ignored\n", rtrn.ival);
-                return false;
-            }
-            act->message[ndx] = rtrn.uval;
-        }
-        return true;
-    }
-    return ReportIllegal(action->type, field);
-}
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
 
-static bool
-HandleRedirectKey(struct xkb_keymap * xkb,
-                  struct xkb_any_action * action,
-                  unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-    struct xkb_redirect_key_action *act;
-    unsigned t1, t2;
-    xkb_keycode_t kc;
-    unsigned long tmp;
-
-    if (array_ndx != NULL)
-        return ReportActionNotArray(action->type, field);
-
-    act = (struct xkb_redirect_key_action *) action;
-    switch (field)
-    {
-    case F_Keycode:
-        if (!ExprResolveKeyName(value, &rtrn))
-            return ReportMismatch(action->type, field, "key name");
-        tmp = KeyNameToLong(rtrn.keyName.name);
-        if (!FindNamedKey(xkb, tmp, &kc, true, CreateKeyNames(xkb), 0))
-        {
-            return ReportNotFound(action->type, field, "Key",
-                                  XkbcKeyNameText(rtrn.keyName.name));
-        }
-        act->new_key = kc;
-        return true;
-    case F_ModsToClear:
-    case F_Modifiers:
-        t1 = 0;
-        if (CheckModifierField(xkb, action->type, value, &t1, &t2))
-        {
-            act->mods_mask |= (t2 & 0xff);
-            if (field == F_Modifiers)
-                act->mods |= (t2 & 0xff);
-            else
-                act->mods &= ~(t2 & 0xff);
-
-            t2 = (t2 >> 8) & 0xffff;
-            act->vmods_mask |= t2;
-            if (field == F_Modifiers)
-                act->vmods |= t2;
-            else
-                act->vmods &= ~t2;
-            return true;
-        }
-        return true;
-    }
-    return ReportIllegal(action->type, field);
-}
+        if (!ExprResolveMask(ctx, value, &mask, ctrlMaskNames))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, action->type,
+                                  field, "controls mask");
 
-static bool
-HandleDeviceBtn(struct xkb_keymap * xkb,
-                struct xkb_any_action * action,
-                unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-    struct xkb_device_button_action *act;
-
-    act = (struct xkb_device_button_action *) action;
-    if (field == F_Button)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveInteger(value, &rtrn))
-            return ReportMismatch(action->type, field,
-                                  "integer (range 1..255)");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("Button must specify default or be in the range 1..255\n");
-            ACTION("Illegal button value %d ignored\n", rtrn.ival);
-            return false;
-        }
-        act->button = rtrn.ival;
+        act->ctrls = mask;
         return true;
     }
-    else if ((action->type == XkbSA_LockDeviceBtn) && (field == F_Affect))
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveEnum(value, &rtrn, lockWhich))
-            return ReportMismatch(action->type, field, "lock or unlock");
-        act->flags &= ~(XkbSA_LockNoLock | XkbSA_LockNoUnlock);
-        act->flags |= rtrn.ival;
-        return true;
-    }
-    else if (field == F_Count)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveButton(value, &rtrn))
-            return ReportMismatch(action->type, field, "integer");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("The count field must have a value in the range 0..255\n");
-            ACTION("Illegal count %d ignored\n", rtrn.ival);
-            return false;
-        }
-        act->count = rtrn.ival;
-        return true;
-    }
-    else if (field == F_Device)
-    {
-        if (array_ndx != NULL)
-            return ReportActionNotArray(action->type, field);
-        if (!ExprResolveInteger(value, &rtrn))
-            return ReportMismatch(action->type, field,
-                                  "integer (range 1..255)");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("Device must specify default or be in the range 1..255\n");
-            ACTION("Illegal device value %d ignored\n", rtrn.ival);
-            return false;
-        }
-        act->device = rtrn.ival;
-        return true;
+    else if (field == ACTION_FIELD_AFFECT) {
+        return CheckAffectField(ctx, action->type, array_ndx, value,
+                                &act->flags);
     }
-    return ReportIllegal(action->type, field);
+
+    return ReportIllegal(ctx, action->type, field);
 }
 
 static bool
-HandleDeviceValuator(struct xkb_keymap * xkb,
-                     struct xkb_any_action * action,
-                     unsigned field, ExprDef * array_ndx, ExprDef * value)
+HandlePrivate(struct xkb_context *ctx, const struct xkb_mod_set *mods,
+              union xkb_action *action, enum action_field field,
+              const ExprDef *array_ndx, const ExprDef *value)
 {
-#if 0
-    ExprResult rtrn;
-    struct xkb_device_valuator_action *act;
+    struct xkb_private_action *act = &action->priv;
 
-    act = (struct xkb_device_valuator_action *) action;
-    /*  XXX - Not yet implemented */
-#endif
-    return false;
-}
+    if (field == ACTION_FIELD_TYPE) {
+        int type;
 
-static bool
-HandlePrivate(struct xkb_keymap * xkb,
-              struct xkb_any_action * action,
-              unsigned field, ExprDef * array_ndx, ExprDef * value)
-{
-    ExprResult rtrn;
-
-    switch (field)
-    {
-    case F_Type:
-        if (!ExprResolveInteger(value, &rtrn))
-            return ReportMismatch(PrivateAction, field, "integer");
-        if ((rtrn.ival < 0) || (rtrn.ival > 255))
-        {
-            ERROR("Private action type must be in the range 0..255\n");
-            ACTION("Illegal type %d ignored\n", rtrn.ival);
+        if (array_ndx)
+            return ReportActionNotArray(ctx, action->type, field);
+
+        if (!ExprResolveInteger(ctx, value, &type))
+            return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+                                  ACTION_TYPE_PRIVATE, field, "integer");
+
+        if (type < 0 || type > 255) {
+            log_err(ctx,
+                    "Private action type must be in the range 0..255; "
+                    "Illegal type %d ignored\n", type);
             return false;
         }
-        action->type = rtrn.uval;
+
+        /*
+         * It's possible for someone to write something like this:
+         *      actions = [ Private(type=3,data[0]=1,data[1]=3,data[2]=3) ]
+         * where the type refers to some existing action type, e.g. LockMods.
+         * This assumes that this action's struct is laid out in memory
+         * exactly as described in the XKB specification and libraries.
+         * We, however, have changed these structs in various ways, so this
+         * assumption is no longer true. Since this is a lousy "feature", we
+         * make actions like these no-ops for now.
+         */
+        if (type < ACTION_TYPE_PRIVATE) {
+            log_info(ctx,
+                     "Private actions of type %s are not supported; Ignored\n",
+                     ActionTypeText(type));
+            act->type = ACTION_TYPE_NONE;
+        }
+        else {
+            act->type = (enum xkb_action_type) type;
+        }
+
         return true;
-    case F_Data:
-        if (array_ndx == NULL)
-        {
-            if (!ExprResolveString(value, &rtrn))
-                return ReportMismatch(action->type, field, "string");
-            else
-            {
-                int len = strlen(rtrn.str);
-                if ((len < 1) || (len > 7))
-                {
-                    WARN("A private action has 7 data bytes\n");
-                    ACTION("Extra %d bytes ignored\n", len - 6);
-                    return false;
-                }
-                strncpy((char *) action->data, rtrn.str, sizeof action->data);
+    }
+    else if (field == ACTION_FIELD_DATA) {
+        if (array_ndx == NULL) {
+            xkb_atom_t val;
+            const char *str;
+            size_t len;
+
+            if (!ExprResolveString(ctx, value, &val))
+                return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE,
+                                      action->type, field, "string");
+
+            str = xkb_atom_text(ctx, val);
+            len = strlen(str);
+            if (len < 1 || len > sizeof(act->data)) {
+                log_warn(ctx,
+                         "A private action has %ld data bytes; "
+                         "Illegal data ignored\n", sizeof(act->data));
+                return false;
             }
-            free(rtrn.str);
+
+            /* act->data may not be null-terminated, this is intentional */
+            memset(act->data, 0, sizeof(act->data));
+            memcpy(act->data, str, len);
             return true;
         }
-        else
-        {
-            unsigned ndx;
-            if (!ExprResolveInteger(array_ndx, &rtrn))
-            {
-                ERROR("Array subscript must be integer\n");
-                ACTION("Illegal subscript ignored\n");
+        else {
+            int ndx, datum;
+
+            if (!ExprResolveInteger(ctx, array_ndx, &ndx)) {
+                log_err(ctx,
+                        "Array subscript must be integer; "
+                        "Illegal subscript ignored\n");
                 return false;
             }
-            ndx = rtrn.uval;
-            if (ndx >= sizeof action->data)
-            {
-                ERROR("The data for a private action is 18 bytes long\n");
-                ACTION("Attempt to use data[%d] ignored\n", ndx);
+
+            if (ndx < 0 || (size_t) ndx >= sizeof(act->data)) {
+                log_err(ctx,
+                        "The data for a private action is %lu bytes long; "
+                        "Attempt to use data[%d] ignored\n",
+                        (unsigned long) sizeof(act->data), ndx);
                 return false;
             }
-            if (!ExprResolveInteger(value, &rtrn))
-                return ReportMismatch(action->type, field, "integer");
-            if ((rtrn.ival < 0) || (rtrn.ival > 255))
-            {
-                ERROR("All data for a private action must be 0..255\n");
-                ACTION("Illegal datum %d ignored\n", rtrn.ival);
+
+            if (!ExprResolveInteger(ctx, value, &datum))
+                return ReportMismatch(ctx, XKB_ERROR_WRONG_FIELD_TYPE, act->type,
+                                      field, "integer");
+
+            if (datum < 0 || datum > 255) {
+                log_err(ctx,
+                        "All data for a private action must be 0..255; "
+                        "Illegal datum %d ignored\n", datum);
                 return false;
             }
-            action->data[ndx] = rtrn.uval;
+
+            act->data[ndx] = (uint8_t) datum;
             return true;
         }
     }
-    return ReportIllegal(PrivateAction, field);
-}
 
-typedef bool (*actionHandler) (struct xkb_keymap *xkb,
-                               struct xkb_any_action *action, unsigned field,
-                               ExprDef *array_ndx, ExprDef *value);
-
-static const actionHandler handleAction[XkbSA_NumActions + 1] = {
-    [XkbSA_NoAction]       = HandleNoAction,
-    [XkbSA_SetMods]        = HandleSetLatchMods,
-    [XkbSA_LatchMods]      = HandleSetLatchMods,
-    [XkbSA_LockMods]       = HandleLockMods,
-    [XkbSA_SetGroup]       = HandleSetLatchGroup,
-    [XkbSA_LatchGroup]     = HandleSetLatchGroup,
-    [XkbSA_LockGroup]      = HandleLockGroup,
-    [XkbSA_MovePtr]        = HandleMovePtr,
-    [XkbSA_PtrBtn]         = HandlePtrBtn,
-    [XkbSA_LockPtrBtn]     = HandlePtrBtn,
-    [XkbSA_SetPtrDflt]     = HandleSetPtrDflt,
-    [XkbSA_ISOLock]        = HandleISOLock,
-    [XkbSA_Terminate]      = HandleNoAction,
-    [XkbSA_SwitchScreen]   = HandleSwitchScreen,
-    [XkbSA_SetControls]    = HandleSetLockControls,
-    [XkbSA_LockControls]   = HandleSetLockControls,
-    [XkbSA_ActionMessage]  = HandleActionMessage,
-    [XkbSA_RedirectKey]    = HandleRedirectKey,
-    [XkbSA_DeviceBtn]      = HandleDeviceBtn,
-    [XkbSA_LockDeviceBtn]  = HandleDeviceBtn,
-    [XkbSA_DeviceValuator] = HandleDeviceValuator,
-    [PrivateAction]        = HandlePrivate,
+    return ReportIllegal(ctx, ACTION_TYPE_NONE, field);
+}
+
+typedef bool (*actionHandler)(struct xkb_context *ctx,
+                              const struct xkb_mod_set *mods,
+                              union xkb_action *action,
+                              enum action_field field,
+                              const ExprDef *array_ndx,
+                              const ExprDef *value);
+
+static const actionHandler handleAction[_ACTION_TYPE_NUM_ENTRIES] = {
+    [ACTION_TYPE_NONE] = HandleNoAction,
+    [ACTION_TYPE_MOD_SET] = HandleSetLatchLockMods,
+    [ACTION_TYPE_MOD_LATCH] = HandleSetLatchLockMods,
+    [ACTION_TYPE_MOD_LOCK] = HandleSetLatchLockMods,
+    [ACTION_TYPE_GROUP_SET] = HandleSetLatchLockGroup,
+    [ACTION_TYPE_GROUP_LATCH] = HandleSetLatchLockGroup,
+    [ACTION_TYPE_GROUP_LOCK] = HandleSetLatchLockGroup,
+    [ACTION_TYPE_PTR_MOVE] = HandleMovePtr,
+    [ACTION_TYPE_PTR_BUTTON] = HandlePtrBtn,
+    [ACTION_TYPE_PTR_LOCK] = HandlePtrBtn,
+    [ACTION_TYPE_PTR_DEFAULT] = HandleSetPtrDflt,
+    [ACTION_TYPE_TERMINATE] = HandleNoAction,
+    [ACTION_TYPE_SWITCH_VT] = HandleSwitchScreen,
+    [ACTION_TYPE_CTRL_SET] = HandleSetLockControls,
+    [ACTION_TYPE_CTRL_LOCK] = HandleSetLockControls,
+    [ACTION_TYPE_PRIVATE] = HandlePrivate,
 };
 
 /***====================================================================***/
 
-static void
-ApplyActionFactoryDefaults(union xkb_action * action)
-{
-    if (action->type == XkbSA_SetPtrDflt)
-    {                           /* increment default button */
-        action->dflt.affect = XkbSA_AffectDfltBtn;
-        action->dflt.flags = 0;
-        action->dflt.value = 1;
-    }
-    else if (action->type == XkbSA_ISOLock)
-    {
-        action->iso.real_mods = LockMask;
-    }
-}
-
-static void
-ActionsInit(void);
-
-int
-HandleActionDef(ExprDef * def,
-                struct xkb_keymap * xkb,
-                struct xkb_any_action * action, unsigned mergeMode, ActionInfo * info)
+bool
+HandleActionDef(struct xkb_context *ctx, ActionsInfo *info,
+                const struct xkb_mod_set *mods, ExprDef *def,
+                union xkb_action *action)
 {
     ExprDef *arg;
     const char *str;
-    unsigned tmp, hndlrType;
-
-    if (!actionsInitialized)
-        ActionsInit();
+    enum xkb_action_type handler_type;
 
-    if (def->op != ExprActionDecl)
-    {
-        ERROR("Expected an action definition, found %s\n",
-               exprOpText(def->op));
+    if (def->expr.op != EXPR_ACTION_DECL) {
+        log_err(ctx, "Expected an action definition, found %s\n",
+                expr_op_type_to_string(def->expr.op));
         return false;
     }
-    str = XkbcAtomText(def->value.action.name);
-    if (!str)
-    {
-        WSGO("Missing name in action definition!!\n");
-        return false;
-    }
-    if (!stringToAction(str, &tmp))
-    {
-        ERROR("Unknown action %s\n", str);
+
+    str = xkb_atom_text(ctx, def->action.name);
+    if (!stringToAction(str, &handler_type)) {
+        log_err(ctx, "Unknown action %s\n", str);
         return false;
     }
-    action->type = hndlrType = tmp;
-    if (action->type != XkbSA_NoAction)
-    {
-        ApplyActionFactoryDefaults((union xkb_action *) action);
-        while (info)
-        {
-            if ((info->action == XkbSA_NoAction)
-                || (info->action == hndlrType))
-            {
-                if (!(*handleAction[hndlrType]) (xkb, action,
-                                                 info->field,
-                                                 info->array_ndx,
-                                                 info->value))
-                {
-                    return false;
-                }
-            }
-            info = info->next;
+
+    /*
+     * Get the default values for this action type, as modified by
+     * statements such as:
+     *     latchMods.clearLocks = True;
+     */
+    *action = info->actions[handler_type];
+
+    /*
+     * Now change the action properties as specified for this
+     * particular instance, e.g. "modifiers" and "clearLocks" in:
+     *     SetMods(modifiers=Alt,clearLocks);
+     */
+    for (arg = def->action.args; arg != NULL;
+         arg = (ExprDef *) arg->common.next) {
+        const ExprDef *value;
+        ExprDef *field, *arrayRtrn;
+        const char *elemRtrn, *fieldRtrn;
+        enum action_field fieldNdx;
+
+        if (arg->expr.op == EXPR_ASSIGN) {
+            field = arg->binary.left;
+            value = arg->binary.right;
         }
-    }
-    for (arg = def->value.action.args; arg != NULL;
-         arg = (ExprDef *) arg->common.next)
-    {
-        ExprDef *field, *value, *arrayRtrn;
-        ExprResult elemRtrn, fieldRtrn;
-        unsigned fieldNdx;
-
-        if (arg->op == OpAssign)
-        {
-            field = arg->value.binary.left;
-            value = arg->value.binary.right;
+        else if (arg->expr.op == EXPR_NOT || arg->expr.op == EXPR_INVERT) {
+            field = arg->unary.child;
+            value = (const ExprDef *) &constFalse;
         }
-        else
-        {
-            if ((arg->op == OpNot) || (arg->op == OpInvert))
-            {
-                field = arg->value.child;
-                constFalse.value.str = xkb_intern_atom("false");
-                value = &constFalse;
-            }
-            else
-            {
-                field = arg;
-                constTrue.value.str = xkb_intern_atom("true");
-                value = &constTrue;
-            }
+        else {
+            field = arg;
+            value = (const ExprDef *) &constTrue;
         }
-        if (!ExprResolveLhs(field, &elemRtrn, &fieldRtrn, &arrayRtrn))
-            return false;       /* internal error -- already reported */
-
-        if (elemRtrn.str != NULL)
-        {
-            ERROR("Cannot change defaults in an action definition\n");
-            ACTION("Ignoring attempt to change %s.%s\n", elemRtrn.str,
-                    fieldRtrn.str);
-            free(elemRtrn.str);
-            free(fieldRtrn.str);
+
+        if (!ExprResolveLhs(ctx, field, &elemRtrn, &fieldRtrn, &arrayRtrn))
             return false;
-        }
-        if (!stringToField(fieldRtrn.str, &fieldNdx))
-        {
-            ERROR("Unknown field name %s\n", uStringText(fieldRtrn.str));
-            free(elemRtrn.str);
-            free(fieldRtrn.str);
+
+        if (elemRtrn) {
+            log_err(ctx,
+                    "Cannot change defaults in an action definition; "
+                    "Ignoring attempt to change %s.%s\n",
+                    elemRtrn, fieldRtrn);
             return false;
         }
-        free(elemRtrn.str);
-        free(fieldRtrn.str);
-        if (!(*handleAction[hndlrType])
-            (xkb, action, fieldNdx, arrayRtrn, value))
-        {
+
+        if (!stringToField(fieldRtrn, &fieldNdx)) {
+            log_err(ctx, "Unknown field name %s\n", fieldRtrn);
             return false;
         }
+
+        if (!handleAction[handler_type](ctx, mods, action, fieldNdx,
+                                        arrayRtrn, value))
+            return false;
     }
+
     return true;
 }
 
-/***====================================================================***/
-
-int
-SetActionField(struct xkb_keymap * xkb,
-               char *elem,
-               char *field,
-               ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
+bool
+SetActionField(struct xkb_context *ctx, ActionsInfo *info,
+               struct xkb_mod_set *mods, const char *elem,
+               const char *field, ExprDef *array_ndx, ExprDef *value)
 {
-    ActionInfo *new, *old;
-
-    if (!actionsInitialized)
-        ActionsInit();
+    enum xkb_action_type action;
+    enum action_field action_field;
 
-    new = uTypedAlloc(ActionInfo);
-    if (new == NULL)
-    {
-        WSGO("Couldn't allocate space for action default\n");
+    if (!stringToAction(elem, &action))
         return false;
-    }
-    if (strcasecmp(elem, "action") == 0)
-        new->action = XkbSA_NoAction;
-    else
-    {
-        if (!stringToAction(elem, &new->action))
-        {
-            free(new);
-            return false;
-        }
-        if (new->action == XkbSA_NoAction)
-        {
-            ERROR("\"%s\" is not a valid field in a NoAction action\n",
-                   field);
-            free(new);
-            return false;
-        }
-    }
-    if (!stringToField(field, &new->field))
-    {
-        ERROR("\"%s\" is not a legal field name\n", field);
-        free(new);
+
+    if (!stringToField(field, &action_field)) {
+        log_err(ctx, "\"%s\" is not a legal field name\n", field);
         return false;
     }
-    new->array_ndx = array_ndx;
-    new->value = value;
-    new->next = NULL;
-    old = *info_rtrn;
-    while ((old) && (old->next))
-        old = old->next;
-    if (old == NULL)
-        *info_rtrn = new;
-    else
-        old->next = new;
-    return true;
-}
 
-/***====================================================================***/
-
-static void
-ActionsInit(void)
-{
-    if (!actionsInitialized)
-    {
-        memset(&constTrue, 0, sizeof(constTrue));
-        memset(&constFalse, 0, sizeof(constFalse));
-        constTrue.common.stmtType = StmtExpr;
-        constTrue.common.next = NULL;
-        constTrue.op = ExprIdent;
-        constTrue.type = TypeBoolean;
-        constTrue.value.str = xkb_intern_atom("true");
-        constFalse.common.stmtType = StmtExpr;
-        constFalse.common.next = NULL;
-        constFalse.op = ExprIdent;
-        constFalse.type = TypeBoolean;
-        constFalse.value.str = xkb_intern_atom("false");
-        actionsInitialized = 1;
-    }
+    return handleAction[action](ctx, mods, &info->actions[action],
+                                action_field, array_ndx, value);
 }