Import xkbcomp sources for CompileKeymap
authorDan Nicholson <dbn.lists@gmail.com>
Fri, 27 Mar 2009 13:55:32 +0000 (06:55 -0700)
committerDan Nicholson <dbn.lists@gmail.com>
Fri, 27 Mar 2009 13:55:32 +0000 (06:55 -0700)
A copy of the xkbcomp sources (except the frontend) have been copied in
to provide a means to compile a XkbDescPtr. This definitely doesn't
build or do the right thing yet.

35 files changed:
configure.ac
src/Makefile.am
src/xkbcomp/Makefile.am [new file with mode: 0644]
src/xkbcomp/action.c [new file with mode: 0644]
src/xkbcomp/action.h [new file with mode: 0644]
src/xkbcomp/alias.c [new file with mode: 0644]
src/xkbcomp/alias.h [new file with mode: 0644]
src/xkbcomp/compat.c [new file with mode: 0644]
src/xkbcomp/compat.h [new file with mode: 0644]
src/xkbcomp/expr.c [new file with mode: 0644]
src/xkbcomp/expr.h [new file with mode: 0644]
src/xkbcomp/geometry.c [new file with mode: 0644]
src/xkbcomp/indicators.c [new file with mode: 0644]
src/xkbcomp/indicators.h [new file with mode: 0644]
src/xkbcomp/keycodes.c [new file with mode: 0644]
src/xkbcomp/keycodes.h [new file with mode: 0644]
src/xkbcomp/keymap.c [new file with mode: 0644]
src/xkbcomp/keytypes.c [new file with mode: 0644]
src/xkbcomp/listing.c [new file with mode: 0644]
src/xkbcomp/misc.c [new file with mode: 0644]
src/xkbcomp/misc.h [new file with mode: 0644]
src/xkbcomp/parseutils.c [new file with mode: 0644]
src/xkbcomp/parseutils.h [new file with mode: 0644]
src/xkbcomp/symbols.c [new file with mode: 0644]
src/xkbcomp/tokens.h [new file with mode: 0644]
src/xkbcomp/utils.c [new file with mode: 0644]
src/xkbcomp/utils.h [new file with mode: 0644]
src/xkbcomp/vmod.c [new file with mode: 0644]
src/xkbcomp/vmod.h [new file with mode: 0644]
src/xkbcomp/xkbcomp.h [new file with mode: 0644]
src/xkbcomp/xkbparse.c [new file with mode: 0644]
src/xkbcomp/xkbparse.y [new file with mode: 0644]
src/xkbcomp/xkbpath.c [new file with mode: 0644]
src/xkbcomp/xkbpath.h [new file with mode: 0644]
src/xkbcomp/xkbscan.c [new file with mode: 0644]

index 652fbf4..03b7502 100644 (file)
@@ -29,6 +29,7 @@ AC_CONFIG_HEADERS([src/config.h])
 
 AC_PROG_LIBTOOL
 AC_PROG_CC
+AC_PROG_YACC
 
 m4_ifndef([PKG_PROG_PKG_CONFIG],
     [m4_fatal([Could not locate the pkg-config autoconf macros.
@@ -38,6 +39,11 @@ m4_ifndef([PKG_PROG_PKG_CONFIG],
     autoreconf/autogen.sh.])])
 PKG_PROG_PKG_CONFIG
 
+# Require xorg-macros version 1.2.0 or newer for XORG_CHANGELOG macro
+m4_ifndef([XORG_MACROS_VERSION],
+    [m4_fatal([must install xorg-macros before running autoconf/autogen.sh])])
+XORG_MACROS_VERSION([1.2.0])
+
 dnl Build native compiler needed for makekeys
 AC_ARG_VAR([CC_FOR_BUILD], [Build native C compiler program])
 if test "x$CC_FOR_BUILD" = x; then
@@ -48,6 +54,10 @@ if test "x$CC_FOR_BUILD" = x; then
     fi
 fi
 
+AC_CHECK_FUNCS([strdup strcasecmp])
+XORG_CHECK_MALLOC_ZERO
+XORG_CWARNFLAGS
+
 PKG_CHECK_MODULES([X11], [xproto kbproto >= 1.0.99.1])
 
 dnl Ensure we have keysym headers
@@ -69,13 +79,14 @@ AC_SUBST([XF86KEYSYM_H])
 
 AC_SUBST([KS_HEADERS], ['$(KEYSYMDEF_H) $(XF86KEYSYM_H)'])
 
-# Require xorg-macros version 1.2.0 or newer for XORG_CHANGELOG macro
-m4_ifndef([XORG_MACROS_VERSION],
-    [m4_fatal([must install xorg-macros before running autoconf/autogen.sh])])
-XORG_MACROS_VERSION([1.2.0])
+AC_ARG_WITH([xkb_config_root],
+    [AC_HELP_STRING([--with-xkb-config-root=<paths>],
+        [Set default XKB config root (default: ${datadir}/X11/xkb)])],
+    [XKBCONFIGROOT="$withval"],
+    [XKBCONFIGROOT='${datadir}/X11/xkb'])
+AC_SUBST([XKBCONFIGROOT])
+
 XORG_RELEASE_VERSION
-XORG_CHECK_MALLOC_ZERO
-XORG_CWARNFLAGS
 XORG_CHANGELOG
 
 AC_OUTPUT([
@@ -83,5 +94,6 @@ Makefile
 include/Makefile
 src/Makefile
 src/makekeys/Makefile
+src/xkbcomp/Makefile
 test/Makefile
 ])
index 72c29af..a440050 100644 (file)
@@ -1,9 +1,10 @@
-SUBDIRS = makekeys
+SUBDIRS = makekeys xkbcomp
 
 INCLUDES = -I$(top_srcdir)/include -I$(builddir)/makekeys
 AM_CFLAGS = $(X11_CFLAGS) $(CWARNFLAGS) $(XMALLOC_ZERO_CFLAGS)
 
 lib_LTLIBRARIES = libxkbcommon.la
+libxkbcommon_la_LIBADD = xkbcomp/libxkbcomp.la
 libxkbcommon_la_SOURCES = \
        XKBcommonint.h \
        alloc.c \
diff --git a/src/xkbcomp/Makefile.am b/src/xkbcomp/Makefile.am
new file mode 100644 (file)
index 0000000..d7beacf
--- /dev/null
@@ -0,0 +1,40 @@
+INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/src
+AM_CFLAGS = $(X11_CFLAGS) $(CWARNFLAGS) \
+       -DDFLT_XKB_CONFIG_ROOT='"$(XKBCONFIGROOT)"'
+
+BUILT_SOURCES = xkbparse.c
+MAINTAINERCLEANFILES = $(BUILT_SOURCES)
+
+noinst_LTLIBRARIES = libxkbcomp.la
+libxkbcomp_la_SOURCES = \
+       action.c \
+       action.h \
+       alias.c \
+       alias.h \
+       compat.c \
+       compat.h \
+       expr.c \
+       expr.h \
+       geometry.c \
+       indicators.c \
+       indicators.h \
+       keycodes.c \
+       keycodes.h \
+       keymap.c \
+       keytypes.c \
+       listing.c \
+       misc.c \
+       misc.h \
+       parseutils.c \
+       parseutils.h \
+       symbols.c \
+       tokens.h \
+       utils.c \
+       utils.h \
+       vmod.c \
+       vmod.h \
+       xkbcomp.h \
+       xkbparse.y \
+       xkbpath.c \
+       xkbpath.h \
+       xkbscan.c
diff --git a/src/xkbcomp/action.c b/src/xkbcomp/action.c
new file mode 100644 (file)
index 0000000..3b82e64
--- /dev/null
@@ -0,0 +1,1468 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.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 Bool
+stringToAction(char *str, unsigned *type_rtrn)
+{
+    if (str == NULL)
+        return False;
+
+    if (uStrCaseCmp(str, "noaction") == 0)
+        *type_rtrn = XkbSA_NoAction;
+    else if (uStrCaseCmp(str, "setmods") == 0)
+        *type_rtrn = XkbSA_SetMods;
+    else if (uStrCaseCmp(str, "latchmods") == 0)
+        *type_rtrn = XkbSA_LatchMods;
+    else if (uStrCaseCmp(str, "lockmods") == 0)
+        *type_rtrn = XkbSA_LockMods;
+    else if (uStrCaseCmp(str, "setgroup") == 0)
+        *type_rtrn = XkbSA_SetGroup;
+    else if (uStrCaseCmp(str, "latchgroup") == 0)
+        *type_rtrn = XkbSA_LatchGroup;
+    else if (uStrCaseCmp(str, "lockgroup") == 0)
+        *type_rtrn = XkbSA_LockGroup;
+    else if (uStrCaseCmp(str, "moveptr") == 0)
+        *type_rtrn = XkbSA_MovePtr;
+    else if (uStrCaseCmp(str, "movepointer") == 0)
+        *type_rtrn = XkbSA_MovePtr;
+    else if (uStrCaseCmp(str, "ptrbtn") == 0)
+        *type_rtrn = XkbSA_PtrBtn;
+    else if (uStrCaseCmp(str, "pointerbutton") == 0)
+        *type_rtrn = XkbSA_PtrBtn;
+    else if (uStrCaseCmp(str, "lockptrbtn") == 0)
+        *type_rtrn = XkbSA_LockPtrBtn;
+    else if (uStrCaseCmp(str, "lockpointerbutton") == 0)
+        *type_rtrn = XkbSA_LockPtrBtn;
+    else if (uStrCaseCmp(str, "lockptrbutton") == 0)
+        *type_rtrn = XkbSA_LockPtrBtn;
+    else if (uStrCaseCmp(str, "lockpointerbtn") == 0)
+        *type_rtrn = XkbSA_LockPtrBtn;
+    else if (uStrCaseCmp(str, "setptrdflt") == 0)
+        *type_rtrn = XkbSA_SetPtrDflt;
+    else if (uStrCaseCmp(str, "setpointerdefault") == 0)
+        *type_rtrn = XkbSA_SetPtrDflt;
+    else if (uStrCaseCmp(str, "isolock") == 0)
+        *type_rtrn = XkbSA_ISOLock;
+    else if (uStrCaseCmp(str, "terminate") == 0)
+        *type_rtrn = XkbSA_Terminate;
+    else if (uStrCaseCmp(str, "terminateserver") == 0)
+        *type_rtrn = XkbSA_Terminate;
+    else if (uStrCaseCmp(str, "switchscreen") == 0)
+        *type_rtrn = XkbSA_SwitchScreen;
+    else if (uStrCaseCmp(str, "setcontrols") == 0)
+        *type_rtrn = XkbSA_SetControls;
+    else if (uStrCaseCmp(str, "lockcontrols") == 0)
+        *type_rtrn = XkbSA_LockControls;
+    else if (uStrCaseCmp(str, "actionmessage") == 0)
+        *type_rtrn = XkbSA_ActionMessage;
+    else if (uStrCaseCmp(str, "messageaction") == 0)
+        *type_rtrn = XkbSA_ActionMessage;
+    else if (uStrCaseCmp(str, "message") == 0)
+        *type_rtrn = XkbSA_ActionMessage;
+    else if (uStrCaseCmp(str, "redirect") == 0)
+        *type_rtrn = XkbSA_RedirectKey;
+    else if (uStrCaseCmp(str, "redirectkey") == 0)
+        *type_rtrn = XkbSA_RedirectKey;
+    else if (uStrCaseCmp(str, "devbtn") == 0)
+        *type_rtrn = XkbSA_DeviceBtn;
+    else if (uStrCaseCmp(str, "devicebtn") == 0)
+        *type_rtrn = XkbSA_DeviceBtn;
+    else if (uStrCaseCmp(str, "devbutton") == 0)
+        *type_rtrn = XkbSA_DeviceBtn;
+    else if (uStrCaseCmp(str, "devicebutton") == 0)
+        *type_rtrn = XkbSA_DeviceBtn;
+    else if (uStrCaseCmp(str, "lockdevbtn") == 0)
+        *type_rtrn = XkbSA_DeviceBtn;
+    else if (uStrCaseCmp(str, "lockdevicebtn") == 0)
+        *type_rtrn = XkbSA_LockDeviceBtn;
+    else if (uStrCaseCmp(str, "lockdevbutton") == 0)
+        *type_rtrn = XkbSA_LockDeviceBtn;
+    else if (uStrCaseCmp(str, "lockdevicebutton") == 0)
+        *type_rtrn = XkbSA_LockDeviceBtn;
+    else if (uStrCaseCmp(str, "devval") == 0)
+        *type_rtrn = XkbSA_DeviceValuator;
+    else if (uStrCaseCmp(str, "deviceval") == 0)
+        *type_rtrn = XkbSA_DeviceValuator;
+    else if (uStrCaseCmp(str, "devvaluator") == 0)
+        *type_rtrn = XkbSA_DeviceValuator;
+    else if (uStrCaseCmp(str, "devicevaluator") == 0)
+        *type_rtrn = XkbSA_DeviceValuator;
+    else if (uStrCaseCmp(str, "private") == 0)
+        *type_rtrn = PrivateAction;
+    else
+        return False;
+    return True;
+}
+
+static Bool
+stringToField(char *str, unsigned *field_rtrn)
+{
+
+    if (str == NULL)
+        return False;
+
+    if (uStrCaseCmp(str, "clearlocks") == 0)
+        *field_rtrn = F_ClearLocks;
+    else if (uStrCaseCmp(str, "latchtolock") == 0)
+        *field_rtrn = F_LatchToLock;
+    else if (uStrCaseCmp(str, "genkeyevent") == 0)
+        *field_rtrn = F_GenKeyEvent;
+    else if (uStrCaseCmp(str, "generatekeyevent") == 0)
+        *field_rtrn = F_GenKeyEvent;
+    else if (uStrCaseCmp(str, "report") == 0)
+        *field_rtrn = F_Report;
+    else if (uStrCaseCmp(str, "default") == 0)
+        *field_rtrn = F_Default;
+    else if (uStrCaseCmp(str, "affect") == 0)
+        *field_rtrn = F_Affect;
+    else if (uStrCaseCmp(str, "increment") == 0)
+        *field_rtrn = F_Increment;
+    else if (uStrCaseCmp(str, "mods") == 0)
+        *field_rtrn = F_Modifiers;
+    else if (uStrCaseCmp(str, "modifiers") == 0)
+        *field_rtrn = F_Modifiers;
+    else if (uStrCaseCmp(str, "group") == 0)
+        *field_rtrn = F_Group;
+    else if (uStrCaseCmp(str, "x") == 0)
+        *field_rtrn = F_X;
+    else if (uStrCaseCmp(str, "y") == 0)
+        *field_rtrn = F_Y;
+    else if (uStrCaseCmp(str, "accel") == 0)
+        *field_rtrn = F_Accel;
+    else if (uStrCaseCmp(str, "accelerate") == 0)
+        *field_rtrn = F_Accel;
+    else if (uStrCaseCmp(str, "repeat") == 0)
+        *field_rtrn = F_Accel;
+    else if (uStrCaseCmp(str, "button") == 0)
+        *field_rtrn = F_Button;
+    else if (uStrCaseCmp(str, "value") == 0)
+        *field_rtrn = F_Value;
+    else if (uStrCaseCmp(str, "controls") == 0)
+        *field_rtrn = F_Controls;
+    else if (uStrCaseCmp(str, "ctrls") == 0)
+        *field_rtrn = F_Controls;
+    else if (uStrCaseCmp(str, "type") == 0)
+        *field_rtrn = F_Type;
+    else if (uStrCaseCmp(str, "count") == 0)
+        *field_rtrn = F_Count;
+    else if (uStrCaseCmp(str, "screen") == 0)
+        *field_rtrn = F_Screen;
+    else if (uStrCaseCmp(str, "same") == 0)
+        *field_rtrn = F_Same;
+    else if (uStrCaseCmp(str, "sameserver") == 0)
+        *field_rtrn = F_Same;
+    else if (uStrCaseCmp(str, "data") == 0)
+        *field_rtrn = F_Data;
+    else if (uStrCaseCmp(str, "device") == 0)
+        *field_rtrn = F_Device;
+    else if (uStrCaseCmp(str, "dev") == 0)
+        *field_rtrn = F_Device;
+    else if (uStrCaseCmp(str, "key") == 0)
+        *field_rtrn = F_Keycode;
+    else if (uStrCaseCmp(str, "keycode") == 0)
+        *field_rtrn = F_Keycode;
+    else if (uStrCaseCmp(str, "kc") == 0)
+        *field_rtrn = F_Keycode;
+    else if (uStrCaseCmp(str, "clearmods") == 0)
+        *field_rtrn = F_ModsToClear;
+    else if (uStrCaseCmp(str, "clearmodifiers") == 0)
+        *field_rtrn = F_ModsToClear;
+    else
+        return False;
+    return True;
+}
+
+static char *
+fieldText(unsigned field)
+{
+    static char buf[32];
+
+    switch (field)
+    {
+    case F_ClearLocks:
+        strcpy(buf, "clearLocks");
+        break;
+    case F_LatchToLock:
+        strcpy(buf, "latchToLock");
+        break;
+    case F_GenKeyEvent:
+        strcpy(buf, "genKeyEvent");
+        break;
+    case F_Report:
+        strcpy(buf, "report");
+        break;
+    case F_Default:
+        strcpy(buf, "default");
+        break;
+    case F_Affect:
+        strcpy(buf, "affect");
+        break;
+    case F_Increment:
+        strcpy(buf, "increment");
+        break;
+    case F_Modifiers:
+        strcpy(buf, "modifiers");
+        break;
+    case F_Group:
+        strcpy(buf, "group");
+        break;
+    case F_X:
+        strcpy(buf, "x");
+        break;
+    case F_Y:
+        strcpy(buf, "y");
+        break;
+    case F_Accel:
+        strcpy(buf, "accel");
+        break;
+    case F_Button:
+        strcpy(buf, "button");
+        break;
+    case F_Value:
+        strcpy(buf, "value");
+        break;
+    case F_Controls:
+        strcpy(buf, "controls");
+        break;
+    case F_Type:
+        strcpy(buf, "type");
+        break;
+    case F_Count:
+        strcpy(buf, "count");
+        break;
+    case F_Screen:
+        strcpy(buf, "screen");
+        break;
+    case F_Same:
+        strcpy(buf, "sameServer");
+        break;
+    case F_Data:
+        strcpy(buf, "data");
+        break;
+    case F_Device:
+        strcpy(buf, "device");
+        break;
+    case F_Keycode:
+        strcpy(buf, "keycode");
+        break;
+    case F_ModsToClear:
+        strcpy(buf, "clearmods");
+        break;
+    default:
+        strcpy(buf, "unknown");
+        break;
+    }
+    return buf;
+}
+
+/***====================================================================***/
+
+static Bool
+ReportMismatch(unsigned action, unsigned field, const char *type)
+{
+    ERROR2("Value of %s field must be of type %s\n", fieldText(field), type);
+    ACTION1("Action %s definition ignored\n",
+            XkbActionTypeText(action, XkbMessage));
+    return False;
+}
+
+static Bool
+ReportIllegal(unsigned action, unsigned field)
+{
+    ERROR2("Field %s is not defined for an action of type %s\n",
+           fieldText(field), XkbActionTypeText(action, XkbMessage));
+    ACTION("Action definition ignored\n");
+    return False;
+}
+
+static Bool
+ReportActionNotArray(unsigned action, unsigned field)
+{
+    ERROR2("The %s field in the %s action is not an array\n",
+           fieldText(field), XkbActionTypeText(action, XkbMessage));
+    ACTION("Action definition ignored\n");
+    return False;
+}
+
+static Bool
+ReportNotFound(unsigned action, unsigned field, const char *what, char *bad)
+{
+    ERROR2("%s named %s not found\n", what, bad);
+    ACTION2("Ignoring the %s field of an %s action\n", fieldText(field),
+            XkbActionTypeText(action, XkbMessage));
+    return False;
+}
+
+static Bool
+HandleNoAction(XkbDescPtr xkb,
+               XkbAnyAction * action,
+               unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+CheckLatchLockFlags(unsigned action,
+                    unsigned field, ExprDef * value, unsigned *flags_inout)
+{
+    unsigned tmp;
+    ExprResult result;
+
+    if (field == F_ClearLocks)
+        tmp = XkbSA_ClearLocks;
+    else if (field == F_LatchToLock)
+        tmp = XkbSA_LatchToLock;
+    else
+        return False;           /* WSGO! */
+    if (!ExprResolveBoolean(value, &result, NULL, NULL))
+        return ReportMismatch(action, field, "boolean");
+    if (result.uval)
+        *flags_inout |= tmp;
+    else
+        *flags_inout &= ~tmp;
+    return True;
+}
+
+static Bool
+CheckModifierField(XkbDescPtr xkb,
+                   unsigned action,
+                   ExprDef * value,
+                   unsigned *flags_inout, unsigned *mods_rtrn)
+{
+    ExprResult rtrn;
+
+    if (value->op == ExprIdent)
+    {
+        register char *valStr;
+        valStr = XkbAtomGetString(NULL, value->value.str);
+        if (valStr && ((uStrCaseCmp(valStr, "usemodmapmods") == 0) ||
+                       (uStrCaseCmp(valStr, "modmapmods") == 0)))
+        {
+
+            *mods_rtrn = 0;
+            *flags_inout |= XkbSA_UseModMapMods;
+            return True;
+        }
+    }
+    if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (XPointer) xkb))
+        return ReportMismatch(action, F_Modifiers, "modifier mask");
+    *mods_rtrn = rtrn.uval;
+    *flags_inout &= ~XkbSA_UseModMapMods;
+    return True;
+}
+
+static Bool
+HandleSetLatchMods(XkbDescPtr xkb,
+                   XkbAnyAction * action,
+                   unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    XkbModAction *act;
+    unsigned rtrn;
+    unsigned t1, t2;
+
+    act = (XkbModAction *) 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);
+            t2 = (t2 >> 8) & 0xffff;
+            XkbSetModActionVMods(act, t2);
+            return True;
+        }
+        return False;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleLockMods(XkbDescPtr xkb,
+               XkbAnyAction * action,
+               unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    XkbModAction *act;
+    unsigned t1, t2;
+
+    act = (XkbModAction *) 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);
+            t2 = (t2 >> 8) & 0xffff;
+            XkbSetModActionVMods(act, t2);
+            return True;
+        }
+        return False;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static LookupEntry groupNames[] = {
+    {"group1", 1},
+    {"group2", 2},
+    {"group3", 3},
+    {"group4", 4},
+    {"group5", 5},
+    {"group6", 6},
+    {"group7", 7},
+    {"group8", 8},
+    {NULL, 0},
+};
+
+static Bool
+CheckGroupField(unsigned action,
+                ExprDef * value, unsigned *flags_inout, int *grp_rtrn)
+{
+    ExprDef *spec;
+    ExprResult rtrn;
+
+    if ((value->op == OpNegate) || (value->op == OpUnaryPlus))
+    {
+        *flags_inout &= ~XkbSA_GroupAbsolute;
+        spec = value->value.child;
+    }
+    else
+    {
+        *flags_inout |= XkbSA_GroupAbsolute;
+        spec = value;
+    }
+
+    if (!ExprResolveInteger(spec, &rtrn, SimpleLookup, (XPointer) groupNames))
+        return ReportMismatch(action, F_Group, "integer (range 1..8)");
+    if ((rtrn.ival < 1) || (rtrn.ival > XkbNumKbdGroups))
+    {
+        ERROR2("Illegal group %d (must be in the range 1..%d)\n", rtrn.ival,
+               XkbNumKbdGroups);
+        ACTION1("Action %s definition ignored\n",
+                XkbActionTypeText(action, XkbMessage));
+        return False;
+    }
+    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;
+}
+
+static Bool
+HandleSetLatchGroup(XkbDescPtr xkb,
+                    XkbAnyAction * action,
+                    unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    XkbGroupAction *act;
+    unsigned rtrn;
+    unsigned t1;
+    int t2;
+
+    act = (XkbGroupAction *) action;
+    if (array_ndx != NULL)
+    {
+        switch (field)
+        {
+        case F_ClearLocks:
+        case F_LatchToLock:
+        case F_Group:
+            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_Group:
+        t1 = act->flags;
+        if (CheckGroupField(action->type, value, &t1, &t2))
+        {
+            act->flags = t1;
+            XkbSASetGroup(act, t2);
+            return True;
+        }
+        return False;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleLockGroup(XkbDescPtr xkb,
+                XkbAnyAction * action,
+                unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    XkbGroupAction *act;
+    unsigned t1;
+    int t2;
+
+    act = (XkbGroupAction *) 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;
+            XkbSASetGroup(act, t2);
+            return True;
+        }
+        return False;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleMovePtr(XkbDescPtr xkb,
+              XkbAnyAction * action,
+              unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbPtrAction *act;
+    Bool absolute;
+
+    act = (XkbPtrAction *) 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, NULL, NULL))
+            return ReportMismatch(action->type, field, "integer");
+        if (field == F_X)
+        {
+            if (absolute)
+                act->flags |= XkbSA_MoveAbsoluteX;
+            XkbSetPtrActionX(act, rtrn.ival);
+        }
+        else
+        {
+            if (absolute)
+                act->flags |= XkbSA_MoveAbsoluteY;
+            XkbSetPtrActionY(act, rtrn.ival);
+        }
+        return True;
+    }
+    else if (field == F_Accel)
+    {
+        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
+            return ReportMismatch(action->type, field, "boolean");
+        if (rtrn.uval)
+            act->flags &= ~XkbSA_NoAcceleration;
+        else
+            act->flags |= XkbSA_NoAcceleration;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static LookupEntry btnNames[] = {
+    {"button1", 1},
+    {"button2", 2},
+    {"button3", 3},
+    {"button4", 4},
+    {"button5", 5},
+    {"default", 0},
+    {NULL, 0}
+};
+
+static LookupEntry lockWhich[] = {
+    {"both", 0},
+    {"lock", XkbSA_LockNoUnlock},
+    {"neither", (XkbSA_LockNoLock | XkbSA_LockNoUnlock)},
+    {"unlock", XkbSA_LockNoLock},
+    {NULL, 0}
+};
+
+static Bool
+HandlePtrBtn(XkbDescPtr xkb,
+             XkbAnyAction * action,
+             unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbPtrBtnAction *act;
+
+    act = (XkbPtrBtnAction *) action;
+    if (field == F_Button)
+    {
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveInteger
+            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
+            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");
+            ACTION1("Illegal button value %d ignored\n", rtrn.ival);
+            return False;
+        }
+        act->button = rtrn.ival;
+        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 (field == F_Count)
+    {
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveInteger
+            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
+            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");
+            ACTION1("Illegal count %d ignored\n", rtrn.ival);
+            return False;
+        }
+        act->count = rtrn.ival;
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static LookupEntry ptrDflts[] = {
+    {"dfltbtn", XkbSA_AffectDfltBtn},
+    {"defaultbutton", XkbSA_AffectDfltBtn},
+    {"button", XkbSA_AffectDfltBtn},
+    {NULL, 0}
+};
+
+static Bool
+HandleSetPtrDflt(XkbDescPtr xkb,
+                 XkbAnyAction * action,
+                 unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbPtrDfltAction *act;
+
+    act = (XkbPtrDfltAction *) 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;
+        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
+        {
+            act->flags |= XkbSA_DfltBtnAbsolute;
+            btn = value;
+        }
+
+        if (!ExprResolveInteger
+            (btn, &rtrn, SimpleLookup, (XPointer) btnNames))
+            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");
+            ACTION1("Illegal default button value %d ignored\n", rtrn.ival);
+            return False;
+        }
+        if (rtrn.ival == 0)
+        {
+            ERROR("Cannot set default pointer button to \"default\"\n");
+            ACTION("Illegal default button setting ignored\n");
+            return False;
+        }
+        if (value->op == OpNegate)
+            XkbSASetPtrDfltValue(act, -rtrn.ival);
+        else
+            XkbSASetPtrDfltValue(act, rtrn.ival);
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static 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(XkbDescPtr xkb,
+              XkbAnyAction * action,
+              unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbISOAction *act;
+    unsigned flags, mods;
+    int group;
+
+    act = (XkbISOAction *) 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;
+            mods = (mods >> 8) & 0xff;
+            XkbSetModActionVMods(act, mods);
+            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;
+            XkbSASetGroup(act, group);
+            return True;
+        }
+        return False;
+    case F_Affect:
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveMask(value, &rtrn, SimpleLookup, (XPointer) isoNames))
+            return ReportMismatch(action->type, field, "keyboard component");
+        act->affect = (~rtrn.uval) & XkbSA_ISOAffectMask;
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleSwitchScreen(XkbDescPtr xkb,
+                   XkbAnyAction * action,
+                   unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbSwitchScreenAction *act;
+
+    act = (XkbSwitchScreenAction *) 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;
+        }
+        else
+        {
+            act->flags |= XkbSA_SwitchAbsolute;
+            scrn = value;
+        }
+
+        if (!ExprResolveInteger(scrn, &rtrn, NULL, NULL))
+            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");
+            ACTION1("Illegal screen value %d ignored\n", rtrn.ival);
+            return False;
+        }
+        if (value->op == OpNegate)
+            XkbSASetScreen(act, -rtrn.ival);
+        else
+            XkbSASetScreen(act, rtrn.ival);
+        return True;
+    }
+    else if (field == F_Same)
+    {
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
+            return ReportMismatch(action->type, field, "boolean");
+        if (rtrn.uval)
+            act->flags &= ~XkbSA_SwitchApplication;
+        else
+            act->flags |= XkbSA_SwitchApplication;
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+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}
+    ,
+    {"overlay1", XkbOverlay1Mask}
+    ,
+    {"overlay2", XkbOverlay2Mask}
+    ,
+    {"ignoregrouplock", XkbIgnoreGroupLockMask}
+    ,
+    {"all", XkbAllBooleanCtrlsMask}
+    ,
+    {"none", 0}
+    ,
+    {NULL, 0}
+};
+
+static Bool
+HandleSetLockControls(XkbDescPtr xkb,
+                      XkbAnyAction * action,
+                      unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbCtrlsAction *act;
+
+    act = (XkbCtrlsAction *) action;
+    if (field == F_Controls)
+    {
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveMask
+            (value, &rtrn, SimpleLookup, (XPointer) ctrlNames))
+            return ReportMismatch(action->type, field, "controls mask");
+        XkbActionSetCtrls(act, rtrn.uval);
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static LookupEntry evNames[] = {
+    {"press", XkbSA_MessageOnPress},
+    {"keypress", XkbSA_MessageOnPress},
+    {"release", XkbSA_MessageOnRelease},
+    {"keyrelease", XkbSA_MessageOnRelease},
+    {"all", XkbSA_MessageOnPress | XkbSA_MessageOnRelease},
+    {"none", 0},
+    {NULL, 0}
+};
+
+static Bool
+HandleActionMessage(XkbDescPtr xkb,
+                    XkbAnyAction * action,
+                    unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbMessageAction *act;
+
+    act = (XkbMessageAction *) action;
+    switch (field)
+    {
+    case F_Report:
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveMask(value, &rtrn, SimpleLookup, (XPointer) 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, NULL, NULL))
+            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, NULL, NULL))
+                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");
+                    ACTION1("Extra %d bytes ignored\n", len - 6);
+                }
+                strncpy((char *) act->message, rtrn.str, 6);
+            }
+            return True;
+        }
+        else
+        {
+            unsigned ndx;
+            if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
+            {
+                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");
+                ACTION1("Attempt to use data[%d] ignored\n", ndx);
+                return False;
+            }
+            if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
+                return ReportMismatch(action->type, field, "integer");
+            if ((rtrn.ival < 0) || (rtrn.ival > 255))
+            {
+                ERROR("Message data must be in the range 0..255\n");
+                ACTION1("Illegal datum %d ignored\n", rtrn.ival);
+                return False;
+            }
+            act->message[ndx] = rtrn.uval;
+        }
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleRedirectKey(XkbDescPtr xkb,
+                  XkbAnyAction * action,
+                  unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbRedirectKeyAction *act;
+    unsigned t1, t2, vmods, vmask;
+    unsigned long tmp;
+
+    if (array_ndx != NULL)
+        return ReportActionNotArray(action->type, field);
+
+    act = (XkbRedirectKeyAction *) action;
+    switch (field)
+    {
+    case F_Keycode:
+        if (!ExprResolveKeyName(value, &rtrn, NULL, NULL))
+            return ReportMismatch(action->type, field, "key name");
+        tmp = KeyNameToLong(rtrn.keyName.name);
+        if (!FindNamedKey(xkb, tmp, &t1, True, CreateKeyNames(xkb), 0))
+        {
+            return ReportNotFound(action->type, field, "Key",
+                                  XkbKeyNameText(rtrn.keyName.name,
+                                                 XkbMessage));
+        }
+        act->new_key = t1;
+        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;
+            vmods = XkbSARedirectVMods(act);
+            vmask = XkbSARedirectVModsMask(act);
+            vmask |= t2;
+            if (field == F_Modifiers)
+                vmods |= t2;
+            else
+                vmods &= ~t2;
+            XkbSARedirectSetVMods(act, vmods);
+            XkbSARedirectSetVModsMask(act, vmask);
+            return True;
+        }
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleDeviceBtn(XkbDescPtr xkb,
+                XkbAnyAction * action,
+                unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbDeviceBtnAction *act;
+
+    act = (XkbDeviceBtnAction *) action;
+    if (field == F_Button)
+    {
+        if (array_ndx != NULL)
+            return ReportActionNotArray(action->type, field);
+        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
+            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");
+            ACTION1("Illegal button value %d ignored\n", rtrn.ival);
+            return False;
+        }
+        act->button = rtrn.ival;
+        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 (!ExprResolveInteger
+            (value, &rtrn, SimpleLookup, (XPointer) btnNames))
+            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");
+            ACTION1("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, NULL, NULL))
+            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");
+            ACTION1("Illegal device value %d ignored\n", rtrn.ival);
+            return False;
+        }
+        act->device = rtrn.ival;
+        return True;
+    }
+    return ReportIllegal(action->type, field);
+}
+
+static Bool
+HandleDeviceValuator(XkbDescPtr xkb,
+                     XkbAnyAction * action,
+                     unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+#if 0
+    ExprResult rtrn;
+    XkbDeviceValuatorAction *act;
+
+    act = (XkbDeviceValuatorAction *) action;
+    /*  XXX - Not yet implemented */
+#endif
+    return False;
+}
+
+static Bool
+HandlePrivate(XkbDescPtr xkb,
+              XkbAnyAction * action,
+              unsigned field, ExprDef * array_ndx, ExprDef * value)
+{
+    ExprResult rtrn;
+
+    switch (field)
+    {
+    case F_Type:
+        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
+            return ReportMismatch(PrivateAction, field, "integer");
+        if ((rtrn.ival < 0) || (rtrn.ival > 255))
+        {
+            ERROR("Private action type must be in the range 0..255\n");
+            ACTION1("Illegal type %d ignored\n", rtrn.ival);
+            return False;
+        }
+        action->type = rtrn.uval;
+        return True;
+    case F_Data:
+        if (array_ndx == NULL)
+        {
+            if (!ExprResolveString(value, &rtrn, NULL, NULL))
+                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");
+                    ACTION1("Extra %d bytes ignored\n", len - 6);
+                    return False;
+                }
+                strncpy((char *) action->data, rtrn.str, 7);
+            }
+            return True;
+        }
+        else
+        {
+            unsigned ndx;
+            if (!ExprResolveInteger(array_ndx, &rtrn, NULL, NULL))
+            {
+                ERROR("Array subscript must be integer\n");
+                ACTION("Illegal subscript ignored\n");
+                return False;
+            }
+            ndx = rtrn.uval;
+            if (ndx > 6)
+            {
+                ERROR("The data for a private action is 7 bytes long\n");
+                ACTION1("Attempt to use data[%d] ignored\n", ndx);
+                return False;
+            }
+            if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
+                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");
+                ACTION1("Illegal datum %d ignored\n", rtrn.ival);
+                return False;
+            }
+            action->data[ndx] = rtrn.uval;
+            return True;
+        }
+    }
+    return ReportIllegal(PrivateAction, field);
+}
+
+typedef Bool(*actionHandler) (XkbDescPtr /* xkb */ ,
+                              XkbAnyAction * /* action */ ,
+                              unsigned /* field */ ,
+                              ExprDef * /* array_ndx */ ,
+                              ExprDef * /* value */
+    );
+
+static actionHandler handleAction[XkbSA_NumActions + 1] = {
+    HandleNoAction /* NoAction     */ ,
+    HandleSetLatchMods /* SetMods      */ ,
+    HandleSetLatchMods /* LatchMods    */ ,
+    HandleLockMods /* LockMods     */ ,
+    HandleSetLatchGroup /* SetGroup     */ ,
+    HandleSetLatchGroup /* LatchGroup   */ ,
+    HandleLockGroup /* LockGroup    */ ,
+    HandleMovePtr /* MovePtr      */ ,
+    HandlePtrBtn /* PtrBtn       */ ,
+    HandlePtrBtn /* LockPtrBtn   */ ,
+    HandleSetPtrDflt /* SetPtrDflt   */ ,
+    HandleISOLock /* ISOLock      */ ,
+    HandleNoAction /* Terminate    */ ,
+    HandleSwitchScreen /* SwitchScreen */ ,
+    HandleSetLockControls /* SetControls  */ ,
+    HandleSetLockControls /* LockControls */ ,
+    HandleActionMessage /* ActionMessage */ ,
+    HandleRedirectKey /* RedirectKey  */ ,
+    HandleDeviceBtn /* DeviceBtn    */ ,
+    HandleDeviceBtn /* LockDeviceBtn */ ,
+    HandleDeviceValuator /* DeviceValuatr */ ,
+    HandlePrivate               /* Private      */
+};
+
+/***====================================================================***/
+
+static void
+ApplyActionFactoryDefaults(XkbAction * action)
+{
+    if (action->type == XkbSA_SetPtrDflt)
+    {                           /* increment default button */
+        action->dflt.affect = XkbSA_AffectDfltBtn;
+        action->dflt.flags = 0;
+        XkbSASetPtrDfltValue(&action->dflt, 1);
+    }
+    else if (action->type == XkbSA_ISOLock)
+    {
+        action->iso.real_mods = LockMask;
+    }
+    return;
+}
+
+
+int
+HandleActionDef(ExprDef * def,
+                XkbDescPtr xkb,
+                XkbAnyAction * action, unsigned mergeMode, ActionInfo * info)
+{
+    ExprDef *arg;
+    register char *str;
+    unsigned tmp, hndlrType;
+
+    if (!actionsInitialized)
+        ActionsInit();
+
+    if (def->op != ExprActionDecl)
+    {
+        ERROR1("Expected an action definition, found %s\n",
+               exprOpText(def->op));
+        return False;
+    }
+    str = XkbAtomGetString(NULL, def->value.action.name);
+    if (!str)
+    {
+        WSGO("Missing name in action definition!!\n");
+        return False;
+    }
+    if (!stringToAction(str, &tmp))
+    {
+        ERROR1("Unknown action %s\n", str);
+        return False;
+    }
+    action->type = hndlrType = tmp;
+    if (action->type != XkbSA_NoAction)
+    {
+        ApplyActionFactoryDefaults((XkbAction *) 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;
+        }
+    }
+    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->op == OpNot) || (arg->op == OpInvert))
+            {
+                field = arg->value.child;
+                value = &constFalse;
+            }
+            else
+            {
+                field = arg;
+                value = &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");
+            ACTION2("Ignoring attempt to change %s.%s\n", elemRtrn.str,
+                    fieldRtrn.str);
+            return False;
+        }
+        if (!stringToField(fieldRtrn.str, &fieldNdx))
+        {
+            ERROR1("Unknown field name %s\n", uStringText(fieldRtrn.str));
+            return False;
+        }
+        if (!(*handleAction[hndlrType])
+            (xkb, action, fieldNdx, arrayRtrn, value))
+        {
+            return False;
+        }
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+int
+SetActionField(XkbDescPtr xkb,
+               char *elem,
+               char *field,
+               ExprDef * array_ndx, ExprDef * value, ActionInfo ** info_rtrn)
+{
+    ActionInfo *new, *old;
+
+    if (!actionsInitialized)
+        ActionsInit();
+
+    new = uTypedAlloc(ActionInfo);
+    if (new == NULL)
+    {
+        WSGO("Couldn't allocate space for action default\n");
+        return False;
+    }
+    if (uStrCaseCmp(elem, "action") == 0)
+        new->action = XkbSA_NoAction;
+    else
+    {
+        if (!stringToAction(elem, &new->action))
+            return False;
+        if (new->action == XkbSA_NoAction)
+        {
+            ERROR1("\"%s\" is not a valid field in a NoAction action\n",
+                   field);
+            return False;
+        }
+    }
+    if (!stringToField(field, &new->field))
+    {
+        ERROR1("\"%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;
+}
+
+/***====================================================================***/
+
+void
+ActionsInit(void)
+{
+    if (!actionsInitialized)
+    {
+        bzero((char *) &constTrue, sizeof(constTrue));
+        bzero((char *) &constFalse, sizeof(constFalse));
+        constTrue.common.stmtType = StmtExpr;
+        constTrue.common.next = NULL;
+        constTrue.op = ExprIdent;
+        constTrue.type = TypeBoolean;
+        constTrue.value.str = XkbInternAtom(NULL, "true", False);
+        constFalse.common.stmtType = StmtExpr;
+        constFalse.common.next = NULL;
+        constFalse.op = ExprIdent;
+        constFalse.type = TypeBoolean;
+        constFalse.value.str = XkbInternAtom(NULL, "false", False);
+        actionsInitialized = 1;
+    }
+    return;
+}
diff --git a/src/xkbcomp/action.h b/src/xkbcomp/action.h
new file mode 100644 (file)
index 0000000..2fb7a5e
--- /dev/null
@@ -0,0 +1,86 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef ACTION_H
+#define ACTION_H 1
+
+#define        F_ClearLocks    0
+#define        F_LatchToLock   1
+#define        F_GenKeyEvent   2
+#define        F_Report        3
+#define        F_Default       4
+#define        F_Affect        5
+#define        F_Increment     6
+#define        F_Modifiers     7
+#define        F_Group         8
+#define        F_X             9
+#define        F_Y             10
+#define        F_Accel         11
+#define        F_Button        12
+#define        F_Value         13
+#define        F_Controls      14
+#define        F_Type          15
+#define        F_Count         16
+#define        F_Screen        17
+#define        F_Same          18
+#define        F_Data          19
+#define        F_Device        20
+#define        F_Keycode       21
+#define        F_ModsToClear   22
+#define        F_LastField     F_ModsToClear
+#define        F_NumFields     (F_LastField+1)
+
+#define        PrivateAction   (XkbSA_LastAction+1)
+
+typedef struct _ActionInfo
+{
+    unsigned action;
+    unsigned field;
+    ExprDef *array_ndx;
+    ExprDef *value;
+    struct _ActionInfo *next;
+} ActionInfo;
+
+extern int HandleActionDef(ExprDef * /* def */ ,
+                           XkbDescPtr /* xkb */ ,
+                           XkbAnyAction * /* action */ ,
+                           unsigned /* mergeMode */ ,
+                           ActionInfo * /* info */
+    );
+
+extern int SetActionField(XkbDescPtr /* xkb */ ,
+                          char * /* elem */ ,
+                          char * /* field */ ,
+                          ExprDef * /* index */ ,
+                          ExprDef * /* value */ ,
+                          ActionInfo ** /* info_rtrn */
+    );
+
+extern void ActionsInit(void);
+
+extern LookupEntry ctrlNames[];
+
+#endif /* ACTION_H */
diff --git a/src/xkbcomp/alias.c b/src/xkbcomp/alias.c
new file mode 100644 (file)
index 0000000..ba55d3d
--- /dev/null
@@ -0,0 +1,289 @@
+/************************************************************
+ Copyright (c) 1995 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "misc.h"
+#include "alias.h"
+#include "keycodes.h"
+
+#include <X11/extensions/XKBgeom.h>
+
+static void
+HandleCollision(AliasInfo * old, AliasInfo * new)
+{
+    if (strncmp(new->real, old->real, XkbKeyNameLength) == 0)
+    {
+        if (((new->def.fileID == old->def.fileID) && (warningLevel > 0)) ||
+            (warningLevel > 9))
+        {
+            WARN2("Alias of %s for %s declared more than once\n",
+                  XkbKeyNameText(new->alias, XkbMessage),
+                  XkbKeyNameText(new->real, XkbMessage));
+            ACTION("First definition ignored\n");
+        }
+    }
+    else
+    {
+        char *use, *ignore;
+        if (new->def.merge == MergeAugment)
+        {
+            use = old->real;
+            ignore = new->real;
+        }
+        else
+        {
+            use = new->real;
+            ignore = old->real;
+        }
+        if (((old->def.fileID == new->def.fileID) && (warningLevel > 0)) ||
+            (warningLevel > 9))
+        {
+            WARN1("Multiple definitions for alias %s\n",
+                  XkbKeyNameText(old->alias, XkbMessage));
+            ACTION2("Using %s, ignoring %s\n",
+                    XkbKeyNameText(use, XkbMessage),
+                    XkbKeyNameText(ignore, XkbMessage));
+        }
+        if (use != old->real)
+            memcpy(old->real, use, XkbKeyNameLength);
+    }
+    old->def.fileID = new->def.fileID;
+    old->def.merge = new->def.merge;
+    return;
+}
+
+static void
+InitAliasInfo(AliasInfo * info,
+              unsigned merge, unsigned file_id, char *alias, char *real)
+{
+    bzero(info, sizeof(AliasInfo));
+    info->def.merge = merge;
+    info->def.fileID = file_id;
+    strncpy(info->alias, alias, XkbKeyNameLength);
+    strncpy(info->real, real, XkbKeyNameLength);
+    return;
+}
+
+int
+HandleAliasDef(KeyAliasDef * def,
+               unsigned merge, unsigned file_id, AliasInfo ** info_in)
+{
+    AliasInfo *info;
+
+    for (info = *info_in; info != NULL; info = (AliasInfo *) info->def.next)
+    {
+        if (strncmp(info->alias, def->alias, XkbKeyNameLength) == 0)
+        {
+            AliasInfo new;
+            InitAliasInfo(&new, merge, file_id, def->alias, def->real);
+            HandleCollision(info, &new);
+            return True;
+        }
+    }
+    info = uTypedCalloc(1, AliasInfo);
+    if (info == NULL)
+    {
+        WSGO("Allocation failure in HandleAliasDef\n");
+        return False;
+    }
+    info->def.fileID = file_id;
+    info->def.merge = merge;
+    info->def.next = (CommonInfo *) * info_in;
+    memcpy(info->alias, def->alias, XkbKeyNameLength);
+    memcpy(info->real, def->real, XkbKeyNameLength);
+    *info_in = (AliasInfo *) AddCommonInfo(&(*info_in)->def, &info->def);
+    return True;
+}
+
+void
+ClearAliases(AliasInfo ** info_in)
+{
+    if ((info_in) && (*info_in))
+        ClearCommonInfo(&(*info_in)->def);
+    return;
+}
+
+Bool
+MergeAliases(AliasInfo ** into, AliasInfo ** merge, unsigned how_merge)
+{
+    AliasInfo *tmp;
+    KeyAliasDef def;
+
+    if ((*merge) == NULL)
+        return True;
+    if ((*into) == NULL)
+    {
+        *into = *merge;
+        *merge = NULL;
+        return True;
+    }
+    bzero((char *) &def, sizeof(KeyAliasDef));
+    for (tmp = *merge; tmp != NULL; tmp = (AliasInfo *) tmp->def.next)
+    {
+        if (how_merge == MergeDefault)
+            def.merge = tmp->def.merge;
+        else
+            def.merge = how_merge;
+        memcpy(def.alias, tmp->alias, XkbKeyNameLength);
+        memcpy(def.real, tmp->real, XkbKeyNameLength);
+        if (!HandleAliasDef(&def, def.merge, tmp->def.fileID, into))
+            return False;
+    }
+    return True;
+}
+
+int
+ApplyAliases(XkbDescPtr xkb, Bool toGeom, AliasInfo ** info_in)
+{
+    register int i;
+    XkbKeyAliasPtr old, a;
+    AliasInfo *info;
+    int nNew, nOld;
+    Status status;
+
+    if (*info_in == NULL)
+        return True;
+    if (toGeom)
+    {
+        nOld = (xkb->geom ? xkb->geom->num_key_aliases : 0);
+        old = (xkb->geom ? xkb->geom->key_aliases : NULL);
+    }
+    else
+    {
+        nOld = (xkb->names ? xkb->names->num_key_aliases : 0);
+        old = (xkb->names ? xkb->names->key_aliases : NULL);
+    }
+    for (nNew = 0, info = *info_in; info != NULL;
+         info = (AliasInfo *) info->def.next)
+    {
+        unsigned long lname;
+        unsigned int kc;
+
+        lname = KeyNameToLong(info->real);
+        if (!FindNamedKey(xkb, lname, &kc, False, CreateKeyNames(xkb), 0))
+        {
+            if (warningLevel > 4)
+            {
+                WARN2("Attempt to alias %s to non-existent key %s\n",
+                      XkbKeyNameText(info->alias, XkbMessage),
+                      XkbKeyNameText(info->real, XkbMessage));
+                ACTION("Ignored\n");
+            }
+            info->alias[0] = '\0';
+            continue;
+        }
+        lname = KeyNameToLong(info->alias);
+        if (FindNamedKey(xkb, lname, &kc, False, False, 0))
+        {
+            if (warningLevel > 4)
+            {
+                WARN("Attempt to create alias with the name of a real key\n");
+                ACTION2("Alias \"%s = %s\" ignored\n",
+                        XkbKeyNameText(info->alias, XkbMessage),
+                        XkbKeyNameText(info->real, XkbMessage));
+            }
+            info->alias[0] = '\0';
+            continue;
+        }
+        nNew++;
+        if (old)
+        {
+            for (i = 0, a = old; i < nOld; i++, a++)
+            {
+                if (strncmp(a->alias, info->alias, XkbKeyNameLength) == 0)
+                {
+                    AliasInfo old;
+                    InitAliasInfo(&old, MergeAugment, 0, a->alias, a->real);
+                    HandleCollision(&old, info);
+                    memcpy(old.real, a->real, XkbKeyNameLength);
+                    info->alias[0] = '\0';
+                    nNew--;
+                    break;
+                }
+            }
+        }
+    }
+    if (nNew == 0)
+    {
+        ClearCommonInfo(&(*info_in)->def);
+        *info_in = NULL;
+        return True;
+    }
+    status = Success;
+    if (toGeom)
+    {
+        if (!xkb->geom)
+        {
+            XkbGeometrySizesRec sizes;
+            bzero((char *) &sizes, sizeof(XkbGeometrySizesRec));
+            sizes.which = XkbGeomKeyAliasesMask;
+            sizes.num_key_aliases = nOld + nNew;
+            status = XkbAllocGeometry(xkb, &sizes);
+        }
+        else
+        {
+            status = XkbAllocGeomKeyAliases(xkb->geom, nOld + nNew);
+        }
+        if (xkb->geom)
+            old = xkb->geom->key_aliases;
+    }
+    else
+    {
+        status = XkbAllocNames(xkb, XkbKeyAliasesMask, 0, nOld + nNew);
+        if (xkb->names)
+            old = xkb->names->key_aliases;
+    }
+    if (status != Success)
+    {
+        WSGO("Allocation failure in ApplyAliases\n");
+        return False;
+    }
+    if (toGeom)
+        a = &xkb->geom->key_aliases[nOld];
+    else
+        a = &xkb->names->key_aliases[nOld];
+    for (info = *info_in; info != NULL; info = (AliasInfo *) info->def.next)
+    {
+        if (info->alias[0] != '\0')
+        {
+            strncpy(a->alias, info->alias, XkbKeyNameLength);
+            strncpy(a->real, info->real, XkbKeyNameLength);
+            a++;
+        }
+    }
+#ifdef DEBUG
+    if ((a - old) != (nOld + nNew))
+    {
+        WSGO2("Expected %d aliases total but created %d\n", nOld + nNew,
+              a - old);
+    }
+#endif
+    if (toGeom)
+        xkb->geom->num_key_aliases += nNew;
+    ClearCommonInfo(&(*info_in)->def);
+    *info_in = NULL;
+    return True;
+}
diff --git a/src/xkbcomp/alias.h b/src/xkbcomp/alias.h
new file mode 100644 (file)
index 0000000..b6fac5b
--- /dev/null
@@ -0,0 +1,56 @@
+/************************************************************
+ Copyright (c) 1995 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.
+
+ ********************************************************/
+
+#ifndef ALIAS_H
+#define ALIAS_H 1
+
+typedef struct _AliasInfo
+{
+    CommonInfo def;
+    char alias[XkbKeyNameLength + 1];
+    char real[XkbKeyNameLength + 1];
+} AliasInfo;
+
+extern int HandleAliasDef(KeyAliasDef * /* def   */ ,
+                          unsigned /* merge */ ,
+                          unsigned /* file_id */ ,
+                          AliasInfo **  /* info  */
+    );
+
+extern void ClearAliases(AliasInfo **   /* info */
+    );
+
+extern Bool MergeAliases(AliasInfo ** /* into */ ,
+                         AliasInfo ** /* merge */ ,
+                         unsigned       /* how_merge */
+    );
+
+extern int ApplyAliases(XkbDescPtr /* xkb */ ,
+                        Bool /* toGeom */ ,
+                        AliasInfo **    /* info */
+    );
+
+#endif /* ALIAS_H */
diff --git a/src/xkbcomp/compat.c b/src/xkbcomp/compat.c
new file mode 100644 (file)
index 0000000..03c29ef
--- /dev/null
@@ -0,0 +1,880 @@
+/************************************************************
+ 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/Xos.h>
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "vmod.h"
+#include "misc.h"
+#include "indicators.h"
+#include "action.h"
+#include "compat.h"
+
+typedef struct _SymInterpInfo
+{
+    CommonInfo defs;
+    XkbSymInterpretRec interp;
+} SymInterpInfo;
+
+#define        _SI_VirtualMod          (1<<0)
+#define        _SI_Action              (1<<1)
+#define        _SI_AutoRepeat          (1<<2)
+#define        _SI_LockingKey          (1<<3)
+#define        _SI_LevelOneOnly        (1<<4)
+
+typedef struct _GroupCompatInfo
+{
+    unsigned char fileID;
+    unsigned char merge;
+    unsigned char real_mods;
+    unsigned short vmods;
+} GroupCompatInfo;
+
+typedef struct _CompatInfo
+{
+    char *name;
+    unsigned fileID;
+    int errorCount;
+    int nInterps;
+    SymInterpInfo *interps;
+    SymInterpInfo dflt;
+    LEDInfo ledDflt;
+    GroupCompatInfo groupCompat[XkbNumKbdGroups];
+    LEDInfo *leds;
+    VModInfo vmods;
+    ActionInfo *act;
+    XkbDescPtr xkb;
+} CompatInfo;
+
+/***====================================================================***/
+
+#define        ReportSINotArray(si,f,i) \
+       ReportNotArray("symbol interpretation",(f),siText((si),(i)))
+#define        ReportSIBadType(si,f,w,i) \
+       ReportBadType("symbol interpretation",(f),siText((si),(i)),(w))
+
+/***====================================================================***/
+
+static char *
+siText(SymInterpInfo * si, CompatInfo * info)
+{
+    static char buf[128];
+
+    if (si == &info->dflt)
+    {
+        snprintf(buf, sizeof(buf), "default");
+    }
+    else
+    {
+        snprintf(buf, sizeof(buf), "%s+%s(%s)", 
+               XkbKeysymText(si->interp.sym, XkbMessage),
+                XkbSIMatchText(si->interp.match, XkbMessage),
+                XkbModMaskText(si->interp.mods, XkbMessage));
+    }
+    return buf;
+}
+
+static void
+InitCompatInfo(CompatInfo * info, XkbDescPtr xkb)
+{
+    register int i;
+
+    info->xkb = xkb;
+    info->name = NULL;
+    info->fileID = 0;
+    info->errorCount = 0;
+    info->nInterps = 0;
+    info->interps = NULL;
+    info->act = NULL;
+    info->dflt.defs.fileID = info->fileID;
+    info->dflt.defs.defined = 0;
+    info->dflt.defs.merge = MergeOverride;
+    info->dflt.interp.flags = 0;
+    info->dflt.interp.virtual_mod = XkbNoModifier;
+    info->dflt.interp.act.type = XkbSA_NoAction;
+    for (i = 0; i < XkbAnyActionDataSize; i++)
+    {
+        info->dflt.interp.act.data[i] = 0;
+    }
+    ClearIndicatorMapInfo(xkb->dpy, &info->ledDflt);
+    info->ledDflt.defs.fileID = info->fileID;
+    info->ledDflt.defs.defined = 0;
+    info->ledDflt.defs.merge = MergeOverride;
+    bzero((char *) &info->groupCompat[0],
+          XkbNumKbdGroups * sizeof(GroupCompatInfo));
+    info->leds = NULL;
+    InitVModInfo(&info->vmods, xkb);
+    return;
+}
+
+static void
+ClearCompatInfo(CompatInfo * info, XkbDescPtr xkb)
+{
+    register int i;
+
+    if (info->name != NULL)
+        uFree(info->name);
+    info->name = NULL;
+    info->dflt.defs.defined = 0;
+    info->dflt.defs.merge = MergeAugment;
+    info->dflt.interp.flags = 0;
+    info->dflt.interp.virtual_mod = XkbNoModifier;
+    info->dflt.interp.act.type = XkbSA_NoAction;
+    for (i = 0; i < XkbAnyActionDataSize; i++)
+    {
+        info->dflt.interp.act.data[i] = 0;
+    }
+    ClearIndicatorMapInfo(xkb->dpy, &info->ledDflt);
+    info->nInterps = 0;
+    info->interps = (SymInterpInfo *) ClearCommonInfo(&info->interps->defs);
+    bzero((char *) &info->groupCompat[0],
+          XkbNumKbdGroups * sizeof(GroupCompatInfo));
+    info->leds = (LEDInfo *) ClearCommonInfo(&info->leds->defs);
+    /* 3/30/94 (ef) -- XXX! Should free action info here */
+    ClearVModInfo(&info->vmods, xkb);
+    return;
+}
+
+static SymInterpInfo *
+NextInterp(CompatInfo * info)
+{
+    SymInterpInfo *si;
+
+    si = uTypedAlloc(SymInterpInfo);
+    if (si)
+    {
+        bzero((char *) si, sizeof(SymInterpInfo));
+        info->interps =
+            (SymInterpInfo *) AddCommonInfo(&info->interps->defs,
+                                            (CommonInfo *) si);
+        info->nInterps++;
+    }
+    return si;
+}
+
+static SymInterpInfo *
+FindMatchingInterp(CompatInfo * info, SymInterpInfo * new)
+{
+    SymInterpInfo *old;
+
+    for (old = info->interps; old != NULL;
+         old = (SymInterpInfo *) old->defs.next)
+    {
+        if ((old->interp.sym == new->interp.sym) &&
+            (old->interp.mods == new->interp.mods) &&
+            (old->interp.match == new->interp.match))
+        {
+            return old;
+        }
+    }
+    return NULL;
+}
+
+static Bool
+AddInterp(CompatInfo * info, SymInterpInfo * new)
+{
+    unsigned collide;
+    SymInterpInfo *old;
+
+    collide = 0;
+    old = FindMatchingInterp(info, new);
+    if (old != NULL)
+    {
+        if (new->defs.merge == MergeReplace)
+        {
+            SymInterpInfo *next = (SymInterpInfo *) old->defs.next;
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Multiple definitions for \"%s\"\n", siText(new, info));
+                ACTION("Earlier interpretation ignored\n");
+            }
+            *old = *new;
+            old->defs.next = &next->defs;
+            return True;
+        }
+        if (UseNewField(_SI_VirtualMod, &old->defs, &new->defs, &collide))
+        {
+            old->interp.virtual_mod = new->interp.virtual_mod;
+            old->defs.defined |= _SI_VirtualMod;
+        }
+        if (UseNewField(_SI_Action, &old->defs, &new->defs, &collide))
+        {
+            old->interp.act = new->interp.act;
+            old->defs.defined |= _SI_Action;
+        }
+        if (UseNewField(_SI_AutoRepeat, &old->defs, &new->defs, &collide))
+        {
+            old->interp.flags &= ~XkbSI_AutoRepeat;
+            old->interp.flags |= (new->interp.flags & XkbSI_AutoRepeat);
+            old->defs.defined |= _SI_AutoRepeat;
+        }
+        if (UseNewField(_SI_LockingKey, &old->defs, &new->defs, &collide))
+        {
+            old->interp.flags &= ~XkbSI_LockingKey;
+            old->interp.flags |= (new->interp.flags & XkbSI_LockingKey);
+            old->defs.defined |= _SI_LockingKey;
+        }
+        if (UseNewField(_SI_LevelOneOnly, &old->defs, &new->defs, &collide))
+        {
+            old->interp.match &= ~XkbSI_LevelOneOnly;
+            old->interp.match |= (new->interp.match & XkbSI_LevelOneOnly);
+            old->defs.defined |= _SI_LevelOneOnly;
+        }
+        if (collide)
+        {
+            WARN1("Multiple interpretations of \"%s\"\n", siText(new, info));
+            ACTION1("Using %s definition for duplicate fields\n",
+                    (new->defs.merge != MergeAugment ? "last" : "first"));
+        }
+        return True;
+    }
+    old = new;
+    if ((new = NextInterp(info)) == NULL)
+        return False;
+    *new = *old;
+    new->defs.next = NULL;
+    return True;
+}
+
+static Bool
+AddGroupCompat(CompatInfo * info, unsigned group, GroupCompatInfo * newGC)
+{
+    GroupCompatInfo *gc;
+    unsigned merge;
+
+    merge = newGC->merge;
+    gc = &info->groupCompat[group];
+    if (((gc->real_mods == newGC->real_mods) && (gc->vmods == newGC->vmods)))
+    {
+        return True;
+    }
+    if (((gc->fileID == newGC->fileID) && (warningLevel > 0))
+        || (warningLevel > 9))
+    {
+        WARN1("Compat map for group %d redefined\n", group + 1);
+        ACTION1("Using %s definition\n",
+                (merge == MergeAugment ? "old" : "new"));
+    }
+    if (merge != MergeAugment)
+        *gc = *newGC;
+    return True;
+}
+
+/***====================================================================***/
+
+static Bool
+ResolveStateAndPredicate(ExprDef * expr,
+                         unsigned *pred_rtrn,
+                         unsigned *mods_rtrn, CompatInfo * info)
+{
+    ExprResult result;
+
+    if (expr == NULL)
+    {
+        *pred_rtrn = XkbSI_AnyOfOrNone;
+        *mods_rtrn = ~0;
+        return True;
+    }
+
+    *pred_rtrn = XkbSI_Exactly;
+    if (expr->op == ExprActionDecl)
+    {
+        char *pred_txt =
+            XkbAtomText(NULL, expr->value.action.name, XkbMessage);
+        if (uStrCaseCmp(pred_txt, "noneof") == 0)
+            *pred_rtrn = XkbSI_NoneOf;
+        else if (uStrCaseCmp(pred_txt, "anyofornone") == 0)
+            *pred_rtrn = XkbSI_AnyOfOrNone;
+        else if (uStrCaseCmp(pred_txt, "anyof") == 0)
+            *pred_rtrn = XkbSI_AnyOf;
+        else if (uStrCaseCmp(pred_txt, "allof") == 0)
+            *pred_rtrn = XkbSI_AllOf;
+        else if (uStrCaseCmp(pred_txt, "exactly") == 0)
+            *pred_rtrn = XkbSI_Exactly;
+        else
+        {
+            ERROR1("Illegal modifier predicate \"%s\"\n", pred_txt);
+            ACTION("Ignored\n");
+            return False;
+        }
+        expr = expr->value.action.args;
+    }
+    else if (expr->op == ExprIdent)
+    {
+        char *pred_txt = XkbAtomText(NULL, expr->value.str, XkbMessage);
+        if ((pred_txt) && (uStrCaseCmp(pred_txt, "any") == 0))
+        {
+            *pred_rtrn = XkbSI_AnyOf;
+            *mods_rtrn = 0xff;
+            return True;
+        }
+    }
+
+    if (ExprResolveModMask(expr, &result, NULL, NULL))
+    {
+        *mods_rtrn = result.uval;
+        return True;
+    }
+    return False;
+}
+
+/***====================================================================***/
+
+static void
+MergeIncludedCompatMaps(CompatInfo * into, CompatInfo * from, unsigned merge)
+{
+    SymInterpInfo *si;
+    LEDInfo *led, *rtrn, *next;
+    GroupCompatInfo *gcm;
+    register int i;
+
+    if (from->errorCount > 0)
+    {
+        into->errorCount += from->errorCount;
+        return;
+    }
+    if (into->name == NULL)
+    {
+        into->name = from->name;
+        from->name = NULL;
+    }
+    for (si = from->interps; si; si = (SymInterpInfo *) si->defs.next)
+    {
+        if (merge != MergeDefault)
+            si->defs.merge = merge;
+        if (!AddInterp(into, si))
+            into->errorCount++;
+    }
+    for (i = 0, gcm = &from->groupCompat[0]; i < XkbNumKbdGroups; i++, gcm++)
+    {
+        if (merge != MergeDefault)
+            gcm->merge = merge;
+        if (!AddGroupCompat(into, i, gcm))
+            into->errorCount++;
+    }
+    for (led = from->leds; led != NULL; led = next)
+    {
+        next = (LEDInfo *) led->defs.next;
+        if (merge != MergeDefault)
+            led->defs.merge = merge;
+        rtrn = AddIndicatorMap(into->leds, led);
+        if (rtrn != NULL)
+            into->leds = rtrn;
+        else
+            into->errorCount++;
+    }
+    return;
+}
+
+typedef void (*FileHandler) (XkbFile * /* rtrn */ ,
+                             XkbDescPtr /* xkb */ ,
+                             unsigned /* merge */ ,
+                             CompatInfo *       /* info */
+    );
+
+static Bool
+HandleIncludeCompatMap(IncludeStmt * stmt,
+                       XkbDescPtr xkb, CompatInfo * info, FileHandler hndlr)
+{
+    unsigned newMerge;
+    XkbFile *rtrn;
+    CompatInfo included;
+    Bool haveSelf;
+
+    haveSelf = False;
+    if ((stmt->file == NULL) && (stmt->map == NULL))
+    {
+        haveSelf = True;
+        included = *info;
+        bzero(info, sizeof(CompatInfo));
+    }
+    else if (ProcessIncludeFile(stmt, XkmCompatMapIndex, &rtrn, &newMerge))
+    {
+        InitCompatInfo(&included, xkb);
+        included.fileID = rtrn->id;
+        included.dflt = info->dflt;
+        included.dflt.defs.fileID = rtrn->id;
+        included.dflt.defs.merge = newMerge;
+        included.ledDflt.defs.fileID = rtrn->id;
+        included.ledDflt.defs.merge = newMerge;
+        included.act = info->act;
+        (*hndlr) (rtrn, xkb, MergeOverride, &included);
+        if (stmt->stmt != NULL)
+        {
+            if (included.name != NULL)
+                uFree(included.name);
+            included.name = stmt->stmt;
+            stmt->stmt = NULL;
+        }
+    }
+    else
+    {
+        info->errorCount += 10;
+        return False;
+    }
+    if ((stmt->next != NULL) && (included.errorCount < 1))
+    {
+        IncludeStmt *next;
+        unsigned op;
+        CompatInfo next_incl;
+
+        for (next = stmt->next; next != NULL; next = next->next)
+        {
+            if ((next->file == NULL) && (next->map == NULL))
+            {
+                haveSelf = True;
+                MergeIncludedCompatMaps(&included, info, next->merge);
+                ClearCompatInfo(info, xkb);
+            }
+            else if (ProcessIncludeFile(next, XkmCompatMapIndex, &rtrn, &op))
+            {
+                InitCompatInfo(&next_incl, xkb);
+                next_incl.fileID = rtrn->id;
+                next_incl.dflt = info->dflt;
+                next_incl.dflt.defs.fileID = rtrn->id;
+                next_incl.dflt.defs.merge = op;
+                next_incl.ledDflt.defs.fileID = rtrn->id;
+                next_incl.ledDflt.defs.merge = op;
+                next_incl.act = info->act;
+                (*hndlr) (rtrn, xkb, MergeOverride, &next_incl);
+                MergeIncludedCompatMaps(&included, &next_incl, op);
+                ClearCompatInfo(&next_incl, xkb);
+            }
+            else
+            {
+                info->errorCount += 10;
+                return False;
+            }
+        }
+    }
+    if (haveSelf)
+        *info = included;
+    else
+    {
+        MergeIncludedCompatMaps(info, &included, newMerge);
+        ClearCompatInfo(&included, xkb);
+    }
+    return (info->errorCount == 0);
+}
+
+static LookupEntry useModMapValues[] = {
+    {"levelone", 1},
+    {"level1", 1},
+    {"anylevel", 0},
+    {"any", 0},
+    {NULL, 0}
+};
+
+static int
+SetInterpField(SymInterpInfo * si,
+               XkbDescPtr xkb,
+               char *field,
+               ExprDef * arrayNdx, ExprDef * value, CompatInfo * info)
+{
+    int ok = 1;
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "action") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportSINotArray(si, field, info);
+        ok = HandleActionDef(value, xkb, &si->interp.act, si->defs.merge,
+                             info->act);
+        if (ok)
+            si->defs.defined |= _SI_Action;
+    }
+    else if ((uStrCaseCmp(field, "virtualmodifier") == 0) ||
+             (uStrCaseCmp(field, "virtualmod") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportSINotArray(si, field, info);
+        ok = ResolveVirtualModifier(value, &tmp, &info->vmods);
+        if (ok)
+        {
+            si->interp.virtual_mod = tmp.uval;
+            si->defs.defined |= _SI_VirtualMod;
+        }
+        else
+            return ReportSIBadType(si, field, "virtual modifier", info);
+    }
+    else if (uStrCaseCmp(field, "repeat") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportSINotArray(si, field, info);
+        ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
+        if (ok)
+        {
+            if (tmp.uval)
+                si->interp.flags |= XkbSI_AutoRepeat;
+            else
+                si->interp.flags &= ~XkbSI_AutoRepeat;
+            si->defs.defined |= _SI_AutoRepeat;
+        }
+        else
+            return ReportSIBadType(si, field, "boolean", info);
+    }
+    else if (uStrCaseCmp(field, "locking") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportSINotArray(si, field, info);
+        ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
+        if (ok)
+        {
+            if (tmp.uval)
+                si->interp.flags |= XkbSI_LockingKey;
+            else
+                si->interp.flags &= ~XkbSI_LockingKey;
+            si->defs.defined |= _SI_LockingKey;
+        }
+        else
+            return ReportSIBadType(si, field, "boolean", info);
+    }
+    else if ((uStrCaseCmp(field, "usemodmap") == 0) ||
+             (uStrCaseCmp(field, "usemodmapmods") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportSINotArray(si, field, info);
+        ok = ExprResolveEnum(value, &tmp, useModMapValues);
+        if (ok)
+        {
+            if (tmp.uval)
+                si->interp.match |= XkbSI_LevelOneOnly;
+            else
+                si->interp.match &= ~XkbSI_LevelOneOnly;
+            si->defs.defined |= _SI_LevelOneOnly;
+        }
+        else
+            return ReportSIBadType(si, field, "level specification", info);
+    }
+    else
+    {
+        ok = ReportBadField("symbol interpretation", field, siText(si, info));
+    }
+    return ok;
+}
+
+LookupEntry groupNames[] = {
+    {"group1", 0x01}
+    ,
+    {"group2", 0x02}
+    ,
+    {"group3", 0x04}
+    ,
+    {"group4", 0x08}
+    ,
+    {"group5", 0x10}
+    ,
+    {"group6", 0x20}
+    ,
+    {"group7", 0x40}
+    ,
+    {"group8", 0x80}
+    ,
+    {"none", 0x00}
+    ,
+    {"all", 0xff}
+    ,
+    {NULL, 0}
+};
+
+static int
+HandleInterpVar(VarDef * stmt, XkbDescPtr xkb, CompatInfo * info)
+{
+    ExprResult elem, field;
+    ExprDef *ndx;
+
+    if (ExprResolveLhs(stmt->name, &elem, &field, &ndx) == 0)
+        return 0;               /* internal error, already reported */
+    if (elem.str && (uStrCaseCmp(elem.str, "interpret") == 0))
+        return SetInterpField(&info->dflt, xkb, field.str, ndx, stmt->value,
+                              info);
+    if (elem.str && (uStrCaseCmp(elem.str, "indicator") == 0))
+    {
+        return SetIndicatorMapField(&info->ledDflt, xkb, field.str, ndx,
+                                    stmt->value);
+    }
+    return SetActionField(xkb, elem.str, field.str, ndx, stmt->value,
+                          &info->act);
+}
+
+static int
+HandleInterpBody(VarDef * def, XkbDescPtr xkb, SymInterpInfo * si,
+                 CompatInfo * info)
+{
+    int ok = 1;
+    ExprResult tmp, field;
+    ExprDef *arrayNdx;
+
+    for (; def != NULL; def = (VarDef *) def->common.next)
+    {
+        if ((def->name) && (def->name->type == ExprFieldRef))
+        {
+            ok = HandleInterpVar(def, xkb, info);
+            continue;
+        }
+        ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
+        if (ok)
+            ok = SetInterpField(si, xkb, field.str, arrayNdx, def->value,
+                                info);
+    }
+    return ok;
+}
+
+static int
+HandleInterpDef(InterpDef * def, XkbDescPtr xkb, unsigned merge,
+                CompatInfo * info)
+{
+    unsigned pred, mods;
+    SymInterpInfo si;
+
+    if (!ResolveStateAndPredicate(def->match, &pred, &mods, info))
+    {
+        ERROR("Couldn't determine matching modifiers\n");
+        ACTION("Symbol interpretation ignored\n");
+        return False;
+    }
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+
+    si = info->dflt;
+    si.defs.merge = merge;
+    si.interp.sym = def->sym;
+    si.interp.match = pred & XkbSI_OpMask;
+    si.interp.mods = mods;
+    if (!HandleInterpBody(def->def, xkb, &si, info))
+    {
+        info->errorCount++;
+        return False;
+    }
+
+    if (!AddInterp(info, &si))
+    {
+        info->errorCount++;
+        return False;
+    }
+    return True;
+}
+
+static int
+HandleGroupCompatDef(GroupCompatDef * def,
+                     XkbDescPtr xkb, unsigned merge, CompatInfo * info)
+{
+    ExprResult val;
+    GroupCompatInfo tmp;
+
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+    if (!XkbIsLegalGroup(def->group - 1))
+    {
+        ERROR1("Keyboard group must be in the range 1..%d\n",
+               XkbNumKbdGroups + 1);
+        ACTION1("Compatibility map for illegal group %d ignored\n",
+                def->group);
+        return False;
+    }
+    tmp.fileID = info->fileID;
+    tmp.merge = merge;
+    if (!ExprResolveModMask(def->def, &val, LookupVModMask, (XPointer) xkb))
+    {
+        ERROR("Expected a modifier mask in group compatibility definition\n");
+        ACTION1("Ignoring illegal compatibility map for group %d\n",
+                def->group);
+        return False;
+    }
+    tmp.real_mods = val.uval & 0xff;
+    tmp.vmods = (val.uval >> 8) & 0xffff;
+    return AddGroupCompat(info, def->group - 1, &tmp);
+}
+
+static void
+HandleCompatMapFile(XkbFile * file,
+                    XkbDescPtr xkb, unsigned merge, CompatInfo * info)
+{
+    ParseCommon *stmt;
+
+    if (merge == MergeDefault)
+        merge = MergeAugment;
+    info->name = uStringDup(file->name);
+    stmt = file->defs;
+    while (stmt)
+    {
+        switch (stmt->stmtType)
+        {
+        case StmtInclude:
+            if (!HandleIncludeCompatMap((IncludeStmt *) stmt, xkb, info,
+                                        HandleCompatMapFile))
+                info->errorCount++;
+            break;
+        case StmtInterpDef:
+            if (!HandleInterpDef((InterpDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtGroupCompatDef:
+            if (!HandleGroupCompatDef
+                ((GroupCompatDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtIndicatorMapDef:
+        {
+            LEDInfo *rtrn;
+            rtrn = HandleIndicatorMapDef((IndicatorMapDef *) stmt, xkb,
+                                         &info->ledDflt, info->leds, merge);
+            if (rtrn != NULL)
+                info->leds = rtrn;
+            else
+                info->errorCount++;
+        }
+            break;
+        case StmtVarDef:
+            if (!HandleInterpVar((VarDef *) stmt, xkb, info))
+                info->errorCount++;
+            break;
+        case StmtVModDef:
+            if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
+                info->errorCount++;
+            break;
+        case StmtKeycodeDef:
+            ERROR("Interpretation files may not include other types\n");
+            ACTION("Ignoring definition of key name\n");
+            info->errorCount++;
+            break;
+        default:
+            WSGO1("Unexpected statement type %d in HandleCompatMapFile\n",
+                  stmt->stmtType);
+            break;
+        }
+        stmt = stmt->next;
+        if (info->errorCount > 10)
+        {
+#ifdef NOISY
+            ERROR("Too many errors\n");
+#endif
+            ACTION1("Abandoning compatibility map \"%s\"\n", file->topName);
+            break;
+        }
+    }
+    return;
+}
+
+static void
+CopyInterps(CompatInfo * info,
+            XkbCompatMapPtr compat, Bool needSymbol, unsigned pred)
+{
+    SymInterpInfo *si;
+
+    for (si = info->interps; si; si = (SymInterpInfo *) si->defs.next)
+    {
+        if (((si->interp.match & XkbSI_OpMask) != pred) ||
+            (needSymbol && (si->interp.sym == NoSymbol)) ||
+            ((!needSymbol) && (si->interp.sym != NoSymbol)))
+            continue;
+        if (compat->num_si >= compat->size_si)
+        {
+            WSGO("No room to merge symbol interpretations\n");
+            ACTION("Symbol interpretations lost\n");
+            return;
+        }
+        compat->sym_interpret[compat->num_si++] = si->interp;
+    }
+    return;
+}
+
+Bool
+CompileCompatMap(XkbFile * file,
+                 XkbFileInfo * result, unsigned merge, LEDInfo ** unboundLEDs)
+{
+    int i;
+    CompatInfo info;
+    XkbDescPtr xkb;
+    GroupCompatInfo *gcm;
+
+    xkb = result->xkb;
+    InitCompatInfo(&info, xkb);
+    info.dflt.defs.merge = merge;
+    info.ledDflt.defs.merge = merge;
+    HandleCompatMapFile(file, xkb, merge, &info);
+
+    if (info.errorCount == 0)
+    {
+        int size;
+        if (XkbAllocCompatMap(xkb, XkbAllCompatMask, info.nInterps) !=
+            Success)
+        {
+            WSGO("Couldn't allocate compatibility map\n");
+            ACTION("Exiting\n");
+            return False;
+        }
+        if (info.name != NULL)
+        {
+            if (XkbAllocNames(xkb, XkbCompatNameMask, 0, 0) == Success)
+                xkb->names->compat =
+                    XkbInternAtom(xkb->dpy, info.name, False);
+            else
+            {
+                WSGO("Couldn't allocate space for compat name\n");
+                ACTION2("Name \"%s\" (from %s) NOT assigned\n",
+                        scanFile, info.name);
+            }
+        }
+        size = info.nInterps * sizeof(XkbSymInterpretRec);
+        if (size > 0)
+        {
+            CopyInterps(&info, xkb->compat, True, XkbSI_Exactly);
+            CopyInterps(&info, xkb->compat, True, XkbSI_AllOf | XkbSI_NoneOf);
+            CopyInterps(&info, xkb->compat, True, XkbSI_AnyOf);
+            CopyInterps(&info, xkb->compat, True, XkbSI_AnyOfOrNone);
+            CopyInterps(&info, xkb->compat, False, XkbSI_Exactly);
+            CopyInterps(&info, xkb->compat, False,
+                        XkbSI_AllOf | XkbSI_NoneOf);
+            CopyInterps(&info, xkb->compat, False, XkbSI_AnyOf);
+            CopyInterps(&info, xkb->compat, False, XkbSI_AnyOfOrNone);
+        }
+        for (i = 0, gcm = &info.groupCompat[0]; i < XkbNumKbdGroups;
+             i++, gcm++)
+        {
+            if ((gcm->fileID != 0) || (gcm->real_mods != 0)
+                || (gcm->vmods != 0))
+            {
+                xkb->compat->groups[i].mask = gcm->real_mods;
+                xkb->compat->groups[i].real_mods = gcm->real_mods;
+                xkb->compat->groups[i].vmods = gcm->vmods;
+            }
+        }
+        if (info.leds != NULL)
+        {
+            if (!CopyIndicatorMapDefs(result, info.leds, unboundLEDs))
+                info.errorCount++;
+            info.leds = NULL;
+        }
+        ClearCompatInfo(&info, xkb);
+        return True;
+    }
+    if (info.interps != NULL)
+        uFree(info.interps);
+    return False;
+}
diff --git a/src/xkbcomp/compat.h b/src/xkbcomp/compat.h
new file mode 100644 (file)
index 0000000..799b215
--- /dev/null
@@ -0,0 +1,7 @@
+
+#ifndef COMPAT_H
+#define COMPAT_H 1
+
+extern LookupEntry groupNames[];
+
+#endif /* COMPAT_H */
diff --git a/src/xkbcomp/expr.c b/src/xkbcomp/expr.c
new file mode 100644 (file)
index 0000000..96fd956
--- /dev/null
@@ -0,0 +1,1065 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+
+#include <ctype.h>
+
+/***====================================================================***/
+
+char *
+exprOpText(unsigned type)
+{
+    static char buf[32];
+
+    switch (type)
+    {
+    case ExprValue:
+        strcpy(buf, "literal");
+        break;
+    case ExprIdent:
+        strcpy(buf, "identifier");
+        break;
+    case ExprActionDecl:
+        strcpy(buf, "action declaration");
+        break;
+    case ExprFieldRef:
+        strcpy(buf, "field reference");
+        break;
+    case ExprArrayRef:
+        strcpy(buf, "array reference");
+        break;
+    case ExprKeysymList:
+        strcpy(buf, "list of keysyms");
+        break;
+    case ExprActionList:
+        strcpy(buf, "list of actions");
+        break;
+    case OpAdd:
+        strcpy(buf, "addition");
+        break;
+    case OpSubtract:
+        strcpy(buf, "subtraction");
+        break;
+    case OpMultiply:
+        strcpy(buf, "multiplication");
+        break;
+    case OpDivide:
+        strcpy(buf, "division");
+        break;
+    case OpAssign:
+        strcpy(buf, "assignment");
+        break;
+    case OpNot:
+        strcpy(buf, "logical not");
+        break;
+    case OpNegate:
+        strcpy(buf, "arithmetic negation");
+        break;
+    case OpInvert:
+        strcpy(buf, "bitwise inversion");
+        break;
+    case OpUnaryPlus:
+        strcpy(buf, "plus sign");
+        break;
+    default:
+        snprintf(buf, sizeof(buf), "illegal(%d)", type);
+        break;
+    }
+    return buf;
+}
+
+char *
+exprTypeText(unsigned type)
+{
+    static char buf[20];
+
+    switch (type)
+    {
+    case TypeUnknown:
+        strcpy(buf, "unknown");
+        break;
+    case TypeBoolean:
+        strcpy(buf, "boolean");
+        break;
+    case TypeInt:
+        strcpy(buf, "int");
+        break;
+    case TypeString:
+        strcpy(buf, "string");
+        break;
+    case TypeAction:
+        strcpy(buf, "action");
+        break;
+    case TypeKeyName:
+        strcpy(buf, "keyname");
+        break;
+    default:
+        snprintf(buf, sizeof(buf), "illegal(%d)", type);
+        break;
+    }
+    return buf;
+}
+
+int
+ExprResolveLhs(ExprDef * expr,
+               ExprResult * elem_rtrn,
+               ExprResult * field_rtrn, ExprDef ** index_rtrn)
+{
+    switch (expr->op)
+    {
+    case ExprIdent:
+        elem_rtrn->str = NULL;
+        field_rtrn->str = XkbAtomGetString(NULL, expr->value.str);
+        *index_rtrn = NULL;
+        return True;
+    case ExprFieldRef:
+        elem_rtrn->str = XkbAtomGetString(NULL, expr->value.field.element);
+        field_rtrn->str = XkbAtomGetString(NULL, expr->value.field.field);
+        *index_rtrn = NULL;
+        return True;
+    case ExprArrayRef:
+        elem_rtrn->str = XkbAtomGetString(NULL, expr->value.array.element);
+        field_rtrn->str = XkbAtomGetString(NULL, expr->value.array.field);
+        *index_rtrn = expr->value.array.entry;
+        return True;
+    }
+    WSGO1("Unexpected operator %d in ResolveLhs\n", expr->op);
+    return False;
+}
+
+Bool
+SimpleLookup(XPointer priv,
+             Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    LookupEntry *entry;
+    register char *str;
+
+    if ((priv == NULL) ||
+        (field == None) || (elem != None) ||
+        ((type != TypeInt) && (type != TypeFloat)))
+    {
+        return False;
+    }
+    str = XkbAtomGetString(NULL, field);
+    for (entry = (LookupEntry *) priv;
+         (entry != NULL) && (entry->name != NULL); entry++)
+    {
+        if (uStrCaseCmp(str, entry->name) == 0)
+        {
+            val_rtrn->uval = entry->result;
+            if (type == TypeFloat)
+                val_rtrn->uval *= XkbGeomPtsPerMM;
+            return True;
+        }
+    }
+    return False;
+}
+
+Bool
+RadioLookup(XPointer priv,
+            Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    register char *str;
+    int rg;
+
+    if ((field == None) || (elem != None) || (type != TypeInt))
+        return False;
+    str = XkbAtomGetString(NULL, field);
+    if (str)
+    {
+        if (uStrCasePrefix("group", str))
+            str += strlen("group");
+        else if (uStrCasePrefix("radiogroup", str))
+            str += strlen("radiogroup");
+        else if (uStrCasePrefix("rg", str))
+            str += strlen("rg");
+        else if (!isdigit(str[0]))
+            str = NULL;
+    }
+    if ((!str) || (sscanf(str, "%i", &rg) < 1) || (rg < 1)
+        || (rg > XkbMaxRadioGroups))
+        return False;
+    val_rtrn->uval = rg;
+    return True;
+}
+
+int
+TableLookup(XPointer priv,
+            Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    LookupTable *tbl = (LookupTable *) priv;
+    register char *str;
+
+    if ((priv == NULL) || (field == None) || (type != TypeInt))
+        return False;
+    str = XkbAtomGetString(NULL, elem);
+    while (tbl)
+    {
+        if (((str == NULL) && (tbl->element == NULL)) ||
+            ((str != NULL) && (tbl->element != NULL) &&
+             (uStrCaseCmp(str, tbl->element) == 0)))
+        {
+            break;
+        }
+        tbl = tbl->nextElement;
+    }
+    if (tbl == NULL)            /* didn't find a matching element */
+        return False;
+    priv = (XPointer) tbl->entries;
+    return SimpleLookup(priv, (Atom) None, field, type, val_rtrn);
+}
+
+static LookupEntry modIndexNames[] = {
+    {"shift", ShiftMapIndex},
+    {"control", ControlMapIndex},
+    {"lock", LockMapIndex},
+    {"mod1", Mod1MapIndex},
+    {"mod2", Mod2MapIndex},
+    {"mod3", Mod3MapIndex},
+    {"mod4", Mod4MapIndex},
+    {"mod5", Mod5MapIndex},
+    {"none", XkbNoModifier},
+    {NULL, 0}
+};
+
+int
+LookupModIndex(XPointer priv,
+               Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    return SimpleLookup((XPointer) modIndexNames, elem, field, type,
+                        val_rtrn);
+}
+
+int
+LookupModMask(XPointer priv,
+              Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    char *str;
+
+    if ((elem != None) || (type != TypeInt))
+        return False;
+    str = XkbAtomGetString(NULL, field);
+    if (str == NULL)
+        return False;
+    if (uStrCaseCmp(str, "all") == 0)
+        val_rtrn->uval = 0xff;
+    else if (uStrCaseCmp(str, "none") == 0)
+        val_rtrn->uval = 0;
+    else if (LookupModIndex(priv, elem, field, type, val_rtrn))
+        val_rtrn->uval = (1 << val_rtrn->uval);
+    else if (priv != NULL)
+    {
+        LookupPriv *lpriv = (LookupPriv *) priv;
+        if ((lpriv->chain == NULL) ||
+            (!(*lpriv->chain) (lpriv->chainPriv, elem, field, type,
+                               val_rtrn)))
+            return False;
+    }
+    else
+        return False;
+    return True;
+}
+
+int
+ExprResolveModIndex(ExprDef * expr,
+                    ExprResult * val_rtrn,
+                    IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    char *bogus = NULL;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type != TypeInt)
+        {
+            ERROR1
+                ("Found constant of type %s where a modifier mask was expected\n",
+                 exprTypeText(expr->type));
+            return False;
+        }
+        else if ((expr->value.ival >= XkbNumModifiers)
+                 || (expr->value.ival < 0))
+        {
+            ERROR2("Illegal modifier index (%d, must be 0..%d)\n",
+                   expr->value.ival, XkbNumModifiers - 1);
+            return False;
+        }
+        val_rtrn->ival = expr->value.ival;
+        return True;
+    case ExprIdent:
+        if (LookupModIndex(lookupPriv, (Atom) None, expr->value.str,
+                           (unsigned) TypeInt, val_rtrn))
+        {
+            return True;
+        }
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeInt, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Cannot determine modifier index for \"%s\"\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        break;
+    case ExprFieldRef:
+        bogus = "field reference";
+        break;
+    case ExprArrayRef:
+        bogus = "array reference";
+        break;
+    case ExprActionDecl:
+        bogus = "function";
+        break;
+    case OpAdd:
+    case OpSubtract:
+    case OpMultiply:
+    case OpDivide:
+    case OpInvert:
+    case OpNegate:
+    case OpNot:
+    case OpUnaryPlus:
+        bogus = "arithmetic operations";
+        break;
+    case OpAssign:
+        bogus = "assignment";
+        break;
+    default:
+        WSGO1("Unknown operator %d in ResolveModIndex\n", expr->op);
+        return False;
+    }
+    if (bogus)
+    {
+        ERROR1("Modifier index must be a name or number, %s ignored\n",
+               bogus);
+        return False;
+    }
+    return ok;
+}
+
+int
+ExprResolveModMask(ExprDef * expr,
+                   ExprResult * val_rtrn,
+                   IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    LookupPriv priv;
+
+    priv.priv = NULL;
+    priv.chain = lookup;
+    priv.chainPriv = lookupPriv;
+    return ExprResolveMask(expr, val_rtrn, LookupModMask, (XPointer) & priv);
+}
+
+int
+ExprResolveBoolean(ExprDef * expr,
+                   ExprResult * val_rtrn,
+                   IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    char *bogus = NULL;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type != TypeBoolean)
+        {
+            ERROR1
+                ("Found constant of type %s where boolean was expected\n",
+                 exprTypeText(expr->type));
+            return False;
+        }
+        val_rtrn->ival = expr->value.ival;
+        return True;
+    case ExprIdent:
+        bogus = XkbAtomGetString(NULL, expr->value.str);
+        if (bogus)
+        {
+            if ((uStrCaseCmp(bogus, "true") == 0) ||
+                (uStrCaseCmp(bogus, "yes") == 0) ||
+                (uStrCaseCmp(bogus, "on") == 0))
+            {
+                val_rtrn->uval = 1;
+                return True;
+            }
+            else if ((uStrCaseCmp(bogus, "false") == 0) ||
+                     (uStrCaseCmp(bogus, "no") == 0) ||
+                     (uStrCaseCmp(bogus, "off") == 0))
+            {
+                val_rtrn->uval = 0;
+                return True;
+            }
+        }
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeBoolean, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Identifier \"%s\" of type int is unknown\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeBoolean, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Default \"%s.%s\" of type boolean is unknown\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case OpInvert:
+    case OpNot:
+        ok = ExprResolveBoolean(expr, val_rtrn, lookup, lookupPriv);
+        if (ok)
+            val_rtrn->uval = !val_rtrn->uval;
+        return ok;
+    case OpAdd:
+        if (bogus == NULL)
+            bogus = "Addition";
+    case OpSubtract:
+        if (bogus == NULL)
+            bogus = "Subtraction";
+    case OpMultiply:
+        if (bogus == NULL)
+            bogus = "Multiplication";
+    case OpDivide:
+        if (bogus == NULL)
+            bogus = "Division";
+    case OpAssign:
+        if (bogus == NULL)
+            bogus = "Assignment";
+    case OpNegate:
+        if (bogus == NULL)
+            bogus = "Negation";
+        ERROR1("%s of boolean values not permitted\n", bogus);
+        break;
+    case OpUnaryPlus:
+        ERROR("Unary \"+\" operator not permitted for boolean values\n");
+        break;
+    default:
+        WSGO1("Unknown operator %d in ResolveBoolean\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+int
+ExprResolveFloat(ExprDef * expr,
+                 ExprResult * val_rtrn,
+                 IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    ExprResult leftRtrn, rightRtrn;
+    ExprDef *left, *right;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type == TypeString)
+        {
+            register char *str;
+            str = XkbAtomGetString(NULL, expr->value.str);
+            if ((str != NULL) && (strlen(str) == 1))
+            {
+                val_rtrn->uval = str[0] * XkbGeomPtsPerMM;
+                return True;
+            }
+        }
+        if ((expr->type != TypeInt) && (expr->type != TypeFloat))
+        {
+            ERROR1("Found constant of type %s, expected a number\n",
+                   exprTypeText(expr->type));
+            return False;
+        }
+        val_rtrn->ival = expr->value.ival;
+        if (expr->type == TypeInt)
+            val_rtrn->ival *= XkbGeomPtsPerMM;
+        return True;
+    case ExprIdent:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeFloat, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Numeric identifier \"%s\" unknown\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeFloat, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Numeric default \"%s.%s\" unknown\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case OpAdd:
+    case OpSubtract:
+    case OpMultiply:
+    case OpDivide:
+        left = expr->value.binary.left;
+        right = expr->value.binary.right;
+        if (ExprResolveFloat(left, &leftRtrn, lookup, lookupPriv) &&
+            ExprResolveFloat(right, &rightRtrn, lookup, lookupPriv))
+        {
+            switch (expr->op)
+            {
+            case OpAdd:
+                val_rtrn->ival = leftRtrn.ival + rightRtrn.ival;
+                break;
+            case OpSubtract:
+                val_rtrn->ival = leftRtrn.ival - rightRtrn.ival;
+                break;
+            case OpMultiply:
+                val_rtrn->ival = leftRtrn.ival * rightRtrn.ival;
+                break;
+            case OpDivide:
+                val_rtrn->ival = leftRtrn.ival / rightRtrn.ival;
+                break;
+            }
+            return True;
+        }
+        return False;
+    case OpAssign:
+        WSGO("Assignment operator not implemented yet\n");
+        break;
+    case OpNot:
+        left = expr->value.child;
+        if (ExprResolveFloat(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The ! operator cannot be applied to a number\n");
+        }
+        return False;
+    case OpInvert:
+    case OpNegate:
+        left = expr->value.child;
+        if (ExprResolveFloat(left, &leftRtrn, lookup, lookupPriv))
+        {
+            if (expr->op == OpNegate)
+                val_rtrn->ival = -leftRtrn.ival;
+            else
+                val_rtrn->ival = ~leftRtrn.ival;
+            return True;
+        }
+        return False;
+    case OpUnaryPlus:
+        left = expr->value.child;
+        return ExprResolveFloat(left, val_rtrn, lookup, lookupPriv);
+    default:
+        WSGO1("Unknown operator %d in ResolveFloat\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+int
+ExprResolveInteger(ExprDef * expr,
+                   ExprResult * val_rtrn,
+                   IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    ExprResult leftRtrn, rightRtrn;
+    ExprDef *left, *right;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type == TypeString)
+        {
+            register char *str;
+            str = XkbAtomGetString(NULL, expr->value.str);
+            if (str != NULL)
+                switch (strlen(str))
+                {
+                case 0:
+                    val_rtrn->uval = 0;
+                    return True;
+                case 1:
+                    val_rtrn->uval = str[0];
+                    return True;
+                default:
+                    break;
+                }
+        }
+        if ((expr->type != TypeInt) && (expr->type != TypeFloat))
+        {
+            ERROR1
+                ("Found constant of type %s where an int was expected\n",
+                 exprTypeText(expr->type));
+            return False;
+        }
+        val_rtrn->ival = expr->value.ival;
+        if (expr->type == TypeFloat)
+            val_rtrn->ival /= XkbGeomPtsPerMM;
+        return True;
+    case ExprIdent:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeInt, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Identifier \"%s\" of type int is unknown\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeInt, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Default \"%s.%s\" of type int is unknown\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case OpAdd:
+    case OpSubtract:
+    case OpMultiply:
+    case OpDivide:
+        left = expr->value.binary.left;
+        right = expr->value.binary.right;
+        if (ExprResolveInteger(left, &leftRtrn, lookup, lookupPriv) &&
+            ExprResolveInteger(right, &rightRtrn, lookup, lookupPriv))
+        {
+            switch (expr->op)
+            {
+            case OpAdd:
+                val_rtrn->ival = leftRtrn.ival + rightRtrn.ival;
+                break;
+            case OpSubtract:
+                val_rtrn->ival = leftRtrn.ival - rightRtrn.ival;
+                break;
+            case OpMultiply:
+                val_rtrn->ival = leftRtrn.ival * rightRtrn.ival;
+                break;
+            case OpDivide:
+                val_rtrn->ival = leftRtrn.ival / rightRtrn.ival;
+                break;
+            }
+            return True;
+        }
+        return False;
+    case OpAssign:
+        WSGO("Assignment operator not implemented yet\n");
+        break;
+    case OpNot:
+        left = expr->value.child;
+        if (ExprResolveInteger(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The ! operator cannot be applied to an integer\n");
+        }
+        return False;
+    case OpInvert:
+    case OpNegate:
+        left = expr->value.child;
+        if (ExprResolveInteger(left, &leftRtrn, lookup, lookupPriv))
+        {
+            if (expr->op == OpNegate)
+                val_rtrn->ival = -leftRtrn.ival;
+            else
+                val_rtrn->ival = ~leftRtrn.ival;
+            return True;
+        }
+        return False;
+    case OpUnaryPlus:
+        left = expr->value.child;
+        return ExprResolveInteger(left, val_rtrn, lookup, lookupPriv);
+    default:
+        WSGO1("Unknown operator %d in ResolveInteger\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+int
+ExprResolveString(ExprDef * expr,
+                  ExprResult * val_rtrn,
+                  IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    ExprResult leftRtrn, rightRtrn;
+    ExprDef *left;
+    ExprDef *right;
+    char *bogus = NULL;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type != TypeString)
+        {
+            ERROR1("Found constant of type %s, expected a string\n",
+                   exprTypeText(expr->type));
+            return False;
+        }
+        val_rtrn->str = XkbAtomGetString(NULL, expr->value.str);
+        if (val_rtrn->str == NULL)
+        {
+            static char *empty = "";
+            val_rtrn->str = empty;
+        }
+        return True;
+    case ExprIdent:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeString, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Identifier \"%s\" of type string not found\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeString, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Default \"%s.%s\" of type string not found\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case OpAdd:
+        left = expr->value.binary.left;
+        right = expr->value.binary.right;
+        if (ExprResolveString(left, &leftRtrn, lookup, lookupPriv) &&
+            ExprResolveString(right, &rightRtrn, lookup, lookupPriv))
+        {
+            int len;
+            char *new;
+            len = strlen(leftRtrn.str) + strlen(rightRtrn.str) + 1;
+            new = (char *) uAlloc(len);
+            if (new)
+            {
+                sprintf(new, "%s%s", leftRtrn.str, rightRtrn.str);
+                val_rtrn->str = new;
+                return True;
+            }
+        }
+        return False;
+    case OpSubtract:
+        if (bogus == NULL)
+            bogus = "Subtraction";
+    case OpMultiply:
+        if (bogus == NULL)
+            bogus = "Multiplication";
+    case OpDivide:
+        if (bogus == NULL)
+            bogus = "Division";
+    case OpAssign:
+        if (bogus == NULL)
+            bogus = "Assignment";
+    case OpNegate:
+        if (bogus == NULL)
+            bogus = "Negation";
+    case OpInvert:
+        if (bogus == NULL)
+            bogus = "Bitwise complement";
+        ERROR1("%s of string values not permitted\n", bogus);
+        return False;
+    case OpNot:
+        left = expr->value.child;
+        if (ExprResolveString(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The ! operator cannot be applied to a string\n");
+        }
+        return False;
+    case OpUnaryPlus:
+        left = expr->value.child;
+        if (ExprResolveString(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The + operator cannot be applied to a string\n");
+        }
+        return False;
+    default:
+        WSGO1("Unknown operator %d in ResolveString\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+int
+ExprResolveKeyName(ExprDef * expr,
+                   ExprResult * val_rtrn,
+                   IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    ExprDef *left;
+    ExprResult leftRtrn;
+    char *bogus = NULL;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type != TypeKeyName)
+        {
+            ERROR1("Found constant of type %s, expected a key name\n",
+                   exprTypeText(expr->type));
+            return False;
+        }
+        memcpy(val_rtrn->keyName.name, expr->value.keyName, XkbKeyNameLength);
+        return True;
+    case ExprIdent:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeString, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Identifier \"%s\" of type string not found\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeString, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Default \"%s.%s\" of type key name not found\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case OpAdd:
+        if (bogus == NULL)
+            bogus = "Addition";
+    case OpSubtract:
+        if (bogus == NULL)
+            bogus = "Subtraction";
+    case OpMultiply:
+        if (bogus == NULL)
+            bogus = "Multiplication";
+    case OpDivide:
+        if (bogus == NULL)
+            bogus = "Division";
+    case OpAssign:
+        if (bogus == NULL)
+            bogus = "Assignment";
+    case OpNegate:
+        if (bogus == NULL)
+            bogus = "Negation";
+    case OpInvert:
+        if (bogus == NULL)
+            bogus = "Bitwise complement";
+        ERROR1("%s of key name values not permitted\n", bogus);
+        return False;
+    case OpNot:
+        left = expr->value.binary.left;
+        if (ExprResolveString(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The ! operator cannot be applied to a key name\n");
+        }
+        return False;
+    case OpUnaryPlus:
+        left = expr->value.binary.left;
+        if (ExprResolveString(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR("The + operator cannot be applied to a key name\n");
+        }
+        return False;
+    default:
+        WSGO1("Unknown operator %d in ResolveKeyName\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+/***====================================================================***/
+
+int
+ExprResolveEnum(ExprDef * expr, ExprResult * val_rtrn, LookupEntry * values)
+{
+    if (expr->op != ExprIdent)
+    {
+        ERROR1("Found a %s where an enumerated value was expected\n",
+               exprOpText(expr->op));
+        return False;
+    }
+    if (!SimpleLookup((XPointer) values, (Atom) None, expr->value.str,
+                      (unsigned) TypeInt, val_rtrn))
+    {
+        int nOut = 0;
+        ERROR1("Illegal identifier %s (expected one of: ",
+               XkbAtomText(NULL, expr->value.str, XkbMessage));
+        while (values && values->name)
+        {
+            if (nOut != 0)
+                INFO1(", %s", values->name);
+            else
+                INFO1("%s", values->name);
+            values++;
+            nOut++;
+        }
+        INFO(")\n");
+        return False;
+    }
+    return True;
+}
+
+int
+ExprResolveMask(ExprDef * expr,
+                ExprResult * val_rtrn,
+                IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    ExprResult leftRtrn, rightRtrn;
+    ExprDef *left, *right;
+    char *bogus = NULL;
+
+    switch (expr->op)
+    {
+    case ExprValue:
+        if (expr->type != TypeInt)
+        {
+            ERROR1
+                ("Found constant of type %s where a mask was expected\n",
+                 exprTypeText(expr->type));
+            return False;
+        }
+        val_rtrn->ival = expr->value.ival;
+        return True;
+    case ExprIdent:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            None, expr->value.str, TypeInt, val_rtrn);
+        }
+        if (!ok)
+            ERROR1("Identifier \"%s\" of type int is unknown\n",
+                   XkbAtomText(NULL, expr->value.str, XkbMessage));
+        return ok;
+    case ExprFieldRef:
+        if (lookup)
+        {
+            ok = (*lookup) (lookupPriv,
+                            expr->value.field.element,
+                            expr->value.field.field, TypeInt, val_rtrn);
+        }
+        if (!ok)
+            ERROR2("Default \"%s.%s\" of type int is unknown\n",
+                   XkbAtomText(NULL, expr->value.field.element, XkbMessage),
+                   XkbAtomText(NULL, expr->value.field.field, XkbMessage));
+        return ok;
+    case ExprArrayRef:
+        bogus = "array reference";
+    case ExprActionDecl:
+        if (bogus == NULL)
+            bogus = "function use";
+        ERROR1("Unexpected %s in mask expression\n", bogus);
+        ACTION("Expression ignored\n");
+        return False;
+    case OpAdd:
+    case OpSubtract:
+    case OpMultiply:
+    case OpDivide:
+        left = expr->value.binary.left;
+        right = expr->value.binary.right;
+        if (ExprResolveMask(left, &leftRtrn, lookup, lookupPriv) &&
+            ExprResolveMask(right, &rightRtrn, lookup, lookupPriv))
+        {
+            switch (expr->op)
+            {
+            case OpAdd:
+                val_rtrn->ival = leftRtrn.ival | rightRtrn.ival;
+                break;
+            case OpSubtract:
+                val_rtrn->ival = leftRtrn.ival & (~rightRtrn.ival);
+                break;
+            case OpMultiply:
+            case OpDivide:
+                ERROR1("Cannot %s masks\n",
+                       expr->op == OpDivide ? "divide" : "multiply");
+                ACTION("Illegal operation ignored\n");
+                return False;
+            }
+            return True;
+        }
+        return False;
+    case OpAssign:
+        WSGO("Assignment operator not implemented yet\n");
+        break;
+    case OpInvert:
+        left = expr->value.child;
+        if (ExprResolveInteger(left, &leftRtrn, lookup, lookupPriv))
+        {
+            val_rtrn->ival = ~leftRtrn.ival;
+            return True;
+        }
+        return False;
+    case OpUnaryPlus:
+    case OpNegate:
+    case OpNot:
+        left = expr->value.child;
+        if (ExprResolveInteger(left, &leftRtrn, lookup, lookupPriv))
+        {
+            ERROR1("The %s operator cannot be used with a mask\n",
+                   (expr->op == OpNegate ? "-" : "!"));
+        }
+        return False;
+    default:
+        WSGO1("Unknown operator %d in ResolveMask\n", expr->op);
+        break;
+    }
+    return False;
+}
+
+int
+ExprResolveKeySym(ExprDef * expr,
+                  ExprResult * val_rtrn,
+                  IdentLookupFunc lookup, XPointer lookupPriv)
+{
+    int ok = 0;
+    KeySym sym;
+
+    if (expr->op == ExprIdent)
+    {
+        char *str;
+        str = XkbAtomGetString(NULL, expr->value.str);
+        if ((str != NULL) && ((sym = XStringToKeysym(str)) != NoSymbol))
+        {
+            val_rtrn->uval = sym;
+            return True;
+        }
+    }
+    ok = ExprResolveInteger(expr, val_rtrn, lookup, lookupPriv);
+    if ((ok) && (val_rtrn->uval < 10))
+        val_rtrn->uval += '0';
+    return ok;
+}
diff --git a/src/xkbcomp/expr.h b/src/xkbcomp/expr.h
new file mode 100644 (file)
index 0000000..02519f9
--- /dev/null
@@ -0,0 +1,172 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef EXPR_H
+#define EXPR_H 1
+
+typedef union _ExprResult
+{
+    char *str;
+    int ival;
+    unsigned uval;
+    XkbKeyNameRec keyName;
+} ExprResult;
+
+typedef Bool(*IdentLookupFunc) (XPointer /* priv */ ,
+                                Atom /* elem */ ,
+                                Atom /* field */ ,
+                                unsigned /* type */ ,
+                                ExprResult *    /* val_rtrn */
+    );
+
+extern char *exprTypeText(unsigned      /* type */
+    );
+
+extern int ExprResolveLhs(ExprDef * /* expr */ ,
+                          ExprResult * /* elem_rtrn */ ,
+                          ExprResult * /* field_rtrn */ ,
+                          ExprDef **    /* index_rtrn */
+    );
+
+typedef struct _LookupPriv
+{
+    XPointer priv;
+    IdentLookupFunc chain;
+    XPointer chainPriv;
+} LookupPriv;
+
+typedef struct _LookupEntry
+{
+    const char *name;
+    unsigned result;
+} LookupEntry;
+
+typedef struct _LookupTable
+{
+    char *element;
+    LookupEntry *entries;
+    struct _LookupTable *nextElement;
+} LookupTable;
+
+
+extern char *exprOpText(unsigned        /* type */
+    );
+
+extern int RadioLookup(XPointer /* priv */ ,
+                       Atom /* elem */ ,
+                       Atom /* field */ ,
+                       unsigned /* type */ ,
+                       ExprResult *     /* val_rtrn */
+    );
+
+extern int SimpleLookup(XPointer /* priv */ ,
+                        Atom /* elem */ ,
+                        Atom /* field */ ,
+                        unsigned /* type */ ,
+                        ExprResult *    /* val_rtrn */
+    );
+
+extern int TableLookup(XPointer /* priv */ ,
+                       Atom /* elem */ ,
+                       Atom /* field */ ,
+                       unsigned /* type */ ,
+                       ExprResult *     /* val_rtrn */
+    );
+
+extern int LookupModIndex(XPointer /* priv */ ,
+                          Atom /* elem */ ,
+                          Atom /* field */ ,
+                          unsigned /* type */ ,
+                          ExprResult *  /* val_rtrn */
+    );
+
+extern int LookupModMask(XPointer /* priv */ ,
+                         Atom /* elem */ ,
+                         Atom /* field */ ,
+                         unsigned /* type */ ,
+                         ExprResult *   /* val_rtrn */
+    );
+
+extern int ExprResolveModIndex(ExprDef * /* expr */ ,
+                               ExprResult * /* val_rtrn */ ,
+                               IdentLookupFunc /* lookup */ ,
+                               XPointer /* lookupPriv */
+    );
+
+extern int ExprResolveModMask(ExprDef * /* expr */ ,
+                              ExprResult * /* val_rtrn */ ,
+                              IdentLookupFunc /* lookup */ ,
+                              XPointer  /* priv */
+    );
+
+extern int ExprResolveBoolean(ExprDef * /* expr */ ,
+                              ExprResult * /* val_rtrn */ ,
+                              IdentLookupFunc /* lookup */ ,
+                              XPointer  /* lookupPriv */
+    );
+
+extern int ExprResolveInteger(ExprDef * /* expr */ ,
+                              ExprResult * /* val_rtrn */ ,
+                              IdentLookupFunc /* lookup */ ,
+                              XPointer  /* lookupPriv */
+    );
+
+extern int ExprResolveFloat(ExprDef * /* expr */ ,
+                            ExprResult * /* val_rtrn */ ,
+                            IdentLookupFunc /* lookup */ ,
+                            XPointer    /* lookupPriv */
+    );
+
+extern int ExprResolveString(ExprDef * /* expr */ ,
+                             ExprResult * /* val_rtrn */ ,
+                             IdentLookupFunc /* lookup */ ,
+                             XPointer   /* lookupPriv */
+    );
+
+extern int ExprResolveKeyName(ExprDef * /* expr */ ,
+                              ExprResult * /* val_rtrn */ ,
+                              IdentLookupFunc /* lookup */ ,
+                              XPointer  /* lookupPriv */
+    );
+
+extern int ExprResolveEnum(ExprDef * /* expr */ ,
+                           ExprResult * /* val_rtrn */ ,
+                           LookupEntry *        /* values */
+    );
+
+extern int ExprResolveMask(ExprDef * /* expr */ ,
+                           ExprResult * /* val_rtrn */ ,
+                           IdentLookupFunc /* lookup */ ,
+                           XPointer     /* lookupPriv */
+    );
+
+extern int ExprResolveKeySym(ExprDef * /* expr */ ,
+                             ExprResult * /* val_rtrn */ ,
+                             IdentLookupFunc /* lookup */ ,
+                             XPointer   /* lookupPriv */
+    );
+
+#endif /* EXPR_H */
diff --git a/src/xkbcomp/geometry.c b/src/xkbcomp/geometry.c
new file mode 100644 (file)
index 0000000..6eb3fb8
--- /dev/null
@@ -0,0 +1,3768 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "vmod.h"
+#include "misc.h"
+#include "indicators.h"
+#include "action.h"
+#include "keycodes.h"
+#include "alias.h"
+
+#include "X11/extensions/XKBgeom.h"
+
+#define        DFLT_FONT       "helvetica"
+#define        DFLT_SLANT      "r"
+#define        DFLT_WEIGHT     "medium"
+#define        DFLT_SET_WIDTH  "normal"
+#define        DFLT_VARIANT    ""
+#define        DFLT_ENCODING   "iso8859-1"
+#define        DFLT_SIZE       120
+
+typedef struct _PropertyInfo
+{
+    CommonInfo defs;
+    char *name;
+    char *value;
+} PropertyInfo;
+
+#define        _GSh_Outlines   (1<<1)
+#define        _GSh_Approx     (1<<2)
+#define        _GSh_Primary    (1<<3)
+typedef struct _ShapeInfo
+{
+    CommonInfo defs;
+    Atom name;
+    short index;
+    unsigned short nOutlines;
+    unsigned short szOutlines;
+    XkbOutlinePtr outlines;
+    XkbOutlinePtr approx;
+    XkbOutlinePtr primary;
+    int dfltCornerRadius;
+} ShapeInfo;
+
+#define        shText(d,s)     \
+               ((s)?XkbAtomText((d),(s)->name,XkbMessage):"default shape")
+
+#define        _GD_Priority    (1<<0)
+#define        _GD_Top         (1<<1)
+#define        _GD_Left        (1<<2)
+#define        _GD_Angle       (1<<3)
+#define        _GD_Shape       (1<<4)
+#define        _GD_FontVariant (1<<4)  /* CHEATING */
+#define        _GD_Corner      (1<<5)
+#define        _GD_Width       (1<<5)  /* CHEATING */
+#define        _GD_Color       (1<<6)
+#define        _GD_OffColor    (1<<7)
+#define        _GD_Height      (1<<7)  /* CHEATING */
+#define        _GD_Text        (1<<8)
+#define        _GD_Font        (1<<9)
+#define        _GD_FontSlant   (1<<10)
+#define        _GD_FontWeight  (1<<11)
+#define        _GD_FontSetWidth (1<<12)
+#define        _GD_FontSize    (1<<13)
+#define        _GD_FontEncoding (1<<14)
+#define        _GD_FontSpec    (1<<15)
+
+
+#define        _GD_FontParts   (_GD_Font|_GD_FontSlant|_GD_FontWeight|_GD_FontSetWidth|_GD_FontSize|_GD_FontEncoding|_GD_FontVariant)
+
+typedef struct _DoodadInfo
+{
+    CommonInfo defs;
+    Atom name;
+    unsigned char type;
+    unsigned char priority;
+    short top;
+    short left;
+    short angle;
+    unsigned short corner;
+    unsigned short width;
+    unsigned short height;
+    Atom shape;
+    Atom color;
+    Atom offColor;
+    Atom text;
+    Atom font;
+    Atom fontSlant;
+    Atom fontWeight;
+    Atom fontSetWidth;
+    Atom fontVariant;
+    unsigned short fontSize;
+    Atom fontEncoding;
+    Atom fontSpec;
+    char *logoName;
+    struct _SectionInfo *section;
+} DoodadInfo;
+
+#define        Yes             1
+#define        No              0
+#define        Undefined       -1
+
+#define        _GK_Default     (1<<0)
+#define        _GK_Name        (1<<1)
+#define        _GK_Gap         (1<<2)
+#define        _GK_Shape       (1<<3)
+#define        _GK_Color       (1<<4)
+typedef struct _KeyInfo
+{
+    CommonInfo defs;
+    char name[8];
+    short gap;
+    short index;
+    Atom shape;
+    Atom color;
+    struct _RowInfo *row;
+} KeyInfo;
+#define        keyText(k)      ((k)&&(k)->name[0]?(k)->name:"default")
+
+#define        _GR_Default     (1<<0)
+#define        _GR_Vertical    (1<<1)
+#define        _GR_Top         (1<<2)
+#define        _GR_Left        (1<<3)
+typedef struct _RowInfo
+{
+    CommonInfo defs;
+    unsigned short top;
+    unsigned short left;
+    short index;
+    Bool vertical;
+    unsigned short nKeys;
+    KeyInfo *keys;
+    KeyInfo dfltKey;
+    struct _SectionInfo *section;
+} RowInfo;
+#define        rowText(d,r)    \
+       ((r)?XkbAtomText((d),(r)->section->name,XkbMessage):"default")
+
+#define        _GOK_UnknownRow -1
+typedef struct _OverlayKeyInfo
+{
+    CommonInfo defs;
+    short sectionRow;
+    short overlayRow;
+    char over[XkbKeyNameLength + 1];
+    char under[XkbKeyNameLength + 1];
+} OverlayKeyInfo;
+
+typedef struct _OverlayInfo
+{
+    CommonInfo defs;
+    Atom name;
+    unsigned short nRows;
+    unsigned short nKeys;
+    OverlayKeyInfo *keys;
+} OverlayInfo;
+#define        oiText(d,o)     ((o)?XkbAtomText((d),(o)->name,XkbMessage):"default")
+
+
+#define        _GS_Default     (1<<0)
+#define        _GS_Name        (1<<1)
+#define        _GS_Top         (1<<2)
+#define        _GS_Left        (1<<3)
+#define        _GS_Width       (1<<4)
+#define        _GS_Height      (1<<5)
+#define        _GS_Angle       (1<<6)
+#define        _GS_Priority    (1<<7)
+typedef struct _SectionInfo
+{
+    CommonInfo defs;
+    Atom name;
+    unsigned short top;
+    unsigned short left;
+    unsigned short width;
+    unsigned short height;
+    unsigned short angle;
+    unsigned short nRows;
+    unsigned short nDoodads;
+    unsigned short nOverlays;
+    unsigned char priority;
+    unsigned char nextDoodadPriority;
+    RowInfo *rows;
+    DoodadInfo *doodads;
+    RowInfo dfltRow;
+    DoodadInfo *dfltDoodads;
+    OverlayInfo *overlays;
+    struct _GeometryInfo *geometry;
+} SectionInfo;
+#define        scText(d,s)     ((s)?XkbAtomText((d),(s)->name,XkbMessage):"default")
+
+typedef struct _GeometryInfo
+{
+    char *name;
+    Display *dpy;
+    unsigned fileID;
+    unsigned merge;
+    int errorCount;
+    unsigned nextPriority;
+    int nProps;
+    int nShapes;
+    int nSections;
+    int nDoodads;
+    PropertyInfo *props;
+    ShapeInfo *shapes;
+    SectionInfo *sections;
+    DoodadInfo *doodads;
+    int widthMM;
+    int heightMM;
+    Atom font;
+    Atom fontSlant;
+    Atom fontWeight;
+    Atom fontSetWidth;
+    Atom fontVariant;
+    unsigned fontSize;
+    Atom fontEncoding;
+    Atom fontSpec;
+    Atom baseColor;
+    Atom labelColor;
+    int dfltCornerRadius;
+    SectionInfo dfltSection;
+    DoodadInfo *dfltDoodads;
+    AliasInfo *aliases;
+} GeometryInfo;
+
+static char *
+ddText(Display * dpy, DoodadInfo * di)
+{
+    static char buf[64];
+
+    if (di == NULL)
+    {
+        strcpy(buf, "default");
+        return buf;
+    }
+    if (di->section)
+    {
+        sprintf(buf, "%s in section %s",
+                XkbAtomText(dpy, di->name, XkbMessage), scText(dpy,
+                                                               di->section));
+        return buf;
+    }
+    return XkbAtomText(dpy, di->name, XkbMessage);
+}
+
+/***====================================================================***/
+
+static void
+InitPropertyInfo(PropertyInfo * pi, GeometryInfo * info)
+{
+    pi->defs.defined = 0;
+    pi->defs.fileID = info->fileID;
+    pi->defs.merge = info->merge;
+    pi->name = pi->value = NULL;
+    return;
+}
+
+static void
+FreeProperties(PropertyInfo * pi, GeometryInfo * info)
+{
+    PropertyInfo *tmp;
+    PropertyInfo *next;
+
+    if (info->props == pi)
+    {
+        info->props = NULL;
+        info->nProps = 0;
+    }
+    for (tmp = pi; tmp != NULL; tmp = next)
+    {
+        if (tmp->name)
+            uFree(tmp->name);
+        if (tmp->value)
+            uFree(tmp->value);
+        tmp->name = tmp->value = NULL;
+        next = (PropertyInfo *) tmp->defs.next;
+        uFree(tmp);
+    }
+    return;
+}
+
+static void
+InitKeyInfo(KeyInfo * key, RowInfo * row, GeometryInfo * info)
+{
+
+    if (key != &row->dfltKey)
+    {
+        *key = row->dfltKey;
+        strcpy(key->name, "unknown");
+        key->defs.defined &= ~_GK_Default;
+    }
+    else
+    {
+        bzero(key, sizeof(KeyInfo));
+        strcpy(key->name, "default");
+        key->defs.defined = _GK_Default;
+        key->defs.fileID = info->fileID;
+        key->defs.merge = info->merge;
+        key->defs.next = NULL;
+        key->row = row;
+    }
+    return;
+}
+
+static void
+ClearKeyInfo(KeyInfo * key)
+{
+    key->defs.defined &= ~_GK_Default;
+    strcpy(key->name, "default");
+    key->gap = 0;
+    key->shape = None;
+    key->color = None;
+    return;
+}
+
+static void
+FreeKeys(KeyInfo * key, RowInfo * row, GeometryInfo * info)
+{
+    KeyInfo *tmp;
+    KeyInfo *next;
+
+    if (row->keys == key)
+    {
+        row->nKeys = 0;
+        row->keys = NULL;
+    }
+    for (tmp = key; tmp != NULL; tmp = next)
+    {
+        ClearKeyInfo(tmp);
+        next = (KeyInfo *) tmp->defs.next;
+        uFree(tmp);
+    }
+    return;
+}
+
+static void
+InitRowInfo(RowInfo * row, SectionInfo * section, GeometryInfo * info)
+{
+    if (row != &section->dfltRow)
+    {
+        *row = section->dfltRow;
+        row->defs.defined &= ~_GR_Default;
+    }
+    else
+    {
+        bzero(row, sizeof(RowInfo *));
+        row->defs.defined = _GR_Default;
+        row->defs.fileID = info->fileID;
+        row->defs.merge = info->merge;
+        row->defs.next = NULL;
+        row->section = section;
+        row->nKeys = 0;
+        row->keys = NULL;
+        InitKeyInfo(&row->dfltKey, row, info);
+    }
+    return;
+}
+
+static void
+ClearRowInfo(RowInfo * row, GeometryInfo * info)
+{
+    row->defs.defined &= ~_GR_Default;
+    row->top = row->left = 0;
+    row->vertical = False;
+    row->nKeys = 0;
+    if (row->keys)
+        FreeKeys(row->keys, row, info);
+    ClearKeyInfo(&row->dfltKey);
+    row->dfltKey.defs.defined |= _GK_Default;
+    return;
+}
+
+static void
+FreeRows(RowInfo * row, SectionInfo * section, GeometryInfo * info)
+{
+    RowInfo *next;
+    RowInfo *tmp;
+
+    if (row == section->rows)
+    {
+        section->nRows = 0;
+        section->rows = NULL;
+    }
+    for (tmp = row; tmp != NULL; tmp = next)
+    {
+        ClearRowInfo(tmp, info);
+        next = (RowInfo *) tmp->defs.next;
+        uFree(tmp);
+    }
+    return;
+}
+
+static DoodadInfo *
+FindDoodadByType(DoodadInfo * di, unsigned type)
+{
+    while (di)
+    {
+        if (di->type == type)
+            return di;
+        di = (DoodadInfo *) di->defs.next;
+    }
+    return NULL;
+}
+
+static DoodadInfo *
+FindDoodadByName(DoodadInfo * di, Atom name)
+{
+    while (di)
+    {
+        if (di->name == name)
+            return di;
+        di = (DoodadInfo *) di->defs.next;
+    }
+    return NULL;
+}
+
+static void
+InitDoodadInfo(DoodadInfo * di, unsigned type, SectionInfo * si,
+               GeometryInfo * info)
+{
+    DoodadInfo *dflt;
+
+    dflt = NULL;
+    if (si && si->dfltDoodads)
+        dflt = FindDoodadByType(si->dfltDoodads, type);
+    if ((dflt == NULL) && (info->dfltDoodads))
+        dflt = FindDoodadByType(info->dfltDoodads, type);
+    if (dflt != NULL)
+    {
+        *di = *dflt;
+        di->defs.next = NULL;
+    }
+    else
+    {
+        bzero(di, sizeof(DoodadInfo));
+        di->defs.fileID = info->fileID;
+        di->type = type;
+    }
+    di->section = si;
+    if (si != NULL)
+    {
+        di->priority = si->nextDoodadPriority++;
+#if XkbGeomMaxPriority < 255
+        if (si->nextDoodadPriority > XkbGeomMaxPriority)
+            si->nextDoodadPriority = XkbGeomMaxPriority;
+#endif
+    }
+    else
+    {
+        di->priority = info->nextPriority++;
+        if (info->nextPriority > XkbGeomMaxPriority)
+            info->nextPriority = XkbGeomMaxPriority;
+    }
+    return;
+}
+
+static void
+ClearDoodadInfo(DoodadInfo * di)
+{
+    CommonInfo defs;
+
+    defs = di->defs;
+    bzero(di, sizeof(DoodadInfo));
+    di->defs = defs;
+    di->defs.defined = 0;
+    return;
+}
+
+static void
+ClearOverlayInfo(OverlayInfo * ol)
+{
+    if (ol && ol->keys)
+    {
+        ol->keys = (OverlayKeyInfo *) ClearCommonInfo(&ol->keys->defs);
+        ol->nKeys = 0;
+    }
+    return;
+}
+
+static void
+FreeDoodads(DoodadInfo * di, SectionInfo * si, GeometryInfo * info)
+{
+    DoodadInfo *tmp;
+    DoodadInfo *next;
+
+    if (si)
+    {
+        if (si->doodads == di)
+        {
+            si->doodads = NULL;
+            si->nDoodads = 0;
+        }
+        if (si->dfltDoodads == di)
+            si->dfltDoodads = NULL;
+    }
+    if (info->doodads == di)
+    {
+        info->doodads = NULL;
+        info->nDoodads = 0;
+    }
+    if (info->dfltDoodads == di)
+        info->dfltDoodads = NULL;
+    for (tmp = di; tmp != NULL; tmp = next)
+    {
+        next = (DoodadInfo *) tmp->defs.next;
+        ClearDoodadInfo(tmp);
+        uFree(tmp);
+    }
+    return;
+}
+
+static void
+InitSectionInfo(SectionInfo * si, GeometryInfo * info)
+{
+    if (si != &info->dfltSection)
+    {
+        *si = info->dfltSection;
+        si->defs.defined &= ~_GS_Default;
+        si->name = XkbInternAtom(info->dpy, "unknown", False);
+        si->priority = info->nextPriority++;
+        if (info->nextPriority > XkbGeomMaxPriority)
+            info->nextPriority = XkbGeomMaxPriority;
+    }
+    else
+    {
+        bzero(si, sizeof(SectionInfo));
+        si->defs.fileID = info->fileID;
+        si->defs.merge = info->merge;
+        si->defs.next = NULL;
+        si->geometry = info;
+        si->name = XkbInternAtom(info->dpy, "default", False);
+        InitRowInfo(&si->dfltRow, si, info);
+    }
+    return;
+}
+
+static void
+DupSectionInfo(SectionInfo * into, SectionInfo * from, GeometryInfo * info)
+{
+    CommonInfo defs;
+
+    defs = into->defs;
+    *into = *from;
+    into->defs.fileID = defs.fileID;
+    into->defs.merge = defs.merge;
+    into->defs.next = NULL;
+    into->dfltRow.defs.fileID = defs.fileID;
+    into->dfltRow.defs.merge = defs.merge;
+    into->dfltRow.defs.next = NULL;
+    into->dfltRow.section = into;
+    into->dfltRow.dfltKey.defs.fileID = defs.fileID;
+    into->dfltRow.dfltKey.defs.merge = defs.merge;
+    into->dfltRow.dfltKey.defs.next = NULL;
+    into->dfltRow.dfltKey.row = &into->dfltRow;
+    return;
+}
+
+static void
+ClearSectionInfo(SectionInfo * si, GeometryInfo * info)
+{
+
+    si->defs.defined &= ~_GS_Default;
+    si->name = XkbInternAtom(info->dpy, "default", False);
+    si->top = si->left = 0;
+    si->width = si->height = 0;
+    si->angle = 0;
+    if (si->rows)
+    {
+        FreeRows(si->rows, si, info);
+        si->rows = NULL;
+    }
+    ClearRowInfo(&si->dfltRow, info);
+    if (si->doodads)
+    {
+        FreeDoodads(si->doodads, si, info);
+        si->doodads = NULL;
+    }
+    si->dfltRow.defs.defined = _GR_Default;
+    return;
+}
+
+static void
+FreeSections(SectionInfo * si, GeometryInfo * info)
+{
+    SectionInfo *tmp;
+    SectionInfo *next;
+
+    if (si == info->sections)
+    {
+        info->nSections = 0;
+        info->sections = NULL;
+    }
+    for (tmp = si; tmp != NULL; tmp = next)
+    {
+        ClearSectionInfo(tmp, info);
+        next = (SectionInfo *) tmp->defs.next;
+        uFree(tmp);
+    }
+    return;
+}
+
+static void
+FreeShapes(ShapeInfo * si, GeometryInfo * info)
+{
+    ShapeInfo *tmp;
+    ShapeInfo *next;
+
+    if (si == info->shapes)
+    {
+        info->nShapes = 0;
+        info->shapes = NULL;
+    }
+    for (tmp = si; tmp != NULL; tmp = next)
+    {
+        if (tmp->outlines)
+        {
+            register int i;
+            for (i = 0; i < tmp->nOutlines; i++)
+            {
+                if (tmp->outlines[i].points != NULL)
+                {
+                    uFree(tmp->outlines[i].points);
+                    tmp->outlines[i].num_points = 0;
+                    tmp->outlines[i].points = NULL;
+                }
+            }
+            uFree(tmp->outlines);
+            tmp->szOutlines = 0;
+            tmp->nOutlines = 0;
+            tmp->outlines = NULL;
+            tmp->primary = tmp->approx = NULL;
+        }
+        next = (ShapeInfo *) tmp->defs.next;
+        uFree(tmp);
+    }
+    return;
+}
+
+/***====================================================================***/
+
+static void
+InitGeometryInfo(GeometryInfo * info, unsigned fileID, unsigned merge)
+{
+    bzero(info, sizeof(GeometryInfo));
+    info->fileID = fileID;
+    info->merge = merge;
+    InitSectionInfo(&info->dfltSection, info);
+    info->dfltSection.defs.defined = _GS_Default;
+    return;
+}
+
+static void
+ClearGeometryInfo(GeometryInfo * info)
+{
+    if (info->name)
+        uFree(info->name);
+    info->name = NULL;
+    if (info->props)
+        FreeProperties(info->props, info);
+    if (info->shapes)
+        FreeShapes(info->shapes, info);
+    if (info->sections)
+        FreeSections(info->sections, info);
+    info->widthMM = 0;
+    info->heightMM = 0;
+    info->dfltCornerRadius = 0;
+    ClearSectionInfo(&info->dfltSection, info);
+    info->dfltSection.defs.defined = _GS_Default;
+    if (info->aliases)
+        ClearAliases(&info->aliases);
+    return;
+}
+
+/***====================================================================***/
+
+static PropertyInfo *
+NextProperty(GeometryInfo * info)
+{
+    PropertyInfo *pi;
+
+    pi = uTypedAlloc(PropertyInfo);
+    if (pi)
+    {
+        bzero((char *) pi, sizeof(PropertyInfo));
+        info->props = (PropertyInfo *) AddCommonInfo(&info->props->defs,
+                                                     (CommonInfo *) pi);
+        info->nProps++;
+    }
+    return pi;
+}
+
+static PropertyInfo *
+FindProperty(GeometryInfo * info, char *name)
+{
+    PropertyInfo *old;
+
+    if (!name)
+        return NULL;
+    for (old = info->props; old != NULL;
+         old = (PropertyInfo *) old->defs.next)
+    {
+        if ((old->name) && (uStringEqual(name, old->name)))
+            return old;
+    }
+    return NULL;
+}
+
+static Bool
+AddProperty(GeometryInfo * info, PropertyInfo * new)
+{
+    PropertyInfo *old;
+
+    if ((!new) || (!new->value) || (!new->name))
+        return False;
+    old = FindProperty(info, new->name);
+    if (old != NULL)
+    {
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Multiple definitions for the \"%s\" property\n",
+                      new->name);
+                ACTION2("Ignoring \"%s\", using \"%s\"\n", old->value,
+                        new->value);
+            }
+            if (old->value)
+                uFree(old->value);
+            old->value = uStringDup(new->value);
+            return True;
+        }
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple definitions for \"%s\" property\n", new->name);
+            ACTION2("Using \"%s\", ignoring \"%s\" \n", old->value,
+                    new->value);
+        }
+        return True;
+    }
+    old = new;
+    if ((new = NextProperty(info)) == NULL)
+        return False;
+    new->defs.next = NULL;
+    new->name = uStringDup(old->name);
+    new->value = uStringDup(old->value);
+    return True;
+}
+
+/***====================================================================***/
+
+static ShapeInfo *
+NextShape(GeometryInfo * info)
+{
+    ShapeInfo *si;
+
+    si = uTypedAlloc(ShapeInfo);
+    if (si)
+    {
+        bzero((char *) si, sizeof(ShapeInfo));
+        info->shapes = (ShapeInfo *) AddCommonInfo(&info->shapes->defs,
+                                                   (CommonInfo *) si);
+        info->nShapes++;
+        si->dfltCornerRadius = info->dfltCornerRadius;
+    }
+    return si;
+}
+
+static ShapeInfo *
+FindShape(GeometryInfo * info, Atom name, const char *type, const char *which)
+{
+    ShapeInfo *old;
+
+    for (old = info->shapes; old != NULL; old = (ShapeInfo *) old->defs.next)
+    {
+        if (name == old->name)
+            return old;
+    }
+    if (type != NULL)
+    {
+        old = info->shapes;
+        WARN3("Unknown shape \"%s\" for %s %s\n",
+              XkbAtomText(info->dpy, name, XkbMessage), type, which);
+        if (old)
+        {
+            ACTION1("Using default shape %s instead\n",
+                    shText(info->dpy, old));
+            return old;
+        }
+        ACTION("No default shape; definition ignored\n");
+        return NULL;
+    }
+    return NULL;
+}
+
+static Bool
+AddShape(GeometryInfo * info, ShapeInfo * new)
+{
+    ShapeInfo *old;
+
+    old = FindShape(info, new->name, NULL, NULL);
+    if (old != NULL)
+    {
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            ShapeInfo *next = (ShapeInfo *) old->defs.next;
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Duplicate shape name \"%s\"\n",
+                      shText(info->dpy, old));
+                ACTION("Using last definition\n");
+            }
+            *old = *new;
+            old->defs.next = &next->defs;
+            return True;
+        }
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple shapes named \"%s\"\n", shText(info->dpy, old));
+            ACTION("Using first definition\n");
+        }
+        return True;
+    }
+    old = new;
+    if ((new = NextShape(info)) == NULL)
+        return False;
+    *new = *old;
+    new->defs.next = NULL;
+    old->szOutlines = old->nOutlines = 0;
+    old->outlines = NULL;
+    old->approx = NULL;
+    old->primary = NULL;
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+ReplaceDoodad(DoodadInfo * into, DoodadInfo * from)
+{
+    CommonInfo *next;
+
+    next = into->defs.next;
+    ClearDoodadInfo(into);
+    *into = *from;
+    into->defs.next = next;
+    next = from->defs.next;
+    ClearDoodadInfo(from);
+    from->defs.next = next;
+    return;
+}
+
+static DoodadInfo *
+NextDfltDoodad(SectionInfo * si, GeometryInfo * info)
+{
+    DoodadInfo *di;
+
+    di = uTypedCalloc(1, DoodadInfo);
+    if (!di)
+        return NULL;
+    if (si)
+    {
+        si->dfltDoodads =
+            (DoodadInfo *) AddCommonInfo(&si->dfltDoodads->defs,
+                                         (CommonInfo *) di);
+    }
+    else
+    {
+        info->dfltDoodads =
+            (DoodadInfo *) AddCommonInfo(&info->dfltDoodads->defs,
+                                         (CommonInfo *) di);
+    }
+    return di;
+}
+
+static DoodadInfo *
+NextDoodad(SectionInfo * si, GeometryInfo * info)
+{
+    DoodadInfo *di;
+
+    di = uTypedCalloc(1, DoodadInfo);
+    if (di)
+    {
+        if (si)
+        {
+            si->doodads = (DoodadInfo *) AddCommonInfo(&si->doodads->defs,
+                                                       (CommonInfo *) di);
+            si->nDoodads++;
+        }
+        else
+        {
+            info->doodads =
+                (DoodadInfo *) AddCommonInfo(&info->doodads->defs,
+                                             (CommonInfo *) di);
+            info->nDoodads++;
+        }
+    }
+    return di;
+}
+
+static Bool
+AddDoodad(SectionInfo * si, GeometryInfo * info, DoodadInfo * new)
+{
+    DoodadInfo *old;
+
+    old = FindDoodadByName((si ? si->doodads : info->doodads), new->name);
+    if (old != NULL)
+    {
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Multiple doodads named \"%s\"\n",
+                      XkbAtomText(info->dpy, old->name, XkbMessage));
+                ACTION("Using last definition\n");
+            }
+            ReplaceDoodad(old, new);
+            old->section = si;
+            return True;
+        }
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple doodads named \"%s\"\n",
+                  XkbAtomText(info->dpy, old->name, XkbMessage));
+            ACTION("Using first definition\n");
+        }
+        return True;
+    }
+    old = new;
+    if ((new = NextDoodad(si, info)) == NULL)
+        return False;
+    ReplaceDoodad(new, old);
+    new->section = si;
+    new->defs.next = NULL;
+    return True;
+}
+
+static DoodadInfo *
+FindDfltDoodadByTypeName(char *name, SectionInfo * si, GeometryInfo * info)
+{
+    DoodadInfo *dflt;
+    unsigned type;
+
+    if (uStrCaseCmp(name, "outline") == 0)
+        type = XkbOutlineDoodad;
+    else if (uStrCaseCmp(name, "solid") == 0)
+        type = XkbSolidDoodad;
+    else if (uStrCaseCmp(name, "text") == 0)
+        type = XkbTextDoodad;
+    else if (uStrCaseCmp(name, "indicator") == 0)
+        type = XkbIndicatorDoodad;
+    else if (uStrCaseCmp(name, "logo") == 0)
+        type = XkbLogoDoodad;
+    else
+        return NULL;
+    if ((si) && (si->dfltDoodads))
+        dflt = FindDoodadByType(si->dfltDoodads, type);
+    else
+        dflt = NULL;
+    if ((!dflt) && (info->dfltDoodads))
+        dflt = FindDoodadByType(info->dfltDoodads, type);
+    if (dflt == NULL)
+    {
+        dflt = NextDfltDoodad(si, info);
+        if (dflt != NULL)
+        {
+            dflt->name = None;
+            dflt->type = type;
+        }
+    }
+    return dflt;
+}
+
+/***====================================================================***/
+
+static Bool
+AddOverlay(SectionInfo * si, GeometryInfo * info, OverlayInfo * new)
+{
+    OverlayInfo *old;
+
+    for (old = si->overlays; old != NULL;
+         old = (OverlayInfo *) old->defs.next)
+    {
+        if (old->name == new->name)
+            break;
+    }
+    if (old != NULL)
+    {
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN2
+                    ("Multiple overlays named \"%s\" for section \"%s\"\n",
+                     XkbAtomText(info->dpy, old->name, XkbMessage),
+                     XkbAtomText(info->dpy, si->name, XkbMessage));
+                ACTION("Using last definition\n");
+            }
+            ClearOverlayInfo(old);
+            old->nKeys = new->nKeys;
+            old->keys = new->keys;
+            new->nKeys = 0;
+            new->keys = NULL;
+            return True;
+        }
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN2("Multiple doodads named \"%s\" in section \"%s\"\n",
+                  XkbAtomText(info->dpy, old->name, XkbMessage),
+                  XkbAtomText(info->dpy, si->name, XkbMessage));
+            ACTION("Using first definition\n");
+        }
+        return True;
+    }
+    old = new;
+    new = uTypedCalloc(1, OverlayInfo);
+    if (!new)
+    {
+        if (warningLevel > 0)
+        {
+            WSGO("Couldn't allocate a new OverlayInfo\n");
+            ACTION2
+                ("Overlay \"%s\" in section \"%s\" will be incomplete\n",
+                 XkbAtomText(info->dpy, old->name, XkbMessage),
+                 XkbAtomText(info->dpy, si->name, XkbMessage));
+        }
+        return False;
+    }
+    *new = *old;
+    old->nKeys = 0;
+    old->keys = NULL;
+    si->overlays = (OverlayInfo *) AddCommonInfo(&si->overlays->defs,
+                                                 (CommonInfo *) new);
+    si->nOverlays++;
+    return True;
+}
+
+/***====================================================================***/
+
+static SectionInfo *
+NextSection(GeometryInfo * info)
+{
+    SectionInfo *si;
+
+    si = uTypedAlloc(SectionInfo);
+    if (si)
+    {
+        *si = info->dfltSection;
+        si->defs.defined &= ~_GS_Default;
+        si->defs.next = NULL;
+        si->nRows = 0;
+        si->rows = NULL;
+        info->sections =
+            (SectionInfo *) AddCommonInfo(&info->sections->defs,
+                                          (CommonInfo *) si);
+        info->nSections++;
+    }
+    return si;
+}
+
+static SectionInfo *
+FindMatchingSection(GeometryInfo * info, SectionInfo * new)
+{
+    SectionInfo *old;
+
+    for (old = info->sections; old != NULL;
+         old = (SectionInfo *) old->defs.next)
+    {
+        if (new->name == old->name)
+            return old;
+    }
+    return NULL;
+}
+
+static Bool
+AddSection(GeometryInfo * info, SectionInfo * new)
+{
+    SectionInfo *old;
+
+    old = FindMatchingSection(info, new);
+    if (old != NULL)
+    {
+#ifdef NOTDEF
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            SectionInfo *next = (SectionInfo *) old->defs.next;
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Duplicate shape name \"%s\"\n",
+                      shText(info->dpy, old));
+                ACTION("Using last definition\n");
+            }
+            *old = *new;
+            old->defs.next = &next->defs;
+            return True;
+        }
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple shapes named \"%s\"\n", shText(info->dpy, old));
+            ACTION("Using first definition\n");
+        }
+        return True;
+#else
+        WARN("Don't know how to merge sections yet\n");
+#endif
+    }
+    old = new;
+    if ((new = NextSection(info)) == NULL)
+        return False;
+    *new = *old;
+    new->defs.next = NULL;
+    old->nRows = old->nDoodads = old->nOverlays = 0;
+    old->rows = NULL;
+    old->doodads = NULL;
+    old->overlays = NULL;
+    if (new->doodads)
+    {
+        DoodadInfo *di;
+        for (di = new->doodads; di; di = (DoodadInfo *) di->defs.next)
+        {
+            di->section = new;
+        }
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+static RowInfo *
+NextRow(SectionInfo * si)
+{
+    RowInfo *row;
+
+    row = uTypedAlloc(RowInfo);
+    if (row)
+    {
+        *row = si->dfltRow;
+        row->defs.defined &= ~_GR_Default;
+        row->defs.next = NULL;
+        row->nKeys = 0;
+        row->keys = NULL;
+        si->rows =
+            (RowInfo *) AddCommonInfo(&si->rows->defs, (CommonInfo *) row);
+        row->index = si->nRows++;
+    }
+    return row;
+}
+
+static Bool
+AddRow(SectionInfo * si, RowInfo * new)
+{
+    RowInfo *old;
+
+    old = new;
+    if ((new = NextRow(si)) == NULL)
+        return False;
+    *new = *old;
+    new->defs.next = NULL;
+    old->nKeys = 0;
+    old->keys = NULL;
+    return True;
+}
+
+/***====================================================================***/
+
+static KeyInfo *
+NextKey(RowInfo * row)
+{
+    KeyInfo *key;
+
+    key = uTypedAlloc(KeyInfo);
+    if (key)
+    {
+        *key = row->dfltKey;
+        key->defs.defined &= ~_GK_Default;
+        key->defs.next = NULL;
+        key->index = row->nKeys++;
+    }
+    return key;
+}
+
+static Bool
+AddKey(RowInfo * row, KeyInfo * new)
+{
+    KeyInfo *old;
+
+    old = new;
+    if ((new = NextKey(row)) == NULL)
+        return False;
+    *new = *old;
+    new->defs.next = NULL;
+    row->keys =
+        (KeyInfo *) AddCommonInfo(&row->keys->defs, (CommonInfo *) new);
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+MergeIncludedGeometry(GeometryInfo * into, GeometryInfo * from,
+                      unsigned merge)
+{
+    Bool clobber;
+
+    if (from->errorCount > 0)
+    {
+        into->errorCount += from->errorCount;
+        return;
+    }
+    clobber = (merge == MergeOverride) || (merge == MergeReplace);
+    if (into->name == NULL)
+    {
+        into->name = from->name;
+        from->name = NULL;
+    }
+    if ((into->widthMM == 0) || ((from->widthMM != 0) && clobber))
+        into->widthMM = from->widthMM;
+    if ((into->heightMM == 0) || ((from->heightMM != 0) && clobber))
+        into->heightMM = from->heightMM;
+    if ((into->font == None) || ((from->font != None) && clobber))
+        into->font = from->font;
+    if ((into->fontSlant == None) || ((from->fontSlant != None) && clobber))
+        into->fontSlant = from->fontSlant;
+    if ((into->fontWeight == None) || ((from->fontWeight != None) && clobber))
+        into->fontWeight = from->fontWeight;
+    if ((into->fontSetWidth == None)
+        || ((from->fontSetWidth != None) && clobber))
+        into->fontSetWidth = from->fontSetWidth;
+    if ((into->fontVariant == None)
+        || ((from->fontVariant != None) && clobber))
+        into->fontVariant = from->fontVariant;
+    if ((into->fontSize == 0) || ((from->fontSize != 0) && clobber))
+        into->fontSize = from->fontSize;
+    if ((into->fontEncoding == None)
+        || ((from->fontEncoding != None) && clobber))
+        into->fontEncoding = from->fontEncoding;
+    if ((into->fontSpec == None) || ((from->fontSpec != None) && clobber))
+        into->fontSpec = from->fontSpec;
+    if ((into->baseColor == None) || ((from->baseColor != None) && clobber))
+        into->baseColor = from->baseColor;
+    if ((into->labelColor == None) || ((from->labelColor != None) && clobber))
+        into->labelColor = from->labelColor;
+    into->nextPriority = from->nextPriority;
+    if (from->props != NULL)
+    {
+        PropertyInfo *pi;
+        for (pi = from->props; pi; pi = (PropertyInfo *) pi->defs.next)
+        {
+            if (!AddProperty(into, pi))
+                into->errorCount++;
+        }
+    }
+    if (from->shapes != NULL)
+    {
+        ShapeInfo *si;
+
+        for (si = from->shapes; si; si = (ShapeInfo *) si->defs.next)
+        {
+            if (!AddShape(into, si))
+                into->errorCount++;
+        }
+    }
+    if (from->sections != NULL)
+    {
+        SectionInfo *si;
+
+        for (si = from->sections; si; si = (SectionInfo *) si->defs.next)
+        {
+            if (!AddSection(into, si))
+                into->errorCount++;
+        }
+    }
+    if (from->doodads != NULL)
+    {
+        DoodadInfo *di;
+
+        for (di = from->doodads; di; di = (DoodadInfo *) di->defs.next)
+        {
+            if (!AddDoodad(NULL, into, di))
+                into->errorCount++;
+        }
+    }
+    if (!MergeAliases(&into->aliases, &from->aliases, merge))
+        into->errorCount++;
+    return;
+}
+
+typedef void (*FileHandler) (XkbFile * /* file */ ,
+                             XkbDescPtr /* xkb */ ,
+                             unsigned /* merge */ ,
+                             GeometryInfo *     /* info */
+    );
+
+static Bool
+HandleIncludeGeometry(IncludeStmt * stmt, XkbDescPtr xkb, GeometryInfo * info,
+                      FileHandler hndlr)
+{
+    unsigned newMerge;
+    XkbFile *rtrn;
+    GeometryInfo included;
+    Bool haveSelf;
+
+    haveSelf = False;
+    if ((stmt->file == NULL) && (stmt->map == NULL))
+    {
+        haveSelf = True;
+        included = *info;
+        bzero(info, sizeof(GeometryInfo));
+    }
+    else if (ProcessIncludeFile(stmt, XkmGeometryIndex, &rtrn, &newMerge))
+    {
+        InitGeometryInfo(&included, rtrn->id, newMerge);
+        included.nextPriority = info->nextPriority;
+        included.dfltCornerRadius = info->dfltCornerRadius;
+        DupSectionInfo(&included.dfltSection, &info->dfltSection, info);
+        (*hndlr) (rtrn, xkb, MergeOverride, &included);
+        if (stmt->stmt != NULL)
+        {
+            if (included.name != NULL)
+                uFree(included.name);
+            included.name = stmt->stmt;
+            stmt->stmt = NULL;
+        }
+    }
+    else
+    {
+        info->errorCount += 10;
+        return False;
+    }
+    if ((stmt->next != NULL) && (included.errorCount < 1))
+    {
+        IncludeStmt *next;
+        unsigned op;
+        GeometryInfo next_incl;
+
+        for (next = stmt->next; next != NULL; next = next->next)
+        {
+            if ((next->file == NULL) && (next->map == NULL))
+            {
+                haveSelf = True;
+                MergeIncludedGeometry(&included, info, next->merge);
+                ClearGeometryInfo(info);
+            }
+            else if (ProcessIncludeFile(next, XkmGeometryIndex, &rtrn, &op))
+            {
+                InitGeometryInfo(&next_incl, rtrn->id, op);
+                next_incl.nextPriority = included.nextPriority;
+                next_incl.dfltCornerRadius = included.dfltCornerRadius;
+                DupSectionInfo(&next_incl.dfltSection,
+                               &included.dfltSection, &included);
+                (*hndlr) (rtrn, xkb, MergeOverride, &next_incl);
+                MergeIncludedGeometry(&included, &next_incl, op);
+                ClearGeometryInfo(&next_incl);
+            }
+            else
+            {
+                info->errorCount += 10;
+                return False;
+            }
+        }
+    }
+    if (haveSelf)
+        *info = included;
+    else
+    {
+        MergeIncludedGeometry(info, &included, newMerge);
+        ClearGeometryInfo(&included);
+    }
+    return (info->errorCount == 0);
+}
+
+static int
+SetShapeField(ShapeInfo * si,
+              char *field,
+              ExprDef * arrayNdx, ExprDef * value, GeometryInfo * info)
+{
+    ExprResult tmp;
+
+    if ((uStrCaseCmp(field, "radius") == 0)
+        || (uStrCaseCmp(field, "corner") == 0)
+        || (uStrCaseCmp(field, "cornerradius") == 0))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("key shape", field, shText(info->dpy, si));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("key shape", field,
+                                 shText(info->dpy, si), "number");
+        }
+        if (si)
+            si->dfltCornerRadius = tmp.ival;
+        else
+            info->dfltCornerRadius = tmp.ival;
+        return True;
+    }
+    info->errorCount++;
+    return ReportBadField("key shape", field, shText(info->dpy, si));
+}
+
+static int
+SetShapeDoodadField(DoodadInfo * di,
+                    char *field,
+                    ExprDef * arrayNdx,
+                    ExprDef * value, SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult tmp;
+    const char *typeName;
+
+    typeName =
+        (di->type == XkbSolidDoodad ? "solid doodad" : "outline doodad");
+    if ((!uStrCaseCmp(field, "corner"))
+        || (!uStrCaseCmp(field, "cornerradius")))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Corner;
+        di->corner = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "angle") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Angle;
+        di->angle = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "shape") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "string");
+        }
+        di->shape = XkbInternAtom(info->dpy, tmp.str, False);
+        di->defs.defined |= _GD_Shape;
+        return True;
+    }
+    return ReportBadField(typeName, field, ddText(info->dpy, di));
+}
+
+#define        FIELD_STRING    0
+#define        FIELD_SHORT     1
+#define        FIELD_USHORT    2
+
+static int
+SetTextDoodadField(DoodadInfo * di,
+                   char *field,
+                   ExprDef * arrayNdx,
+                   ExprDef * value, SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult tmp;
+    unsigned def;
+    unsigned type;
+    char *typeName = "text doodad";
+    union
+    {
+        Atom *str;
+        short *ival;
+        unsigned short *uval;
+    } pField;
+
+    if (uStrCaseCmp(field, "angle") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Angle;
+        di->angle = tmp.ival;
+        return True;
+    }
+    if (uStrCaseCmp(field, "width") == 0)
+    {
+        type = FIELD_USHORT;
+        pField.uval = &di->width;
+        def = _GD_Width;
+    }
+    else if (uStrCaseCmp(field, "height") == 0)
+    {
+        type = FIELD_USHORT;
+        pField.uval = &di->height;
+        def = _GD_Height;
+    }
+    else if (uStrCaseCmp(field, "text") == 0)
+    {
+        type = FIELD_STRING;
+        pField.str = &di->text;
+        def = _GD_Text;
+    }
+    else if (uStrCaseCmp(field, "font") == 0)
+    {
+        type = FIELD_STRING;
+        pField.str = &di->font;
+        def = _GD_Font;
+    }
+    else if ((uStrCaseCmp(field, "fontslant") == 0) ||
+             (uStrCaseCmp(field, "slant") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontSlant;
+        def = _GD_FontSlant;
+    }
+    else if ((uStrCaseCmp(field, "fontweight") == 0) ||
+             (uStrCaseCmp(field, "weight") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontWeight;
+        def = _GD_FontWeight;
+    }
+    else if ((uStrCaseCmp(field, "fontwidth") == 0) ||
+             (uStrCaseCmp(field, "setwidth") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontSetWidth;
+        def = _GD_FontSetWidth;
+    }
+    else if ((uStrCaseCmp(field, "fontvariant") == 0) ||
+             (uStrCaseCmp(field, "variant") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontVariant;
+        def = _GD_FontVariant;
+    }
+    else if ((uStrCaseCmp(field, "fontencoding") == 0) ||
+             (uStrCaseCmp(field, "encoding") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontEncoding;
+        def = _GD_FontEncoding;
+    }
+    else if ((uStrCaseCmp(field, "xfont") == 0) ||
+             (uStrCaseCmp(field, "xfontname") == 0))
+    {
+        type = FIELD_STRING;
+        pField.str = &di->fontSpec;
+        def = _GD_FontSpec;
+    }
+    else if (uStrCaseCmp(field, "fontsize") == 0)
+    {
+        type = FIELD_USHORT;
+        pField.uval = &di->fontSize;
+        def = _GD_FontSize;
+    }
+    else
+    {
+        return ReportBadField(typeName, field, ddText(info->dpy, di));
+    }
+    if (arrayNdx != NULL)
+    {
+        info->errorCount++;
+        return ReportNotArray(typeName, field, ddText(info->dpy, di));
+    }
+    if (type == FIELD_STRING)
+    {
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "string");
+        }
+        di->defs.defined |= def;
+        *pField.str = XkbInternAtom(NULL, tmp.str, False);
+    }
+    else
+    {
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        if ((type == FIELD_USHORT) && (tmp.ival < 0))
+        {
+            info->errorCount++;
+            return
+                ReportBadType(typeName, field, ddText(info->dpy, di),
+                              "unsigned");
+        }
+        di->defs.defined |= def;
+        if (type == FIELD_USHORT)
+            *pField.uval = tmp.uval;
+        else
+            *pField.ival = tmp.ival;
+    }
+    return True;
+}
+
+static int
+SetIndicatorDoodadField(DoodadInfo * di,
+                        char *field,
+                        ExprDef * arrayNdx,
+                        ExprDef * value,
+                        SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult tmp;
+
+    if ((uStrCaseCmp(field, "oncolor") == 0)
+        || (uStrCaseCmp(field, "offcolor") == 0)
+        || (uStrCaseCmp(field, "shape") == 0))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("indicator doodad", field,
+                                  ddText(info->dpy, di));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("indicator doodad", field,
+                                 ddText(info->dpy, di), "string");
+        }
+        if (uStrCaseCmp(field, "oncolor") == 0)
+        {
+            di->defs.defined |= _GD_Color;
+            di->color = XkbInternAtom(NULL, tmp.str, False);
+        }
+        else if (uStrCaseCmp(field, "offcolor") == 0)
+        {
+            di->defs.defined |= _GD_OffColor;
+            di->offColor = XkbInternAtom(NULL, tmp.str, False);
+        }
+        else if (uStrCaseCmp(field, "shape") == 0)
+        {
+            di->defs.defined |= _GD_Shape;
+            di->shape = XkbInternAtom(info->dpy, tmp.str, False);
+        }
+        return True;
+    }
+    return ReportBadField("indicator doodad", field, ddText(info->dpy, di));
+}
+
+static int
+SetLogoDoodadField(DoodadInfo * di,
+                   char *field,
+                   ExprDef * arrayNdx,
+                   ExprDef * value, SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult tmp;
+    char *typeName = "logo doodad";
+
+    if ((!uStrCaseCmp(field, "corner"))
+        || (!uStrCaseCmp(field, "cornerradius")))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Corner;
+        di->corner = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "angle") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Angle;
+        di->angle = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "shape") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "string");
+        }
+        di->shape = XkbInternAtom(info->dpy, tmp.str, False);
+        di->defs.defined |= _GD_Shape;
+        return True;
+    }
+    else if ((!uStrCaseCmp(field, "logoname"))
+             || (!uStrCaseCmp(field, "name")))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray(typeName, field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType(typeName, field, ddText(info->dpy, di),
+                                 "string");
+        }
+        di->logoName = uStringDup(tmp.str);
+        return True;
+    }
+    return ReportBadField(typeName, field, ddText(info->dpy, di));
+}
+
+static int
+SetDoodadField(DoodadInfo * di,
+               char *field,
+               ExprDef * arrayNdx,
+               ExprDef * value, SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "priority") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("doodad", field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveInteger(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("doodad", field, ddText(info->dpy, di),
+                                 "integer");
+        }
+        if ((tmp.ival < 0) || (tmp.ival > XkbGeomMaxPriority))
+        {
+            info->errorCount++;
+            ERROR2("Doodad priority %d out of range (must be 0..%d)\n",
+                   tmp.ival, XkbGeomMaxPriority);
+            ACTION1("Priority for doodad %s not changed",
+                    ddText(info->dpy, di));
+            return False;
+        }
+        di->defs.defined |= _GD_Priority;
+        di->priority = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "left") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("doodad", field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("doodad", field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Left;
+        di->left = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "top") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("doodad", field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("doodad", field, ddText(info->dpy, di),
+                                 "number");
+        }
+        di->defs.defined |= _GD_Top;
+        di->top = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "color") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("doodad", field, ddText(info->dpy, di));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("doodad", field, ddText(info->dpy, di),
+                                 "string");
+        }
+        di->defs.defined |= _GD_Color;
+        di->color = XkbInternAtom(NULL, tmp.str, False);
+        return True;
+    }
+    switch (di->type)
+    {
+    case XkbOutlineDoodad:
+    case XkbSolidDoodad:
+        return SetShapeDoodadField(di, field, arrayNdx, value, si, info);
+    case XkbTextDoodad:
+        return SetTextDoodadField(di, field, arrayNdx, value, si, info);
+    case XkbIndicatorDoodad:
+        return SetIndicatorDoodadField(di, field, arrayNdx, value, si, info);
+    case XkbLogoDoodad:
+        return SetLogoDoodadField(di, field, arrayNdx, value, si, info);
+    }
+    WSGO1("Unknown doodad type %d in SetDoodadField\n",
+          (unsigned int) di->type);
+    ACTION2("Definition of %s in %s ignored\n", field, ddText(info->dpy, di));
+    return False;
+}
+
+static int
+SetSectionField(SectionInfo * si,
+                char *field,
+                ExprDef * arrayNdx, ExprDef * value, GeometryInfo * info)
+{
+    unsigned short *pField;
+    unsigned def;
+    ExprResult tmp;
+
+    pField = NULL;
+    def = 0;
+    if (uStrCaseCmp(field, "priority") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard section", field,
+                                  scText(info->dpy, si));
+        }
+        if (!ExprResolveInteger(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            ReportBadType("keyboard section", field,
+                          scText(info->dpy, si), "integer");
+            return False;
+        }
+        if ((tmp.ival < 0) || (tmp.ival > XkbGeomMaxPriority))
+        {
+            info->errorCount++;
+            ERROR2("Section priority %d out of range (must be 0..%d)\n",
+                   tmp.ival, XkbGeomMaxPriority);
+            ACTION1("Priority for section %s not changed",
+                    scText(info->dpy, si));
+            return False;
+        }
+        si->priority = tmp.ival;
+        si->defs.defined |= _GS_Priority;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "top") == 0)
+    {
+        pField = &si->top;
+        def = _GS_Top;
+    }
+    else if (uStrCaseCmp(field, "left") == 0)
+    {
+        pField = &si->left;
+        def = _GS_Left;
+    }
+    else if (uStrCaseCmp(field, "width") == 0)
+    {
+        pField = &si->width;
+        def = _GS_Width;
+    }
+    else if (uStrCaseCmp(field, "height") == 0)
+    {
+        pField = &si->height;
+        def = _GS_Height;
+    }
+    else if (uStrCaseCmp(field, "angle") == 0)
+    {
+        pField = &si->angle;
+        def = _GS_Angle;
+    }
+    else
+    {
+        info->errorCount++;
+        return ReportBadField("keyboard section", field,
+                              scText(info->dpy, si));
+    }
+    if (arrayNdx != NULL)
+    {
+        info->errorCount++;
+        return ReportNotArray("keyboard section", field,
+                              scText(info->dpy, si));
+    }
+    if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+    {
+        info->errorCount++;
+        ReportBadType("keyboard section", field, scText(info->dpy, si),
+                      "number");
+        return False;
+    }
+    si->defs.defined |= def;
+    *pField = tmp.uval;
+    return True;
+}
+
+static int
+SetRowField(RowInfo * row,
+            char *field,
+            ExprDef * arrayNdx, ExprDef * value, GeometryInfo * info)
+{
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "top") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard row", field,
+                                  rowText(info->dpy, row));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard row", field,
+                                 rowText(info->dpy, row), "number");
+        }
+        row->defs.defined |= _GR_Top;
+        row->top = tmp.uval;
+    }
+    else if (uStrCaseCmp(field, "left") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard row", field,
+                                  rowText(info->dpy, row));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard row", field,
+                                 rowText(info->dpy, row), "number");
+        }
+        row->defs.defined |= _GR_Left;
+        row->left = tmp.uval;
+    }
+    else if (uStrCaseCmp(field, "vertical") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard row", field,
+                                  rowText(info->dpy, row));
+        }
+        if (!ExprResolveBoolean(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard row", field,
+                                 rowText(info->dpy, row), "boolean");
+        }
+        row->defs.defined |= _GR_Vertical;
+        row->vertical = tmp.uval;
+    }
+    else
+    {
+        info->errorCount++;
+        return ReportBadField("keyboard row", field, rowText(info->dpy, row));
+    }
+    return True;
+}
+
+static int
+SetKeyField(KeyInfo * key,
+            const char *field,
+            ExprDef * arrayNdx, ExprDef * value, GeometryInfo * info)
+{
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "gap") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("key", field, keyText(key));
+        }
+        if (!ExprResolveFloat(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("key", field, keyText(key), "number");
+        }
+        key->defs.defined |= _GK_Gap;
+        key->gap = tmp.ival;
+    }
+    else if (uStrCaseCmp(field, "shape") == 0)
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("key", field, keyText(key));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("key", field, keyText(key), "string");
+        }
+        key->defs.defined |= _GK_Shape;
+        key->shape = XkbInternAtom(info->dpy, tmp.str, False);
+    }
+    else if ((uStrCaseCmp(field, "color") == 0) ||
+             (uStrCaseCmp(field, "keycolor") == 0))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("key", field, keyText(key));
+        }
+        if (!ExprResolveString(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("key", field, keyText(key), "string");
+        }
+        key->defs.defined |= _GK_Color;
+        key->color = XkbInternAtom(NULL, tmp.str, False);
+    }
+    else if ((uStrCaseCmp(field, "name") == 0)
+             || (uStrCaseCmp(field, "keyname") == 0))
+    {
+        if (arrayNdx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("key", field, keyText(key));
+        }
+        if (!ExprResolveKeyName(value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("key", field, keyText(key), "key name");
+        }
+        key->defs.defined |= _GK_Name;
+        bzero(key->name, XkbKeyNameLength + 1);
+        strncpy(key->name, tmp.keyName.name, XkbKeyNameLength);
+    }
+    else
+    {
+        info->errorCount++;
+        return ReportBadField("key", field, keyText(key));
+    }
+    return True;
+}
+
+static int
+SetGeometryProperty(GeometryInfo * info, char *property, ExprDef * value)
+{
+    PropertyInfo pi;
+    ExprResult result;
+
+    InitPropertyInfo(&pi, info);
+    pi.name = property;
+    if (!ExprResolveString(value, &result, NULL, NULL))
+    {
+        info->errorCount++;
+        ERROR("Property values must be type string\n");
+        ACTION1("Ignoring illegal definition of \"%s\" property\n", property);
+        return False;
+    }
+    pi.value = result.str;
+    return AddProperty(info, &pi);
+}
+
+static int
+HandleGeometryVar(VarDef * stmt, XkbDescPtr xkb, GeometryInfo * info)
+{
+    ExprResult elem, field, tmp;
+    ExprDef *ndx;
+    DoodadInfo *di;
+    Atom *pField;
+
+    if (ExprResolveLhs(stmt->name, &elem, &field, &ndx) == 0)
+        return 0;               /* internal error, already reported */
+    if (elem.str && (uStrCaseCmp(elem.str, "shape") == 0))
+        return SetShapeField(NULL, field.str, ndx, stmt->value, info);
+    if (elem.str && (uStrCaseCmp(elem.str, "key") == 0))
+        return SetKeyField(&info->dfltSection.dfltRow.dfltKey,
+                           field.str, ndx, stmt->value, info);
+    if (elem.str && (uStrCaseCmp(elem.str, "row") == 0))
+        return SetRowField(&info->dfltSection.dfltRow, field.str, ndx,
+                           stmt->value, info);
+    if (elem.str && (uStrCaseCmp(elem.str, "section") == 0))
+    {
+        return SetSectionField(&info->dfltSection, field.str, ndx,
+                               stmt->value, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "property") == 0))
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            ERROR1("The %s geometry property is not an array\n", field.str);
+            ACTION("Ignoring illegal property definition\n");
+            return False;
+        }
+        return SetGeometryProperty(info, field.str, stmt->value);
+    }
+    if (elem.str
+        && ((di = FindDfltDoodadByTypeName(elem.str, NULL, info)) != NULL))
+    {
+        return SetDoodadField(di, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "solid") == 0))
+    {
+        DoodadInfo *dflt;
+        dflt = FindDoodadByType(info->dfltDoodads, XkbSolidDoodad);
+        if (dflt == NULL)
+            dflt = NextDfltDoodad(NULL, info);
+        return SetDoodadField(dflt, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "outline") == 0))
+    {
+        DoodadInfo *dflt;
+        dflt = FindDoodadByType(info->dfltDoodads, XkbOutlineDoodad);
+        if (dflt == NULL)
+            dflt = NextDfltDoodad(NULL, info);
+        return SetDoodadField(dflt, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "text") == 0))
+    {
+        DoodadInfo *dflt;
+        dflt = FindDoodadByType(info->dfltDoodads, XkbTextDoodad);
+        if (dflt == NULL)
+            dflt = NextDfltDoodad(NULL, info);
+        return SetDoodadField(dflt, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "indicator") == 0))
+    {
+        DoodadInfo *dflt;
+        dflt = FindDoodadByType(info->dfltDoodads, XkbIndicatorDoodad);
+        if (dflt == NULL)
+            dflt = NextDfltDoodad(NULL, info);
+        return SetDoodadField(dflt, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str && (uStrCaseCmp(elem.str, "logo") == 0))
+    {
+        DoodadInfo *dflt;
+        dflt = FindDoodadByType(info->dfltDoodads, XkbLogoDoodad);
+        if (dflt == NULL)
+            dflt = NextDfltDoodad(NULL, info);
+        return SetDoodadField(dflt, field.str, ndx, stmt->value, NULL, info);
+    }
+    if (elem.str)
+    {
+        WARN("Assignment to field of unknown element\n");
+        ACTION2("No value assigned to %s.%s\n", elem.str, field.str);
+        return False;
+    }
+
+    if ((uStrCaseCmp(field.str, "width") == 0) ||
+        (uStrCaseCmp(field.str, "widthmm") == 0))
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard", field.str, "geometry");
+        }
+        if (!ExprResolveFloat(stmt->value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard", field.str, "geometry", "number");
+        }
+        if (tmp.ival < 1)
+        {
+            WARN("Keyboard width must be positive\n");
+            ACTION1("Ignoring illegal keyboard width %s\n",
+                    XkbGeomFPText(tmp.ival, XkbMessage));
+            return True;
+        }
+        if (info->widthMM != 0)
+        {
+            WARN("Keyboard width multiply defined\n");
+            ACTION1("Using last definition (%s),",
+                    XkbGeomFPText(tmp.ival, XkbMessage));
+            INFO1(" ignoring first (%s)\n",
+                  XkbGeomFPText(info->widthMM, XkbMessage));
+        }
+        info->widthMM = tmp.ival;
+        return True;
+    }
+    else if ((uStrCaseCmp(field.str, "height") == 0) ||
+             (uStrCaseCmp(field.str, "heightmm") == 0))
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard", field.str, "geometry");
+        }
+        if (!ExprResolveFloat(stmt->value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard", field.str, "geometry", "number");
+        }
+        if (tmp.ival < 1)
+        {
+            WARN("Keyboard height must be positive\n");
+            ACTION1("Ignoring illegal keyboard height %s\n",
+                    XkbGeomFPText(tmp.ival, XkbMessage));
+            return True;
+        }
+        if (info->heightMM != 0)
+        {
+            WARN("Keyboard height multiply defined\n");
+            ACTION1("Using last definition (%s),",
+                    XkbGeomFPText(tmp.ival, XkbMessage));
+            INFO1(" ignoring first (%s)\n",
+                  XkbGeomFPText(info->heightMM, XkbMessage));
+        }
+        info->heightMM = tmp.ival;
+        return True;
+    }
+    else if (uStrCaseCmp(field.str, "font") == 0)
+    {
+        pField = &info->font;
+    }
+    else if ((uStrCaseCmp(field.str, "fontslant") == 0) ||
+             (uStrCaseCmp(field.str, "slant") == 0))
+    {
+        pField = &info->fontSlant;
+    }
+    else if ((uStrCaseCmp(field.str, "fontweight") == 0) ||
+             (uStrCaseCmp(field.str, "weight") == 0))
+    {
+        pField = &info->fontWeight;
+    }
+    else if ((uStrCaseCmp(field.str, "fontwidth") == 0) ||
+             (uStrCaseCmp(field.str, "setwidth") == 0))
+    {
+        pField = &info->fontWeight;
+    }
+    else if ((uStrCaseCmp(field.str, "fontencoding") == 0) ||
+             (uStrCaseCmp(field.str, "encoding") == 0))
+    {
+        pField = &info->fontEncoding;
+    }
+    else if ((uStrCaseCmp(field.str, "xfont") == 0) ||
+             (uStrCaseCmp(field.str, "xfontname") == 0))
+    {
+        pField = &info->fontSpec;
+    }
+    else if (uStrCaseCmp(field.str, "fontsize") == 0)
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard", field.str, "geometry");
+        }
+        if (!ExprResolveFloat(stmt->value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard", field.str, "geometry", "number");
+        }
+        if ((tmp.ival < 40) || (tmp.ival > 2550))
+        {
+            info->errorCount++;
+            ERROR1("Illegal font size %d (must be 4..255)\n", tmp.ival);
+            ACTION("Ignoring font size in keyboard geometry\n");
+            return False;
+        }
+        info->fontSize = tmp.ival;
+        return True;
+    }
+    else if ((uStrCaseCmp(field.str, "color") == 0) ||
+             (uStrCaseCmp(field.str, "basecolor") == 0))
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard", field.str, "geometry");
+        }
+        if (!ExprResolveString(stmt->value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard", field.str, "geometry", "string");
+        }
+        info->baseColor = XkbInternAtom(NULL, tmp.str, False);
+        return True;
+    }
+    else if (uStrCaseCmp(field.str, "labelcolor") == 0)
+    {
+        if (ndx != NULL)
+        {
+            info->errorCount++;
+            return ReportNotArray("keyboard", field.str, "geometry");
+        }
+        if (!ExprResolveString(stmt->value, &tmp, NULL, NULL))
+        {
+            info->errorCount++;
+            return ReportBadType("keyboard", field.str, "geometry", "string");
+        }
+        info->labelColor = XkbInternAtom(NULL, tmp.str, False);
+        return True;
+    }
+    else
+    {
+        return SetGeometryProperty(info, field.str, stmt->value);
+    }
+
+    if (ndx != NULL)
+    {
+        info->errorCount++;
+        return ReportNotArray("keyboard", field.str, "geometry");
+    }
+    if (!ExprResolveString(stmt->value, &tmp, NULL, NULL))
+    {
+        info->errorCount++;
+        return ReportBadType("keyboard", field.str, "geometry", "string");
+    }
+    *pField = XkbInternAtom(NULL, tmp.str, False);
+    return True;
+}
+
+/***====================================================================***/
+
+static Bool
+HandleShapeBody(ShapeDef * def, ShapeInfo * si, unsigned merge,
+                GeometryInfo * info)
+{
+    OutlineDef *ol;
+    int nOut, nPt;
+    XkbOutlinePtr outline;
+    ExprDef *pt;
+
+    if (def->nOutlines < 1)
+    {
+        WARN1("Shape \"%s\" has no outlines\n", shText(info->dpy, si));
+        ACTION("Definition ignored\n");
+        return True;
+    }
+    si->nOutlines = def->nOutlines;
+    si->outlines = uTypedCalloc(def->nOutlines, XkbOutlineRec);
+    if (!si->outlines)
+    {
+        ERROR1("Couldn't allocate outlines for \"%s\"\n",
+               shText(info->dpy, si));
+        ACTION("Definition ignored\n");
+        info->errorCount++;
+        return False;
+    }
+    for (nOut = 0, ol = def->outlines; ol != NULL;
+         ol = (OutlineDef *) ol->common.next)
+    {
+        if (ol->nPoints < 1)
+        {
+            SetShapeField(si, XkbAtomGetString(NULL, ol->field), NULL,
+                          ol->points, info);
+            continue;
+        }
+        outline = NULL;
+        outline = &si->outlines[nOut++];
+        outline->num_points = ol->nPoints;
+        outline->corner_radius = si->dfltCornerRadius;
+        outline->points = uTypedCalloc(ol->nPoints, XkbPointRec);
+        if (!outline->points)
+        {
+            ERROR1("Can't allocate points for \"%s\"\n",
+                   shText(info->dpy, si));
+            ACTION("Definition ignored\n");
+            info->errorCount++;
+            return False;
+        }
+        for (nPt = 0, pt = ol->points; pt != NULL;
+             pt = (ExprDef *) pt->common.next)
+        {
+            outline->points[nPt].x = pt->value.coord.x;
+            outline->points[nPt].y = pt->value.coord.y;
+            nPt++;
+        }
+        if (ol->field != None)
+        {
+            char *str = XkbAtomText(NULL, ol->field, XkbMessage);
+            if ((uStrCaseCmp(str, "approximation") == 0) ||
+                (uStrCaseCmp(str, "approx") == 0))
+            {
+                if (si->approx == NULL)
+                    si->approx = outline;
+                else
+                {
+                    WARN1("Multiple approximations for \"%s\"\n",
+                          shText(info->dpy, si));
+                    ACTION("Treating all but the first as normal outlines\n");
+                }
+            }
+            else if (uStrCaseCmp(str, "primary") == 0)
+            {
+                if (si->primary == NULL)
+                    si->primary = outline;
+                else
+                {
+                    WARN1("Multiple primary outlines for \"%s\"\n",
+                          shText(info->dpy, si));
+                    ACTION("Treating all but the first as normal outlines\n");
+                }
+            }
+            else
+            {
+                WARN2("Unknown outline type %s for \"%s\"\n", str,
+                      shText(info->dpy, si));
+                ACTION("Treated as a normal outline\n");
+            }
+        }
+    }
+    if (nOut != si->nOutlines)
+    {
+        WSGO2("Expected %d outlines, got %d\n",
+              (unsigned int) si->nOutlines, nOut);
+        si->nOutlines = nOut;
+    }
+    return True;
+}
+
+static int
+HandleShapeDef(ShapeDef * def, XkbDescPtr xkb, unsigned merge,
+               GeometryInfo * info)
+{
+    ShapeInfo si;
+
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+
+    bzero(&si, sizeof(ShapeInfo));
+    si.defs.merge = merge;
+    si.name =
+        XkbInternAtom(info->dpy, XkbAtomGetString(NULL, def->name), False);
+    si.dfltCornerRadius = info->dfltCornerRadius;
+    if (!HandleShapeBody(def, &si, merge, info))
+        return False;
+    if (!AddShape(info, &si))
+        return False;
+    return True;
+}
+
+/***====================================================================***/
+
+static int
+HandleDoodadDef(DoodadDef * def,
+                unsigned merge, SectionInfo * si, GeometryInfo * info)
+{
+    ExprResult elem, field;
+    ExprDef *ndx;
+    DoodadInfo new;
+    VarDef *var;
+
+    if (def->common.stmtType == StmtIndicatorMapDef)
+    {
+        def->common.stmtType = StmtDoodadDef;
+        def->type = XkbIndicatorDoodad;
+    }
+    InitDoodadInfo(&new, def->type, si, info);
+    new.name =
+        XkbInternAtom(info->dpy, XkbAtomGetString(NULL, def->name), False);
+    for (var = def->body; var != NULL; var = (VarDef *) var->common.next)
+    {
+        if (ExprResolveLhs(var->name, &elem, &field, &ndx) == 0)
+            return 0;           /* internal error, already reported */
+        if (elem.str != NULL)
+        {
+            WARN1("Assignment to field of unknown element in doodad %s\n",
+                  ddText(info->dpy, &new));
+            ACTION2("No value assigned to %s.%s\n", elem.str, field.str);
+        }
+        else if (!SetDoodadField(&new, field.str, ndx, var->value, si, info))
+            return False;
+    }
+    if (!AddDoodad(si, info, &new))
+        return False;
+    ClearDoodadInfo(&new);
+    return True;
+}
+
+/***====================================================================***/
+
+static int
+HandleOverlayDef(OverlayDef * def,
+                 unsigned merge, SectionInfo * si, GeometryInfo * info)
+{
+    OverlayKeyDef *keyDef;
+    OverlayKeyInfo *key;
+    OverlayInfo ol;
+
+    if ((def->nKeys < 1) && (warningLevel > 3))
+    {
+        WARN2("Overlay \"%s\" in section \"%s\" has no keys\n",
+              XkbAtomText(NULL, def->name, XkbMessage), scText(info->dpy,
+                                                               si));
+        ACTION("Overlay ignored\n");
+        return True;
+    }
+    bzero(&ol, sizeof(OverlayInfo));
+    ol.name =
+        XkbInternAtom(info->dpy, XkbAtomGetString(NULL, def->name), False);
+    for (keyDef = def->keys; keyDef;
+         keyDef = (OverlayKeyDef *) keyDef->common.next)
+    {
+        key = uTypedCalloc(1, OverlayKeyInfo);
+        if ((!key) && warningLevel > 0)
+        {
+            WSGO("Couldn't allocate OverlayKeyInfo\n");
+            ACTION2("Overlay %s for section %s will be incomplete\n",
+                    oiText(info->dpy, &ol), scText(info->dpy, si));
+            return False;
+        }
+        strncpy(key->over, keyDef->over, XkbKeyNameLength);
+        strncpy(key->under, keyDef->under, XkbKeyNameLength);
+        key->sectionRow = _GOK_UnknownRow;
+        key->overlayRow = _GOK_UnknownRow;
+        ol.keys = (OverlayKeyInfo *) AddCommonInfo(&ol.keys->defs,
+                                                   (CommonInfo *) key);
+        ol.nKeys++;
+    }
+    if (!AddOverlay(si, info, &ol))
+        return False;
+    ClearOverlayInfo(&ol);
+    return True;
+}
+
+/***====================================================================***/
+
+static Bool
+HandleComplexKey(KeyDef * def, KeyInfo * key, GeometryInfo * info)
+{
+    RowInfo *row;
+    ExprDef *expr;
+
+    row = key->row;
+    for (expr = def->expr; expr != NULL; expr = (ExprDef *) expr->common.next)
+    {
+        if (expr->op == OpAssign)
+        {
+            ExprResult elem, f;
+            ExprDef *ndx;
+            if (ExprResolveLhs(expr->value.binary.left, &elem, &f, &ndx) == 0)
+                return False;   /* internal error, already reported */
+            if ((elem.str == NULL) || (uStrCaseCmp(elem.str, "key") == 0))
+            {
+                if (!SetKeyField
+                    (key, f.str, ndx, expr->value.binary.right, info))
+                    return False;
+            }
+            else
+            {
+                ERROR("Illegal element used in a key definition\n");
+                ACTION2("Assignment to %s.%s ignored\n", elem.str, f.str);
+                return False;
+            }
+        }
+        else
+        {
+            switch (expr->type)
+            {
+            case TypeInt:
+            case TypeFloat:
+                if (!SetKeyField(key, "gap", NULL, expr, info))
+                    return False;
+                break;
+            case TypeString:
+                if (!SetKeyField(key, "shape", NULL, expr, info))
+                    return False;
+                break;
+            case TypeKeyName:
+                if (!SetKeyField(key, "name", NULL, expr, info))
+                    return False;
+                break;
+            default:
+                ERROR("Cannot determine field for unnamed expression\n");
+                ACTION3("Ignoring key %d in row %d of section %s\n",
+                        row->nKeys + 1, row->section->nRows + 1,
+                        rowText(info->dpy, row));
+                return False;
+            }
+        }
+    }
+    return True;
+}
+
+static Bool
+HandleRowBody(RowDef * def, RowInfo * row, unsigned merge,
+              GeometryInfo * info)
+{
+    KeyDef *keyDef;
+
+    if ((def->nKeys < 1) && (warningLevel > 3))
+    {
+        ERROR1("Row in section %s has no keys\n", rowText(info->dpy, row));
+        ACTION("Section ignored\n");
+        return True;
+    }
+    for (keyDef = def->keys; keyDef != NULL;
+         keyDef = (KeyDef *) keyDef->common.next)
+    {
+        if (keyDef->common.stmtType == StmtVarDef)
+        {
+            VarDef *var = (VarDef *) keyDef;
+            ExprResult elem, field;
+            ExprDef *ndx;
+            if (ExprResolveLhs(var->name, &elem, &field, &ndx) == 0)
+                return 0;       /* internal error, already reported */
+            if ((elem.str == NULL) || (uStrCaseCmp(elem.str, "row") == 0))
+            {
+                if (!SetRowField(row, field.str, ndx, var->value, info))
+                    return False;
+            }
+            else if (uStrCaseCmp(elem.str, "key") == 0)
+            {
+                if (!SetKeyField
+                    (&row->dfltKey, field.str, ndx, var->value, info))
+                    return False;
+            }
+            else
+            {
+                WARN("Assignment to field of unknown element in row\n");
+                ACTION2("No value assigned to %s.%s\n", elem.str, field.str);
+            }
+        }
+        else if (keyDef->common.stmtType == StmtKeyDef)
+        {
+            KeyInfo key;
+            InitKeyInfo(&key, row, info);
+            if (keyDef->name != NULL)
+            {
+                int len = strlen(keyDef->name);
+                if ((len < 1) || (len > XkbKeyNameLength))
+                {
+                    ERROR2("Illegal name %s for key in section %s\n",
+                           keyDef->name, rowText(info->dpy, row));
+                    ACTION("Section not compiled\n");
+                    return False;
+                }
+                bzero(key.name, XkbKeyNameLength + 1);
+                strncpy(key.name, keyDef->name, XkbKeyNameLength);
+                key.defs.defined |= _GK_Name;
+            }
+            else if (!HandleComplexKey(keyDef, &key, info))
+                return False;
+            if (!AddKey(row, &key))
+                return False;
+        }
+        else
+        {
+            WSGO1("Unexpected statement (type %d) in row body\n",
+                  keyDef->common.stmtType);
+            return False;
+        }
+    }
+    return True;
+}
+
+static Bool
+HandleSectionBody(SectionDef * def,
+                  SectionInfo * si, unsigned merge, GeometryInfo * info)
+{
+    RowDef *rowDef;
+    DoodadInfo *di;
+
+    for (rowDef = def->rows; rowDef != NULL;
+         rowDef = (RowDef *) rowDef->common.next)
+    {
+        if (rowDef->common.stmtType == StmtVarDef)
+        {
+            VarDef *var = (VarDef *) rowDef;
+            ExprResult elem, field;
+            ExprDef *ndx;
+            if (ExprResolveLhs(var->name, &elem, &field, &ndx) == 0)
+                return 0;       /* internal error, already reported */
+            if ((elem.str == NULL) || (uStrCaseCmp(elem.str, "section") == 0))
+            {
+                if (!SetSectionField(si, field.str, ndx, var->value, info))
+                    return False;
+            }
+            else if (uStrCaseCmp(elem.str, "row") == 0)
+            {
+                if (!SetRowField
+                    (&si->dfltRow, field.str, ndx, var->value, info))
+                    return False;
+            }
+            else if (uStrCaseCmp(elem.str, "key") == 0)
+            {
+                if (!SetKeyField(&si->dfltRow.dfltKey, field.str, ndx,
+                                 var->value, info))
+                    return False;
+            }
+            else if ((di =
+                      FindDfltDoodadByTypeName(elem.str, si, info)) != NULL)
+            {
+                if (!SetDoodadField(di, field.str, ndx, var->value, si, info))
+                    return False;
+            }
+            else
+            {
+                WARN("Assignment to field of unknown element in section\n");
+                ACTION2("No value assigned to %s.%s\n", elem.str, field.str);
+            }
+        }
+        else if (rowDef->common.stmtType == StmtRowDef)
+        {
+            RowInfo row;
+            InitRowInfo(&row, si, info);
+            if (!HandleRowBody(rowDef, &row, merge, info))
+                return False;
+            if (!AddRow(si, &row))
+                return False;
+/*         ClearRowInfo(&row,info);*/
+        }
+        else if ((rowDef->common.stmtType == StmtDoodadDef) ||
+                 (rowDef->common.stmtType == StmtIndicatorMapDef))
+        {
+            if (!HandleDoodadDef((DoodadDef *) rowDef, merge, si, info))
+                return False;
+        }
+        else if (rowDef->common.stmtType == StmtOverlayDef)
+        {
+            if (!HandleOverlayDef((OverlayDef *) rowDef, merge, si, info))
+                return False;
+        }
+        else
+        {
+            WSGO1("Unexpected statement (type %d) in section body\n",
+                  rowDef->common.stmtType);
+            return False;
+        }
+    }
+    if (si->nRows != def->nRows)
+    {
+        WSGO2("Expected %d rows, found %d\n", (unsigned int) def->nRows,
+              (unsigned int) si->nRows);
+        ACTION1("Definition of section %s might be incorrect\n",
+                scText(info->dpy, si));
+    }
+    return True;
+}
+
+static int
+HandleSectionDef(SectionDef * def,
+                 XkbDescPtr xkb, unsigned merge, GeometryInfo * info)
+{
+    SectionInfo si;
+    char *str;
+
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+    InitSectionInfo(&si, info);
+    si.defs.merge = merge;
+    str = XkbAtomGetString(NULL, def->name);
+    if ((str == NULL) || (strlen(str) < 1))
+    {
+        ERROR("Section defined without a name\n");
+        ACTION("Definition ignored\n");
+        return False;
+    }
+    si.name =
+        XkbInternAtom(info->dpy, XkbAtomGetString(NULL, def->name), False);
+    if (!HandleSectionBody(def, &si, merge, info))
+        return False;
+    if (!AddSection(info, &si))
+        return False;
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+HandleGeometryFile(XkbFile * file,
+                   XkbDescPtr xkb, unsigned merge, GeometryInfo * info)
+{
+    ParseCommon *stmt;
+    char *failWhat;
+
+    if (merge == MergeDefault)
+        merge = MergeAugment;
+    info->name = uStringDup(file->name);
+    stmt = file->defs;
+    while (stmt)
+    {
+        failWhat = NULL;
+        switch (stmt->stmtType)
+        {
+        case StmtInclude:
+            if (!HandleIncludeGeometry((IncludeStmt *) stmt, xkb, info,
+                                       HandleGeometryFile))
+                info->errorCount++;
+            break;
+        case StmtKeyAliasDef:
+            if (!HandleAliasDef((KeyAliasDef *) stmt,
+                                merge, info->fileID, &info->aliases))
+            {
+                info->errorCount++;
+            }
+            break;
+        case StmtVarDef:
+            if (!HandleGeometryVar((VarDef *) stmt, xkb, info))
+                info->errorCount++;
+            break;
+        case StmtShapeDef:
+            if (!HandleShapeDef((ShapeDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtSectionDef:
+            if (!HandleSectionDef((SectionDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtIndicatorMapDef:
+        case StmtDoodadDef:
+            if (!HandleDoodadDef((DoodadDef *) stmt, merge, NULL, info))
+                info->errorCount++;
+            break;
+        case StmtVModDef:
+            if (!failWhat)
+                failWhat = "virtual modfier";
+        case StmtInterpDef:
+            if (!failWhat)
+                failWhat = "symbol interpretation";
+        case StmtGroupCompatDef:
+            if (!failWhat)
+                failWhat = "group compatibility map";
+        case StmtKeycodeDef:
+            if (!failWhat)
+                failWhat = "key name";
+            ERROR("Interpretation files may not include other types\n");
+            ACTION1("Ignoring %s definition.\n", failWhat);
+            info->errorCount++;
+            break;
+        default:
+            WSGO1("Unexpected statement type %d in HandleGeometryFile\n",
+                  stmt->stmtType);
+            break;
+        }
+        stmt = stmt->next;
+        if (info->errorCount > 10)
+        {
+#ifdef NOISY
+            ERROR("Too many errors\n");
+#endif
+            ACTION1("Abandoning geometry file \"%s\"\n", file->topName);
+            break;
+        }
+    }
+    return;
+}
+
+/***====================================================================***/
+
+static Bool
+CopyShapeDef(Display * dpy, XkbGeometryPtr geom, ShapeInfo * si)
+{
+    register int i, n;
+    XkbShapePtr shape;
+    XkbOutlinePtr old_outline, outline;
+    Atom name;
+
+    si->index = geom->num_shapes;
+    name = XkbInternAtom(dpy, XkbAtomGetString(NULL, si->name), False);
+    shape = XkbAddGeomShape(geom, name, si->nOutlines);
+    if (!shape)
+    {
+        WSGO("Couldn't allocate shape in geometry\n");
+        ACTION1("Shape %s not compiled\n", shText(dpy, si));
+        return False;
+    }
+    old_outline = si->outlines;
+    for (i = 0; i < si->nOutlines; i++, old_outline++)
+    {
+        outline = XkbAddGeomOutline(shape, old_outline->num_points);
+        if (!outline)
+        {
+            WSGO("Couldn't allocate outline in shape\n");
+            ACTION1("Shape %s is incomplete\n", shText(dpy, si));
+            return False;
+        }
+        n = old_outline->num_points;
+        memcpy(outline->points, old_outline->points, n * sizeof(XkbPointRec));
+        outline->num_points = old_outline->num_points;
+        outline->corner_radius = old_outline->corner_radius;
+    }
+    if (si->approx)
+    {
+        n = (si->approx - si->outlines);
+        shape->approx = &shape->outlines[n];
+    }
+    if (si->primary)
+    {
+        n = (si->primary - si->outlines);
+        shape->primary = &shape->outlines[n];
+    }
+    XkbComputeShapeBounds(shape);
+    return True;
+}
+
+static Bool
+VerifyDoodadInfo(DoodadInfo * di, GeometryInfo * info)
+{
+    if ((di->defs.defined & (_GD_Top | _GD_Left)) != (_GD_Top | _GD_Left))
+    {
+        if (warningLevel < 9)
+        {
+            ERROR1("No position defined for doodad %s\n",
+                   ddText(info->dpy, di));
+            ACTION("Illegal doodad ignored\n");
+            return False;
+        }
+    }
+    if ((di->defs.defined & _GD_Priority) == 0)
+    {
+        /* calculate priority -- should be just above previous doodad/row */
+    }
+    switch (di->type)
+    {
+    case XkbOutlineDoodad:
+    case XkbSolidDoodad:
+        if ((di->defs.defined & _GD_Shape) == 0)
+        {
+            ERROR2("No shape defined for %s doodad %s\n",
+                   (di->type == XkbOutlineDoodad ? "outline" : "filled"),
+                   ddText(info->dpy, di));
+            ACTION("Incomplete definition ignored\n");
+            return False;
+        }
+        else
+        {
+            ShapeInfo *si;
+            si = FindShape(info, di->shape,
+                           (di->type ==
+                            XkbOutlineDoodad ? "outline doodad" :
+                            "solid doodad"), ddText(info->dpy, di));
+            if (si)
+                di->shape = si->name;
+            else
+            {
+                ERROR1("No legal shape for %s\n", ddText(info->dpy, di));
+                ACTION("Incomplete definition ignored\n");
+                return False;
+            }
+        }
+        if ((di->defs.defined & _GD_Color) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No color for doodad %s\n", ddText(info->dpy, di));
+                ACTION("Using black\n");
+            }
+            di->color = XkbInternAtom(NULL, "black", False);
+        }
+        break;
+    case XkbTextDoodad:
+        if ((di->defs.defined & _GD_Text) == 0)
+        {
+            ERROR1("No text specified for text doodad %s\n",
+                   ddText(info->dpy, di));
+            ACTION("Illegal doodad definition ignored\n");
+            return False;
+        }
+        if ((di->defs.defined & _GD_Angle) == 0)
+            di->angle = 0;
+        if ((di->defs.defined & _GD_Color) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No color specified for doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION("Using black\n");
+            }
+            di->color = XkbInternAtom(NULL, "black", False);
+        }
+        if ((di->defs.defined & _GD_FontSpec) != 0)
+        {
+            if ((di->defs.defined & _GD_FontParts) == 0)
+                return True;
+            if (warningLevel < 9)
+            {
+                WARN1
+                    ("Text doodad %s has full and partial font definition\n",
+                     ddText(info->dpy, di));
+                ACTION("Full specification ignored\n");
+            }
+            di->defs.defined &= ~_GD_FontSpec;
+            di->fontSpec = None;
+        }
+        if ((di->defs.defined & _GD_Font) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No font specified for doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_FONT);
+            }
+            di->font = XkbInternAtom(NULL, DFLT_FONT, False);
+        }
+        if ((di->defs.defined & _GD_FontSlant) == 0)
+        {
+            if (warningLevel > 7)
+            {
+                WARN1("No font slant for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_SLANT);
+            }
+            di->fontSlant = XkbInternAtom(NULL, DFLT_SLANT, False);
+        }
+        if ((di->defs.defined & _GD_FontWeight) == 0)
+        {
+            if (warningLevel > 7)
+            {
+                WARN1("No font weight for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_WEIGHT);
+            }
+            di->fontWeight = XkbInternAtom(NULL, DFLT_WEIGHT, False);
+        }
+        if ((di->defs.defined & _GD_FontSetWidth) == 0)
+        {
+            if (warningLevel > 9)
+            {
+                WARN1("No font set width for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_SET_WIDTH);
+            }
+            di->fontSetWidth = XkbInternAtom(NULL, DFLT_SET_WIDTH, False);
+        }
+        if ((di->defs.defined & _GD_FontVariant) == 0)
+        {
+            if (warningLevel > 9)
+            {
+                WARN1("No font variant for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_VARIANT);
+            }
+            di->fontVariant = XkbInternAtom(NULL, DFLT_VARIANT, False);
+        }
+        if ((di->defs.defined & _GD_FontEncoding) == 0)
+        {
+            if (warningLevel > 7)
+            {
+                WARN1("No font encoding for doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using \"%s\"\n", DFLT_ENCODING);
+            }
+            di->fontEncoding = XkbInternAtom(NULL, DFLT_ENCODING, False);
+        }
+        if ((di->defs.defined & _GD_FontSize) == 0)
+        {
+            if (warningLevel > 7)
+            {
+                WARN1("No font size for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using %s point text\n",
+                        XkbGeomFPText(DFLT_SIZE, XkbMessage));
+            }
+            di->fontSize = DFLT_SIZE;
+        }
+        if ((di->defs.defined & _GD_Height) == 0)
+        {
+            unsigned size, nLines;
+            char *tmp;
+            size = (di->fontSize * 120) / 100;
+            size = (size * 254) / 720;  /* convert to mm/10 */
+            for (nLines = 1, tmp = XkbAtomGetString(NULL, di->text); *tmp;
+                 tmp++)
+            {
+                if (*tmp == '\n')
+                    nLines++;
+            }
+            size *= nLines;
+            if (warningLevel > 5)
+            {
+                WARN1("No height for text doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION1("Using calculated height %s millimeters\n",
+                        XkbGeomFPText(size, XkbMessage));
+            }
+            di->height = size;
+        }
+        if ((di->defs.defined & _GD_Width) == 0)
+        {
+            unsigned width, tmp;
+            char *str;
+            width = tmp = 0;
+            for (str = XkbAtomGetString(NULL, di->text); *str; str++)
+            {
+                if (*str != '\n')
+                    tmp++;
+                else
+                {
+                    if (tmp > width)
+                        width = tmp;
+                    tmp = 1;
+                }
+            }
+            if (width == 0)
+                width = tmp;
+            width *= (di->height * 2) / 3;
+            if (warningLevel > 5)
+            {
+                WARN1("No width for text doodad %s\n", ddText(info->dpy, di));
+                ACTION1("Using calculated width %s millimeters\n",
+                        XkbGeomFPText(width, XkbMessage));
+            }
+            di->width = width;
+        }
+        break;
+    case XkbIndicatorDoodad:
+        if ((di->defs.defined & _GD_Shape) == 0)
+        {
+            ERROR1("No shape defined for indicator doodad %s\n",
+                   ddText(info->dpy, di));
+            ACTION("Incomplete definition ignored\n");
+            return False;
+        }
+        else
+        {
+            ShapeInfo *si;
+            si = FindShape(info, di->shape, "indicator doodad",
+                           ddText(info->dpy, di));
+            if (si)
+                di->shape = si->name;
+            else
+            {
+                ERROR1("No legal shape for doodad %s\n",
+                       ddText(info->dpy, di));
+                ACTION("Incomplete definition ignored\n");
+                return False;
+            }
+        }
+        if ((di->defs.defined & _GD_Color) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No \"on\" color for indicator doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION("Using green\n");
+            }
+            di->color = XkbInternAtom(NULL, "green", False);
+        }
+        if ((di->defs.defined & _GD_OffColor) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No \"off\" color for indicator doodad %s\n",
+                      ddText(info->dpy, di));
+                ACTION("Using black\n");
+            }
+            di->offColor = XkbInternAtom(NULL, "black", False);
+        }
+        break;
+    case XkbLogoDoodad:
+        if (di->logoName == NULL)
+        {
+            ERROR1("No logo name defined for logo doodad %s\n",
+                   ddText(info->dpy, di));
+            ACTION("Incomplete definition ignored\n");
+            return False;
+        }
+        if ((di->defs.defined & _GD_Shape) == 0)
+        {
+            ERROR1("No shape defined for logo doodad %s\n",
+                   ddText(info->dpy, di));
+            ACTION("Incomplete definition ignored\n");
+            return False;
+        }
+        else
+        {
+            ShapeInfo *si;
+            si = FindShape(info, di->shape, "logo doodad",
+                           ddText(info->dpy, di));
+            if (si)
+                di->shape = si->name;
+            else
+            {
+                ERROR1("No legal shape for %s\n", ddText(info->dpy, di));
+                ACTION("Incomplete definition ignored\n");
+                return False;
+            }
+        }
+        if ((di->defs.defined & _GD_Color) == 0)
+        {
+            if (warningLevel > 5)
+            {
+                WARN1("No color for doodad %s\n", ddText(info->dpy, di));
+                ACTION("Using black\n");
+            }
+            di->color = XkbInternAtom(NULL, "black", False);
+        }
+        break;
+    default:
+        WSGO1("Uknown doodad type %d in VerifyDoodad\n",
+              (unsigned int) di->type);
+        return False;
+    }
+    return True;
+}
+
+#define        FONT_TEMPLATE   "-*-%s-%s-%s-%s-%s-*-%d-*-*-*-*-%s"
+
+static char *
+FontFromParts(Atom fontTok,
+              Atom weightTok,
+              Atom slantTok,
+              Atom setWidthTok, Atom varTok, int size, Atom encodingTok)
+{
+    int totalSize;
+    char *font, *weight, *slant, *setWidth, *variant, *encoding;
+    char *rtrn;
+
+    font = (fontTok != None ? XkbAtomGetString(NULL, fontTok) : DFLT_FONT);
+    weight =
+        (weightTok != None ? XkbAtomGetString(NULL, weightTok) : DFLT_WEIGHT);
+    slant =
+        (slantTok != None ? XkbAtomGetString(NULL, slantTok) : DFLT_SLANT);
+    setWidth =
+        (setWidthTok !=
+         None ? XkbAtomGetString(NULL, setWidthTok) : DFLT_SET_WIDTH);
+    variant =
+        (varTok != None ? XkbAtomGetString(NULL, varTok) : DFLT_VARIANT);
+    encoding =
+        (encodingTok !=
+         None ? XkbAtomGetString(NULL, encodingTok) : DFLT_ENCODING);
+    if (size == 0)
+        size = DFLT_SIZE;
+    totalSize =
+        strlen(FONT_TEMPLATE) + strlen(font) + strlen(weight) + strlen(slant);
+    totalSize += strlen(setWidth) + strlen(variant) + strlen(encoding);
+    rtrn = uCalloc(totalSize, 1);
+    if (rtrn)
+    {
+        sprintf(rtrn, FONT_TEMPLATE, font, weight, slant, setWidth, variant,
+                size, encoding);
+    }
+    return rtrn;
+}
+
+static Bool
+CopyDoodadDef(XkbGeometryPtr geom,
+              XkbSectionPtr section, DoodadInfo * di, GeometryInfo * info)
+{
+    Atom name;
+    XkbDoodadPtr doodad;
+    XkbColorPtr color;
+    XkbShapePtr shape;
+    ShapeInfo *si;
+
+    if (!VerifyDoodadInfo(di, info))
+        return False;
+    name = XkbInternAtom(NULL, XkbAtomGetString(NULL, di->name), False);
+    doodad = XkbAddGeomDoodad(geom, section, name);
+    if (!doodad)
+    {
+        WSGO1("Couldn't allocate doodad in %s\n",
+              (section ? "section" : "geometry"));
+        ACTION1("Cannot copy doodad %s\n", ddText(info->dpy, di));
+        return False;
+    }
+    doodad->any.type = di->type;
+    doodad->any.priority = di->priority;
+    doodad->any.top = di->top;
+    doodad->any.left = di->left;
+    switch (di->type)
+    {
+    case XkbOutlineDoodad:
+    case XkbSolidDoodad:
+        si = FindShape(info, di->shape, NULL, NULL);
+        if (!si)
+            return False;
+        doodad->shape.angle = di->angle;
+        color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, di->color),
+                            geom->num_colors);
+        shape = &geom->shapes[si->index];
+        XkbSetShapeDoodadColor(geom, &doodad->shape, color);
+        XkbSetShapeDoodadShape(geom, &doodad->shape, shape);
+        break;
+    case XkbTextDoodad:
+        doodad->text.angle = di->angle;
+        doodad->text.width = di->width;
+        doodad->text.height = di->height;
+        if (di->fontSpec == None)
+            doodad->text.font = FontFromParts(di->font, di->fontWeight,
+                                              di->fontSlant,
+                                              di->fontSetWidth,
+                                              di->fontVariant, di->fontSize,
+                                              di->fontEncoding);
+        else
+            doodad->text.font = XkbAtomGetString(NULL, di->fontSpec);
+        doodad->text.text = XkbAtomGetString(NULL, di->text);
+        color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, di->color),
+                            geom->num_colors);
+        XkbSetTextDoodadColor(geom, &doodad->text, color);
+        break;
+    case XkbIndicatorDoodad:
+        si = FindShape(info, di->shape, NULL, NULL);
+        if (!si)
+            return False;
+        shape = &geom->shapes[si->index];
+        color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, di->color),
+                            geom->num_colors);
+        XkbSetIndicatorDoodadShape(geom, &doodad->indicator, shape);
+        XkbSetIndicatorDoodadOnColor(geom, &doodad->indicator, color);
+        color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, di->offColor),
+                            geom->num_colors);
+        XkbSetIndicatorDoodadOffColor(geom, &doodad->indicator, color);
+        break;
+    case XkbLogoDoodad:
+        si = FindShape(info, di->shape, NULL, NULL);
+        if (!si)
+            return False;
+        doodad->logo.angle = di->angle;
+        color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, di->color),
+                            geom->num_colors);
+        shape = &geom->shapes[si->index];
+        XkbSetLogoDoodadColor(geom, &doodad->logo, color);
+        XkbSetLogoDoodadShape(geom, &doodad->logo, shape);
+        doodad->logo.logo_name = di->logoName;
+        di->logoName = NULL;
+        break;
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+static Bool
+VerifyOverlayInfo(XkbGeometryPtr geom,
+                  XkbSectionPtr section,
+                  OverlayInfo * oi,
+                  GeometryInfo * info, short rowMap[256], short rowSize[256])
+{
+    register OverlayKeyInfo *ki, *next;
+    unsigned long oKey, uKey, sKey;
+    XkbRowPtr row;
+    XkbKeyPtr key;
+    int r, k;
+
+    /* find out which row each key is in */
+    for (ki = oi->keys; ki != NULL; ki = (OverlayKeyInfo *) ki->defs.next)
+    {
+        oKey = KeyNameToLong(ki->over);
+        uKey = KeyNameToLong(ki->under);
+        for (r = 0, row = section->rows; (r < section->num_rows) && oKey;
+             r++, row++)
+        {
+            for (k = 0, key = row->keys; (k < row->num_keys) && oKey;
+                 k++, key++)
+            {
+                sKey = KeyNameToLong(key->name.name);
+                if (sKey == oKey)
+                {
+                    if (warningLevel > 0)
+                    {
+                        WARN3
+                            ("Key %s in section \"%s\" and overlay \"%s\"\n",
+                             XkbKeyNameText(key->name.name,
+                                            XkbMessage),
+                             XkbAtomText(info->dpy, section->name,
+                                         XkbMessage),
+                             XkbAtomText(info->dpy, oi->name, XkbMessage));
+                        ACTION("Overlay definition ignored\n");
+                    }
+                    oKey = 0;
+                }
+                else if (sKey == uKey)
+                {
+                    ki->sectionRow = r;
+                    oKey = 0;
+                }
+            }
+        }
+        if ((ki->sectionRow == _GOK_UnknownRow) && (warningLevel > 0))
+        {
+            WARN3
+                ("Key %s not in \"%s\", but has an overlay key in \"%s\"\n",
+                 XkbKeyNameText(ki->under, XkbMessage),
+                 XkbAtomText(info->dpy, section->name, XkbMessage),
+                 XkbAtomText(info->dpy, oi->name, XkbMessage));
+            ACTION("Definition ignored\n");
+        }
+    }
+    /* now prune out keys that aren't in the section */
+    while ((oi->keys != NULL) && (oi->keys->sectionRow == _GOK_UnknownRow))
+    {
+        next = (OverlayKeyInfo *) oi->keys->defs.next;
+        uFree(oi->keys);
+        oi->keys = next;
+        oi->nKeys--;
+    }
+    for (ki = oi->keys; (ki != NULL) && (ki->defs.next != NULL); ki = next)
+    {
+        next = (OverlayKeyInfo *) ki->defs.next;
+        if (next->sectionRow == _GOK_UnknownRow)
+        {
+            ki->defs.next = next->defs.next;
+            oi->nKeys--;
+            uFree(next);
+            next = (OverlayKeyInfo *) ki->defs.next;
+        }
+    }
+    if (oi->nKeys < 1)
+    {
+        ERROR2("Overlay \"%s\" for section \"%s\" has no legal keys\n",
+               XkbAtomText(info->dpy, oi->name, XkbMessage),
+               XkbAtomText(info->dpy, section->name, XkbMessage));
+        ACTION("Overlay definition ignored\n");
+        return False;
+    }
+    /* now figure out how many rows are defined for the overlay */
+    bzero(rowSize, sizeof(short) * 256);
+    for (k = 0; k < 256; k++)
+    {
+        rowMap[k] = -1;
+    }
+    oi->nRows = 0;
+    for (ki = oi->keys; ki != NULL; ki = (OverlayKeyInfo *) ki->defs.next)
+    {
+        if (rowMap[ki->sectionRow] == -1)
+            rowMap[ki->sectionRow] = oi->nRows++;
+        ki->overlayRow = rowMap[ki->sectionRow];
+        rowSize[ki->overlayRow]++;
+    }
+    return True;
+}
+
+static Bool
+CopyOverlayDef(XkbGeometryPtr geom,
+               XkbSectionPtr section, OverlayInfo * oi, GeometryInfo * info)
+{
+    Atom name;
+    XkbOverlayPtr ol;
+    XkbOverlayRowPtr row;
+    XkbOverlayKeyPtr key;
+    OverlayKeyInfo *ki;
+    short rowMap[256], rowSize[256];
+    int i;
+
+    if (!VerifyOverlayInfo(geom, section, oi, info, rowMap, rowSize))
+        return False;
+    name = XkbInternAtom(NULL, XkbAtomGetString(NULL, oi->name), False);
+    ol = XkbAddGeomOverlay(section, name, oi->nRows);
+    if (!ol)
+    {
+        WSGO2("Couldn't add overlay \"%s\" to section \"%s\"\n",
+              XkbAtomText(info->dpy, name, XkbMessage),
+              XkbAtomText(info->dpy, section->name, XkbMessage));
+        return False;
+    }
+    for (i = 0; i < oi->nRows; i++)
+    {
+        int tmp, row_under;
+        for (tmp = 0, row_under = -1;
+             (tmp < section->num_rows) && (row_under < 0); tmp++)
+        {
+            if (rowMap[tmp] == i)
+                row_under = tmp;
+        }
+        if (!XkbAddGeomOverlayRow(ol, row_under, rowSize[i]))
+        {
+            WSGO3
+                ("Can't add row %d to overlay \"%s\" of section \"%s\"\n",
+                 i, XkbAtomText(info->dpy, name, XkbMessage),
+                 XkbAtomText(info->dpy, section->name, XkbMessage));
+            return False;
+        }
+    }
+    for (ki = oi->keys; ki != NULL; ki = (OverlayKeyInfo *) ki->defs.next)
+    {
+        row = &ol->rows[ki->overlayRow];
+        key = &row->keys[row->num_keys++];
+        bzero(key, sizeof(XkbOverlayKeyRec));
+        strncpy(key->over.name, ki->over, XkbKeyNameLength);
+        strncpy(key->under.name, ki->under, XkbKeyNameLength);
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+static Bool
+CopySectionDef(XkbGeometryPtr geom, SectionInfo * si, GeometryInfo * info)
+{
+    XkbSectionPtr section;
+    XkbRowPtr row;
+    XkbKeyPtr key;
+    KeyInfo *ki;
+    RowInfo *ri;
+    Atom name;
+
+    name = XkbInternAtom(NULL, XkbAtomGetString(NULL, si->name), False);
+    section =
+        XkbAddGeomSection(geom, name, si->nRows, si->nDoodads, si->nOverlays);
+    if (section == NULL)
+    {
+        WSGO("Couldn't allocate section in geometry\n");
+        ACTION1("Section %s not compiled\n", scText(info->dpy, si));
+        return False;
+    }
+    section->top = si->top;
+    section->left = si->left;
+    section->width = si->width;
+    section->height = si->height;
+    section->angle = si->angle;
+    section->priority = si->priority;
+    for (ri = si->rows; ri != NULL; ri = (RowInfo *) ri->defs.next)
+    {
+        row = XkbAddGeomRow(section, ri->nKeys);
+        if (row == NULL)
+        {
+            WSGO("Couldn't allocate row in section\n");
+            ACTION1("Section %s is incomplete\n", scText(info->dpy, si));
+            return False;
+        }
+        row->top = ri->top;
+        row->left = ri->left;
+        row->vertical = ri->vertical;
+        for (ki = ri->keys; ki != NULL; ki = (KeyInfo *) ki->defs.next)
+        {
+            XkbColorPtr color;
+            if ((ki->defs.defined & _GK_Name) == 0)
+            {
+                ERROR3("Key %d of row %d in section %s has no name\n",
+                       (int) ki->index, (int) ri->index,
+                       scText(info->dpy, si));
+                ACTION1("Section %s ignored\n", scText(info->dpy, si));
+                return False;
+            }
+            key = XkbAddGeomKey(row);
+            if (key == NULL)
+            {
+                WSGO("Couldn't allocate key in row\n");
+                ACTION1("Section %s is incomplete\n", scText(info->dpy, si));
+                return False;
+            }
+            memcpy(key->name.name, ki->name, XkbKeyNameLength);
+            key->gap = ki->gap;
+            if (ki->shape == None)
+                key->shape_ndx = 0;
+            else
+            {
+                ShapeInfo *si;
+                si = FindShape(info, ki->shape, "key", keyText(ki));
+                if (!si)
+                    return False;
+                key->shape_ndx = si->index;
+            }
+            if (ki->color != None)
+                color =
+                    XkbAddGeomColor(geom,
+                                    XkbAtomGetString(NULL, ki->color),
+                                    geom->num_colors);
+            else
+                color = XkbAddGeomColor(geom, "white", geom->num_colors);
+            XkbSetKeyColor(geom, key, color);
+        }
+    }
+    if (si->doodads != NULL)
+    {
+        DoodadInfo *di;
+        for (di = si->doodads; di != NULL; di = (DoodadInfo *) di->defs.next)
+        {
+            CopyDoodadDef(geom, section, di, info);
+        }
+    }
+    if (si->overlays != NULL)
+    {
+        OverlayInfo *oi;
+        for (oi = si->overlays; oi != NULL;
+             oi = (OverlayInfo *) oi->defs.next)
+        {
+            CopyOverlayDef(geom, section, oi, info);
+        }
+    }
+    if (XkbComputeSectionBounds(geom, section))
+    {
+        /* 7/6/94 (ef) --  check for negative origin and translate */
+        if ((si->defs.defined & _GS_Width) == 0)
+            section->width = section->bounds.x2;
+        if ((si->defs.defined & _GS_Height) == 0)
+            section->height = section->bounds.y2;
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+Bool
+CompileGeometry(XkbFile * file, XkbFileInfo * result, unsigned merge)
+{
+    GeometryInfo info;
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    InitGeometryInfo(&info, file->id, merge);
+    info.dpy = xkb->dpy;
+    HandleGeometryFile(file, xkb, merge, &info);
+
+    if (info.errorCount == 0)
+    {
+        XkbGeometryPtr geom;
+        XkbGeometrySizesRec sizes;
+        bzero(&sizes, sizeof(sizes));
+        sizes.which = XkbGeomAllMask;
+        sizes.num_properties = info.nProps;
+        sizes.num_colors = 8;
+        sizes.num_shapes = info.nShapes;
+        sizes.num_sections = info.nSections;
+        sizes.num_doodads = info.nDoodads;
+        if (XkbAllocGeometry(xkb, &sizes) != Success)
+        {
+            WSGO("Couldn't allocate GeometryRec\n");
+            ACTION("Geometry not compiled\n");
+            return False;
+        }
+        geom = xkb->geom;
+
+        geom->width_mm = info.widthMM;
+        geom->height_mm = info.heightMM;
+        if (info.name != NULL)
+        {
+            geom->name = XkbInternAtom(xkb->dpy, info.name, False);
+            if (XkbAllocNames(xkb, XkbGeometryNameMask, 0, 0) == Success)
+                xkb->names->geometry = geom->name;
+        }
+        if (info.fontSpec != None)
+            geom->label_font =
+                uStringDup(XkbAtomGetString(NULL, info.fontSpec));
+        else
+            geom->label_font = FontFromParts(info.font, info.fontWeight,
+                                             info.fontSlant,
+                                             info.fontSetWidth,
+                                             info.fontVariant,
+                                             info.fontSize,
+                                             info.fontEncoding);
+        XkbAddGeomColor(geom, "black", geom->num_colors);
+        XkbAddGeomColor(geom, "white", geom->num_colors);
+
+        if (info.baseColor == None)
+            info.baseColor = XkbInternAtom(NULL, "white", False);
+        if (info.labelColor == None)
+            info.labelColor = XkbInternAtom(NULL, "black", False);
+        geom->base_color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, info.baseColor),
+                            geom->num_colors);
+        geom->label_color =
+            XkbAddGeomColor(geom, XkbAtomGetString(NULL, info.labelColor),
+                            geom->num_colors);
+
+        if (info.props)
+        {
+            PropertyInfo *pi;
+            for (pi = info.props; pi != NULL;
+                 pi = (PropertyInfo *) pi->defs.next)
+            {
+                if (!XkbAddGeomProperty(geom, pi->name, pi->value))
+                    return False;
+            }
+        }
+        if (info.shapes)
+        {
+            ShapeInfo *si;
+            for (si = info.shapes; si != NULL;
+                 si = (ShapeInfo *) si->defs.next)
+            {
+                if (!CopyShapeDef(xkb->dpy, geom, si))
+                    return False;
+            }
+        }
+        if (info.sections)
+        {
+            SectionInfo *si;
+            for (si = info.sections; si != NULL;
+                 si = (SectionInfo *) si->defs.next)
+            {
+                if (!CopySectionDef(geom, si, &info))
+                    return False;
+            }
+        }
+        if (info.doodads)
+        {
+            DoodadInfo *di;
+            for (di = info.doodads; di != NULL;
+                 di = (DoodadInfo *) di->defs.next)
+            {
+                if (!CopyDoodadDef(geom, NULL, di, &info))
+                    return False;
+            }
+        }
+        if (info.aliases)
+            ApplyAliases(xkb, True, &info.aliases);
+        ClearGeometryInfo(&info);
+        return True;
+    }
+    return False;
+}
diff --git a/src/xkbcomp/indicators.c b/src/xkbcomp/indicators.c
new file mode 100644 (file)
index 0000000..d4a362f
--- /dev/null
@@ -0,0 +1,575 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "misc.h"
+#include "tokens.h"
+#include "expr.h"
+#include "vmod.h"
+#include "indicators.h"
+#include "action.h"
+#include "compat.h"
+
+/***====================================================================***/
+
+#define        ReportIndicatorBadType(d,l,f,w) \
+               ReportBadType("indicator map",(f),\
+                       XkbAtomText((d),(l)->name,XkbMessage),(w))
+#define        ReportIndicatorNotArray(d,l,f)  \
+               ReportNotArray("indicator map",(f),\
+                       XkbAtomText((d),(l)->name,XkbMessage))
+
+/***====================================================================***/
+
+void
+ClearIndicatorMapInfo(Display * dpy, LEDInfo * info)
+{
+    info->name = XkbInternAtom(dpy, "default", False);
+    info->indicator = _LED_NotBound;
+    info->flags = info->which_mods = info->real_mods = 0;
+    info->vmods = 0;
+    info->which_groups = info->groups = 0;
+    info->ctrls = 0;
+    return;
+}
+
+LEDInfo *
+AddIndicatorMap(LEDInfo * oldLEDs, LEDInfo * new)
+{
+    LEDInfo *old, *last;
+    unsigned collide;
+
+    last = NULL;
+    for (old = oldLEDs; old != NULL; old = (LEDInfo *) old->defs.next)
+    {
+        if (old->name == new->name)
+        {
+            if ((old->real_mods == new->real_mods) &&
+                (old->vmods == new->vmods) &&
+                (old->groups == new->groups) &&
+                (old->ctrls == new->ctrls) &&
+                (old->which_mods == new->which_mods) &&
+                (old->which_groups == new->which_groups))
+            {
+                old->defs.defined |= new->defs.defined;
+                return oldLEDs;
+            }
+            if (new->defs.merge == MergeReplace)
+            {
+                CommonInfo *next = old->defs.next;
+                if (((old->defs.fileID == new->defs.fileID)
+                     && (warningLevel > 0)) || (warningLevel > 9))
+                {
+                    WARN1("Map for indicator %s redefined\n",
+                          XkbAtomText(NULL, old->name, XkbMessage));
+                    ACTION("Earlier definition ignored\n");
+                }
+                *old = *new;
+                old->defs.next = next;
+                return oldLEDs;
+            }
+            collide = 0;
+            if (UseNewField(_LED_Index, &old->defs, &new->defs, &collide))
+            {
+                old->indicator = new->indicator;
+                old->defs.defined |= _LED_Index;
+            }
+            if (UseNewField(_LED_Mods, &old->defs, &new->defs, &collide))
+            {
+                old->which_mods = new->which_mods;
+                old->real_mods = new->real_mods;
+                old->vmods = new->vmods;
+                old->defs.defined |= _LED_Mods;
+            }
+            if (UseNewField(_LED_Groups, &old->defs, &new->defs, &collide))
+            {
+                old->which_groups = new->which_groups;
+                old->groups = new->groups;
+                old->defs.defined |= _LED_Groups;
+            }
+            if (UseNewField(_LED_Ctrls, &old->defs, &new->defs, &collide))
+            {
+                old->ctrls = new->ctrls;
+                old->defs.defined |= _LED_Ctrls;
+            }
+            if (UseNewField(_LED_Explicit, &old->defs, &new->defs, &collide))
+            {
+                old->flags &= ~XkbIM_NoExplicit;
+                old->flags |= (new->flags & XkbIM_NoExplicit);
+                old->defs.defined |= _LED_Explicit;
+            }
+            if (UseNewField(_LED_Automatic, &old->defs, &new->defs, &collide))
+            {
+                old->flags &= ~XkbIM_NoAutomatic;
+                old->flags |= (new->flags & XkbIM_NoAutomatic);
+                old->defs.defined |= _LED_Automatic;
+            }
+            if (UseNewField(_LED_DrivesKbd, &old->defs, &new->defs, &collide))
+            {
+                old->flags &= ~XkbIM_LEDDrivesKB;
+                old->flags |= (new->flags & XkbIM_LEDDrivesKB);
+                old->defs.defined |= _LED_DrivesKbd;
+            }
+            if (collide)
+            {
+                WARN1("Map for indicator %s redefined\n",
+                      XkbAtomText(NULL, old->name, XkbMessage));
+                ACTION1("Using %s definition for duplicate fields\n",
+                        (new->defs.merge == MergeAugment ? "first" : "last"));
+            }
+            return oldLEDs;
+        }
+        if (old->defs.next == NULL)
+            last = old;
+    }
+    /* new definition */
+    old = uTypedAlloc(LEDInfo);
+    if (!old)
+    {
+        WSGO("Couldn't allocate indicator map\n");
+        ACTION1("Map for indicator %s not compiled\n",
+                XkbAtomText(NULL, new->name, XkbMessage));
+        return NULL;
+    }
+    *old = *new;
+    old->defs.next = NULL;
+    if (last)
+    {
+        last->defs.next = &old->defs;
+        return oldLEDs;
+    }
+    return old;
+}
+
+static LookupEntry modComponentNames[] = {
+    {"base", XkbIM_UseBase}
+    ,
+    {"latched", XkbIM_UseLatched}
+    ,
+    {"locked", XkbIM_UseLocked}
+    ,
+    {"effective", XkbIM_UseEffective}
+    ,
+    {"compat", XkbIM_UseCompat}
+    ,
+    {"any", XkbIM_UseAnyMods}
+    ,
+    {"none", 0}
+    ,
+    {NULL, 0}
+};
+static LookupEntry groupComponentNames[] = {
+    {"base", XkbIM_UseBase}
+    ,
+    {"latched", XkbIM_UseLatched}
+    ,
+    {"locked", XkbIM_UseLocked}
+    ,
+    {"effective", XkbIM_UseEffective}
+    ,
+    {"any", XkbIM_UseAnyGroup}
+    ,
+    {"none", 0}
+    ,
+    {NULL, 0}
+};
+
+int
+SetIndicatorMapField(LEDInfo * led,
+                     XkbDescPtr xkb,
+                     char *field, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult rtrn;
+    Bool ok;
+
+    ok = True;
+    if ((uStrCaseCmp(field, "modifiers") == 0)
+        || (uStrCaseCmp(field, "mods") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (XPointer) xkb))
+            return ReportIndicatorBadType(xkb->dpy, led, field,
+                                          "modifier mask");
+        led->real_mods = rtrn.uval & 0xff;
+        led->vmods = (rtrn.uval >> 8) & 0xff;
+        led->defs.defined |= _LED_Mods;
+    }
+    else if (uStrCaseCmp(field, "groups") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveMask
+            (value, &rtrn, SimpleLookup, (XPointer) groupNames))
+            return ReportIndicatorBadType(xkb->dpy, led, field, "group mask");
+        led->groups = rtrn.uval;
+        led->defs.defined |= _LED_Groups;
+    }
+    else if ((uStrCaseCmp(field, "controls") == 0) ||
+             (uStrCaseCmp(field, "ctrls") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveMask
+            (value, &rtrn, SimpleLookup, (XPointer) ctrlNames))
+            return ReportIndicatorBadType(xkb->dpy, led, field,
+                                          "controls mask");
+        led->ctrls = rtrn.uval;
+        led->defs.defined |= _LED_Ctrls;
+    }
+    else if (uStrCaseCmp(field, "allowexplicit") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
+            return ReportIndicatorBadType(xkb->dpy, led, field, "boolean");
+        if (rtrn.uval)
+            led->flags &= ~XkbIM_NoExplicit;
+        else
+            led->flags |= XkbIM_NoExplicit;
+        led->defs.defined |= _LED_Explicit;
+    }
+    else if ((uStrCaseCmp(field, "whichmodstate") == 0) ||
+             (uStrCaseCmp(field, "whichmodifierstate") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveMask(value, &rtrn, SimpleLookup,
+                             (XPointer) modComponentNames))
+        {
+            return ReportIndicatorBadType(xkb->dpy, led, field,
+                                          "mask of modifier state components");
+        }
+        led->which_mods = rtrn.uval;
+    }
+    else if (uStrCaseCmp(field, "whichgroupstate") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveMask(value, &rtrn, SimpleLookup,
+                             (XPointer) groupComponentNames))
+        {
+            return ReportIndicatorBadType(xkb->dpy, led, field,
+                                          "mask of group state components");
+        }
+        led->which_groups = rtrn.uval;
+    }
+    else if ((uStrCaseCmp(field, "driveskbd") == 0) ||
+             (uStrCaseCmp(field, "driveskeyboard") == 0) ||
+             (uStrCaseCmp(field, "leddriveskbd") == 0) ||
+             (uStrCaseCmp(field, "leddriveskeyboard") == 0) ||
+             (uStrCaseCmp(field, "indicatordriveskbd") == 0) ||
+             (uStrCaseCmp(field, "indicatordriveskeyboard") == 0))
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveBoolean(value, &rtrn, NULL, NULL))
+            return ReportIndicatorBadType(xkb->dpy, led, field, "boolean");
+        if (rtrn.uval)
+            led->flags |= XkbIM_LEDDrivesKB;
+        else
+            led->flags &= ~XkbIM_LEDDrivesKB;
+        led->defs.defined |= _LED_DrivesKbd;
+    }
+    else if (uStrCaseCmp(field, "index") == 0)
+    {
+        if (arrayNdx != NULL)
+            return ReportIndicatorNotArray(xkb->dpy, led, field);
+        if (!ExprResolveInteger(value, &rtrn, NULL, NULL))
+            return ReportIndicatorBadType(xkb->dpy, led, field,
+                                          "indicator index");
+        if ((rtrn.uval < 1) || (rtrn.uval > 32))
+        {
+            ERROR2("Illegal indicator index %d (range 1..%d)\n",
+                   rtrn.uval, XkbNumIndicators);
+            ACTION1("Index definition for %s indicator ignored\n",
+                    XkbAtomText(NULL, led->name, XkbMessage));
+            return False;
+        }
+        led->indicator = rtrn.uval;
+        led->defs.defined |= _LED_Index;
+    }
+    else
+    {
+        ERROR2("Unknown field %s in map for %s indicator\n", field,
+               XkbAtomText(NULL, led->name, XkbMessage));
+        ACTION("Definition ignored\n");
+        ok = False;
+    }
+    return ok;
+}
+
+LEDInfo *
+HandleIndicatorMapDef(IndicatorMapDef * def,
+                      XkbDescPtr xkb,
+                      LEDInfo * dflt, LEDInfo * oldLEDs, unsigned merge)
+{
+    LEDInfo led, *rtrn;
+    VarDef *var;
+    Bool ok;
+
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+
+    led = *dflt;
+    led.defs.merge = merge;
+    led.name = def->name;
+
+    ok = True;
+    for (var = def->body; var != NULL; var = (VarDef *) var->common.next)
+    {
+        ExprResult elem, field;
+        ExprDef *arrayNdx;
+        if (!ExprResolveLhs(var->name, &elem, &field, &arrayNdx))
+        {
+            ok = False;
+            continue;
+        }
+        if (elem.str != NULL)
+        {
+            ERROR1
+                ("Cannot set defaults for \"%s\" element in indicator map\n",
+                 elem.str);
+            ACTION2("Assignment to %s.%s ignored\n", elem.str, field.str);
+            ok = False;
+        }
+        else
+        {
+            ok = SetIndicatorMapField(&led, xkb, field.str, arrayNdx,
+                                      var->value) && ok;
+        }
+    }
+    if (ok)
+    {
+        rtrn = AddIndicatorMap(oldLEDs, &led);
+        return rtrn;
+    }
+    return NULL;
+}
+
+Bool
+CopyIndicatorMapDefs(XkbFileInfo * result, LEDInfo * leds,
+                     LEDInfo ** unboundRtrn)
+{
+    LEDInfo *led, *next;
+    LEDInfo *unbound, *last;
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    if (XkbAllocNames(xkb, XkbIndicatorNamesMask, 0, 0) != Success)
+    {
+        WSGO("Couldn't allocate names\n");
+        ACTION("Indicator names may be incorrect\n");
+    }
+    if (XkbAllocIndicatorMaps(xkb) != Success)
+    {
+        WSGO("Can't allocate indicator maps\n");
+        ACTION("Indicator map definitions may be lost\n");
+        return False;
+    }
+    last = unbound = (unboundRtrn ? *unboundRtrn : NULL);
+    while ((last != NULL) && (last->defs.next != NULL))
+    {
+        last = (LEDInfo *) last->defs.next;
+    }
+    for (led = leds; led != NULL; led = next)
+    {
+        next = (LEDInfo *) led->defs.next;
+        if ((led->groups != 0) && (led->which_groups == 0))
+            led->which_groups = XkbIM_UseEffective;
+        if ((led->which_mods == 0) && ((led->real_mods) || (led->vmods)))
+            led->which_mods = XkbIM_UseEffective;
+        if ((led->indicator == _LED_NotBound) || (!xkb->indicators))
+        {
+            if (unboundRtrn != NULL)
+            {
+                led->defs.next = NULL;
+                if (last != NULL)
+                    last->defs.next = (CommonInfo *) led;
+                else
+                    unbound = led;
+                last = led;
+            }
+            else
+                uFree(led);
+        }
+        else
+        {
+            register XkbIndicatorMapPtr im;
+            im = &xkb->indicators->maps[led->indicator - 1];
+            im->flags = led->flags;
+            im->which_groups = led->which_groups;
+            im->groups = led->groups;
+            im->which_mods = led->which_mods;
+            im->mods.mask = led->real_mods;
+            im->mods.real_mods = led->real_mods;
+            im->mods.vmods = led->vmods;
+            im->ctrls = led->ctrls;
+            if (xkb->names != NULL)
+                xkb->names->indicators[led->indicator - 1] = led->name;
+            uFree(led);
+        }
+    }
+    if (unboundRtrn != NULL)
+    {
+        *unboundRtrn = unbound;
+    }
+    return True;
+}
+
+Bool
+BindIndicators(XkbFileInfo * result,
+               Bool force, LEDInfo * unbound, LEDInfo ** unboundRtrn)
+{
+    XkbDescPtr xkb;
+    register int i;
+    register LEDInfo *led, *next, *last;
+
+    xkb = result->xkb;
+    if (xkb->names != NULL)
+    {
+        for (led = unbound; led != NULL; led = (LEDInfo *) led->defs.next)
+        {
+            if (led->indicator == _LED_NotBound)
+            {
+                for (i = 0; i < XkbNumIndicators; i++)
+                {
+                    if (xkb->names->indicators[i] == led->name)
+                    {
+                        led->indicator = i + 1;
+                        break;
+                    }
+                }
+            }
+        }
+        if (force)
+        {
+            for (led = unbound; led != NULL; led = (LEDInfo *) led->defs.next)
+            {
+                if (led->indicator == _LED_NotBound)
+                {
+                    for (i = 0; i < XkbNumIndicators; i++)
+                    {
+                        if (xkb->names->indicators[i] == None)
+                        {
+                            xkb->names->indicators[i] = led->name;
+                            led->indicator = i + 1;
+                            xkb->indicators->phys_indicators &= ~(1 << i);
+                            break;
+                        }
+                    }
+                    if (led->indicator == _LED_NotBound)
+                    {
+                        ERROR("No unnamed indicators found\n");
+                        ACTION1
+                            ("Virtual indicator map \"%s\" not bound\n",
+                             XkbAtomGetString(xkb->dpy, led->name));
+                        continue;
+                    }
+                }
+            }
+        }
+    }
+    for (last = NULL, led = unbound; led != NULL; led = next)
+    {
+        next = (LEDInfo *) led->defs.next;
+        if (led->indicator == _LED_NotBound)
+        {
+            if (force)
+            {
+                unbound = next;
+                uFree(led);
+            }
+            else
+            {
+                if (last)
+                    last->defs.next = &led->defs;
+                else
+                    unbound = led;
+                last = led;
+            }
+        }
+        else
+        {
+            if ((xkb->names != NULL) &&
+                (xkb->names->indicators[led->indicator - 1] != led->name))
+            {
+                Atom old = xkb->names->indicators[led->indicator - 1];
+                ERROR1("Multiple names bound to indicator %d\n",
+                       (unsigned int) led->indicator);
+                ACTION2("Using %s, ignoring %s\n",
+                        XkbAtomGetString(xkb->dpy, old),
+                        XkbAtomGetString(xkb->dpy, led->name));
+                led->indicator = _LED_NotBound;
+                if (force)
+                {
+                    uFree(led);
+                    unbound = next;
+                }
+                else
+                {
+                    if (last)
+                        last->defs.next = &led->defs;
+                    else
+                        unbound = led;
+                    last = led;
+                }
+            }
+            else
+            {
+                XkbIndicatorMapPtr map;
+                map = &xkb->indicators->maps[led->indicator - 1];
+                map->flags = led->flags;
+                map->which_groups = led->which_groups;
+                map->groups = led->groups;
+                map->which_mods = led->which_mods;
+                map->mods.mask = led->real_mods;
+                map->mods.real_mods = led->real_mods;
+                map->mods.vmods = led->vmods;
+                map->ctrls = led->ctrls;
+                if (last)
+                    last->defs.next = &next->defs;
+                else
+                    unbound = next;
+                led->defs.next = NULL;
+                uFree(led);
+            }
+        }
+    }
+    if (unboundRtrn)
+    {
+        *unboundRtrn = unbound;
+    }
+    else if (unbound)
+    {
+        for (led = unbound; led != NULL; led = next)
+        {
+            next = (LEDInfo *) led->defs.next;
+            uFree(led);
+        }
+    }
+    return True;
+}
diff --git a/src/xkbcomp/indicators.h b/src/xkbcomp/indicators.h
new file mode 100644 (file)
index 0000000..35ae38a
--- /dev/null
@@ -0,0 +1,88 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef INDICATORS_H
+#define INDICATORS_H 1
+
+#define        _LED_Index      (1<<0)
+#define        _LED_Mods       (1<<1)
+#define        _LED_Groups     (1<<2)
+#define        _LED_Ctrls      (1<<3)
+#define        _LED_Explicit   (1<<4)
+#define        _LED_Automatic  (1<<5)
+#define        _LED_DrivesKbd  (1<<6)
+
+#define        _LED_NotBound   255
+
+typedef struct _LEDInfo
+{
+    CommonInfo defs;
+    Atom name;
+    unsigned char indicator;
+    unsigned char flags;
+    unsigned char which_mods;
+    unsigned char real_mods;
+    unsigned short vmods;
+    unsigned char which_groups;
+    unsigned char groups;
+    unsigned int ctrls;
+} LEDInfo;
+
+extern void ClearIndicatorMapInfo(Display * /* dpy */ ,
+                                  LEDInfo *     /* info */
+    );
+
+
+extern LEDInfo *AddIndicatorMap(LEDInfo * /* oldLEDs */ ,
+                                LEDInfo *       /* newLED */
+    );
+
+extern int SetIndicatorMapField(LEDInfo * /* led */ ,
+                                XkbDescPtr /* xkb */ ,
+                                char * /* field */ ,
+                                ExprDef * /* arrayNdx */ ,
+                                ExprDef *       /* value */
+    );
+
+extern LEDInfo *HandleIndicatorMapDef(IndicatorMapDef * /* stmt */ ,
+                                      XkbDescPtr /* xkb */ ,
+                                      LEDInfo * /* dflt */ ,
+                                      LEDInfo * /* oldLEDs */ ,
+                                      unsigned  /* mergeMode */
+    );
+
+extern Bool CopyIndicatorMapDefs(XkbFileInfo * /* result */ ,
+                                 LEDInfo * /* leds */ ,
+                                 LEDInfo **     /* unboundRtrn */
+    );
+
+extern Bool BindIndicators(XkbFileInfo * /* result */ ,
+                           Bool /* force */ ,
+                           LEDInfo * /* unbound */ ,
+                           LEDInfo **   /* unboundRtrn */
+    );
+
+#endif /* INDICATORS_H */
diff --git a/src/xkbcomp/keycodes.c b/src/xkbcomp/keycodes.c
new file mode 100644 (file)
index 0000000..1bff7fa
--- /dev/null
@@ -0,0 +1,896 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "keycodes.h"
+#include "misc.h"
+#include "alias.h"
+
+char *
+longText(unsigned long val, unsigned format)
+{
+    char buf[4];
+
+    LongToKeyName(val, buf);
+    return XkbKeyNameText(buf, format);
+}
+
+/***====================================================================***/
+
+void
+LongToKeyName(unsigned long val, char *name)
+{
+    name[0] = ((val >> 24) & 0xff);
+    name[1] = ((val >> 16) & 0xff);
+    name[2] = ((val >> 8) & 0xff);
+    name[3] = (val & 0xff);
+    return;
+}
+
+/***====================================================================***/
+
+typedef struct _IndicatorNameInfo
+{
+    CommonInfo defs;
+    int ndx;
+    Atom name;
+    Bool virtual;
+} IndicatorNameInfo;
+
+typedef struct _KeyNamesInfo
+{
+    char *name;     /* e.g. evdev+aliases(qwerty) */
+    int errorCount;
+    unsigned fileID;
+    unsigned merge;
+    int computedMin; /* lowest keycode stored */
+    int computedMax; /* highest keycode stored */
+    int explicitMin;
+    int explicitMax;
+    int effectiveMin;
+    int effectiveMax;
+    unsigned long names[XkbMaxLegalKeyCode + 1]; /* 4-letter name of key, keycode is the index */
+    unsigned files[XkbMaxLegalKeyCode + 1];
+    unsigned char has_alt_forms[XkbMaxLegalKeyCode + 1];
+    IndicatorNameInfo *leds;
+    AliasInfo *aliases;
+} KeyNamesInfo;
+
+static void HandleKeycodesFile(XkbFile * file,
+                               XkbDescPtr xkb,
+                               unsigned merge,
+                               KeyNamesInfo * info);
+
+static void
+InitIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
+{
+    ii->defs.defined = 0;
+    ii->defs.merge = info->merge;
+    ii->defs.fileID = info->fileID;
+    ii->defs.next = NULL;
+    ii->ndx = 0;
+    ii->name = None;
+    ii->virtual = False;
+    return;
+}
+
+static void
+ClearIndicatorNameInfo(IndicatorNameInfo * ii, KeyNamesInfo * info)
+{
+    if (ii == info->leds)
+    {
+        ClearCommonInfo(&ii->defs);
+        info->leds = NULL;
+    }
+    return;
+}
+
+static IndicatorNameInfo *
+NextIndicatorName(KeyNamesInfo * info)
+{
+    IndicatorNameInfo *ii;
+
+    ii = uTypedAlloc(IndicatorNameInfo);
+    if (ii)
+    {
+        InitIndicatorNameInfo(ii, info);
+        info->leds = (IndicatorNameInfo *) AddCommonInfo(&info->leds->defs,
+                                                         (CommonInfo *) ii);
+    }
+    return ii;
+}
+
+static IndicatorNameInfo *
+FindIndicatorByIndex(KeyNamesInfo * info, int ndx)
+{
+    IndicatorNameInfo *old;
+
+    for (old = info->leds; old != NULL;
+         old = (IndicatorNameInfo *) old->defs.next)
+    {
+        if (old->ndx == ndx)
+            return old;
+    }
+    return NULL;
+}
+
+static IndicatorNameInfo *
+FindIndicatorByName(KeyNamesInfo * info, Atom name)
+{
+    IndicatorNameInfo *old;
+
+    for (old = info->leds; old != NULL;
+         old = (IndicatorNameInfo *) old->defs.next)
+    {
+        if (old->name == name)
+            return old;
+    }
+    return NULL;
+}
+
+static Bool
+AddIndicatorName(KeyNamesInfo * info, IndicatorNameInfo * new)
+{
+    IndicatorNameInfo *old;
+    Bool replace;
+    const char *action;
+
+    replace = (new->defs.merge == MergeReplace) ||
+        (new->defs.merge == MergeOverride);
+    old = FindIndicatorByName(info, new->name);
+    if (old)
+    {
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple indicators named %s\n",
+                  XkbAtomText(NULL, new->name, XkbMessage));
+            if (old->ndx == new->ndx)
+            {
+                if (old->virtual != new->virtual)
+                {
+                    if (replace)
+                        old->virtual = new->virtual;
+                    action = "Using %s instead of %s\n";
+                }
+                else
+                {
+                    action = "Identical definitions ignored\n";
+                }
+                ACTION2(action, (old->virtual ? "virtual" : "real"),
+                        (old->virtual ? "real" : "virtual"));
+                return True;
+            }
+            else
+            {
+                if (replace)
+                    action = "Ignoring %d, using %d\n";
+                else
+                    action = "Using %d, ignoring %d\n";
+                ACTION2(action, old->ndx, new->ndx);
+            }
+            if (replace)
+            {
+                if (info->leds == old)
+                    info->leds = (IndicatorNameInfo *) old->defs.next;
+                else
+                {
+                    IndicatorNameInfo *tmp;
+                    tmp = info->leds;
+                    for (; tmp != NULL;
+                         tmp = (IndicatorNameInfo *) tmp->defs.next)
+                    {
+                        if (tmp->defs.next == (CommonInfo *) old)
+                        {
+                            tmp->defs.next = old->defs.next;
+                            break;
+                        }
+                    }
+                }
+                uFree(old);
+            }
+        }
+    }
+    old = FindIndicatorByIndex(info, new->ndx);
+    if (old)
+    {
+        if (((old->defs.fileID == new->defs.fileID) && (warningLevel > 0))
+            || (warningLevel > 9))
+        {
+            WARN1("Multiple names for indicator %d\n", new->ndx);
+            if ((old->name == new->name) && (old->virtual == new->virtual))
+                action = "Identical definitions ignored\n";
+            else
+            {
+                const char *oldType, *newType;
+                Atom using, ignoring;
+                if (old->virtual)
+                    oldType = "virtual indicator";
+                else
+                    oldType = "real indicator";
+                if (new->virtual)
+                    newType = "virtual indicator";
+                else
+                    newType = "real indicator";
+                if (replace)
+                {
+                    using = new->name;
+                    ignoring = old->name;
+                }
+                else
+                {
+                    using = old->name;
+                    ignoring = new->name;
+                }
+                ACTION4("Using %s %s, ignoring %s %s\n",
+                        oldType, XkbAtomText(NULL, using, XkbMessage),
+                        newType, XkbAtomText(NULL, ignoring, XkbMessage));
+            }
+        }
+        if (replace)
+        {
+            old->name = new->name;
+            old->virtual = new->virtual;
+        }
+        return True;
+    }
+    old = new;
+    new = NextIndicatorName(info);
+    if (!new)
+    {
+        WSGO1("Couldn't allocate name for indicator %d\n", new->ndx);
+        ACTION("Ignored\n");
+        return False;
+    }
+    new->name = old->name;
+    new->ndx = old->ndx;
+    new->virtual = old->virtual;
+    return True;
+}
+
+static void
+ClearKeyNamesInfo(KeyNamesInfo * info)
+{
+    if (info->name != NULL)
+        uFree(info->name);
+    info->name = NULL;
+    info->computedMax = info->explicitMax = info->explicitMin = -1;
+    info->computedMin = 256;
+    info->effectiveMin = 8;
+    info->effectiveMax = 255;
+    bzero((char *) info->names, sizeof(info->names));
+    bzero((char *) info->files, sizeof(info->files));
+    bzero((char *) info->has_alt_forms, sizeof(info->has_alt_forms));
+    if (info->leds)
+        ClearIndicatorNameInfo(info->leds, info);
+    if (info->aliases)
+        ClearAliases(&info->aliases);
+    return;
+}
+
+static void
+InitKeyNamesInfo(KeyNamesInfo * info)
+{
+    info->name = NULL;
+    info->leds = NULL;
+    info->aliases = NULL;
+    ClearKeyNamesInfo(info);
+    info->errorCount = 0;
+    return;
+}
+
+static int
+FindKeyByLong(KeyNamesInfo * info, unsigned long name)
+{
+    register int i;
+
+    for (i = info->effectiveMin; i <= info->effectiveMax; i++)
+    {
+        if (info->names[i] == name)
+            return i;
+    }
+    return 0;
+}
+
+/**
+ * Store the name of the key as a long in the info struct under the given
+ * keycode. If the same keys is referred to twice, print a warning.
+ * Note that the key's name is stored as a long, the keycode is the index.
+ */
+static Bool
+AddKeyName(KeyNamesInfo * info,
+           int kc,
+           char *name, unsigned merge, unsigned fileID, Bool reportCollisions)
+{
+    int old;
+    unsigned long lval;
+
+    if ((kc < info->effectiveMin) || (kc > info->effectiveMax))
+    {
+        ERROR2("Illegal keycode %d for name <%s>\n", kc, name);
+        ACTION2("Must be in the range %d-%d inclusive\n",
+                info->effectiveMin, info->effectiveMax);
+        return False;
+    }
+    if (kc < info->computedMin)
+        info->computedMin = kc;
+    if (kc > info->computedMax)
+        info->computedMax = kc;
+    lval = KeyNameToLong(name);
+
+    if (reportCollisions)
+    {
+        reportCollisions = ((warningLevel > 7) ||
+                            ((warningLevel > 0)
+                             && (fileID == info->files[kc])));
+    }
+
+    if (info->names[kc] != 0)
+    {
+        char buf[6];
+
+        LongToKeyName(info->names[kc], buf);
+        buf[4] = '\0';
+        if (info->names[kc] == lval)
+        {
+            if (info->has_alt_forms[kc] || (merge == MergeAltForm))
+            {
+                info->has_alt_forms[kc] = True;
+            }
+            else if (reportCollisions)
+            {
+                WARN("Multiple identical key name definitions\n");
+                ACTION2("Later occurences of \"<%s> = %d\" ignored\n",
+                        buf, kc);
+            }
+            return True;
+        }
+        if (merge == MergeAugment)
+        {
+            if (reportCollisions)
+            {
+                WARN1("Multiple names for keycode %d\n", kc);
+                ACTION2("Using <%s>, ignoring <%s>\n", buf, name);
+            }
+            return True;
+        }
+        else
+        {
+            if (reportCollisions)
+            {
+                WARN1("Multiple names for keycode %d\n", kc);
+                ACTION2("Using <%s>, ignoring <%s>\n", name, buf);
+            }
+            info->names[kc] = 0;
+            info->files[kc] = 0;
+        }
+    }
+    old = FindKeyByLong(info, lval);
+    if ((old != 0) && (old != kc))
+    {
+        if (merge == MergeOverride)
+        {
+            info->names[old] = 0;
+            info->files[old] = 0;
+            info->has_alt_forms[old] = True;
+            if (reportCollisions)
+            {
+                WARN1("Key name <%s> assigned to multiple keys\n", name);
+                ACTION2("Using %d, ignoring %d\n", kc, old);
+            }
+        }
+        else if (merge != MergeAltForm)
+        {
+            if ((reportCollisions) && (warningLevel > 3))
+            {
+                WARN1("Key name <%s> assigned to multiple keys\n", name);
+                ACTION2("Using %d, ignoring %d\n", old, kc);
+                ACTION
+                    ("Use 'alternate' keyword to assign the same name to multiple keys\n");
+            }
+            return True;
+        }
+        else
+        {
+            info->has_alt_forms[old] = True;
+        }
+    }
+    info->names[kc] = lval;
+    info->files[kc] = fileID;
+    info->has_alt_forms[kc] = (merge == MergeAltForm);
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+MergeIncludedKeycodes(KeyNamesInfo * into, KeyNamesInfo * from,
+                      unsigned merge)
+{
+    register int i;
+    char buf[5];
+
+    if (from->errorCount > 0)
+    {
+        into->errorCount += from->errorCount;
+        return;
+    }
+    if (into->name == NULL)
+    {
+        into->name = from->name;
+        from->name = NULL;
+    }
+    for (i = from->computedMin; i <= from->computedMax; i++)
+    {
+        unsigned thisMerge;
+        if (from->names[i] == 0)
+            continue;
+        LongToKeyName(from->names[i], buf);
+        buf[4] = '\0';
+        if (from->has_alt_forms[i])
+            thisMerge = MergeAltForm;
+        else
+            thisMerge = merge;
+        if (!AddKeyName(into, i, buf, thisMerge, from->fileID, False))
+            into->errorCount++;
+    }
+    if (from->leds)
+    {
+        IndicatorNameInfo *led, *next;
+        for (led = from->leds; led != NULL; led = next)
+        {
+            if (merge != MergeDefault)
+                led->defs.merge = merge;
+            if (!AddIndicatorName(into, led))
+                into->errorCount++;
+            next = (IndicatorNameInfo *) led->defs.next;
+        }
+    }
+    if (!MergeAliases(&into->aliases, &from->aliases, merge))
+        into->errorCount++;
+    if (from->explicitMin > 0)
+    {
+        if ((into->explicitMin < 0)
+            || (into->explicitMin > from->explicitMin))
+            into->effectiveMin = into->explicitMin = from->explicitMin;
+    }
+    if (from->explicitMax > 0)
+    {
+        if ((into->explicitMax < 0)
+            || (into->explicitMax < from->explicitMax))
+            into->effectiveMax = into->explicitMax = from->explicitMax;
+    }
+    return;
+}
+
+/**
+ * Handle the given include statement (e.g. "include "evdev+aliases(qwerty)").
+ *
+ * @param stmt The include statement from the keymap file.
+ * @param xkb Unused for all but the xkb->flags.
+ * @param info Struct to store the key info in.
+ */
+static Bool
+HandleIncludeKeycodes(IncludeStmt * stmt, XkbDescPtr xkb, KeyNamesInfo * info)
+{
+    unsigned newMerge;
+    XkbFile *rtrn;
+    KeyNamesInfo included = {NULL};
+    Bool haveSelf;
+
+    haveSelf = False;
+    if ((stmt->file == NULL) && (stmt->map == NULL))
+    {
+        haveSelf = True;
+        included = *info;
+        bzero(info, sizeof(KeyNamesInfo));
+    }
+    else if (strcmp(stmt->file, "computed") == 0)
+    {
+        xkb->flags |= AutoKeyNames;
+        info->explicitMin = XkbMinLegalKeyCode;
+        info->explicitMax = XkbMaxLegalKeyCode;
+        return (info->errorCount == 0);
+    } /* parse file, store returned info in the xkb struct */
+    else if (ProcessIncludeFile(stmt, XkmKeyNamesIndex, &rtrn, &newMerge))
+    {
+        InitKeyNamesInfo(&included);
+        HandleKeycodesFile(rtrn, xkb, MergeOverride, &included);
+        if (stmt->stmt != NULL)
+        {
+            if (included.name != NULL)
+                uFree(included.name);
+            included.name = stmt->stmt;
+            stmt->stmt = NULL;
+        }
+    }
+    else
+    {
+        info->errorCount += 10; /* XXX: why 10?? */
+        return False;
+    }
+    /* Do we have more than one include statement? */
+    if ((stmt->next != NULL) && (included.errorCount < 1))
+    {
+        IncludeStmt *next;
+        unsigned op;
+        KeyNamesInfo next_incl;
+
+        for (next = stmt->next; next != NULL; next = next->next)
+        {
+            if ((next->file == NULL) && (next->map == NULL))
+            {
+                haveSelf = True;
+                MergeIncludedKeycodes(&included, info, next->merge);
+                ClearKeyNamesInfo(info);
+            }
+            else if (ProcessIncludeFile(next, XkmKeyNamesIndex, &rtrn, &op))
+            {
+                InitKeyNamesInfo(&next_incl);
+                HandleKeycodesFile(rtrn, xkb, MergeOverride, &next_incl);
+                MergeIncludedKeycodes(&included, &next_incl, op);
+                ClearKeyNamesInfo(&next_incl);
+            }
+            else
+            {
+                info->errorCount += 10; /* XXX: Why 10?? */
+                return False;
+            }
+        }
+    }
+    if (haveSelf)
+        *info = included;
+    else
+    {
+        MergeIncludedKeycodes(info, &included, newMerge);
+        ClearKeyNamesInfo(&included);
+    }
+    return (info->errorCount == 0);
+}
+
+/**
+ * Parse the given statement and store the output in the info struct.
+ * e.g. <ESC> = 9
+ */
+static int
+HandleKeycodeDef(KeycodeDef * stmt, unsigned merge, KeyNamesInfo * info)
+{
+    int code;
+    ExprResult result;
+
+    if (!ExprResolveInteger(stmt->value, &result, NULL, NULL))
+    {
+        ACTION1("No value keycode assigned to name <%s>\n", stmt->name);
+        return 0;
+    }
+    code = result.ival;
+    if ((code < info->effectiveMin) || (code > info->effectiveMax))
+    {
+        ERROR2("Illegal keycode %d for name <%s>\n", code, stmt->name);
+        ACTION2("Must be in the range %d-%d inclusive\n",
+                info->effectiveMin, info->effectiveMax);
+        return 0;
+    }
+    if (stmt->merge != MergeDefault)
+    {
+        if (stmt->merge == MergeReplace)
+            merge = MergeOverride;
+        else
+            merge = stmt->merge;
+    }
+    return AddKeyName(info, code, stmt->name, merge, info->fileID, True);
+}
+
+#define        MIN_KEYCODE_DEF         0
+#define        MAX_KEYCODE_DEF         1
+
+/**
+ * Handle the minimum/maximum statement of the xkb file.
+ * Sets explicitMin/Max and effectiveMin/Max of the info struct.
+ *
+ * @return 1 on success, 0 otherwise.
+ */
+static int
+HandleKeyNameVar(VarDef * stmt, KeyNamesInfo * info)
+{
+    ExprResult tmp, field;
+    ExprDef *arrayNdx;
+    int which;
+
+    if (ExprResolveLhs(stmt->name, &tmp, &field, &arrayNdx) == 0)
+        return 0;               /* internal error, already reported */
+
+    if (tmp.str != NULL)
+    {
+        ERROR1("Unknown element %s encountered\n", tmp.str);
+        ACTION1("Default for field %s ignored\n", field.str);
+        return 0;
+    }
+    if (uStrCaseCmp(field.str, "minimum") == 0)
+        which = MIN_KEYCODE_DEF;
+    else if (uStrCaseCmp(field.str, "maximum") == 0)
+        which = MAX_KEYCODE_DEF;
+    else
+    {
+        ERROR("Unknown field encountered\n");
+        ACTION1("Assigment to field %s ignored\n", field.str);
+        return 0;
+    }
+    if (arrayNdx != NULL)
+    {
+        ERROR1("The %s setting is not an array\n", field.str);
+        ACTION("Illegal array reference ignored\n");
+        return 0;
+    }
+
+    if (ExprResolveInteger(stmt->value, &tmp, NULL, NULL) == 0)
+    {
+        ACTION1("Assignment to field %s ignored\n", field.str);
+        return 0;
+    }
+    if ((tmp.ival < XkbMinLegalKeyCode) || (tmp.ival > XkbMaxLegalKeyCode))
+    {
+        ERROR3
+            ("Illegal keycode %d (must be in the range %d-%d inclusive)\n",
+             tmp.ival, XkbMinLegalKeyCode, XkbMaxLegalKeyCode);
+        ACTION1("Value of \"%s\" not changed\n", field.str);
+        return 0;
+    }
+    if (which == MIN_KEYCODE_DEF)
+    {
+        if ((info->explicitMax > 0) && (info->explicitMax < tmp.ival))
+        {
+            ERROR2
+                ("Minimum key code (%d) must be <= maximum key code (%d)\n",
+                 tmp.ival, info->explicitMax);
+            ACTION("Minimum key code value not changed\n");
+            return 0;
+        }
+        if ((info->computedMax > 0) && (info->computedMin < tmp.ival))
+        {
+            ERROR2
+                ("Minimum key code (%d) must be <= lowest defined key (%d)\n",
+                 tmp.ival, info->computedMin);
+            ACTION("Minimum key code value not changed\n");
+            return 0;
+        }
+        info->explicitMin = tmp.ival;
+        info->effectiveMin = tmp.ival;
+    }
+    if (which == MAX_KEYCODE_DEF)
+    {
+        if ((info->explicitMin > 0) && (info->explicitMin > tmp.ival))
+        {
+            ERROR2("Maximum code (%d) must be >= minimum key code (%d)\n",
+                   tmp.ival, info->explicitMin);
+            ACTION("Maximum code value not changed\n");
+            return 0;
+        }
+        if ((info->computedMax > 0) && (info->computedMax > tmp.ival))
+        {
+            ERROR2
+                ("Maximum code (%d) must be >= highest defined key (%d)\n",
+                 tmp.ival, info->computedMax);
+            ACTION("Maximum code value not changed\n");
+            return 0;
+        }
+        info->explicitMax = tmp.ival;
+        info->effectiveMax = tmp.ival;
+    }
+    return 1;
+}
+
+static int
+HandleIndicatorNameDef(IndicatorNameDef * def,
+                       unsigned merge, KeyNamesInfo * info)
+{
+    IndicatorNameInfo ii;
+    ExprResult tmp;
+
+    if ((def->ndx < 1) || (def->ndx > XkbNumIndicators))
+    {
+        info->errorCount++;
+        ERROR1("Name specified for illegal indicator index %d\n", def->ndx);
+        ACTION("Ignored\n");
+        return False;
+    }
+    InitIndicatorNameInfo(&ii, info);
+    ii.ndx = def->ndx;
+    if (!ExprResolveString(def->name, &tmp, NULL, NULL))
+    {
+        char buf[20];
+        snprintf(buf, sizeof(buf), "%d", def->ndx);
+        info->errorCount++;
+        return ReportBadType("indicator", "name", buf, "string");
+    }
+    ii.name = XkbInternAtom(NULL, tmp.str, False);
+    ii.virtual = def->virtual;
+    if (!AddIndicatorName(info, &ii))
+        return False;
+    return True;
+}
+
+/**
+ * Handle the xkb_keycodes section of a xkb file.
+ * All information about parsed keys is stored in the info struct.
+ *
+ * Such a section may have include statements, in which case this function is
+ * semi-recursive (it calls HandleIncludeKeycodes, which may call
+ * HandleKeycodesFile again).
+ *
+ * @param file The input file (parsed xkb_keycodes section)
+ * @param xkb Necessary to pass down, may have flags changed.
+ * @param merge Merge strategy (MergeOverride, etc.)
+ * @param info Struct to contain the fully parsed key information.
+ */
+static void
+HandleKeycodesFile(XkbFile * file,
+                   XkbDescPtr xkb, unsigned merge, KeyNamesInfo * info)
+{
+    ParseCommon *stmt;
+
+    info->name = uStringDup(file->name);
+    stmt = file->defs;
+    while (stmt)
+    {
+        switch (stmt->stmtType)
+        {
+        case StmtInclude:    /* e.g. include "evdev+aliases(qwerty)" */
+            if (!HandleIncludeKeycodes((IncludeStmt *) stmt, xkb, info))
+                info->errorCount++;
+            break;
+        case StmtKeycodeDef: /* e.g. <ESC> = 9; */
+            if (!HandleKeycodeDef((KeycodeDef *) stmt, merge, info))
+                info->errorCount++;
+            break;
+        case StmtKeyAliasDef: /* e.g. alias <MENU> = <COMP>; */
+            if (!HandleAliasDef((KeyAliasDef *) stmt,
+                                merge, info->fileID, &info->aliases))
+                info->errorCount++;
+            break;
+        case StmtVarDef: /* e.g. minimum, maximum */
+            if (!HandleKeyNameVar((VarDef *) stmt, info))
+                info->errorCount++;
+            break;
+        case StmtIndicatorNameDef: /* e.g. indicator 1 = "Caps Lock"; */
+            if (!HandleIndicatorNameDef((IndicatorNameDef *) stmt,
+                                        merge, info))
+            {
+                info->errorCount++;
+            }
+            break;
+        case StmtInterpDef:
+        case StmtVModDef:
+            ERROR("Keycode files may define key and indicator names only\n");
+            ACTION1("Ignoring definition of %s\n",
+                    ((stmt->stmtType ==
+                      StmtInterpDef) ? "a symbol interpretation" :
+                     "virtual modifiers"));
+            info->errorCount++;
+            break;
+        default:
+            WSGO1("Unexpected statement type %d in HandleKeycodesFile\n",
+                  stmt->stmtType);
+            break;
+        }
+        stmt = stmt->next;
+        if (info->errorCount > 10)
+        {
+#ifdef NOISY
+            ERROR("Too many errors\n");
+#endif
+            ACTION1("Abandoning keycodes file \"%s\"\n", file->topName);
+            break;
+        }
+    }
+    return;
+}
+
+/**
+ * Compile the xkb_keycodes section, parse it's output, return the results.
+ *
+ * @param file The parsed XKB file (may have include statements requiring
+ * further parsing)
+ * @param result The effective keycodes, as gathered from the file.
+ * @param merge Merge strategy.
+ *
+ * @return True on success, False otherwise.
+ */
+Bool
+CompileKeycodes(XkbFile * file, XkbFileInfo * result, unsigned merge)
+{
+    KeyNamesInfo info; /* contains all the info after parsing */
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    InitKeyNamesInfo(&info);
+    HandleKeycodesFile(file, xkb, merge, &info);
+
+    /* all the keys are now stored in info */
+
+    if (info.errorCount == 0)
+    {
+        if (info.explicitMin > 0) /* if "minimum" statement was present */
+            xkb->min_key_code = info.effectiveMin;
+        else
+            xkb->min_key_code = info.computedMin;
+        if (info.explicitMax > 0) /* if "maximum" statement was present */
+            xkb->max_key_code = info.effectiveMax;
+        else
+            xkb->max_key_code = info.computedMax;
+        if (XkbAllocNames(xkb, XkbKeyNamesMask | XkbIndicatorNamesMask, 0, 0)
+                == Success)
+        {
+            register int i;
+            xkb->names->keycodes = XkbInternAtom(xkb->dpy, info.name, False);
+            uDEBUG2(1, "key range: %d..%d\n", xkb->min_key_code,
+                    xkb->max_key_code);
+            for (i = info.computedMin; i <= info.computedMax; i++)
+            {
+                LongToKeyName(info.names[i], xkb->names->keys[i].name);
+                uDEBUG2(2, "key %d = %s\n", i,
+                        XkbKeyNameText(xkb->names->keys[i].name, XkbMessage));
+            }
+        }
+        else
+        {
+            WSGO("Cannot create XkbNamesRec in CompileKeycodes\n");
+            return False;
+        }
+        if (info.leds)
+        {
+            IndicatorNameInfo *ii;
+            if (XkbAllocIndicatorMaps(xkb) != Success)
+            {
+                WSGO("Couldn't allocate IndicatorRec in CompileKeycodes\n");
+                ACTION("Physical indicators not set\n");
+            }
+            for (ii = info.leds; ii != NULL;
+                 ii = (IndicatorNameInfo *) ii->defs.next)
+            {
+                xkb->names->indicators[ii->ndx - 1] =
+                    XkbInternAtom(xkb->dpy,
+                                  XkbAtomGetString(NULL, ii->name), False);
+                if (xkb->indicators != NULL)
+                {
+                    register unsigned bit;
+                    bit = 1 << (ii->ndx - 1);
+                    if (ii->virtual)
+                        xkb->indicators->phys_indicators &= ~bit;
+                    else
+                        xkb->indicators->phys_indicators |= bit;
+                }
+            }
+        }
+        if (info.aliases)
+            ApplyAliases(xkb, False, &info.aliases);
+        return True;
+    }
+    ClearKeyNamesInfo(&info);
+    return False;
+}
diff --git a/src/xkbcomp/keycodes.h b/src/xkbcomp/keycodes.h
new file mode 100644 (file)
index 0000000..11f4460
--- /dev/null
@@ -0,0 +1,40 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef KEYCODES_H
+#define KEYCODES_H 1
+
+#define        KeyNameToLong(n)        ((((unsigned long)n[0])<<24)|(((unsigned long)n[1])<<16)|(((unsigned long)n[2])<<8)|n[3])
+
+extern char *longText(unsigned long /* val */ ,
+                      unsigned  /* format */
+    );
+
+extern void LongToKeyName(unsigned long /* val */ ,
+                          char *        /* name_rtrn */
+    );
+
+#endif /* KEYCODES_H */
diff --git a/src/xkbcomp/keymap.c b/src/xkbcomp/keymap.c
new file mode 100644 (file)
index 0000000..a419d8c
--- /dev/null
@@ -0,0 +1,183 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "vmod.h"
+#include "action.h"
+#include "misc.h"
+#include "indicators.h"
+
+#define        KEYCODES        0
+#define        GEOMETRY        1
+#define        TYPES           2
+#define        COMPAT          3
+#define        SYMBOLS         4
+#define        MAX_SECTIONS    5
+
+static XkbFile *sections[MAX_SECTIONS];
+
+/**
+ * Compile the given file and store the output in result.
+ * @param file A list of XkbFiles, each denoting one type (e.g.
+ * XkmKeyNamesIdx, etc.)
+ */
+Bool
+CompileKeymap(XkbFile * file, XkbFileInfo * result, unsigned merge)
+{
+    unsigned have;
+    Bool ok;
+    unsigned required, legal;
+    unsigned mainType;
+    char *mainName;
+    LEDInfo *unbound = NULL;
+
+    bzero(sections, MAX_SECTIONS * sizeof(XkbFile *));
+    mainType = file->type;
+    mainName = file->name;
+    switch (mainType)
+    {
+    case XkmSemanticsFile:
+        required = XkmSemanticsRequired;
+        legal = XkmSemanticsLegal;
+        break;
+    case XkmLayoutFile:        /* standard type  if setxkbmap -print */
+        required = XkmLayoutRequired;
+        legal = XkmKeymapLegal;
+        break;
+    case XkmKeymapFile:
+        required = XkmKeymapRequired;
+        legal = XkmKeymapLegal;
+        break;
+    default:
+        ERROR1("Cannot compile %s alone into an XKM file\n",
+               XkbConfigText(mainType, XkbMessage));
+        return False;
+    }
+    have = 0;
+    ok = 1;
+    file = (XkbFile *) file->defs;
+    /* Check for duplicate entries in the input file */
+    while ((file) && (ok))
+    {
+        file->topName = mainName;
+        if ((have & (1 << file->type)) != 0)
+        {
+            ERROR2("More than one %s section in a %s file\n",
+                   XkbConfigText(file->type, XkbMessage),
+                   XkbConfigText(mainType, XkbMessage));
+            ACTION("All sections after the first ignored\n");
+            ok = False;
+        }
+        else if ((1 << file->type) & (~legal))
+        {
+            ERROR2("Cannot define %s in a %s file\n",
+                   XkbConfigText(file->type, XkbMessage),
+                   XkbConfigText(mainType, XkbMessage));
+            ok = False;
+        }
+        else
+            switch (file->type)
+            {
+            case XkmSemanticsFile:
+            case XkmLayoutFile:
+            case XkmKeymapFile:
+                WSGO2("Illegal %s configuration in a %s file\n",
+                      XkbConfigText(file->type, XkbMessage),
+                      XkbConfigText(mainType, XkbMessage));
+                ACTION("Ignored\n");
+                ok = False;
+                break;
+            case XkmKeyNamesIndex:
+                sections[KEYCODES] = file;
+                break;
+            case XkmTypesIndex:
+                sections[TYPES] = file;
+                break;
+            case XkmSymbolsIndex:
+                sections[SYMBOLS] = file;
+                break;
+            case XkmCompatMapIndex:
+                sections[COMPAT] = file;
+                break;
+            case XkmGeometryIndex:
+            case XkmGeometryFile:
+                sections[GEOMETRY] = file;
+                break;
+            case XkmVirtualModsIndex:
+            case XkmIndicatorsIndex:
+                WSGO1("Found an isolated %s section\n",
+                      XkbConfigText(file->type, XkbMessage));
+                break;
+            default:
+                WSGO1("Unknown file type %d\n", file->type);
+                break;
+            }
+        if (ok)
+            have |= (1 << file->type);
+        file = (XkbFile *) file->common.next;
+    }
+    /* compile the sections we have in the file one-by-one, or fail. */
+    if (ok)
+    {
+        if (ok && (sections[KEYCODES] != NULL))
+            ok = CompileKeycodes(sections[KEYCODES], result, MergeOverride);
+        if (ok && (sections[GEOMETRY] != NULL))
+            ok = CompileGeometry(sections[GEOMETRY], result, MergeOverride);
+        if (ok && (sections[TYPES] != NULL))
+            ok = CompileKeyTypes(sections[TYPES], result, MergeOverride);
+        if (ok && (sections[COMPAT] != NULL))
+            ok = CompileCompatMap(sections[COMPAT], result, MergeOverride,
+                                  &unbound);
+        if (ok && (sections[SYMBOLS] != NULL))
+            ok = CompileSymbols(sections[SYMBOLS], result, MergeOverride);
+    }
+    if (!ok)
+        return False;
+    result->defined = have;
+    if (required & (~have))
+    {
+        register int i, bit;
+        unsigned missing;
+        missing = required & (~have);
+        for (i = 0, bit = 1; missing != 0; i++, bit <<= 1)
+        {
+            if (missing & bit)
+            {
+                ERROR2("Missing %s section in a %s file\n",
+                       XkbConfigText(i, XkbMessage),
+                       XkbConfigText(mainType, XkbMessage));
+                missing &= ~bit;
+            }
+        }
+        ACTION1("Description of %s not compiled\n",
+                XkbConfigText(mainType, XkbMessage));
+        ok = False;
+    }
+    ok = BindIndicators(result, True, unbound, NULL);
+    return ok;
+}
diff --git a/src/xkbcomp/keytypes.c b/src/xkbcomp/keytypes.c
new file mode 100644 (file)
index 0000000..da55d75
--- /dev/null
@@ -0,0 +1,1293 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "vmod.h"
+#include "action.h"
+#include "misc.h"
+
+typedef struct _PreserveInfo
+{
+    CommonInfo defs;
+    short matchingMapIndex;
+    unsigned char indexMods;
+    unsigned char preMods;
+    unsigned short indexVMods;
+    unsigned short preVMods;
+} PreserveInfo;
+
+#define        _KT_Name        (1<<0)
+#define        _KT_Mask        (1<<1)
+#define        _KT_Map         (1<<2)
+#define        _KT_Preserve    (1<<3)
+#define        _KT_LevelNames  (1<<4)
+
+typedef struct _KeyTypeInfo
+{
+    CommonInfo defs;
+    Display *dpy;
+    Atom name;
+    int fileID;
+    unsigned mask;
+    unsigned vmask;
+    Bool groupInfo;
+    int numLevels;
+    int nEntries;
+    int szEntries;
+    XkbKTMapEntryPtr entries;
+    PreserveInfo *preserve;
+    int szNames;
+    Atom *lvlNames;
+} KeyTypeInfo;
+
+typedef struct _KeyTypesInfo
+{
+    Display *dpy;
+    char *name;
+    int errorCount;
+    int fileID;
+    unsigned stdPresent;
+    int nTypes;
+    KeyTypeInfo *types;
+    KeyTypeInfo dflt;
+    VModInfo vmods;
+} KeyTypesInfo;
+
+Atom tok_ONE_LEVEL;
+Atom tok_TWO_LEVEL;
+Atom tok_ALPHABETIC;
+Atom tok_KEYPAD;
+
+/***====================================================================***/
+
+#define        ReportTypeShouldBeArray(t,f) \
+       ReportShouldBeArray("key type",(f),TypeTxt(t))
+#define        ReportTypeBadType(t,f,w) \
+       ReportBadType("key type",(f),TypeTxt(t),(w))
+
+/***====================================================================***/
+
+extern Bool AddMapEntry(XkbDescPtr /* xkb */ ,
+                        KeyTypeInfo * /* type */ ,
+                        XkbKTMapEntryPtr /* new */ ,
+                        Bool /* clobber */ ,
+                        Bool    /* report */
+    );
+
+extern Bool AddPreserve(XkbDescPtr /* xkb */ ,
+                        KeyTypeInfo * /* type */ ,
+                        PreserveInfo * /* new */ ,
+                        Bool /* clobber */ ,
+                        Bool    /* report */
+    );
+
+extern Bool AddLevelName(KeyTypeInfo * /* type */ ,
+                         unsigned /* level */ ,
+                         Atom /* name */ ,
+                         Bool /* clobber */ ,
+                         Bool   /* report */
+    );
+
+#define        MapEntryTxt(t,x,e)      \
+    XkbVModMaskText((t)->dpy,(x),(e)->mods.real_mods,(e)->mods.vmods,XkbMessage)
+#define        PreserveIndexTxt(t,x,p) \
+       XkbVModMaskText((t)->dpy,(x),(p)->indexMods,(p)->indexVMods,XkbMessage)
+#define        PreserveTxt(t,x,p)      \
+       XkbVModMaskText((t)->dpy,(x),(p)->preMods,(p)->preVMods,XkbMessage)
+#define        TypeTxt(t)      XkbAtomText((t)->dpy,(t)->name,XkbMessage)
+#define        TypeMaskTxt(t,x)        \
+       XkbVModMaskText((t)->dpy,(x),(t)->mask,(t)->vmask,XkbMessage)
+
+/***====================================================================***/
+
+static void
+InitKeyTypesInfo(KeyTypesInfo * info, XkbDescPtr xkb, KeyTypesInfo * from)
+{
+    tok_ONE_LEVEL = XkbInternAtom(NULL, "ONE_LEVEL", False);
+    tok_TWO_LEVEL = XkbInternAtom(NULL, "TWO_LEVEL", False);
+    tok_ALPHABETIC = XkbInternAtom(NULL, "ALPHABETIC", False);
+    tok_KEYPAD = XkbInternAtom(NULL, "KEYPAD", False);
+    info->dpy = NULL;
+    info->name = uStringDup("default");
+    info->errorCount = 0;
+    info->stdPresent = 0;
+    info->nTypes = 0;
+    info->types = NULL;
+    info->dflt.defs.defined = 0;
+    info->dflt.defs.fileID = 0;
+    info->dflt.defs.merge = MergeOverride;
+    info->dflt.defs.next = NULL;
+    info->dflt.name = None;
+    info->dflt.mask = 0;
+    info->dflt.vmask = 0;
+    info->dflt.groupInfo = False;
+    info->dflt.numLevels = 1;
+    info->dflt.nEntries = info->dflt.szEntries = 0;
+    info->dflt.entries = NULL;
+    info->dflt.szNames = 0;
+    info->dflt.lvlNames = NULL;
+    info->dflt.preserve = NULL;
+    InitVModInfo(&info->vmods, xkb);
+    if (from != NULL)
+    {
+        info->dpy = from->dpy;
+        info->dflt = from->dflt;
+        if (from->dflt.entries)
+        {
+            info->dflt.entries = uTypedCalloc(from->dflt.szEntries,
+                                              XkbKTMapEntryRec);
+            if (info->dflt.entries)
+            {
+                unsigned sz = from->dflt.nEntries * sizeof(XkbKTMapEntryRec);
+                memcpy(info->dflt.entries, from->dflt.entries, sz);
+            }
+        }
+        if (from->dflt.lvlNames)
+        {
+            info->dflt.lvlNames = uTypedCalloc(from->dflt.szNames, Atom);
+            if (info->dflt.lvlNames)
+            {
+                register unsigned sz = from->dflt.szNames * sizeof(Atom);
+                memcpy(info->dflt.lvlNames, from->dflt.lvlNames, sz);
+            }
+        }
+        if (from->dflt.preserve)
+        {
+            PreserveInfo *old, *new, *last;
+            last = NULL;
+            old = from->dflt.preserve;
+            for (; old; old = (PreserveInfo *) old->defs.next)
+            {
+                new = uTypedAlloc(PreserveInfo);
+                if (!new)
+                    return;
+                *new = *old;
+                new->defs.next = NULL;
+                if (last)
+                    last->defs.next = (CommonInfo *) new;
+                else
+                    info->dflt.preserve = new;
+                last = new;
+            }
+        }
+    }
+    return;
+}
+
+static void
+FreeKeyTypeInfo(KeyTypeInfo * type)
+{
+    if (type->entries != NULL)
+    {
+        uFree(type->entries);
+        type->entries = NULL;
+    }
+    if (type->lvlNames != NULL)
+    {
+        uFree(type->lvlNames);
+        type->lvlNames = NULL;
+    }
+    if (type->preserve != NULL)
+    {
+        ClearCommonInfo(&type->preserve->defs);
+        type->preserve = NULL;
+    }
+    return;
+}
+
+static void
+FreeKeyTypesInfo(KeyTypesInfo * info)
+{
+    info->dpy = NULL;
+    if (info->name)
+        uFree(info->name);
+    info->name = NULL;
+    if (info->types)
+    {
+        register KeyTypeInfo *type;
+        for (type = info->types; type; type = (KeyTypeInfo *) type->defs.next)
+        {
+            FreeKeyTypeInfo(type);
+        }
+        info->types = (KeyTypeInfo *) ClearCommonInfo(&info->types->defs);
+    }
+    FreeKeyTypeInfo(&info->dflt);
+    return;
+}
+
+static KeyTypeInfo *
+NextKeyType(KeyTypesInfo * info)
+{
+    KeyTypeInfo *type;
+
+    type = uTypedAlloc(KeyTypeInfo);
+    if (type != NULL)
+    {
+        bzero(type, sizeof(KeyTypeInfo));
+        type->defs.fileID = info->fileID;
+        type->dpy = info->dpy;
+        info->types = (KeyTypeInfo *) AddCommonInfo(&info->types->defs,
+                                                    (CommonInfo *) type);
+        info->nTypes++;
+    }
+    return type;
+}
+
+static KeyTypeInfo *
+FindMatchingKeyType(KeyTypesInfo * info, KeyTypeInfo * new)
+{
+    KeyTypeInfo *old;
+
+    for (old = info->types; old; old = (KeyTypeInfo *) old->defs.next)
+    {
+        if (old->name == new->name)
+            return old;
+    }
+    return NULL;
+}
+
+static Bool
+ReportTypeBadWidth(const char *type, int has, int needs)
+{
+    ERROR3("Key type \"%s\" has %d levels, must have %d\n", type, has, needs);
+    ACTION("Illegal type definition ignored\n");
+    return False;
+}
+
+static Bool
+AddKeyType(XkbDescPtr xkb, KeyTypesInfo * info, KeyTypeInfo * new)
+{
+    KeyTypeInfo *old;
+
+    if (new->name == tok_ONE_LEVEL)
+    {
+        if (new->numLevels > 1)
+            return ReportTypeBadWidth("ONE_LEVEL", new->numLevels, 1);
+        info->stdPresent |= XkbOneLevelMask;
+    }
+    else if (new->name == tok_TWO_LEVEL)
+    {
+        if (new->numLevels > 2)
+            return ReportTypeBadWidth("TWO_LEVEL", new->numLevels, 2);
+        else if (new->numLevels < 2)
+            new->numLevels = 2;
+        info->stdPresent |= XkbTwoLevelMask;
+    }
+    else if (new->name == tok_ALPHABETIC)
+    {
+        if (new->numLevels > 2)
+            return ReportTypeBadWidth("ALPHABETIC", new->numLevels, 2);
+        else if (new->numLevels < 2)
+            new->numLevels = 2;
+        info->stdPresent |= XkbAlphabeticMask;
+    }
+    else if (new->name == tok_KEYPAD)
+    {
+        if (new->numLevels > 2)
+            return ReportTypeBadWidth("KEYPAD", new->numLevels, 2);
+        else if (new->numLevels < 2)
+            new->numLevels = 2;
+        info->stdPresent |= XkbKeypadMask;
+    }
+
+    old = FindMatchingKeyType(info, new);
+    if (old != NULL)
+    {
+        Bool report;
+        if ((new->defs.merge == MergeReplace)
+            || (new->defs.merge == MergeOverride))
+        {
+            KeyTypeInfo *next = (KeyTypeInfo *) old->defs.next;
+            if (((old->defs.fileID == new->defs.fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                WARN1("Multiple definitions of the %s key type\n",
+                      XkbAtomGetString(NULL, new->name));
+                ACTION("Earlier definition ignored\n");
+            }
+            FreeKeyTypeInfo(old);
+            *old = *new;
+            new->szEntries = new->nEntries = 0;
+            new->entries = NULL;
+            new->preserve = NULL;
+            new->lvlNames = NULL;
+            old->defs.next = &next->defs;
+            return True;
+        }
+        report = (old->defs.fileID == new->defs.fileID) && (warningLevel > 0);
+        if (report)
+        {
+            WARN1("Multiple definitions of the %s key type\n",
+                  XkbAtomGetString(NULL, new->name));
+            ACTION("Later definition ignored\n");
+        }
+        FreeKeyTypeInfo(new);
+        return True;
+    }
+    old = NextKeyType(info);
+    if (old == NULL)
+        return False;
+    *old = *new;
+    old->defs.next = NULL;
+    new->nEntries = new->szEntries = 0;
+    new->entries = NULL;
+    new->szNames = 0;
+    new->lvlNames = NULL;
+    new->preserve = NULL;
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+MergeIncludedKeyTypes(KeyTypesInfo * into,
+                      KeyTypesInfo * from, unsigned merge, XkbDescPtr xkb)
+{
+    KeyTypeInfo *type;
+
+    if (from->errorCount > 0)
+    {
+        into->errorCount += from->errorCount;
+        return;
+    }
+    if (into->name == NULL)
+    {
+        into->name = from->name;
+        from->name = NULL;
+    }
+    for (type = from->types; type; type = (KeyTypeInfo *) type->defs.next)
+    {
+        if (merge != MergeDefault)
+            type->defs.merge = merge;
+        if (!AddKeyType(xkb, into, type))
+            into->errorCount++;
+    }
+    into->stdPresent |= from->stdPresent;
+    return;
+}
+
+typedef void (*FileHandler) (XkbFile * /* file */ ,
+                             XkbDescPtr /* xkb */ ,
+                             unsigned /* merge */ ,
+                             KeyTypesInfo *     /* included */
+    );
+
+static Bool
+HandleIncludeKeyTypes(IncludeStmt * stmt,
+                      XkbDescPtr xkb, KeyTypesInfo * info, FileHandler hndlr)
+{
+    unsigned newMerge;
+    XkbFile *rtrn;
+    KeyTypesInfo included;
+    Bool haveSelf;
+
+    haveSelf = False;
+    if ((stmt->file == NULL) && (stmt->map == NULL))
+    {
+        haveSelf = True;
+        included = *info;
+        bzero(info, sizeof(KeyTypesInfo));
+    }
+    else if (ProcessIncludeFile(stmt, XkmTypesIndex, &rtrn, &newMerge))
+    {
+        InitKeyTypesInfo(&included, xkb, info);
+        included.fileID = included.dflt.defs.fileID = rtrn->id;
+        included.dflt.defs.merge = newMerge;
+
+        (*hndlr) (rtrn, xkb, newMerge, &included);
+        if (stmt->stmt != NULL)
+        {
+            if (included.name != NULL)
+                uFree(included.name);
+            included.name = stmt->stmt;
+            stmt->stmt = NULL;
+        }
+    }
+    else
+    {
+        info->errorCount += 10;
+        return False;
+    }
+    if ((stmt->next != NULL) && (included.errorCount < 1))
+    {
+        IncludeStmt *next;
+        unsigned op;
+        KeyTypesInfo next_incl;
+
+        for (next = stmt->next; next != NULL; next = next->next)
+        {
+            if ((next->file == NULL) && (next->map == NULL))
+            {
+                haveSelf = True;
+                MergeIncludedKeyTypes(&included, info, next->merge, xkb);
+                FreeKeyTypesInfo(info);
+            }
+            else if (ProcessIncludeFile(next, XkmTypesIndex, &rtrn, &op))
+            {
+                InitKeyTypesInfo(&next_incl, xkb, &included);
+                next_incl.fileID = next_incl.dflt.defs.fileID = rtrn->id;
+                next_incl.dflt.defs.merge = op;
+                (*hndlr) (rtrn, xkb, op, &next_incl);
+                MergeIncludedKeyTypes(&included, &next_incl, op, xkb);
+                FreeKeyTypesInfo(&next_incl);
+            }
+            else
+            {
+                info->errorCount += 10;
+                return False;
+            }
+        }
+    }
+    if (haveSelf)
+        *info = included;
+    else
+    {
+        MergeIncludedKeyTypes(info, &included, newMerge, xkb);
+        FreeKeyTypesInfo(&included);
+    }
+    return (info->errorCount == 0);
+}
+
+/***====================================================================***/
+
+static XkbKTMapEntryPtr
+FindMatchingMapEntry(KeyTypeInfo * type, unsigned mask, unsigned vmask)
+{
+    register int i;
+    XkbKTMapEntryPtr entry;
+
+    for (i = 0, entry = type->entries; i < type->nEntries; i++, entry++)
+    {
+        if ((entry->mods.real_mods == mask) && (entry->mods.vmods == vmask))
+            return entry;
+    }
+    return NULL;
+}
+
+static void
+DeleteLevel1MapEntries(KeyTypeInfo * type)
+{
+    register int i, n;
+
+    for (i = 0; i < type->nEntries; i++)
+    {
+        if (type->entries[i].level == 0)
+        {
+            for (n = i; n < type->nEntries - 1; n++)
+            {
+                type->entries[n] = type->entries[n + 1];
+            }
+            type->nEntries--;
+        }
+    }
+    return;
+}
+
+/**
+ * Return a pointer to the next free XkbKTMapEntry, reallocating space if
+ * necessary.
+ */
+static XkbKTMapEntryPtr
+NextMapEntry(KeyTypeInfo * type)
+{
+    if (type->entries == NULL)
+    {
+        type->entries = uTypedCalloc(2, XkbKTMapEntryRec);
+        if (type->entries == NULL)
+        {
+            ERROR1("Couldn't allocate map entries for %s\n", TypeTxt(type));
+            ACTION("Map entries lost\n");
+            return NULL;
+        }
+        type->szEntries = 2;
+        type->nEntries = 0;
+    }
+    else if (type->nEntries >= type->szEntries)
+    {
+        type->szEntries *= 2;
+        type->entries = uTypedRecalloc(type->entries,
+                                       type->nEntries, type->szEntries,
+                                       XkbKTMapEntryRec);
+        if (type->entries == NULL)
+        {
+            ERROR1("Couldn't reallocate map entries for %s\n", TypeTxt(type));
+            ACTION("Map entries lost\n");
+            return NULL;
+        }
+    }
+    return &type->entries[type->nEntries++];
+}
+
+Bool
+AddPreserve(XkbDescPtr xkb,
+            KeyTypeInfo * type, PreserveInfo * new, Bool clobber, Bool report)
+{
+    PreserveInfo *old;
+
+    old = type->preserve;
+    while (old != NULL)
+    {
+        if ((old->indexMods != new->indexMods) ||
+            (old->indexVMods != new->indexVMods))
+        {
+            old = (PreserveInfo *) old->defs.next;
+            continue;
+        }
+        if ((old->preMods == new->preMods)
+            && (old->preVMods == new->preVMods))
+        {
+            if (warningLevel > 9)
+            {
+                WARN2("Identical definitions for preserve[%s] in %s\n",
+                      PreserveIndexTxt(type, xkb, old), TypeTxt(type));
+                ACTION("Ignored\n");
+            }
+            return True;
+        }
+        if (report && (warningLevel > 0))
+        {
+            char *str;
+            WARN2("Multiple definitions for preserve[%s] in %s\n",
+                  PreserveIndexTxt(type, xkb, old), TypeTxt(type));
+
+            if (clobber)
+                str = PreserveTxt(type, xkb, new);
+            else
+                str = PreserveTxt(type, xkb, old);
+            ACTION1("Using %s, ", str);
+            if (clobber)
+                str = PreserveTxt(type, xkb, old);
+            else
+                str = PreserveTxt(type, xkb, new);
+            INFO1("ignoring %s\n", str);
+        }
+        if (clobber)
+        {
+            old->preMods = new->preMods;
+            old->preVMods = new->preVMods;
+        }
+        return True;
+    }
+    old = uTypedAlloc(PreserveInfo);
+    if (!old)
+    {
+        WSGO1("Couldn't allocate preserve in %s\n", TypeTxt(type));
+        ACTION1("Preserve[%s] lost\n", PreserveIndexTxt(type, xkb, old));
+        return False;
+    }
+    *old = *new;
+    old->matchingMapIndex = -1;
+    type->preserve =
+        (PreserveInfo *) AddCommonInfo(&type->preserve->defs, &old->defs);
+    return True;
+}
+
+/**
+ * Add a new KTMapEntry to the given key type. If an entry with the same mods
+ * already exists, the level is updated (if clobber is TRUE). Otherwise, a new
+ * entry is created.
+ *
+ * @param clobber Overwrite existing entry.
+ * @param report True if a warning is to be printed on.
+ */
+Bool
+AddMapEntry(XkbDescPtr xkb,
+            KeyTypeInfo * type,
+            XkbKTMapEntryPtr new, Bool clobber, Bool report)
+{
+    XkbKTMapEntryPtr old;
+
+    if ((old =
+         FindMatchingMapEntry(type, new->mods.real_mods, new->mods.vmods)))
+    {
+        if (report && (old->level != new->level))
+        {
+            unsigned use, ignore;
+            if (clobber)
+            {
+                use = new->level + 1;
+                ignore = old->level + 1;
+            }
+            else
+            {
+                use = old->level + 1;
+                ignore = new->level + 1;
+            }
+            WARN2("Multiple map entries for %s in %s\n",
+                  MapEntryTxt(type, xkb, new), TypeTxt(type));
+            ACTION2("Using %d, ignoring %d\n", use, ignore);
+        }
+        else if (warningLevel > 9)
+        {
+            WARN3("Multiple occurences of map[%s]= %d in %s\n",
+                  MapEntryTxt(type, xkb, new), new->level + 1, TypeTxt(type));
+            ACTION("Ignored\n");
+            return True;
+        }
+        if (clobber)
+            old->level = new->level;
+        return True;
+    }
+    if ((old = NextMapEntry(type)) == NULL)
+        return False;           /* allocation failure, already reported */
+    if (new->level >= type->numLevels)
+        type->numLevels = new->level + 1;
+    if (new->mods.vmods == 0)
+        old->active = True;
+    else
+        old->active = False;
+    old->mods.mask = new->mods.real_mods;
+    old->mods.real_mods = new->mods.real_mods;
+    old->mods.vmods = new->mods.vmods;
+    old->level = new->level;
+    return True;
+}
+
+static LookupEntry lnames[] = {
+    {"level1", 1},
+    {"level2", 2},
+    {"level3", 3},
+    {"level4", 4},
+    {"level5", 5},
+    {"level6", 6},
+    {"level7", 7},
+    {"level8", 8},
+    {NULL, 0}
+};
+
+static Bool
+SetMapEntry(KeyTypeInfo * type,
+            XkbDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult rtrn;
+    XkbKTMapEntryRec entry;
+
+    if (arrayNdx == NULL)
+        return ReportTypeShouldBeArray(type, "map entry");
+    if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (XPointer) xkb))
+        return ReportTypeBadType(type, "map entry", "modifier mask");
+    entry.mods.real_mods = rtrn.uval & 0xff;      /* modifiers < 512 */
+    entry.mods.vmods = (rtrn.uval >> 8) & 0xffff; /* modifiers > 512 */
+    if ((entry.mods.real_mods & (~type->mask)) ||
+        ((entry.mods.vmods & (~type->vmask)) != 0))
+    {
+        if (warningLevel > 0)
+        {
+            WARN1("Map entry for unused modifiers in %s\n", TypeTxt(type));
+            ACTION1("Using %s instead of ",
+                    XkbVModMaskText(type->dpy, xkb,
+                                    entry.mods.real_mods & type->mask,
+                                    entry.mods.vmods & type->vmask,
+                                    XkbMessage));
+            INFO1("%s\n", MapEntryTxt(type, xkb, &entry));
+        }
+        entry.mods.real_mods &= type->mask;
+        entry.mods.vmods &= type->vmask;
+    }
+    if (!ExprResolveInteger(value, &rtrn, SimpleLookup, (XPointer) lnames))
+    {
+        ERROR("Level specifications in a key type must be integer\n");
+        ACTION("Ignoring malformed level specification\n");
+        return False;
+    }
+    if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
+    {
+        ERROR3("Shift level %d out of range (1..%d) in key type %s\n",
+               XkbMaxShiftLevel + 1, rtrn.ival, TypeTxt(type));
+        ACTION1("Ignoring illegal definition of map[%s]\n",
+                MapEntryTxt(type, xkb, &entry));
+        return False;
+    }
+    entry.level = rtrn.ival - 1;
+    return AddMapEntry(xkb, type, &entry, True, True);
+}
+
+static Bool
+SetPreserve(KeyTypeInfo * type,
+            XkbDescPtr xkb, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult rtrn;
+    PreserveInfo new;
+
+    if (arrayNdx == NULL)
+        return ReportTypeShouldBeArray(type, "preserve entry");
+    if (!ExprResolveModMask(arrayNdx, &rtrn, LookupVModMask, (XPointer) xkb))
+        return ReportTypeBadType(type, "preserve entry", "modifier mask");
+    new.defs = type->defs;
+    new.defs.next = NULL;
+    new.indexMods = rtrn.uval & 0xff;
+    new.indexVMods = (rtrn.uval >> 8) & 0xffff;
+    if ((new.indexMods & (~type->mask)) || (new.indexVMods & (~type->vmask)))
+    {
+        if (warningLevel > 0)
+        {
+            WARN1("Preserve for modifiers not used by the %s type\n",
+                  TypeTxt(type));
+            ACTION1("Index %s converted to ",
+                    PreserveIndexTxt(type, xkb, &new));
+        }
+        new.indexMods &= type->mask;
+        new.indexVMods &= type->vmask;
+        if (warningLevel > 0)
+            INFO1("%s\n", PreserveIndexTxt(type, xkb, &new));
+    }
+    if (!ExprResolveModMask(value, &rtrn, LookupVModMask, (XPointer) xkb))
+    {
+        ERROR("Preserve value in a key type is not a modifier mask\n");
+        ACTION2("Ignoring preserve[%s] in type %s\n",
+                PreserveIndexTxt(type, xkb, &new), TypeTxt(type));
+        return False;
+    }
+    new.preMods = rtrn.uval & 0xff;
+    new.preVMods = (rtrn.uval >> 16) & 0xffff;
+    if ((new.preMods & (~new.indexMods))
+        || (new.preVMods && (~new.indexVMods)))
+    {
+        if (warningLevel > 0)
+        {
+            WARN2("Illegal value for preserve[%s] in type %s\n",
+                  PreserveTxt(type, xkb, &new), TypeTxt(type));
+            ACTION1("Converted %s to ", PreserveIndexTxt(type, xkb, &new));
+        }
+        new.preMods &= new.indexMods;
+        new.preVMods &= new.indexVMods;
+        if (warningLevel > 0)
+        {
+            INFO1("%s\n", PreserveIndexTxt(type, xkb, &new));
+        }
+    }
+    return AddPreserve(xkb, type, &new, True, True);
+}
+
+/***====================================================================***/
+
+Bool
+AddLevelName(KeyTypeInfo * type,
+             unsigned level, Atom name, Bool clobber, Bool report)
+{
+    if ((type->lvlNames == NULL) || (type->szNames <= level))
+    {
+        type->lvlNames =
+            uTypedRecalloc(type->lvlNames, type->szNames, level + 1, Atom);
+        if (type->lvlNames == NULL)
+        {
+            ERROR1("Couldn't allocate level names for type %s\n",
+                   TypeTxt(type));
+            ACTION("Level names lost\n");
+            type->szNames = 0;
+            return False;
+        }
+        type->szNames = level + 1;
+    }
+    else if (type->lvlNames[level] == name)
+    {
+        if (warningLevel > 9)
+        {
+            WARN2("Duplicate names for level %d of key type %s\n",
+                  level + 1, TypeTxt(type));
+            ACTION("Ignored\n");
+        }
+        return True;
+    }
+    else if (type->lvlNames[level] != None)
+    {
+        if (warningLevel > 0)
+        {
+            char *old, *new;
+            old = XkbAtomText(type->dpy, type->lvlNames[level], XkbMessage);
+            new = XkbAtomText(type->dpy, name, XkbMessage);
+            WARN2("Multiple names for level %d of key type %s\n",
+                  level + 1, TypeTxt(type));
+            if (clobber)
+                ACTION2("Using %s, ignoring %s\n", new, old);
+            else
+                ACTION2("Using %s, ignoring %s\n", old, new);
+        }
+        if (!clobber)
+            return True;
+    }
+    if (level >= type->numLevels)
+        type->numLevels = level + 1;
+    type->lvlNames[level] = name;
+    return True;
+}
+
+static Bool
+SetLevelName(KeyTypeInfo * type, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult rtrn;
+    unsigned level;
+
+    if (arrayNdx == NULL)
+        return ReportTypeShouldBeArray(type, "level name");
+    if (!ExprResolveInteger(arrayNdx, &rtrn, SimpleLookup, (XPointer) lnames))
+        return ReportTypeBadType(type, "level name", "integer");
+    if ((rtrn.ival < 1) || (rtrn.ival > XkbMaxShiftLevel + 1))
+    {
+        ERROR3("Level name %d out of range (1..%d) in key type %s\n",
+               rtrn.ival,
+               XkbMaxShiftLevel + 1,
+               XkbAtomText(type->dpy, type->name, XkbMessage));
+        ACTION("Ignoring illegal level name definition\n");
+        return False;
+    }
+    level = rtrn.ival - 1;
+    if (!ExprResolveString(value, &rtrn, NULL, NULL))
+    {
+        ERROR2("Non-string name for level %d in key type %s\n", level + 1,
+               XkbAtomText(type->dpy, type->name, XkbMessage));
+        ACTION("Ignoring illegal level name definition\n");
+        return False;
+    }
+    return
+        AddLevelName(type, level, XkbInternAtom(NULL, rtrn.str, False), True,
+                     True);
+}
+
+/***====================================================================***/
+
+/**
+ * Parses the fields in a type "..." { } description.
+ *
+ * @param field The field to parse (e.g. modifiers, map, level_name)
+ */
+static Bool
+SetKeyTypeField(KeyTypeInfo * type,
+                XkbDescPtr xkb,
+                char *field,
+                ExprDef * arrayNdx, ExprDef * value, KeyTypesInfo * info)
+{
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "modifiers") == 0)
+    {
+        unsigned mods, vmods;
+        if (arrayNdx != NULL)
+        {
+            WARN("The modifiers field of a key type is not an array\n");
+            ACTION("Illegal array subscript ignored\n");
+        }
+        /* get modifier mask for current type */
+        if (!ExprResolveModMask(value, &tmp, LookupVModMask, (XPointer) xkb))
+        {
+            ERROR("Key type mask field must be a modifier mask\n");
+            ACTION("Key type definition ignored\n");
+            return False;
+        }
+        mods = tmp.uval & 0xff; /* core mods */
+        vmods = (tmp.uval >> 8) & 0xffff; /* xkb virtual mods */
+        if (type->defs.defined & _KT_Mask)
+        {
+            WARN1("Multiple modifier mask definitions for key type %s\n",
+                  XkbAtomText(type->dpy, type->name, XkbMessage));
+            ACTION1("Using %s, ", TypeMaskTxt(type, xkb));
+            INFO1("ignoring %s\n", XkbVModMaskText(type->dpy, xkb, mods,
+                                                   vmods, XkbMessage));
+            return False;
+        }
+        type->mask = mods;
+        type->vmask = vmods;
+        type->defs.defined |= _KT_Mask;
+        return True;
+    }
+    else if (uStrCaseCmp(field, "map") == 0)
+    {
+        type->defs.defined |= _KT_Map;
+        return SetMapEntry(type, xkb, arrayNdx, value);
+    }
+    else if (uStrCaseCmp(field, "preserve") == 0)
+    {
+        type->defs.defined |= _KT_Preserve;
+        return SetPreserve(type, xkb, arrayNdx, value);
+    }
+    else if ((uStrCaseCmp(field, "levelname") == 0) ||
+             (uStrCaseCmp(field, "level_name") == 0))
+    {
+        type->defs.defined |= _KT_LevelNames;
+        return SetLevelName(type, arrayNdx, value);
+    }
+    ERROR2("Unknown field %s in key type %s\n", field, TypeTxt(type));
+    ACTION("Definition ignored\n");
+    return False;
+}
+
+static Bool
+HandleKeyTypeVar(VarDef * stmt, XkbDescPtr xkb, KeyTypesInfo * info)
+{
+    ExprResult elem, field;
+    ExprDef *arrayNdx;
+
+    if (!ExprResolveLhs(stmt->name, &elem, &field, &arrayNdx))
+        return False;           /* internal error, already reported */
+    if (elem.str && (uStrCaseCmp(elem.str, "type") == 0))
+        return SetKeyTypeField(&info->dflt, xkb, field.str, arrayNdx,
+                               stmt->value, info);
+    if (elem.str != NULL)
+    {
+        ERROR1("Default for unknown element %s\n", uStringText(elem.str));
+        ACTION1("Value for field %s ignored\n", uStringText(field.str));
+    }
+    else if (field.str != NULL)
+    {
+        ERROR1("Default defined for unknown field %s\n",
+               uStringText(field.str));
+        ACTION("Ignored\n");
+    }
+    return False;
+}
+
+static int
+HandleKeyTypeBody(VarDef * def,
+                  XkbDescPtr xkb, KeyTypeInfo * type, KeyTypesInfo * info)
+{
+    int ok = 1;
+    ExprResult tmp, field;
+    ExprDef *arrayNdx;
+
+    for (; def != NULL; def = (VarDef *) def->common.next)
+    {
+        if ((def->name) && (def->name->type == ExprFieldRef))
+        {
+            ok = HandleKeyTypeVar(def, xkb, info);
+            continue;
+        }
+        ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
+        if (ok)
+            ok = SetKeyTypeField(type, xkb, field.str, arrayNdx, def->value,
+                                 info);
+    }
+    return ok;
+}
+
+/**
+ * Process a type "XYZ" { } specification in the xkb_types section.
+ *
+ */
+static int
+HandleKeyTypeDef(KeyTypeDef * def,
+                 XkbDescPtr xkb, unsigned merge, KeyTypesInfo * info)
+{
+    register int i;
+    KeyTypeInfo type;
+
+    if (def->merge != MergeDefault)
+        merge = def->merge;
+
+    type.defs.defined = 0;
+    type.defs.fileID = info->fileID;
+    type.defs.merge = merge;
+    type.defs.next = NULL;
+    type.dpy = info->dpy;
+    type.name = def->name;
+    type.mask = info->dflt.mask;
+    type.vmask = info->dflt.vmask;
+    type.groupInfo = info->dflt.groupInfo;
+    type.numLevels = 1;
+    type.nEntries = type.szEntries = 0;
+    type.entries = NULL;
+    type.szNames = 0;
+    type.lvlNames = NULL;
+    type.preserve = NULL;
+
+    /* Parse the actual content. */
+    if (!HandleKeyTypeBody(def->body, xkb, &type, info))
+    {
+        info->errorCount++;
+        return False;
+    }
+
+    /* now copy any appropriate map, preserve or level names from the */
+    /* default type */
+    for (i = 0; i < info->dflt.nEntries; i++)
+    {
+        XkbKTMapEntryPtr dflt;
+        dflt = &info->dflt.entries[i];
+        if (((dflt->mods.real_mods & type.mask) == dflt->mods.real_mods) &&
+            ((dflt->mods.vmods & type.vmask) == dflt->mods.vmods))
+        {
+            AddMapEntry(xkb, &type, dflt, False, False);
+        }
+    }
+    if (info->dflt.preserve)
+    {
+        PreserveInfo *dflt = info->dflt.preserve;
+        while (dflt)
+        {
+            if (((dflt->indexMods & type.mask) == dflt->indexMods) &&
+                ((dflt->indexVMods & type.vmask) == dflt->indexVMods))
+            {
+                AddPreserve(xkb, &type, dflt, False, False);
+            }
+            dflt = (PreserveInfo *) dflt->defs.next;
+        }
+    }
+    for (i = 0; i < info->dflt.szNames; i++)
+    {
+        if ((i < type.numLevels) && (info->dflt.lvlNames[i] != None))
+        {
+            AddLevelName(&type, i, info->dflt.lvlNames[i], False, False);
+        }
+    }
+    /* Now add the new keytype to the info struct */
+    if (!AddKeyType(xkb, info, &type))
+    {
+        info->errorCount++;
+        return False;
+    }
+    return True;
+}
+
+/**
+ * Process an xkb_types section.
+ *
+ * @param file The parsed xkb_types section.
+ * @param merge Merge Strategy (e.g. MergeOverride)
+ * @param info Pointer to memory where the outcome will be stored.
+ */
+static void
+HandleKeyTypesFile(XkbFile * file,
+                   XkbDescPtr xkb, unsigned merge, KeyTypesInfo * info)
+{
+    ParseCommon *stmt;
+
+    info->name = uStringDup(file->name);
+    stmt = file->defs;
+    while (stmt)
+    {
+        switch (stmt->stmtType)
+        {
+        case StmtInclude:
+            if (!HandleIncludeKeyTypes((IncludeStmt *) stmt, xkb, info,
+                                       HandleKeyTypesFile))
+                info->errorCount++;
+            break;
+        case StmtKeyTypeDef: /* e.g. type "ONE_LEVEL" */
+            if (!HandleKeyTypeDef((KeyTypeDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtVarDef:
+            if (!HandleKeyTypeVar((VarDef *) stmt, xkb, info))
+                info->errorCount++;
+            break;
+        case StmtVModDef: /* virtual_modifiers NumLock, ... */
+            if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
+                info->errorCount++;
+            break;
+        case StmtKeyAliasDef:
+            ERROR("Key type files may not include other declarations\n");
+            ACTION("Ignoring definition of key alias\n");
+            info->errorCount++;
+            break;
+        case StmtKeycodeDef:
+            ERROR("Key type files may not include other declarations\n");
+            ACTION("Ignoring definition of key name\n");
+            info->errorCount++;
+            break;
+        case StmtInterpDef:
+            ERROR("Key type files may not include other declarations\n");
+            ACTION("Ignoring definition of symbol interpretation\n");
+            info->errorCount++;
+            break;
+        default:
+            WSGO1("Unexpected statement type %d in HandleKeyTypesFile\n",
+                  stmt->stmtType);
+            break;
+        }
+        stmt = stmt->next;
+        if (info->errorCount > 10)
+        {
+#ifdef NOISY
+            ERROR("Too many errors\n");
+#endif
+            ACTION1("Abandoning keytypes file \"%s\"\n", file->topName);
+            break;
+        }
+    }
+    return;
+}
+
+static Bool
+CopyDefToKeyType(XkbDescPtr xkb, XkbKeyTypePtr type, KeyTypeInfo * def)
+{
+    register int i;
+    PreserveInfo *pre;
+
+    for (pre = def->preserve; pre != NULL;
+         pre = (PreserveInfo *) pre->defs.next)
+    {
+        XkbKTMapEntryPtr match;
+        XkbKTMapEntryRec tmp;
+        tmp.mods.real_mods = pre->indexMods;
+        tmp.mods.vmods = pre->indexVMods;
+        tmp.level = 0;
+        AddMapEntry(xkb, def, &tmp, False, False);
+        match = FindMatchingMapEntry(def, pre->indexMods, pre->indexVMods);
+        if (!match)
+        {
+            WSGO("Couldn't find matching entry for preserve\n");
+            ACTION("Aborting\n");
+            return False;
+        }
+        pre->matchingMapIndex = match - def->entries;
+    }
+    type->mods.real_mods = def->mask;
+    type->mods.vmods = def->vmask;
+    type->num_levels = def->numLevels;
+    type->map_count = def->nEntries;
+    type->map = def->entries;
+    if (def->preserve)
+    {
+        type->preserve = uTypedCalloc(type->map_count, XkbModsRec);
+        if (!type->preserve)
+        {
+            WARN("Couldn't allocate preserve array in CopyDefToKeyType\n");
+            ACTION1("Preserve setting for type %s lost\n",
+                    XkbAtomText(def->dpy, def->name, XkbMessage));
+        }
+        else
+        {
+            pre = def->preserve;
+            for (; pre != NULL; pre = (PreserveInfo *) pre->defs.next)
+            {
+                int ndx = pre->matchingMapIndex;
+                type->preserve[ndx].mask = pre->preMods;
+                type->preserve[ndx].real_mods = pre->preMods;
+                type->preserve[ndx].vmods = pre->preVMods;
+            }
+        }
+    }
+    else
+        type->preserve = NULL;
+    type->name = (Atom) def->name;
+    if (def->szNames > 0)
+    {
+        type->level_names = uTypedCalloc(def->numLevels, Atom);
+
+        /* assert def->szNames<=def->numLevels */
+        for (i = 0; i < def->szNames; i++)
+        {
+            type->level_names[i] = (Atom) def->lvlNames[i];
+        }
+    }
+    else
+    {
+        type->level_names = NULL;
+    }
+
+    def->nEntries = def->szEntries = 0;
+    def->entries = NULL;
+    return XkbComputeEffectiveMap(xkb, type, NULL);
+}
+
+Bool
+CompileKeyTypes(XkbFile * file, XkbFileInfo * result, unsigned merge)
+{
+    KeyTypesInfo info;
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    InitKeyTypesInfo(&info, xkb, NULL);
+    info.fileID = file->id;
+    HandleKeyTypesFile(file, xkb, merge, &info);
+
+    if (info.errorCount == 0)
+    {
+        register int i;
+        register KeyTypeInfo *def;
+        register XkbKeyTypePtr type, next;
+
+        if (info.name != NULL)
+        {
+            if (XkbAllocNames(xkb, XkbTypesNameMask, 0, 0) == Success)
+                xkb->names->types = XkbInternAtom(xkb->dpy, info.name, False);
+            else
+            {
+                WSGO("Couldn't allocate space for types name\n");
+                ACTION2("Name \"%s\" (from %s) NOT assigned\n",
+                        scanFile, info.name);
+            }
+        }
+        i = info.nTypes;
+        if ((info.stdPresent & XkbOneLevelMask) == 0)
+            i++;
+        if ((info.stdPresent & XkbTwoLevelMask) == 0)
+            i++;
+        if ((info.stdPresent & XkbKeypadMask) == 0)
+            i++;
+        if ((info.stdPresent & XkbAlphabeticMask) == 0)
+            i++;
+        if (XkbAllocClientMap(xkb, XkbKeyTypesMask, i) != Success)
+        {
+            WSGO("Couldn't allocate client map\n");
+            ACTION("Exiting\n");
+            return False;
+        }
+        xkb->map->num_types = i;
+        if (XkbAllRequiredTypes & (~info.stdPresent))
+        {
+            unsigned missing, keypadVMod;
+
+            missing = XkbAllRequiredTypes & (~info.stdPresent);
+            keypadVMod = FindKeypadVMod(xkb);
+            if (XkbInitCanonicalKeyTypes(xkb, missing, keypadVMod) != Success)
+            {
+                WSGO("Couldn't initialize canonical key types\n");
+                ACTION("Exiting\n");
+                return False;
+            }
+            if (missing & XkbOneLevelMask)
+                xkb->map->types[XkbOneLevelIndex].name = tok_ONE_LEVEL;
+            if (missing & XkbTwoLevelMask)
+                xkb->map->types[XkbTwoLevelIndex].name = tok_TWO_LEVEL;
+            if (missing & XkbAlphabeticMask)
+                xkb->map->types[XkbAlphabeticIndex].name = tok_ALPHABETIC;
+            if (missing & XkbKeypadMask)
+                xkb->map->types[XkbKeypadIndex].name = tok_KEYPAD;
+        }
+        next = &xkb->map->types[XkbLastRequiredType + 1];
+        for (i = 0, def = info.types; i < info.nTypes; i++)
+        {
+            if (def->name == tok_ONE_LEVEL)
+                type = &xkb->map->types[XkbOneLevelIndex];
+            else if (def->name == tok_TWO_LEVEL)
+                type = &xkb->map->types[XkbTwoLevelIndex];
+            else if (def->name == tok_ALPHABETIC)
+                type = &xkb->map->types[XkbAlphabeticIndex];
+            else if (def->name == tok_KEYPAD)
+                type = &xkb->map->types[XkbKeypadIndex];
+            else
+                type = next++;
+            DeleteLevel1MapEntries(def);
+            if (!CopyDefToKeyType(xkb, type, def))
+                return False;
+            def = (KeyTypeInfo *) def->defs.next;
+        }
+        return True;
+    }
+    return False;
+}
diff --git a/src/xkbcomp/listing.c b/src/xkbcomp/listing.c
new file mode 100644 (file)
index 0000000..146ecba
--- /dev/null
@@ -0,0 +1,495 @@
+/************************************************************
+ Copyright 1996 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 1988, 1998  The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice 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
+OPEN GROUP 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.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+
+Copyright 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
+
+                        All Rights Reserved
+
+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 Digital not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.  
+
+DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+DIGITAL 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 <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <X11/keysym.h>
+
+#if defined(sgi)
+#include <malloc.h>
+#endif
+
+#define        DEBUG_VAR listingDebug
+#include "xkbcomp.h"
+#include <stdlib.h>
+
+#ifdef _POSIX_SOURCE
+# include <limits.h>
+#else
+# define _POSIX_SOURCE
+# include <limits.h>
+# undef _POSIX_SOURCE
+#endif
+
+#ifndef PATH_MAX
+#ifdef WIN32
+#define PATH_MAX 512
+#else
+#include <sys/param.h>
+#endif
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+#endif
+
+#ifdef WIN32
+# include <windows.h>
+# define FileName(file) file.cFileName
+# undef TEXT
+# undef ALTERNATE
+#else
+# include <dirent.h>
+# define FileName(file) file->d_name
+#endif
+
+#include "xkbpath.h"
+#include "parseutils.h"
+#include "misc.h"
+#include "tokens.h"
+#include <X11/extensions/XKBgeom.h>
+
+#define        lowbit(x)       ((x) & (-(x)))
+
+unsigned int listingDebug;
+
+static int szListing = 0;
+static int nListed = 0;
+static int nFilesListed = 0;
+
+typedef struct _Listing
+{
+    char *file;
+    char *map;
+} Listing;
+
+static int szMapOnly;
+static int nMapOnly;
+static char **mapOnly;
+
+static Listing *list = NULL;
+
+/***====================================================================***/
+
+int
+AddMapOnly(char *map)
+{
+    if (nMapOnly >= szMapOnly)
+    {
+        if (szMapOnly < 1)
+            szMapOnly = 5;
+        else
+            szMapOnly *= 2;
+        mapOnly = uTypedRealloc(list, szMapOnly, char *);
+        if (!mapOnly)
+        {
+            WSGO("Couldn't allocate list of maps\n");
+            return 0;
+        }
+    }
+    mapOnly[nMapOnly++] = map;
+    return 1;
+}
+
+int
+AddListing(char *file, char *map)
+{
+    if (nListed >= szListing)
+    {
+        if (szListing < 1)
+            szListing = 10;
+        else
+            szListing *= 2;
+        list = uTypedRealloc(list, szListing, Listing);
+        if (!list)
+        {
+            WSGO("Couldn't allocate list of files and maps\n");
+            ACTION("Exiting\n");
+            exit(1);
+        }
+    }
+
+    list[nListed].file = file;
+    list[nListed].map = map;
+    nListed++;
+    if (file != NULL)
+        nFilesListed++;
+    return 1;
+}
+
+/***====================================================================***/
+
+static void
+ListFile(FILE * outFile, char *fileName, XkbFile * map)
+{
+    register unsigned flags;
+    char *mapName;
+
+    flags = map->flags;
+    if ((flags & XkbLC_Hidden) && (!(verboseLevel & WantHiddenMaps)))
+        return;
+    if ((flags & XkbLC_Partial) && (!(verboseLevel & WantPartialMaps)))
+        return;
+    if (verboseLevel & WantLongListing)
+    {
+        fprintf(outFile, (flags & XkbLC_Hidden) ? "h" : "-");
+        fprintf(outFile, (flags & XkbLC_Default) ? "d" : "-");
+        fprintf(outFile, (flags & XkbLC_Partial) ? "p" : "-");
+        fprintf(outFile, "----- ");
+        if (map->type == XkmSymbolsIndex)
+        {
+            fprintf(outFile, (flags & XkbLC_AlphanumericKeys) ? "a" : "-");
+            fprintf(outFile, (flags & XkbLC_ModifierKeys) ? "m" : "-");
+            fprintf(outFile, (flags & XkbLC_KeypadKeys) ? "k" : "-");
+            fprintf(outFile, (flags & XkbLC_FunctionKeys) ? "f" : "-");
+            fprintf(outFile, (flags & XkbLC_AlternateGroup) ? "g" : "-");
+            fprintf(outFile, "--- ");
+        }
+        else
+            fprintf(outFile, "-------- ");
+    }
+    mapName = map->name;
+    if ((!(verboseLevel & WantFullNames)) && ((flags & XkbLC_Default) != 0))
+        mapName = NULL;
+    if (dirsToStrip > 0)
+    {
+        char *tmp, *last;
+        int i;
+        for (i = 0, tmp = last = fileName; (i < dirsToStrip) && tmp; i++)
+        {
+            last = tmp;
+            tmp = strchr(tmp, '/');
+            if (tmp != NULL)
+                tmp++;
+        }
+        fileName = (tmp ? tmp : last);
+    }
+    if (mapName)
+        fprintf(outFile, "%s(%s)\n", fileName, mapName);
+    else
+        fprintf(outFile, "%s\n", fileName);
+    return;
+}
+
+/***====================================================================***/
+
+static int
+AddDirectory(char *head, char *ptrn, char *rest, char *map)
+{
+#ifdef WIN32
+    HANDLE dirh;
+    WIN32_FIND_DATA file;
+#else
+    DIR *dirp;
+    struct dirent *file;
+#endif
+    int nMatch;
+
+    if (map == NULL)
+    {
+        char *tmp = ptrn;
+        if ((rest == NULL) && (ptrn != NULL) && (strchr(ptrn, '/') == NULL))
+        {
+            tmp = ptrn;
+            map = strchr(ptrn, '(');
+        }
+        else if ((rest == NULL) && (ptrn == NULL) &&
+                 (head != NULL) && (strchr(head, '/') == NULL))
+        {
+            tmp = head;
+            map = strchr(head, '(');
+        }
+        if (map != NULL)
+        {
+            tmp = strchr(tmp, ')');
+            if ((tmp == NULL) || (tmp[1] != '\0'))
+            {
+                ERROR1("File and map must have the format file(map)\n");
+                return 0;
+            }
+            *map = '\0';
+            map++;
+            *tmp = '\0';
+        }
+    }
+#ifdef WIN32
+    if ((dirh = FindFirstFile("*.*", &file)) == INVALID_HANDLE_VALUE)
+        return 0;
+#else
+    if ((dirp = opendir((head ? head : "."))) == NULL)
+        return 0;
+    nMatch = 0;
+#endif
+#ifdef WIN32
+    do
+#else
+    while ((file = readdir(dirp)) != NULL)
+#endif
+    {
+        char *tmp, *filename;
+        struct stat sbuf;
+
+        filename = FileName(file);
+        if (!filename || filename[0] == '.')
+            continue;
+        if (ptrn && (!XkbNameMatchesPattern(filename, ptrn)))
+            continue;
+        tmp =
+            (char *) uAlloc((head ? strlen(head) : 0) + strlen(filename) + 2);
+        if (!tmp)
+            continue;
+        sprintf(tmp, "%s%s%s", (head ? head : ""), (head ? "/" : ""),
+                filename);
+        if (stat(tmp, &sbuf) < 0)
+        {
+            uFree(tmp);
+            continue;
+        }
+        if (((rest != NULL) && (!S_ISDIR(sbuf.st_mode))) ||
+            ((map != NULL) && (S_ISDIR(sbuf.st_mode))))
+        {
+            uFree(tmp);
+            continue;
+        }
+        if (S_ISDIR(sbuf.st_mode))
+        {
+            if ((rest != NULL) || (verboseLevel & ListRecursive))
+                nMatch += AddDirectory(tmp, rest, NULL, map);
+        }
+        else
+            nMatch += AddListing(tmp, map);
+    }
+#ifdef WIN32
+    while (FindNextFile(dirh, &file));
+#endif
+    return nMatch;
+}
+
+/***====================================================================***/
+
+Bool
+AddMatchingFiles(char *head_in)
+{
+    char *str, *head, *ptrn, *rest = NULL;
+
+    if (head_in == NULL)
+        return 0;
+    ptrn = NULL;
+    for (str = head_in; (*str != '\0') && (*str != '?') && (*str != '*');
+         str++)
+    {
+        if ((str != head_in) && (*str == '/'))
+            ptrn = str;
+    }
+    if (*str == '\0')
+    {                           /* no wildcards */
+        head = head_in;
+        ptrn = NULL;
+        rest = NULL;
+    }
+    else if (ptrn == NULL)
+    {                           /* no slash before the first wildcard */
+        head = NULL;
+        ptrn = head_in;
+    }
+    else
+    {                           /* slash followed by wildcard */
+        head = head_in;
+        *ptrn = '\0';
+        ptrn++;
+    }
+    if (ptrn)
+    {
+        rest = strchr(ptrn, '/');
+        if (rest != NULL)
+        {
+            *rest = '\0';
+            rest++;
+        }
+    }
+    if (((rest && ptrn)
+         && ((strchr(ptrn, '(') != NULL) || (strchr(ptrn, ')') != NULL)))
+        || (head
+            && ((strchr(head, '(') != NULL) || (strchr(head, ')') != NULL))))
+    {
+        ERROR1("Files/maps to list must have the form file(map)\n");
+        ACTION("Illegal specifier ignored\n");
+        return 0;
+    }
+    return AddDirectory(head, ptrn, rest, NULL);
+}
+
+/***====================================================================***/
+
+static Bool
+MapMatches(char *mapToConsider, char *ptrn)
+{
+    int i;
+
+    if (ptrn != NULL)
+        return XkbNameMatchesPattern(mapToConsider, ptrn);
+    if (nMapOnly < 1)
+        return True;
+    for (i = 0; i < nMapOnly; i++)
+    {
+        if (XkbNameMatchesPattern(mapToConsider, mapOnly[i]))
+            return True;
+    }
+    return False;
+}
+
+int
+GenerateListing(char *out_name)
+{
+    int i;
+    FILE *inputFile, *outFile;
+    XkbFile *rtrn, *mapToUse;
+    unsigned oldWarningLevel;
+    char *mapName;
+
+    if (nFilesListed < 1)
+    {
+        ERROR1("Must specify at least one file or pattern to list\n");
+        return 0;
+    }
+    if ((!out_name) || ((out_name[0] == '-') && (out_name[1] == '\0')))
+        outFile = stdout;
+    else if ((outFile = fopen(out_name, "w")) == NULL)
+    {
+        ERROR1("Cannot open \"%s\" to write keyboard description\n",
+               out_name);
+        ACTION("Exiting\n");
+        return 0;
+    }
+#ifdef DEBUG
+    if (warningLevel > 9)
+        fprintf(stderr, "should list:\n");
+#endif
+    for (i = 0; i < nListed; i++)
+    {
+#ifdef DEBUG
+        if (warningLevel > 9)
+        {
+            fprintf(stderr, "%s(%s)\n",
+                    (list[i].file ? list[i].file : "*"),
+                    (list[i].map ? list[i].map : "*"));
+        }
+#endif
+        oldWarningLevel = warningLevel;
+        warningLevel = 0;
+        if (list[i].file)
+        {
+            struct stat sbuf;
+
+            if (stat(list[i].file, &sbuf) < 0)
+            {
+                if (oldWarningLevel > 5)
+                    WARN1("Couldn't open \"%s\"\n", list[i].file);
+                continue;
+            }
+            if (S_ISDIR(sbuf.st_mode))
+            {
+                if (verboseLevel & ListRecursive)
+                    AddDirectory(list[i].file, NULL, NULL, NULL);
+                continue;
+            }
+
+            inputFile = fopen(list[i].file, "r");
+            if (!inputFile)
+            {
+                if (oldWarningLevel > 5)
+                    WARN1("Couldn't open \"%s\"\n", list[i].file);
+                continue;
+            }
+            setScanState(list[i].file, 1);
+            if (XKBParseFile(inputFile, &rtrn) && (rtrn != NULL))
+            {
+                mapName = list[i].map;
+                mapToUse = rtrn;
+                for (; mapToUse; mapToUse = (XkbFile *) mapToUse->common.next)
+                {
+                    if (!MapMatches(mapToUse->name, mapName))
+                        continue;
+                    ListFile(outFile, list[i].file, mapToUse);
+                }
+            }
+            fclose(inputFile);
+        }
+        warningLevel = oldWarningLevel;
+    }
+    return 1;
+}
diff --git a/src/xkbcomp/misc.c b/src/xkbcomp/misc.c
new file mode 100644 (file)
index 0000000..0e4f61d
--- /dev/null
@@ -0,0 +1,576 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "xkbpath.h"
+#include "tokens.h"
+#include "keycodes.h"
+#include "misc.h"
+#include <X11/keysym.h>
+#include "parseutils.h"
+
+#include <X11/extensions/XKBgeom.h>
+
+/***====================================================================***/
+
+/**
+ * Open the file given in the include statement and parse it's content.
+ * If the statement defines a specific map to use, this map is returned in
+ * file_rtrn. Otherwise, the default map is returned.
+ *
+ * @param stmt The include statement, specifying the file name to look for.
+ * @param file_type Type of file (XkmKeyNamesIdx, etc.)
+ * @param file_rtrn Returns the key map to be used.
+ * @param merge_rtrn Always returns stmt->merge.
+ *
+ * @return True on success or False otherwise.
+ */
+Bool
+ProcessIncludeFile(IncludeStmt * stmt,
+                   unsigned file_type,
+                   XkbFile ** file_rtrn, unsigned *merge_rtrn)
+{
+    FILE *file;
+    XkbFile *rtrn, *mapToUse;
+    char oldFile[1024] = {0};
+    int oldLine = lineNum;
+
+    rtrn = XkbFindFileInCache(stmt->file, file_type, &stmt->path);
+    if (rtrn == NULL)
+    {
+        /* file not in cache, open it, parse it and store it in cache for next
+           time. */
+        file = XkbFindFileInPath(stmt->file, file_type, &stmt->path);
+        if (file == NULL)
+        {
+            ERROR2("Can't find file \"%s\" for %s include\n", stmt->file,
+                   XkbDirectoryForInclude(file_type));
+            ACTION("Exiting\n");
+            return False;
+        }
+        strcpy(oldFile, scanFile);
+        oldLine = lineNum;
+        setScanState(stmt->file, 1);
+        if (debugFlags & 2)
+            INFO1("About to parse include file %s\n", stmt->file);
+        /* parse the file */
+        if ((XKBParseFile(file, &rtrn) == 0) || (rtrn == NULL))
+        {
+            setScanState(oldFile, oldLine);
+            ERROR1("Error interpreting include file \"%s\"\n", stmt->file);
+            ACTION("Exiting\n");
+            fclose(file);
+            return False;
+        }
+        fclose(file);
+        XkbAddFileToCache(stmt->file, file_type, stmt->path, rtrn);
+    }
+    mapToUse = rtrn;
+    if (stmt->map != NULL)
+    {
+        while ((mapToUse) && ((!uStringEqual(mapToUse->name, stmt->map)) ||
+                              (mapToUse->type != file_type)))
+        {
+            mapToUse = (XkbFile *) mapToUse->common.next;
+        }
+        if (!mapToUse)
+        {
+            ERROR3("No %s named \"%s\" in the include file \"%s\"\n",
+                   XkbConfigText(file_type, XkbMessage), stmt->map,
+                   stmt->file);
+            ACTION("Exiting\n");
+            return False;
+        }
+    }
+    else if ((rtrn->common.next != NULL) && (warningLevel > 5))
+    {
+        WARN1("No map in include statement, but \"%s\" contains several\n",
+              stmt->file);
+        ACTION1("Using first defined map, \"%s\"\n", rtrn->name);
+    }
+    setScanState(oldFile, oldLine);
+    if (mapToUse->type != file_type)
+    {
+        ERROR2("Include file wrong type (expected %s, got %s)\n",
+               XkbConfigText(file_type, XkbMessage),
+               XkbConfigText(mapToUse->type, XkbMessage));
+        ACTION1("Include file \"%s\" ignored\n", stmt->file);
+        return False;
+    }
+    /* FIXME: we have to check recursive includes here (or somewhere) */
+
+    mapToUse->compiled = True;
+    *file_rtrn = mapToUse;
+    *merge_rtrn = stmt->merge;
+    return True;
+}
+
+/***====================================================================***/
+
+int
+ReportNotArray(const char *type, const char *field, const char *name)
+{
+    ERROR2("The %s %s field is not an array\n", type, field);
+    ACTION1("Ignoring illegal assignment in %s\n", name);
+    return False;
+}
+
+int
+ReportShouldBeArray(const char *type, const char *field, char *name)
+{
+    ERROR2("Missing subscript for %s %s\n", type, field);
+    ACTION1("Ignoring illegal assignment in %s\n", name);
+    return False;
+}
+
+int
+ReportBadType(const char *type, const char *field,
+              const char *name, const char *wanted)
+{
+    ERROR3("The %s %s field must be a %s\n", type, field, wanted);
+    ACTION1("Ignoring illegal assignment in %s\n", name);
+    return False;
+}
+
+int
+ReportBadIndexType(char *type, char *field, char *name, char *wanted)
+{
+    ERROR3("Index for the %s %s field must be a %s\n", type, field, wanted);
+    ACTION1("Ignoring assignment to illegal field in %s\n", name);
+    return False;
+}
+
+int
+ReportBadField(const char *type, const char *field, const char *name)
+{
+    ERROR3("Unknown %s field %s in %s\n", type, field, name);
+    ACTION1("Ignoring assignment to unknown field in %s\n", name);
+    return False;
+}
+
+int
+ReportMultipleDefs(char *type, char *field, char *name)
+{
+    WARN3("Multiple definitions of %s in %s \"%s\"\n", field, type, name);
+    ACTION("Using last definition\n");
+    return False;
+}
+
+/***====================================================================***/
+
+Bool
+UseNewField(unsigned field,
+            CommonInfo * oldDefs, CommonInfo * newDefs, unsigned *pCollide)
+{
+    Bool useNew;
+
+    useNew = False;
+    if (oldDefs->defined & field)
+    {
+        if (newDefs->defined & field)
+        {
+            if (((oldDefs->fileID == newDefs->fileID)
+                 && (warningLevel > 0)) || (warningLevel > 9))
+            {
+                *pCollide |= field;
+            }
+            if (newDefs->merge != MergeAugment)
+                useNew = True;
+        }
+    }
+    else if (newDefs->defined & field)
+        useNew = True;
+    return useNew;
+}
+
+Bool
+MergeNewField(unsigned field,
+              CommonInfo * oldDefs, CommonInfo * newDefs, unsigned *pCollide)
+{
+    if ((oldDefs->defined & field) && (newDefs->defined & field))
+    {
+        if (((oldDefs->fileID == newDefs->fileID) && (warningLevel > 0)) ||
+            (warningLevel > 9))
+        {
+            *pCollide |= field;
+        }
+        if (newDefs->merge == MergeAugment)
+            return True;
+    }
+    return False;
+}
+
+XPointer
+ClearCommonInfo(CommonInfo * cmn)
+{
+    if (cmn != NULL)
+    {
+        CommonInfo *this, *next;
+        for (this = cmn; this != NULL; this = next)
+        {
+            next = this->next;
+            uFree(this);
+        }
+    }
+    return NULL;
+}
+
+XPointer
+AddCommonInfo(CommonInfo * old, CommonInfo * new)
+{
+    CommonInfo *first;
+
+    first = old;
+    while (old && old->next)
+    {
+        old = old->next;
+    }
+    new->next = NULL;
+    if (old)
+    {
+        old->next = new;
+        return (XPointer) first;
+    }
+    return (XPointer) new;
+}
+
+/***====================================================================***/
+
+typedef struct _KeyNameDesc
+{
+    KeySym level1;
+    KeySym level2;
+    char name[5];
+    Bool used;
+} KeyNameDesc;
+
+static KeyNameDesc dfltKeys[] = {
+    {XK_Escape, NoSymbol, "ESC\0"},
+    {XK_quoteleft, XK_asciitilde, "TLDE"},
+    {XK_1, XK_exclam, "AE01"},
+    {XK_2, XK_at, "AE02"},
+    {XK_3, XK_numbersign, "AE03"},
+    {XK_4, XK_dollar, "AE04"},
+    {XK_5, XK_percent, "AE05"},
+    {XK_6, XK_asciicircum, "AE06"},
+    {XK_7, XK_ampersand, "AE07"},
+    {XK_8, XK_asterisk, "AE08"},
+    {XK_9, XK_parenleft, "AE09"},
+    {XK_0, XK_parenright, "AE10"},
+    {XK_minus, XK_underscore, "AE11"},
+    {XK_equal, XK_plus, "AE12"},
+    {XK_BackSpace, NoSymbol, "BKSP"},
+    {XK_Tab, NoSymbol, "TAB\0"},
+    {XK_q, XK_Q, "AD01"},
+    {XK_w, XK_W, "AD02"},
+    {XK_e, XK_E, "AD03"},
+    {XK_r, XK_R, "AD04"},
+    {XK_t, XK_T, "AD05"},
+    {XK_y, XK_Y, "AD06"},
+    {XK_u, XK_U, "AD07"},
+    {XK_i, XK_I, "AD08"},
+    {XK_o, XK_O, "AD09"},
+    {XK_p, XK_P, "AD10"},
+    {XK_bracketleft, XK_braceleft, "AD11"},
+    {XK_bracketright, XK_braceright, "AD12"},
+    {XK_Return, NoSymbol, "RTRN"},
+    {XK_Caps_Lock, NoSymbol, "CAPS"},
+    {XK_a, XK_A, "AC01"},
+    {XK_s, XK_S, "AC02"},
+    {XK_d, XK_D, "AC03"},
+    {XK_f, XK_F, "AC04"},
+    {XK_g, XK_G, "AC05"},
+    {XK_h, XK_H, "AC06"},
+    {XK_j, XK_J, "AC07"},
+    {XK_k, XK_K, "AC08"},
+    {XK_l, XK_L, "AC09"},
+    {XK_semicolon, XK_colon, "AC10"},
+    {XK_quoteright, XK_quotedbl, "AC11"},
+    {XK_Shift_L, NoSymbol, "LFSH"},
+    {XK_z, XK_Z, "AB01"},
+    {XK_x, XK_X, "AB02"},
+    {XK_c, XK_C, "AB03"},
+    {XK_v, XK_V, "AB04"},
+    {XK_b, XK_B, "AB05"},
+    {XK_n, XK_N, "AB06"},
+    {XK_m, XK_M, "AB07"},
+    {XK_comma, XK_less, "AB08"},
+    {XK_period, XK_greater, "AB09"},
+    {XK_slash, XK_question, "AB10"},
+    {XK_backslash, XK_bar, "BKSL"},
+    {XK_Control_L, NoSymbol, "LCTL"},
+    {XK_space, NoSymbol, "SPCE"},
+    {XK_Shift_R, NoSymbol, "RTSH"},
+    {XK_Alt_L, NoSymbol, "LALT"},
+    {XK_space, NoSymbol, "SPCE"},
+    {XK_Control_R, NoSymbol, "RCTL"},
+    {XK_Alt_R, NoSymbol, "RALT"},
+    {XK_F1, NoSymbol, "FK01"},
+    {XK_F2, NoSymbol, "FK02"},
+    {XK_F3, NoSymbol, "FK03"},
+    {XK_F4, NoSymbol, "FK04"},
+    {XK_F5, NoSymbol, "FK05"},
+    {XK_F6, NoSymbol, "FK06"},
+    {XK_F7, NoSymbol, "FK07"},
+    {XK_F8, NoSymbol, "FK08"},
+    {XK_F9, NoSymbol, "FK09"},
+    {XK_F10, NoSymbol, "FK10"},
+    {XK_F11, NoSymbol, "FK11"},
+    {XK_F12, NoSymbol, "FK12"},
+    {XK_Print, NoSymbol, "PRSC"},
+    {XK_Scroll_Lock, NoSymbol, "SCLK"},
+    {XK_Pause, NoSymbol, "PAUS"},
+    {XK_Insert, NoSymbol, "INS\0"},
+    {XK_Home, NoSymbol, "HOME"},
+    {XK_Prior, NoSymbol, "PGUP"},
+    {XK_Delete, NoSymbol, "DELE"},
+    {XK_End, NoSymbol, "END"},
+    {XK_Next, NoSymbol, "PGDN"},
+    {XK_Up, NoSymbol, "UP\0\0"},
+    {XK_Left, NoSymbol, "LEFT"},
+    {XK_Down, NoSymbol, "DOWN"},
+    {XK_Right, NoSymbol, "RGHT"},
+    {XK_Num_Lock, NoSymbol, "NMLK"},
+    {XK_KP_Divide, NoSymbol, "KPDV"},
+    {XK_KP_Multiply, NoSymbol, "KPMU"},
+    {XK_KP_Subtract, NoSymbol, "KPSU"},
+    {NoSymbol, XK_KP_7, "KP7\0"},
+    {NoSymbol, XK_KP_8, "KP8\0"},
+    {NoSymbol, XK_KP_9, "KP9\0"},
+    {XK_KP_Add, NoSymbol, "KPAD"},
+    {NoSymbol, XK_KP_4, "KP4\0"},
+    {NoSymbol, XK_KP_5, "KP5\0"},
+    {NoSymbol, XK_KP_6, "KP6\0"},
+    {NoSymbol, XK_KP_1, "KP1\0"},
+    {NoSymbol, XK_KP_2, "KP2\0"},
+    {NoSymbol, XK_KP_3, "KP3\0"},
+    {XK_KP_Enter, NoSymbol, "KPEN"},
+    {NoSymbol, XK_KP_0, "KP0\0"},
+    {XK_KP_Delete, NoSymbol, "KPDL"},
+    {XK_less, XK_greater, "LSGT"},
+    {XK_KP_Separator, NoSymbol, "KPCO"},
+    {XK_Find, NoSymbol, "FIND"},
+    {NoSymbol, NoSymbol, "\0\0\0\0"}
+};
+
+Status
+ComputeKbdDefaults(XkbDescPtr xkb)
+{
+    Status rtrn;
+    register int i, tmp, nUnknown;
+    KeyNameDesc *name;
+    KeySym *syms;
+
+    if ((xkb->names == NULL) || (xkb->names->keys == NULL))
+    {
+        if ((rtrn = XkbAllocNames(xkb, XkbKeyNamesMask, 0, 0)) != Success)
+            return rtrn;
+    }
+    for (name = dfltKeys; (name->name[0] != '\0'); name++)
+    {
+        name->used = False;
+    }
+    nUnknown = 0;
+    for (i = xkb->min_key_code; i <= xkb->max_key_code; i++)
+    {
+        tmp = XkbKeyNumSyms(xkb, i);
+        if ((xkb->names->keys[i].name[0] == '\0') && (tmp > 0))
+        {
+            tmp = XkbKeyGroupsWidth(xkb, i);
+            syms = XkbKeySymsPtr(xkb, i);
+            for (name = dfltKeys; (name->name[0] != '\0'); name++)
+            {
+                Bool match = True;
+                if (((name->level1 != syms[0])
+                     && (name->level1 != NoSymbol))
+                    || ((name->level2 != NoSymbol) && (tmp < 2))
+                    || ((name->level2 != syms[1])
+                        && (name->level2 != NoSymbol)))
+                {
+                    match = False;
+                }
+                if (match)
+                {
+                    if (!name->used)
+                    {
+                        memcpy(xkb->names->keys[i].name, name->name,
+                               XkbKeyNameLength);
+                        name->used = True;
+                    }
+                    else
+                    {
+                        if (warningLevel > 2)
+                        {
+                            WARN1
+                                ("Several keys match pattern for %s\n",
+                                 XkbKeyNameText(name->name, XkbMessage));
+                            ACTION2("Using <U%03d> for key %d\n",
+                                    nUnknown, i);
+                        }
+                        sprintf(xkb->names->keys[i].name, "U%03d",
+                                nUnknown++);
+                    }
+                    break;
+                }
+            }
+            if (xkb->names->keys[i].name[0] == '\0')
+            {
+                if (warningLevel > 2)
+                {
+                    WARN1("Key %d does not match any defaults\n", i);
+                    ACTION1("Using name <U%03d>\n", nUnknown);
+                    sprintf(xkb->names->keys[i].name, "U%03d", nUnknown++);
+                }
+            }
+        }
+    }
+    return Success;
+}
+
+/**
+ * Find the key with the given name and return its keycode in kc_rtrn.
+ *
+ * @param name The 4-letter name of the key as a long.
+ * @param kc_rtrn Set to the keycode if the key was found, otherwise 0.
+ * @param use_aliases True if the key aliases should be searched too.
+ * @param create If True and the key is not found, it is added to the
+ *        xkb->names at the first free keycode.
+ * @param start_from Keycode to start searching from.
+ *
+ * @return True if found, False otherwise.
+ */
+Bool
+FindNamedKey(XkbDescPtr xkb,
+             unsigned long name,
+             unsigned int *kc_rtrn,
+             Bool use_aliases, Bool create, int start_from)
+{
+    register unsigned n;
+
+    if (start_from < xkb->min_key_code)
+    {
+        start_from = xkb->min_key_code;
+    }
+    else if (start_from > xkb->max_key_code)
+    {
+        return False;
+    }
+
+    *kc_rtrn = 0;               /* some callers rely on this */
+    if (xkb && xkb->names && xkb->names->keys)
+    {
+        for (n = start_from; n <= xkb->max_key_code; n++)
+        {
+            unsigned long tmp;
+            tmp = KeyNameToLong(xkb->names->keys[n].name);
+            if (tmp == name)
+            {
+                *kc_rtrn = n;
+                return True;
+            }
+        }
+        if (use_aliases)
+        {
+            unsigned long new_name;
+            if (FindKeyNameForAlias(xkb, name, &new_name))
+                return FindNamedKey(xkb, new_name, kc_rtrn, False, create, 0);
+        }
+    }
+    if (create)
+    {
+        if ((!xkb->names) || (!xkb->names->keys))
+        {
+            if (xkb->min_key_code < XkbMinLegalKeyCode)
+            {
+                xkb->min_key_code = XkbMinLegalKeyCode;
+                xkb->max_key_code = XkbMaxLegalKeyCode;
+            }
+            if (XkbAllocNames(xkb, XkbKeyNamesMask, 0, 0) != Success)
+            {
+                if (warningLevel > 0)
+                {
+                    WARN("Couldn't allocate key names in FindNamedKey\n");
+                    ACTION1("Key \"%s\" not automatically created\n",
+                            longText(name, XkbMessage));
+                }
+                return False;
+            }
+        }
+        /* Find first unused keycode and store our key here */
+        for (n = xkb->min_key_code; n <= xkb->max_key_code; n++)
+        {
+            if (xkb->names->keys[n].name[0] == '\0')
+            {
+                char buf[XkbKeyNameLength + 1];
+                LongToKeyName(name, buf);
+                memcpy(xkb->names->keys[n].name, buf, XkbKeyNameLength);
+                *kc_rtrn = n;
+                return True;
+            }
+        }
+    }
+    return False;
+}
+
+Bool
+FindKeyNameForAlias(XkbDescPtr xkb, unsigned long lname,
+                    unsigned long *real_name)
+{
+    register int i;
+    char name[XkbKeyNameLength + 1];
+
+    if (xkb && xkb->geom && xkb->geom->key_aliases)
+    {
+        XkbKeyAliasPtr a;
+        a = xkb->geom->key_aliases;
+        LongToKeyName(lname, name);
+        name[XkbKeyNameLength] = '\0';
+        for (i = 0; i < xkb->geom->num_key_aliases; i++, a++)
+        {
+            if (strncmp(name, a->alias, XkbKeyNameLength) == 0)
+            {
+                *real_name = KeyNameToLong(a->real);
+                return True;
+            }
+        }
+    }
+    if (xkb && xkb->names && xkb->names->key_aliases)
+    {
+        XkbKeyAliasPtr a;
+        a = xkb->names->key_aliases;
+        LongToKeyName(lname, name);
+        name[XkbKeyNameLength] = '\0';
+        for (i = 0; i < xkb->names->num_key_aliases; i++, a++)
+        {
+            if (strncmp(name, a->alias, XkbKeyNameLength) == 0)
+            {
+                *real_name = KeyNameToLong(a->real);
+                return True;
+            }
+        }
+    }
+    return False;
+}
diff --git a/src/xkbcomp/misc.h b/src/xkbcomp/misc.h
new file mode 100644 (file)
index 0000000..4fa4b6d
--- /dev/null
@@ -0,0 +1,111 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef MISC_H
+#define MISC_H 1
+
+typedef struct _CommonInfo
+{
+    unsigned short defined;
+    unsigned char fileID;
+    unsigned char merge;
+    struct _CommonInfo *next;
+} CommonInfo;
+
+extern Bool UseNewField(unsigned /* field */ ,
+                        CommonInfo * /* oldDefs */ ,
+                        CommonInfo * /* newDefs */ ,
+                        unsigned *      /* pCollide */
+    );
+
+extern Bool MergeNewField(unsigned /* field */ ,
+                          CommonInfo * /* oldDefs */ ,
+                          CommonInfo * /* newDefs */ ,
+                          unsigned *    /* pCollide */
+    );
+
+extern XPointer ClearCommonInfo(CommonInfo *    /* cmn */
+    );
+
+extern XPointer AddCommonInfo(CommonInfo * /* old */ ,
+                              CommonInfo *      /* new */
+    );
+
+extern int ReportNotArray(const char * /* type */ ,
+                          const char * /* field */ ,
+                          const char *  /* name */
+    );
+
+extern int ReportShouldBeArray(const char * /* type */ ,
+                               const char * /* field */ ,
+                               char *   /* name */
+    );
+
+extern int ReportBadType(const char * /* type */ ,
+                         const char * /* field */ ,
+                         const char * /* name */ ,
+                         const char *   /* wanted */
+    );
+
+extern int ReportBadIndexType(char * /* type */ ,
+                              char * /* field */ ,
+                              char * /* name */ ,
+                              char *    /* wanted */
+    );
+
+extern int ReportBadField(const char * /* type */ ,
+                          const char * /* field */ ,
+                          const char *  /* name */
+    );
+
+extern int ReportMultipleDefs(char * /* type */ ,
+                              char * /* field */ ,
+                              char *    /* which */
+    );
+
+extern Bool ProcessIncludeFile(IncludeStmt * /* stmt */ ,
+                               unsigned /* file_type */ ,
+                               XkbFile ** /* file_rtrn */ ,
+                               unsigned *       /* merge_rtrn */
+    );
+
+extern Status ComputeKbdDefaults(XkbDescPtr     /* xkb */
+    );
+
+extern Bool FindNamedKey(XkbDescPtr /* xkb */ ,
+                         unsigned long /* name */ ,
+                         unsigned int * /* kc_rtrn */ ,
+                         Bool /* use_aliases */ ,
+                         Bool /* create */ ,
+                         int    /* start_from */
+    );
+
+extern Bool FindKeyNameForAlias(XkbDescPtr /* xkb */ ,
+                                unsigned long /* lname */ ,
+                                unsigned long * /* real_name */
+    );
+
+#endif /* MISC_H */
diff --git a/src/xkbcomp/parseutils.c b/src/xkbcomp/parseutils.c
new file mode 100644 (file)
index 0000000..ad1b0d1
--- /dev/null
@@ -0,0 +1,834 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#define        DEBUG_VAR parseDebug
+#include "parseutils.h"
+#include "xkbpath.h"
+#include <X11/keysym.h>
+#include <X11/extensions/XKBgeom.h>
+#include <X11/Xalloca.h>
+
+XkbFile *rtrnValue;
+
+ParseCommon *
+AppendStmt(ParseCommon * to, ParseCommon * append)
+{
+    ParseCommon *start = to;
+
+    if (append == NULL)
+        return to;
+    while ((to != NULL) && (to->next != NULL))
+    {
+        to = to->next;
+    }
+    if (to)
+    {
+        to->next = append;
+        return start;
+    }
+    return append;
+}
+
+ExprDef *
+ExprCreate(unsigned op, unsigned type)
+{
+    ExprDef *expr;
+    expr = uTypedAlloc(ExprDef);
+    if (expr)
+    {
+        expr->common.stmtType = StmtExpr;
+        expr->common.next = NULL;
+        expr->op = op;
+        expr->type = type;
+    }
+    else
+    {
+        FATAL("Couldn't allocate expression in parser\n");
+        /* NOTREACHED */
+    }
+    return expr;
+}
+
+ExprDef *
+ExprCreateUnary(unsigned op, unsigned type, ExprDef * child)
+{
+    ExprDef *expr;
+    expr = uTypedAlloc(ExprDef);
+    if (expr)
+    {
+        expr->common.stmtType = StmtExpr;
+        expr->common.next = NULL;
+        expr->op = op;
+        expr->type = type;
+        expr->value.child = child;
+    }
+    else
+    {
+        FATAL("Couldn't allocate expression in parser\n");
+        /* NOTREACHED */
+    }
+    return expr;
+}
+
+ExprDef *
+ExprCreateBinary(unsigned op, ExprDef * left, ExprDef * right)
+{
+    ExprDef *expr;
+    expr = uTypedAlloc(ExprDef);
+    if (expr)
+    {
+        expr->common.stmtType = StmtExpr;
+        expr->common.next = NULL;
+        expr->op = op;
+        if ((op == OpAssign) || (left->type == TypeUnknown))
+            expr->type = right->type;
+        else if ((left->type == right->type) || (right->type == TypeUnknown))
+            expr->type = left->type;
+        else
+            expr->type = TypeUnknown;
+        expr->value.binary.left = left;
+        expr->value.binary.right = right;
+    }
+    else
+    {
+        FATAL("Couldn't allocate expression in parser\n");
+        /* NOTREACHED */
+    }
+    return expr;
+}
+
+KeycodeDef *
+KeycodeCreate(char *name, ExprDef * value)
+{
+    KeycodeDef *def;
+
+    def = uTypedAlloc(KeycodeDef);
+    if (def)
+    {
+        def->common.stmtType = StmtKeycodeDef;
+        def->common.next = NULL;
+        strncpy(def->name, name, XkbKeyNameLength);
+        def->name[XkbKeyNameLength] = '\0';
+        def->value = value;
+    }
+    else
+    {
+        FATAL("Couldn't allocate key name definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+KeyAliasDef *
+KeyAliasCreate(char *alias, char *real)
+{
+    KeyAliasDef *def;
+
+    def = uTypedAlloc(KeyAliasDef);
+    if (def)
+    {
+        def->common.stmtType = StmtKeyAliasDef;
+        def->common.next = NULL;
+        strncpy(def->alias, alias, XkbKeyNameLength);
+        def->alias[XkbKeyNameLength] = '\0';
+        strncpy(def->real, real, XkbKeyNameLength);
+        def->real[XkbKeyNameLength] = '\0';
+    }
+    else
+    {
+        FATAL("Couldn't allocate key alias definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+VModDef *
+VModCreate(Atom name, ExprDef * value)
+{
+    VModDef *def;
+    def = uTypedAlloc(VModDef);
+    if (def)
+    {
+        def->common.stmtType = StmtVModDef;
+        def->common.next = NULL;
+        def->name = name;
+        def->value = value;
+    }
+    else
+    {
+        FATAL("Couldn't allocate variable definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+VarDef *
+VarCreate(ExprDef * name, ExprDef * value)
+{
+    VarDef *def;
+    def = uTypedAlloc(VarDef);
+    if (def)
+    {
+        def->common.stmtType = StmtVarDef;
+        def->common.next = NULL;
+        def->name = name;
+        def->value = value;
+    }
+    else
+    {
+        FATAL("Couldn't allocate variable definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+VarDef *
+BoolVarCreate(Atom nameToken, unsigned set)
+{
+    ExprDef *name, *value;
+
+    name = ExprCreate(ExprIdent, TypeUnknown);
+    name->value.str = nameToken;
+    value = ExprCreate(ExprValue, TypeBoolean);
+    value->value.uval = set;
+    return VarCreate(name, value);
+}
+
+InterpDef *
+InterpCreate(KeySym sym, ExprDef * match)
+{
+    InterpDef *def;
+
+    def = uTypedAlloc(InterpDef);
+    if (def)
+    {
+        def->common.stmtType = StmtInterpDef;
+        def->common.next = NULL;
+        def->sym = sym;
+        def->match = match;
+    }
+    else
+    {
+        FATAL("Couldn't allocate interp definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+KeyTypeDef *
+KeyTypeCreate(Atom name, VarDef * body)
+{
+    KeyTypeDef *def;
+
+    def = uTypedAlloc(KeyTypeDef);
+    if (def)
+    {
+        def->common.stmtType = StmtKeyTypeDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        def->name = name;
+        def->body = body;
+    }
+    else
+    {
+        FATAL("Couldn't allocate key type definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+SymbolsDef *
+SymbolsCreate(char *keyName, ExprDef * symbols)
+{
+    SymbolsDef *def;
+
+    def = uTypedAlloc(SymbolsDef);
+    if (def)
+    {
+        def->common.stmtType = StmtSymbolsDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        bzero(def->keyName, 5);
+        strncpy(def->keyName, keyName, 4);
+        def->symbols = symbols;
+    }
+    else
+    {
+        FATAL("Couldn't allocate symbols definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+GroupCompatDef *
+GroupCompatCreate(int group, ExprDef * val)
+{
+    GroupCompatDef *def;
+
+    def = uTypedAlloc(GroupCompatDef);
+    if (def)
+    {
+        def->common.stmtType = StmtGroupCompatDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        def->group = group;
+        def->def = val;
+    }
+    else
+    {
+        FATAL("Couldn't allocate group compat definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+ModMapDef *
+ModMapCreate(Atom modifier, ExprDef * keys)
+{
+    ModMapDef *def;
+
+    def = uTypedAlloc(ModMapDef);
+    if (def)
+    {
+        def->common.stmtType = StmtModMapDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        def->modifier = modifier;
+        def->keys = keys;
+    }
+    else
+    {
+        FATAL("Couldn't allocate mod mask definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+IndicatorMapDef *
+IndicatorMapCreate(Atom name, VarDef * body)
+{
+    IndicatorMapDef *def;
+
+    def = uTypedAlloc(IndicatorMapDef);
+    if (def)
+    {
+        def->common.stmtType = StmtIndicatorMapDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        def->name = name;
+        def->body = body;
+    }
+    else
+    {
+        FATAL("Couldn't allocate indicator map definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+IndicatorNameDef *
+IndicatorNameCreate(int ndx, ExprDef * name, Bool virtual)
+{
+    IndicatorNameDef *def;
+
+    def = uTypedAlloc(IndicatorNameDef);
+    if (def)
+    {
+        def->common.stmtType = StmtIndicatorNameDef;
+        def->common.next = NULL;
+        def->merge = MergeDefault;
+        def->ndx = ndx;
+        def->name = name;
+        def->virtual = virtual;
+    }
+    else
+    {
+        FATAL("Couldn't allocate indicator index definition in parser\n");
+        /* NOTREACHED */
+    }
+    return def;
+}
+
+ExprDef *
+ActionCreate(Atom name, ExprDef * args)
+{
+    ExprDef *act;
+
+    act = uTypedAlloc(ExprDef);
+    if (act)
+    {
+        act->common.stmtType = StmtExpr;
+        act->common.next = NULL;
+        act->op = ExprActionDecl;
+        act->value.action.name = name;
+        act->value.action.args = args;
+        return act;
+    }
+    FATAL("Couldn't allocate ActionDef in parser\n");
+    return NULL;
+}
+
+ExprDef *
+CreateKeysymList(KeySym sym)
+{
+    ExprDef *def;
+
+    def = ExprCreate(ExprKeysymList, TypeSymbols);
+    if (def)
+    {
+        def->value.list.nSyms = 1;
+        def->value.list.szSyms = 2;
+        def->value.list.syms = uTypedCalloc(2, KeySym);
+        if (def->value.list.syms != NULL)
+        {
+            def->value.list.syms[0] = sym;
+            return def;
+        }
+    }
+    FATAL("Couldn't allocate expression for keysym list in parser\n");
+    return NULL;
+}
+
+ShapeDef *
+ShapeDeclCreate(Atom name, OutlineDef * outlines)
+{
+    ShapeDef *shape;
+    OutlineDef *ol;
+
+    shape = uTypedAlloc(ShapeDef);
+    if (shape != NULL)
+    {
+        bzero(shape, sizeof(ShapeDef));
+        shape->common.stmtType = StmtShapeDef;
+        shape->common.next = NULL;
+        shape->merge = MergeDefault;
+        shape->name = name;
+        shape->nOutlines = 0;
+        shape->outlines = outlines;
+        for (ol = outlines; ol != NULL; ol = (OutlineDef *) ol->common.next)
+        {
+            if (ol->nPoints > 0)
+                shape->nOutlines++;
+        }
+    }
+    return shape;
+}
+
+OutlineDef *
+OutlineCreate(Atom field, ExprDef * points)
+{
+    OutlineDef *outline;
+    ExprDef *pt;
+
+    outline = uTypedAlloc(OutlineDef);
+    if (outline != NULL)
+    {
+        bzero(outline, sizeof(OutlineDef));
+        outline->common.stmtType = StmtOutlineDef;
+        outline->common.next = NULL;
+        outline->field = field;
+        outline->nPoints = 0;
+        if (points->op == ExprCoord)
+        {
+            for (pt = points; pt != NULL; pt = (ExprDef *) pt->common.next)
+            {
+                outline->nPoints++;
+            }
+        }
+        outline->points = points;
+    }
+    return outline;
+}
+
+KeyDef *
+KeyDeclCreate(char *name, ExprDef * expr)
+{
+    KeyDef *key;
+
+    key = uTypedAlloc(KeyDef);
+    if (key != NULL)
+    {
+        bzero(key, sizeof(KeyDef));
+        key->common.stmtType = StmtKeyDef;
+        key->common.next = NULL;
+        if (name)
+            key->name = name;
+        else
+            key->expr = expr;
+    }
+    return key;
+}
+
+KeyDef *
+KeyDeclMerge(KeyDef * into, KeyDef * from)
+{
+    into->expr =
+        (ExprDef *) AppendStmt(&into->expr->common, &from->expr->common);
+    from->expr = NULL;
+    uFree(from);
+    return into;
+}
+
+RowDef *
+RowDeclCreate(KeyDef * keys)
+{
+    RowDef *row;
+    KeyDef *key;
+
+    row = uTypedAlloc(RowDef);
+    if (row != NULL)
+    {
+        bzero(row, sizeof(RowDef));
+        row->common.stmtType = StmtRowDef;
+        row->common.next = NULL;
+        row->nKeys = 0;
+        row->keys = keys;
+        for (key = keys; key != NULL; key = (KeyDef *) key->common.next)
+        {
+            if (key->common.stmtType == StmtKeyDef)
+                row->nKeys++;
+        }
+    }
+    return row;
+}
+
+SectionDef *
+SectionDeclCreate(Atom name, RowDef * rows)
+{
+    SectionDef *section;
+    RowDef *row;
+
+    section = uTypedAlloc(SectionDef);
+    if (section != NULL)
+    {
+        bzero(section, sizeof(SectionDef));
+        section->common.stmtType = StmtSectionDef;
+        section->common.next = NULL;
+        section->name = name;
+        section->nRows = 0;
+        section->rows = rows;
+        for (row = rows; row != NULL; row = (RowDef *) row->common.next)
+        {
+            if (row->common.stmtType == StmtRowDef)
+                section->nRows++;
+        }
+    }
+    return section;
+}
+
+OverlayKeyDef *
+OverlayKeyCreate(char *under, char *over)
+{
+    OverlayKeyDef *key;
+
+    key = uTypedAlloc(OverlayKeyDef);
+    if (key != NULL)
+    {
+        bzero(key, sizeof(OverlayKeyDef));
+        key->common.stmtType = StmtOverlayKeyDef;
+        strncpy(key->over, over, XkbKeyNameLength);
+        strncpy(key->under, under, XkbKeyNameLength);
+        if (over)
+            uFree(over);
+        if (under)
+            uFree(under);
+    }
+    return key;
+}
+
+OverlayDef *
+OverlayDeclCreate(Atom name, OverlayKeyDef * keys)
+{
+    OverlayDef *ol;
+    OverlayKeyDef *key;
+
+    ol = uTypedAlloc(OverlayDef);
+    if (ol != NULL)
+    {
+        bzero(ol, sizeof(OverlayDef));
+        ol->common.stmtType = StmtOverlayDef;
+        ol->name = name;
+        ol->keys = keys;
+        for (key = keys; key != NULL;
+             key = (OverlayKeyDef *) key->common.next)
+        {
+            ol->nKeys++;
+        }
+    }
+    return ol;
+}
+
+DoodadDef *
+DoodadCreate(unsigned type, Atom name, VarDef * body)
+{
+    DoodadDef *doodad;
+
+    doodad = uTypedAlloc(DoodadDef);
+    if (doodad != NULL)
+    {
+        bzero(doodad, sizeof(DoodadDef));
+        doodad->common.stmtType = StmtDoodadDef;
+        doodad->common.next = NULL;
+        doodad->type = type;
+        doodad->name = name;
+        doodad->body = body;
+    }
+    return doodad;
+}
+
+ExprDef *
+AppendKeysymList(ExprDef * list, KeySym sym)
+{
+    if (list->value.list.nSyms >= list->value.list.szSyms)
+    {
+        list->value.list.szSyms *= 2;
+        list->value.list.syms = uTypedRecalloc(list->value.list.syms,
+                                               list->value.list.nSyms,
+                                               list->value.list.szSyms,
+                                               KeySym);
+        if (list->value.list.syms == NULL)
+        {
+            FATAL("Couldn't resize list of symbols for append\n");
+            return NULL;
+        }
+    }
+    list->value.list.syms[list->value.list.nSyms++] = sym;
+    return list;
+}
+
+int
+LookupKeysym(char *str, KeySym * sym_rtrn)
+{
+    KeySym sym;
+
+    if ((!str) || (uStrCaseCmp(str, "any") == 0)
+        || (uStrCaseCmp(str, "nosymbol") == 0))
+    {
+        *sym_rtrn = NoSymbol;
+        return 1;
+    }
+    else if ((uStrCaseCmp(str, "none") == 0)
+             || (uStrCaseCmp(str, "voidsymbol") == 0))
+    {
+        *sym_rtrn = XK_VoidSymbol;
+        return 1;
+    }
+    sym = XStringToKeysym(str);
+    if (sym != NoSymbol)
+    {
+        *sym_rtrn = sym;
+        return 1;
+    }
+    return 0;
+}
+
+IncludeStmt *
+IncludeCreate(char *str, unsigned merge)
+{
+    IncludeStmt *incl, *first;
+    char *file, *map, *stmt, *tmp, *extra_data;
+    char nextop;
+    Bool haveSelf;
+
+    haveSelf = False;
+    incl = first = NULL;
+    file = map = NULL;
+    tmp = str;
+    stmt = uStringDup(str);
+    while ((tmp) && (*tmp))
+    {
+        if (XkbParseIncludeMap(&tmp, &file, &map, &nextop, &extra_data))
+        {
+            if ((file == NULL) && (map == NULL))
+            {
+                if (haveSelf)
+                    goto BAIL;
+                haveSelf = True;
+            }
+            if (first == NULL)
+                first = incl = uTypedAlloc(IncludeStmt);
+            else
+            {
+                incl->next = uTypedAlloc(IncludeStmt);
+                incl = incl->next;
+            }
+            if (incl)
+            {
+                incl->common.stmtType = StmtInclude;
+                incl->common.next = NULL;
+                incl->merge = merge;
+                incl->stmt = NULL;
+                incl->file = file;
+                incl->map = map;
+                incl->modifier = extra_data;
+                incl->path = NULL;
+                incl->next = NULL;
+            }
+            else
+            {
+                WSGO("Allocation failure in IncludeCreate\n");
+                ACTION("Using only part of the include\n");
+                break;
+            }
+            if (nextop == '|')
+                merge = MergeAugment;
+            else
+                merge = MergeOverride;
+        }
+        else
+        {
+            goto BAIL;
+        }
+    }
+    if (first)
+        first->stmt = stmt;
+    else if (stmt)
+        uFree(stmt);
+    return first;
+  BAIL:
+    ERROR1("Illegal include statement \"%s\"\n", stmt);
+    ACTION("Ignored\n");
+    while (first)
+    {
+        incl = first->next;
+        if (first->file)
+            uFree(first->file);
+        if (first->map)
+            uFree(first->map);
+        if (first->modifier)
+            uFree(first->modifier);
+        if (first->path)
+            uFree(first->path);
+        first->file = first->map = first->path = NULL;
+        uFree(first);
+        first = incl;
+    }
+    if (stmt)
+        uFree(stmt);
+    return NULL;
+}
+
+#ifdef DEBUG
+void
+PrintStmtAddrs(ParseCommon * stmt)
+{
+    fprintf(stderr, "0x%x", stmt);
+    if (stmt)
+    {
+        do
+        {
+            fprintf(stderr, "->0x%x", stmt->next);
+            stmt = stmt->next;
+        }
+        while (stmt);
+    }
+    fprintf(stderr, "\n");
+}
+#endif
+
+static void
+CheckDefaultMap(XkbFile * maps)
+{
+    XkbFile *dflt, *tmp;
+
+    dflt = NULL;
+    for (tmp = maps, dflt = NULL; tmp != NULL;
+         tmp = (XkbFile *) tmp->common.next)
+    {
+        if (tmp->flags & XkbLC_Default)
+        {
+            if (dflt == NULL)
+                dflt = tmp;
+            else
+            {
+                if (warningLevel > 2)
+                {
+                    WARN1("Multiple default components in %s\n",
+                          (scanFile ? scanFile : "(unknown)"));
+                    ACTION2("Using %s, ignoring %s\n",
+                            (dflt->name ? dflt->name : "(first)"),
+                            (tmp->name ? tmp->name : "(subsequent)"));
+                }
+                tmp->flags &= (~XkbLC_Default);
+            }
+        }
+    }
+    return;
+}
+
+int
+XKBParseFile(FILE * file, XkbFile ** pRtrn)
+{
+    if (file)
+    {
+        yyin = file;
+        rtrnValue = NULL;
+        if (yyparse() == 0)
+        {
+            *pRtrn = rtrnValue;
+            CheckDefaultMap(rtrnValue);
+            rtrnValue = NULL;
+            return 1;
+        }
+        *pRtrn = NULL;
+        return 0;
+    }
+    *pRtrn = NULL;
+    return 1;
+}
+
+XkbFile *
+CreateXKBFile(int type, char *name, ParseCommon * defs, unsigned flags)
+{
+    XkbFile *file;
+    static int fileID;
+
+    file = uTypedAlloc(XkbFile);
+    if (file)
+    {
+        XkbEnsureSafeMapName(name);
+        bzero(file, sizeof(XkbFile));
+        file->type = type;
+        file->topName = uStringDup(name);
+        file->name = name;
+        file->defs = defs;
+        file->id = fileID++;
+        file->compiled = False;
+        file->flags = flags;
+    }
+    return file;
+}
+
+unsigned
+StmtSetMerge(ParseCommon * stmt, unsigned merge)
+{
+    if ((merge == MergeAltForm) && (stmt->stmtType != StmtKeycodeDef))
+    {
+        yyerror("illegal use of 'alternate' merge mode");
+        merge = MergeDefault;
+    }
+    return merge;
+}
diff --git a/src/xkbcomp/parseutils.h b/src/xkbcomp/parseutils.h
new file mode 100644 (file)
index 0000000..73a0ec8
--- /dev/null
@@ -0,0 +1,208 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef XKBPARSE_H
+#define        XKBPARSE_H 1
+
+#ifndef DEBUG_VAR
+#define        DEBUG_VAR       parseDebug
+#endif
+
+#include "xkbcomp.h"
+
+extern char *scanStr;
+extern int scanInt;
+extern int lineNum;
+
+extern XkbFile *rtrnValue;
+
+#ifdef DEBUG
+#define        d(str)          fprintf(stderr,"%s\n",str);
+#define d1(str,a)      fprintf(stderr,str,a);
+#define d2(str,a,b)    fprintf(stderr,str,a,b);
+#else
+#define        d(str)
+#define        d1(str,a)
+#define d2(str,a,b)
+#endif
+
+
+extern ParseCommon *AppendStmt(ParseCommon * /* to */ ,
+                               ParseCommon *    /* append */
+    );
+
+extern ExprDef *ExprCreate(unsigned /* op */ ,
+                           unsigned     /* type */
+    );
+
+extern ExprDef *ExprCreateUnary(unsigned /* op */ ,
+                                unsigned /* type */ ,
+                                ExprDef *       /* child */
+    );
+
+extern ExprDef *ExprCreateBinary(unsigned /* op */ ,
+                                 ExprDef * /* left */ ,
+                                 ExprDef *      /* right */
+    );
+
+extern KeycodeDef *KeycodeCreate(char * /* name */ ,
+                                 ExprDef *      /* value */
+    );
+
+extern KeyAliasDef *KeyAliasCreate(char * /* alias */ ,
+                                   char *       /* real */
+    );
+
+extern VModDef *VModCreate(Atom /* name */ ,
+                           ExprDef *    /* value */
+    );
+
+extern VarDef *VarCreate(ExprDef * /* name */ ,
+                         ExprDef *      /* value */
+    );
+
+extern VarDef *BoolVarCreate(Atom /* nameToken */ ,
+                             unsigned   /* set */
+    );
+
+extern InterpDef *InterpCreate(KeySym /* sym */ ,
+                               ExprDef *        /* match */
+    );
+
+extern KeyTypeDef *KeyTypeCreate(Atom /* name */ ,
+                                 VarDef *       /* body */
+    );
+
+extern SymbolsDef *SymbolsCreate(char * /* keyName */ ,
+                                 ExprDef *      /* symbols */
+    );
+
+extern GroupCompatDef *GroupCompatCreate(int /* group */ ,
+                                         ExprDef *      /* def */
+    );
+
+extern ModMapDef *ModMapCreate(Atom /* modifier */ ,
+                               ExprDef *        /* keys */
+    );
+
+extern IndicatorMapDef *IndicatorMapCreate(Atom /* name */ ,
+                                           VarDef *     /* body */
+    );
+
+extern IndicatorNameDef *IndicatorNameCreate(int /* ndx */ ,
+                                             ExprDef * /* name */ ,
+                                             Bool       /* virtual */
+    );
+
+extern ExprDef *ActionCreate(Atom /* name */ ,
+                             ExprDef *  /* args */
+    );
+
+extern ExprDef *CreateKeysymList(KeySym /* sym */
+    );
+
+extern ShapeDef *ShapeDeclCreate(Atom /* name */ ,
+                                 OutlineDef *   /* outlines */
+    );
+
+extern OutlineDef *OutlineCreate(Atom /* field */ ,
+                                 ExprDef *      /* points */
+    );
+
+extern KeyDef *KeyDeclCreate(char * /* name */ ,
+                             ExprDef *  /* expr */
+    );
+
+extern KeyDef *KeyDeclMerge(KeyDef * /* into */ ,
+                            KeyDef *    /* from */
+    );
+
+extern RowDef *RowDeclCreate(KeyDef *   /* keys */
+    );
+
+extern SectionDef *SectionDeclCreate(Atom /* name */ ,
+                                     RowDef *   /* rows */
+    );
+
+extern OverlayKeyDef *OverlayKeyCreate(char * /* under */ ,
+                                       char *   /* over  */
+    );
+
+extern OverlayDef *OverlayDeclCreate(Atom /* name */ ,
+                                     OverlayKeyDef *    /* rows */
+    );
+
+extern DoodadDef *DoodadCreate(unsigned /* type */ ,
+                               Atom /* name */ ,
+                               VarDef * /* body */
+    );
+
+extern ExprDef *AppendKeysymList(ExprDef * /* list */ ,
+                                 KeySym /* sym */
+    );
+
+extern int LookupKeysym(char * /* str */ ,
+                        KeySym *        /* sym_rtrn */
+    );
+
+extern IncludeStmt *IncludeCreate(char * /* str */ ,
+                                  unsigned      /* merge */
+    );
+
+extern unsigned StmtSetMerge(ParseCommon * /* stmt */ ,
+                             unsigned   /* merge */
+    );
+
+#ifdef DEBUG
+extern void PrintStmtAddrs(ParseCommon *        /* stmt */
+    );
+#endif
+
+extern int XKBParseFile(FILE * /* file */ ,
+                        XkbFile **      /* pRtrn */
+    );
+
+extern XkbFile *CreateXKBFile(int /* type */ ,
+                              char * /* name */ ,
+                              ParseCommon * /* defs */ ,
+                              unsigned  /* flags */
+    );
+
+extern void yyerror(const char *        /* s */
+    );
+
+extern int yywrap(void);
+
+extern int yylex(void);
+extern int yyparse(void);
+
+extern int setScanState(char * /* file */ ,
+                        int     /* line */
+    );
+
+extern FILE *yyin;
+
+#endif /* XKBPARSE_H */
diff --git a/src/xkbcomp/symbols.c b/src/xkbcomp/symbols.c
new file mode 100644 (file)
index 0000000..47ad67b
--- /dev/null
@@ -0,0 +1,2301 @@
+/************************************************************
+ Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.
+
+ Permission to use, copy, modify, and distribute this
+ software and its documentation for any purpose and without
+ fee is hereby granted, provided that the above copyright
+ notice appear in all copies and that both that copyright
+ notice and this permission notice appear in supporting
+ documentation, and that the name of Silicon Graphics not be 
+ used in advertising or publicity pertaining to distribution 
+ of the software without specific prior written permission.
+ Silicon Graphics makes no representation about the suitability 
+ of this software for any purpose. It is provided "as is"
+ without any express or implied warranty.
+ SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
+ AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
+ GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
+ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
+ DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
+ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ********************************************************/
+
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+#include <stdlib.h>
+
+#include "expr.h"
+#include "vmod.h"
+#include "action.h"
+#include "keycodes.h"
+#include "misc.h"
+#include "alias.h"
+
+extern Atom tok_ONE_LEVEL;
+extern Atom tok_TWO_LEVEL;
+extern Atom tok_KEYPAD;
+
+/***====================================================================***/
+
+#define        RepeatYes       1
+#define        RepeatNo        0
+#define        RepeatUndefined ~((unsigned)0)
+
+#define        _Key_Syms       (1<<0)
+#define        _Key_Acts       (1<<1)
+#define        _Key_Repeat     (1<<2)
+#define        _Key_Behavior   (1<<3)
+#define        _Key_Type_Dflt  (1<<4)
+#define        _Key_Types      (1<<5)
+#define        _Key_GroupInfo  (1<<6)
+#define        _Key_VModMap    (1<<7)
+
+typedef struct _KeyInfo
+{
+    CommonInfo defs;
+    unsigned long name; /* the 4 chars of the key name, as long */
+    unsigned char groupInfo;
+    unsigned char typesDefined;
+    unsigned char symsDefined;
+    unsigned char actsDefined;
+    short numLevels[XkbNumKbdGroups];
+    KeySym *syms[XkbNumKbdGroups];
+    XkbAction *acts[XkbNumKbdGroups];
+    Atom types[XkbNumKbdGroups];
+    unsigned repeat;
+    XkbBehavior behavior;
+    unsigned short vmodmap;
+    unsigned long nameForOverlayKey;
+    unsigned long allowNone;
+    Atom dfltType;
+} KeyInfo;
+
+/**
+ * Init the given key info to sane values.
+ */
+static void
+InitKeyInfo(KeyInfo * info)
+{
+    register int i;
+    static char dflt[4] = "*";
+
+    info->defs.defined = 0;
+    info->defs.fileID = 0;
+    info->defs.merge = MergeOverride;
+    info->defs.next = NULL;
+    info->name = KeyNameToLong(dflt);
+    info->groupInfo = 0;
+    info->typesDefined = info->symsDefined = info->actsDefined = 0;
+    for (i = 0; i < XkbNumKbdGroups; i++)
+    {
+        info->numLevels[i] = 0;
+        info->types[i] = None;
+        info->syms[i] = NULL;
+        info->acts[i] = NULL;
+    }
+    info->dfltType = None;
+    info->behavior.type = XkbKB_Default;
+    info->behavior.data = 0;
+    info->vmodmap = 0;
+    info->nameForOverlayKey = 0;
+    info->repeat = RepeatUndefined;
+    info->allowNone = 0;
+    return;
+}
+
+/**
+ * Free memory associated with this key info and reset to sane values.
+ */
+static void
+FreeKeyInfo(KeyInfo * info)
+{
+    register int i;
+
+    info->defs.defined = 0;
+    info->defs.fileID = 0;
+    info->defs.merge = MergeOverride;
+    info->defs.next = NULL;
+    info->groupInfo = 0;
+    info->typesDefined = info->symsDefined = info->actsDefined = 0;
+    for (i = 0; i < XkbNumKbdGroups; i++)
+    {
+        info->numLevels[i] = 0;
+        info->types[i] = None;
+        if (info->syms[i] != NULL)
+            uFree(info->syms[i]);
+        info->syms[i] = NULL;
+        if (info->acts[i] != NULL)
+            uFree(info->acts[i]);
+        info->acts[i] = NULL;
+    }
+    info->dfltType = None;
+    info->behavior.type = XkbKB_Default;
+    info->behavior.data = 0;
+    info->vmodmap = 0;
+    info->nameForOverlayKey = 0;
+    info->repeat = RepeatUndefined;
+    info->allowNone = 0;
+    return;
+}
+
+/**
+ * Copy old into new, optionally reset old to 0.
+ * If old is reset, new simply re-uses old's memory. Otherwise, the memory is
+ * newly allocated and new points to the new memory areas.
+ */
+static Bool
+CopyKeyInfo(KeyInfo * old, KeyInfo * new, Bool clearOld)
+{
+    register int i;
+
+    *new = *old;
+    new->defs.next = NULL;
+    if (clearOld)
+    {
+        for (i = 0; i < XkbNumKbdGroups; i++)
+        {
+            old->numLevels[i] = 0;
+            old->syms[i] = NULL;
+            old->acts[i] = NULL;
+        }
+    }
+    else
+    {
+        int width;
+        for (i = 0; i < XkbNumKbdGroups; i++)
+        {
+            width = new->numLevels[i];
+            if (old->syms[i] != NULL)
+            {
+                new->syms[i] = uTypedCalloc(width, KeySym);
+                if (!new->syms[i])
+                {
+                    new->syms[i] = NULL;
+                    new->numLevels[i] = 0;
+                    return False;
+                }
+                memcpy((char *) new->syms[i], (char *) old->syms[i],
+                       width * sizeof(KeySym));
+            }
+            if (old->acts[i] != NULL)
+            {
+                new->acts[i] = uTypedCalloc(width, XkbAction);
+                if (!new->acts[i])
+                {
+                    new->acts[i] = NULL;
+                    return False;
+                }
+                memcpy((char *) new->acts[i], (char *) old->acts[i],
+                       width * sizeof(XkbAction));
+            }
+        }
+    }
+    return True;
+}
+
+/***====================================================================***/
+
+typedef struct _ModMapEntry
+{
+    CommonInfo defs;
+    Bool haveSymbol;
+    int modifier;
+    union
+    {
+        unsigned long keyName;
+        KeySym keySym;
+    } u;
+} ModMapEntry;
+
+#define        SYMBOLS_INIT_SIZE       110
+#define        SYMBOLS_CHUNK           20
+typedef struct _SymbolsInfo
+{
+    char *name;         /* e.g. pc+us+inet(evdev) */
+    int errorCount;
+    unsigned fileID;
+    unsigned merge;
+    unsigned explicit_group;
+    unsigned groupInfo;
+    unsigned szKeys;
+    unsigned nKeys;
+    KeyInfo *keys;
+    KeyInfo dflt;
+    VModInfo vmods;
+    ActionInfo *action;
+    Atom groupNames[XkbNumKbdGroups];
+
+    ModMapEntry *modMap;
+    AliasInfo *aliases;
+} SymbolsInfo;
+
+static void
+InitSymbolsInfo(SymbolsInfo * info, XkbDescPtr xkb)
+{
+    register int i;
+
+    tok_ONE_LEVEL = XkbInternAtom(NULL, "ONE_LEVEL", False);
+    tok_TWO_LEVEL = XkbInternAtom(NULL, "TWO_LEVEL", False);
+    tok_KEYPAD = XkbInternAtom(NULL, "KEYPAD", False);
+    info->name = NULL;
+    info->explicit_group = 0;
+    info->errorCount = 0;
+    info->fileID = 0;
+    info->merge = MergeOverride;
+    info->groupInfo = 0;
+    info->szKeys = SYMBOLS_INIT_SIZE;
+    info->nKeys = 0;
+    info->keys = uTypedCalloc(SYMBOLS_INIT_SIZE, KeyInfo);
+    info->modMap = NULL;
+    for (i = 0; i < XkbNumKbdGroups; i++)
+        info->groupNames[i] = None;
+    InitKeyInfo(&info->dflt);
+    InitVModInfo(&info->vmods, xkb);
+    info->action = NULL;
+    info->aliases = NULL;
+    return;
+}
+
+static void
+FreeSymbolsInfo(SymbolsInfo * info)
+{
+    register int i;
+
+    if (info->name)
+        uFree(info->name);
+    info->name = NULL;
+    if (info->keys)
+    {
+        for (i = 0; i < info->nKeys; i++)
+        {
+            FreeKeyInfo(&info->keys[i]);
+        }
+        uFree(info->keys);
+        info->keys = NULL;
+    }
+    if (info->modMap)
+    {
+        ClearCommonInfo(&info->modMap->defs);
+        info->modMap = NULL;
+    }
+    if (info->aliases)
+    {
+        ClearAliases(&info->aliases);
+        info->aliases = NULL;
+    }
+    bzero((char *) info, sizeof(SymbolsInfo));
+    return;
+}
+
+static Bool
+ResizeKeyGroup(KeyInfo * key,
+               unsigned group, unsigned atLeastSize, Bool forceActions)
+{
+    Bool tooSmall;
+    unsigned newWidth;
+
+    tooSmall = (key->numLevels[group] < atLeastSize);
+    if (tooSmall)
+        newWidth = atLeastSize;
+    else
+        newWidth = key->numLevels[group];
+
+    if ((key->syms[group] == NULL) || tooSmall)
+    {
+        key->syms[group] = uTypedRecalloc(key->syms[group],
+                                          key->numLevels[group], newWidth,
+                                          KeySym);
+        if (!key->syms[group])
+            return False;
+    }
+    if (((forceActions) && (tooSmall || (key->acts[group] == NULL))) ||
+        (tooSmall && (key->acts[group] != NULL)))
+    {
+        key->acts[group] = uTypedRecalloc(key->acts[group],
+                                          key->numLevels[group], newWidth,
+                                          XkbAction);
+        if (!key->acts[group])
+            return False;
+    }
+    key->numLevels[group] = newWidth;
+    return True;
+}
+
+static Bool
+MergeKeyGroups(SymbolsInfo * info,
+               KeyInfo * into, KeyInfo * from, unsigned group)
+{
+    KeySym *resultSyms;
+    XkbAction *resultActs;
+    int resultWidth;
+    register int i;
+    Bool report, clobber;
+
+    clobber = (from->defs.merge != MergeAugment);
+    report = (warningLevel > 9) ||
+        ((into->defs.fileID == from->defs.fileID) && (warningLevel > 0));
+    if (into->numLevels[group] >= from->numLevels[group])
+    {
+        resultSyms = into->syms[group];
+        resultActs = into->acts[group];
+        resultWidth = into->numLevels[group];
+    }
+    else
+    {
+        resultSyms = from->syms[group];
+        resultActs = from->acts[group];
+        resultWidth = from->numLevels[group];
+    }
+    if (resultSyms == NULL)
+    {
+        resultSyms = uTypedCalloc(resultWidth, KeySym);
+        if (!resultSyms)
+        {
+            WSGO("Could not allocate symbols for group merge\n");
+            ACTION2("Group %d of key %s not merged\n", group,
+                    longText(into->name, XkbMessage));
+            return False;
+        }
+    }
+    if ((resultActs == NULL) && (into->acts[group] || from->acts[group]))
+    {
+        resultActs = uTypedCalloc(resultWidth, XkbAction);
+        if (!resultActs)
+        {
+            WSGO("Could not allocate actions for group merge\n");
+            ACTION2("Group %d of key %s not merged\n", group,
+                    longText(into->name, XkbMessage));
+            return False;
+        }
+    }
+    for (i = 0; i < resultWidth; i++)
+    {
+        KeySym fromSym, toSym;
+        if (from->syms[group] && (i < from->numLevels[group]))
+            fromSym = from->syms[group][i];
+        else
+            fromSym = NoSymbol;
+        if (into->syms[group] && (i < into->numLevels[group]))
+            toSym = into->syms[group][i];
+        else
+            toSym = NoSymbol;
+        if ((fromSym == NoSymbol) || (fromSym == toSym))
+            resultSyms[i] = toSym;
+        else if (toSym == NoSymbol)
+            resultSyms[i] = fromSym;
+        else
+        {
+            KeySym use, ignore;
+            if (clobber)
+            {
+                use = fromSym;
+                ignore = toSym;
+            }
+            else
+            {
+                use = toSym;
+                ignore = fromSym;
+            }
+            if (report)
+            {
+                WARN3
+                    ("Multiple symbols for level %d/group %d on key %s\n",
+                     i + 1, group + 1, longText(into->name, XkbMessage));
+                ACTION2("Using %s, ignoring %s\n",
+                        XkbKeysymText(use, XkbMessage),
+                        XkbKeysymText(ignore, XkbMessage));
+            }
+            resultSyms[i] = use;
+        }
+        if (resultActs != NULL)
+        {
+            XkbAction *fromAct, *toAct;
+            fromAct = (from->acts[group] ? &from->acts[group][i] : NULL);
+            toAct = (into->acts[group] ? &into->acts[group][i] : NULL);
+            if (((fromAct == NULL) || (fromAct->type == XkbSA_NoAction))
+                && (toAct != NULL))
+            {
+                resultActs[i] = *toAct;
+            }
+            else if (((toAct == NULL) || (toAct->type == XkbSA_NoAction))
+                     && (fromAct != NULL))
+            {
+                resultActs[i] = *fromAct;
+            }
+            else
+            {
+                XkbAction *use, *ignore;
+                if (clobber)
+                {
+                    use = fromAct;
+                    ignore = toAct;
+                }
+                else
+                {
+                    use = toAct;
+                    ignore = fromAct;
+                }
+                if (report)
+                {
+                    WARN3
+                        ("Multiple actions for level %d/group %d on key %s\n",
+                         i + 1, group + 1, longText(into->name, XkbMessage));
+                    ACTION2("Using %s, ignoring %s\n",
+                            XkbActionTypeText(use->type, XkbMessage),
+                            XkbActionTypeText(ignore->type, XkbMessage));
+                }
+                resultActs[i] = *use;
+            }
+        }
+    }
+    if ((into->syms[group] != NULL) && (resultSyms != into->syms[group]))
+        uFree(into->syms[group]);
+    if ((from->syms[group] != NULL) && (resultSyms != from->syms[group]))
+        uFree(from->syms[group]);
+    if ((into->acts[group] != NULL) && (resultActs != into->acts[group]))
+        uFree(into->acts[group]);
+    if ((from->acts[group] != NULL) && (resultActs != from->acts[group]))
+        uFree(from->acts[group]);
+    into->numLevels[group] = resultWidth;
+    into->syms[group] = resultSyms;
+    from->syms[group] = NULL;
+    into->acts[group] = resultActs;
+    from->acts[group] = NULL;
+    into->symsDefined |= (1 << group);
+    from->symsDefined &= ~(1 << group);
+    into->actsDefined |= (1 << group);
+    from->actsDefined &= ~(1 << group);
+    return True;
+}
+
+static Bool
+MergeKeys(SymbolsInfo * info, KeyInfo * into, KeyInfo * from)
+{
+    register int i;
+    unsigned collide = 0;
+    Bool report;
+
+    if (from->defs.merge == MergeReplace)
+    {
+        for (i = 0; i < XkbNumKbdGroups; i++)
+        {
+            if (into->numLevels[i] != 0)
+            {
+                if (into->syms[i])
+                    uFree(into->syms[i]);
+                if (into->acts[i])
+                    uFree(into->acts[i]);
+            }
+        }
+        *into = *from;
+        bzero(from, sizeof(KeyInfo));
+        return True;
+    }
+    report = ((warningLevel > 9) ||
+              ((into->defs.fileID == from->defs.fileID)
+               && (warningLevel > 0)));
+    for (i = 0; i < XkbNumKbdGroups; i++)
+    {
+        if (from->numLevels[i] > 0)
+        {
+            if (into->numLevels[i] == 0)
+            {
+                into->numLevels[i] = from->numLevels[i];
+                into->syms[i] = from->syms[i];
+                into->acts[i] = from->acts[i];
+                into->symsDefined |= (1 << i);
+                from->syms[i] = NULL;
+                from->acts[i] = NULL;
+                from->numLevels[i] = 0;
+                from->symsDefined &= ~(1 << i);
+                if (into->syms[i])
+                    into->defs.defined |= _Key_Syms;
+                if (into->acts[i])
+                    into->defs.defined |= _Key_Acts;
+            }
+            else
+            {
+                if (report)
+                {
+                    if (into->syms[i])
+                        collide |= _Key_Syms;
+                    if (into->acts[i])
+                        collide |= _Key_Acts;
+                }
+                MergeKeyGroups(info, into, from, (unsigned) i);
+            }
+        }
+        if (from->types[i] != None)
+        {
+            if ((into->types[i] != None) && (report) &&
+                (into->types[i] != from->types[i]))
+            {
+                Atom use, ignore;
+                collide |= _Key_Types;
+                if (from->defs.merge != MergeAugment)
+                {
+                    use = from->types[i];
+                    ignore = into->types[i];
+                }
+                else
+                {
+                    use = into->types[i];
+                    ignore = from->types[i];
+                }
+                WARN2
+                    ("Multiple definitions for group %d type of key %s\n",
+                     i, longText(into->name, XkbMessage));
+                ACTION2("Using %s, ignoring %s\n",
+                        XkbAtomText(NULL, use, XkbMessage),
+                        XkbAtomText(NULL, ignore, XkbMessage));
+            }
+            if ((from->defs.merge != MergeAugment)
+                || (into->types[i] == None))
+            {
+                into->types[i] = from->types[i];
+            }
+        }
+    }
+    if (UseNewField(_Key_Behavior, &into->defs, &from->defs, &collide))
+    {
+        into->behavior = from->behavior;
+        into->nameForOverlayKey = from->nameForOverlayKey;
+        into->defs.defined |= _Key_Behavior;
+    }
+    if (UseNewField(_Key_VModMap, &into->defs, &from->defs, &collide))
+    {
+        into->vmodmap = from->vmodmap;
+        into->defs.defined |= _Key_VModMap;
+    }
+    if (UseNewField(_Key_Repeat, &into->defs, &from->defs, &collide))
+    {
+        into->repeat = from->repeat;
+        into->defs.defined |= _Key_Repeat;
+    }
+    if (UseNewField(_Key_Type_Dflt, &into->defs, &from->defs, &collide))
+    {
+        into->dfltType = from->dfltType;
+        into->defs.defined |= _Key_Type_Dflt;
+    }
+    if (UseNewField(_Key_GroupInfo, &into->defs, &from->defs, &collide))
+    {
+        into->groupInfo = from->groupInfo;
+        into->defs.defined |= _Key_GroupInfo;
+    }
+    if (collide)
+    {
+        WARN1("Symbol map for key %s redefined\n",
+              longText(into->name, XkbMessage));
+        ACTION1("Using %s definition for conflicting fields\n",
+                (from->defs.merge == MergeAugment ? "first" : "last"));
+    }
+    return True;
+}
+
+static Bool
+AddKeySymbols(SymbolsInfo * info, KeyInfo * key, XkbDescPtr xkb)
+{
+    register int i;
+    unsigned long real_name;
+
+    for (i = 0; i < info->nKeys; i++)
+    {
+        if (info->keys[i].name == key->name)
+            return MergeKeys(info, &info->keys[i], key);
+    }
+    if (FindKeyNameForAlias(xkb, key->name, &real_name))
+    {
+        for (i = 0; i < info->nKeys; i++)
+        {
+            if (info->keys[i].name == real_name)
+                return MergeKeys(info, &info->keys[i], key);
+        }
+    }
+    if (info->nKeys >= info->szKeys)
+    {
+        info->szKeys += SYMBOLS_CHUNK;
+        info->keys =
+            uTypedRecalloc(info->keys, info->nKeys, info->szKeys, KeyInfo);
+        if (!info->keys)
+        {
+            WSGO("Could not allocate key symbols descriptions\n");
+            ACTION("Some key symbols definitions may be lost\n");
+            return False;
+        }
+    }
+    return CopyKeyInfo(key, &info->keys[info->nKeys++], True);
+}
+
+static Bool
+AddModMapEntry(SymbolsInfo * info, ModMapEntry * new)
+{
+    ModMapEntry *mm;
+    Bool clobber;
+
+    clobber = (new->defs.merge != MergeAugment);
+    for (mm = info->modMap; mm != NULL; mm = (ModMapEntry *) mm->defs.next)
+    {
+        if (new->haveSymbol && mm->haveSymbol
+            && (new->u.keySym == mm->u.keySym))
+        {
+            unsigned use, ignore;
+            if (mm->modifier != new->modifier)
+            {
+                if (clobber)
+                {
+                    use = new->modifier;
+                    ignore = mm->modifier;
+                }
+                else
+                {
+                    use = mm->modifier;
+                    ignore = new->modifier;
+                }
+                ERROR1
+                    ("%s added to symbol map for multiple modifiers\n",
+                     XkbKeysymText(new->u.keySym, XkbMessage));
+                ACTION2("Using %s, ignoring %s.\n",
+                        XkbModIndexText(use, XkbMessage),
+                        XkbModIndexText(ignore, XkbMessage));
+                mm->modifier = use;
+            }
+            return True;
+        }
+        if ((!new->haveSymbol) && (!mm->haveSymbol) &&
+            (new->u.keyName == mm->u.keyName))
+        {
+            unsigned use, ignore;
+            if (mm->modifier != new->modifier)
+            {
+                if (clobber)
+                {
+                    use = new->modifier;
+                    ignore = mm->modifier;
+                }
+                else
+                {
+                    use = mm->modifier;
+                    ignore = new->modifier;
+                }
+                ERROR1("Key %s added to map for multiple modifiers\n",
+                       longText(new->u.keyName, XkbMessage));
+                ACTION2("Using %s, ignoring %s.\n",
+                        XkbModIndexText(use, XkbMessage),
+                        XkbModIndexText(ignore, XkbMessage));
+                mm->modifier = use;
+            }
+            return True;
+        }
+    }
+    mm = uTypedAlloc(ModMapEntry);
+    if (mm == NULL)
+    {
+        WSGO("Could not allocate modifier map entry\n");
+        ACTION1("Modifier map for %s will be incomplete\n",
+                XkbModIndexText(new->modifier, XkbMessage));
+        return False;
+    }
+    *mm = *new;
+    mm->defs.next = &info->modMap->defs;
+    info->modMap = mm;
+    return True;
+}
+
+/***====================================================================***/
+
+static void
+MergeIncludedSymbols(SymbolsInfo * into, SymbolsInfo * from,
+                     unsigned merge, XkbDescPtr xkb)
+{
+    register int i;
+    KeyInfo *key;
+
+    if (from->errorCount > 0)
+    {
+        into->errorCount += from->errorCount;
+        return;
+    }
+    if (into->name == NULL)
+    {
+        into->name = from->name;
+        from->name = NULL;
+    }
+    for (i = 0; i < XkbNumKbdGroups; i++)
+    {
+        if (from->groupNames[i] != None)
+        {
+            if ((merge != MergeAugment) || (into->groupNames[i] == None))
+                into->groupNames[i] = from->groupNames[i];
+        }
+    }
+    for (i = 0, key = from->keys; i < from->nKeys; i++, key++)
+    {
+        if (merge != MergeDefault)
+            key->defs.merge = merge;
+        if (!AddKeySymbols(into, key, xkb))
+            into->errorCount++;
+    }
+    if (from->modMap != NULL)
+    {
+        ModMapEntry *mm, *next;
+        for (mm = from->modMap; mm != NULL; mm = next)
+        {
+            if (merge != MergeDefault)
+                mm->defs.merge = merge;
+            if (!AddModMapEntry(into, mm))
+                into->errorCount++;
+            next = (ModMapEntry *) mm->defs.next;
+            uFree(mm);
+        }
+        from->modMap = NULL;
+    }
+    if (!MergeAliases(&into->aliases, &from->aliases, merge))
+        into->errorCount++;
+    return;
+}
+
+typedef void (*FileHandler) (XkbFile * /* rtrn */ ,
+                             XkbDescPtr /* xkb */ ,
+                             unsigned /* merge */ ,
+                             SymbolsInfo *      /* included */
+    );
+
+static Bool
+HandleIncludeSymbols(IncludeStmt * stmt,
+                     XkbDescPtr xkb, SymbolsInfo * info, FileHandler hndlr)
+{
+    unsigned newMerge;
+    XkbFile *rtrn;
+    SymbolsInfo included;
+    Bool haveSelf;
+
+    haveSelf = False;
+    if ((stmt->file == NULL) && (stmt->map == NULL))
+    {
+        haveSelf = True;
+        included = *info;
+        bzero(info, sizeof(SymbolsInfo));
+    }
+    else if (ProcessIncludeFile(stmt, XkmSymbolsIndex, &rtrn, &newMerge))
+    {
+        InitSymbolsInfo(&included, xkb);
+        included.fileID = included.dflt.defs.fileID = rtrn->id;
+        included.merge = included.dflt.defs.merge = MergeOverride;
+        if (stmt->modifier)
+        {
+            included.explicit_group = atoi(stmt->modifier) - 1;
+        }
+        else
+        {
+            included.explicit_group = info->explicit_group;
+        }
+        (*hndlr) (rtrn, xkb, MergeOverride, &included);
+        if (stmt->stmt != NULL)
+        {
+            if (included.name != NULL)
+                uFree(included.name);
+            included.name = stmt->stmt;
+            stmt->stmt = NULL;
+        }
+    }
+    else
+    {
+        info->errorCount += 10;
+        return False;
+    }
+    if ((stmt->next != NULL) && (included.errorCount < 1))
+    {
+        IncludeStmt *next;
+        unsigned op;
+        SymbolsInfo next_incl;
+
+        for (next = stmt->next; next != NULL; next = next->next)
+        {
+            if ((next->file == NULL) && (next->map == NULL))
+            {
+                haveSelf = True;
+                MergeIncludedSymbols(&included, info, next->merge, xkb);
+                FreeSymbolsInfo(info);
+            }
+            else if (ProcessIncludeFile(next, XkmSymbolsIndex, &rtrn, &op))
+            {
+                InitSymbolsInfo(&next_incl, xkb);
+                next_incl.fileID = next_incl.dflt.defs.fileID = rtrn->id;
+                next_incl.merge = next_incl.dflt.defs.merge = MergeOverride;
+                if (next->modifier)
+                {
+                    next_incl.explicit_group = atoi(next->modifier) - 1;
+                }
+                else
+                {
+                    next_incl.explicit_group = info->explicit_group;
+                }
+                (*hndlr) (rtrn, xkb, MergeOverride, &next_incl);
+                MergeIncludedSymbols(&included, &next_incl, op, xkb);
+                FreeSymbolsInfo(&next_incl);
+            }
+            else
+            {
+                info->errorCount += 10;
+                return False;
+            }
+        }
+    }
+    if (haveSelf)
+        *info = included;
+    else
+    {
+        MergeIncludedSymbols(info, &included, newMerge, xkb);
+        FreeSymbolsInfo(&included);
+    }
+    return (info->errorCount == 0);
+}
+
+static LookupEntry groupNames[] = {
+    {"group1", 1},
+    {"group2", 2},
+    {"group3", 3},
+    {"group4", 4},
+    {"group5", 5},
+    {"group6", 6},
+    {"group7", 7},
+    {"group8", 8},
+    {NULL, 0}
+};
+
+
+#define        SYMBOLS 1
+#define        ACTIONS 2
+
+static Bool
+GetGroupIndex(KeyInfo * key,
+              ExprDef * arrayNdx, unsigned what, unsigned *ndx_rtrn)
+{
+    const char *name;
+    ExprResult tmp;
+
+    if (what == SYMBOLS)
+        name = "symbols";
+    else
+        name = "actions";
+
+    if (arrayNdx == NULL)
+    {
+        register int i;
+        unsigned defined;
+        if (what == SYMBOLS)
+            defined = key->symsDefined;
+        else
+            defined = key->actsDefined;
+
+        for (i = 0; i < XkbNumKbdGroups; i++)
+        {
+            if ((defined & (1 << i)) == 0)
+            {
+                *ndx_rtrn = i;
+                return True;
+            }
+        }
+        ERROR3("Too many groups of %s for key %s (max %d)\n", name,
+               longText(key->name, XkbMessage), XkbNumKbdGroups + 1);
+        ACTION1("Ignoring %s defined for extra groups\n", name);
+        return False;
+    }
+    if (!ExprResolveInteger
+        (arrayNdx, &tmp, SimpleLookup, (XPointer) groupNames))
+    {
+        ERROR2("Illegal group index for %s of key %s\n", name,
+               longText(key->name, XkbMessage));
+        ACTION("Definition with non-integer array index ignored\n");
+        return False;
+    }
+    if ((tmp.uval < 1) || (tmp.uval > XkbNumKbdGroups))
+    {
+        ERROR3("Group index for %s of key %s is out of range (1..%d)\n",
+               name, longText(key->name, XkbMessage), XkbNumKbdGroups + 1);
+        ACTION2("Ignoring %s for group %d\n", name, tmp.uval);
+        return False;
+    }
+    *ndx_rtrn = tmp.uval - 1;
+    return True;
+}
+
+static Bool
+AddSymbolsToKey(KeyInfo * key,
+                XkbDescPtr xkb,
+                char *field,
+                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+{
+    unsigned ndx, nSyms;
+    int i;
+
+    if (!GetGroupIndex(key, arrayNdx, SYMBOLS, &ndx))
+        return False;
+    if (value == NULL)
+    {
+        key->symsDefined |= (1 << ndx);
+        return True;
+    }
+    if (value->op != ExprKeysymList)
+    {
+        ERROR1("Expected a list of symbols, found %s\n",
+               exprOpText(value->op));
+        ACTION2("Ignoring symbols for group %d of %s\n", ndx,
+                longText(key->name, XkbMessage));
+        return False;
+    }
+    if (key->syms[ndx] != NULL)
+    {
+        WSGO2("Symbols for key %s, group %d already defined\n",
+              longText(key->name, XkbMessage), ndx);
+        return False;
+    }
+    nSyms = value->value.list.nSyms;
+    if (((key->numLevels[ndx] < nSyms) || (key->syms[ndx] == NULL)) &&
+        (!ResizeKeyGroup(key, ndx, nSyms, False)))
+    {
+        WSGO2("Could not resize group %d of key %s\n", ndx,
+              longText(key->name, XkbMessage));
+        ACTION("Symbols lost\n");
+        return False;
+    }
+    key->symsDefined |= (1 << ndx);
+    memcpy((char *) key->syms[ndx], (char *) value->value.list.syms,
+           nSyms * sizeof(KeySym));
+    for (i = key->numLevels[ndx] - 1;
+         (i >= 0) && (key->syms[ndx][i] == NoSymbol); i--)
+    {
+        key->numLevels[ndx]--;
+    }
+    return True;
+}
+
+static Bool
+AddActionsToKey(KeyInfo * key,
+                XkbDescPtr xkb,
+                char *field,
+                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+{
+    register int i;
+    unsigned ndx, nActs;
+    ExprDef *act;
+    XkbAnyAction *toAct;
+
+    if (!GetGroupIndex(key, arrayNdx, ACTIONS, &ndx))
+        return False;
+
+    if (value == NULL)
+    {
+        key->actsDefined |= (1 << ndx);
+        return True;
+    }
+    if (value->op != ExprActionList)
+    {
+        WSGO1("Bad expression type (%d) for action list value\n", value->op);
+        ACTION2("Ignoring actions for group %d of %s\n", ndx,
+                longText(key->name, XkbMessage));
+        return False;
+    }
+    if (key->acts[ndx] != NULL)
+    {
+        WSGO2("Actions for key %s, group %d already defined\n",
+              longText(key->name, XkbMessage), ndx);
+        return False;
+    }
+    for (nActs = 0, act = value->value.child; act != NULL; nActs++)
+    {
+        act = (ExprDef *) act->common.next;
+    }
+    if (nActs < 1)
+    {
+        WSGO("Action list but not actions in AddActionsToKey\n");
+        return False;
+    }
+    if (((key->numLevels[ndx] < nActs) || (key->acts[ndx] == NULL)) &&
+        (!ResizeKeyGroup(key, ndx, nActs, True)))
+    {
+        WSGO2("Could not resize group %d of key %s\n", ndx,
+              longText(key->name, XkbMessage));
+        ACTION("Actions lost\n");
+        return False;
+    }
+    key->actsDefined |= (1 << ndx);
+
+    toAct = (XkbAnyAction *) key->acts[ndx];
+    act = value->value.child;
+    for (i = 0; i < nActs; i++, toAct++)
+    {
+        if (!HandleActionDef(act, xkb, toAct, MergeOverride, info->action))
+        {
+            ERROR1("Illegal action definition for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION2("Action for group %d/level %d ignored\n", ndx + 1, i + 1);
+        }
+        act = (ExprDef *) act->common.next;
+    }
+    return True;
+}
+
+static int
+SetAllowNone(KeyInfo * key, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult tmp;
+    unsigned radio_groups = 0;
+
+    if (arrayNdx == NULL)
+    {
+        radio_groups = XkbAllRadioGroupsMask;
+    }
+    else
+    {
+        if (!ExprResolveInteger(arrayNdx, &tmp, RadioLookup, NULL))
+        {
+            ERROR("Illegal index in group name definition\n");
+            ACTION("Definition with non-integer array index ignored\n");
+            return False;
+        }
+        if ((tmp.uval < 1) || (tmp.uval > XkbMaxRadioGroups))
+        {
+            ERROR1("Illegal radio group specified (must be 1..%d)\n",
+                   XkbMaxRadioGroups + 1);
+            ACTION1("Value of \"allow none\" for group %d ignored\n",
+                    tmp.uval);
+            return False;
+        }
+        radio_groups |= (1 << (tmp.uval - 1));
+    }
+    if (!ExprResolveBoolean(value, &tmp, NULL, NULL))
+    {
+        ERROR1("Illegal \"allow none\" value for %s\n",
+               longText(key->name, XkbMessage));
+        ACTION("Non-boolean value ignored\n");
+        return False;
+    }
+    if (tmp.uval)
+        key->allowNone |= radio_groups;
+    else
+        key->allowNone &= ~radio_groups;
+    return True;
+}
+
+
+static LookupEntry lockingEntries[] = {
+    {"true", XkbKB_Lock},
+    {"yes", XkbKB_Lock},
+    {"on", XkbKB_Lock},
+    {"false", XkbKB_Default},
+    {"no", XkbKB_Default},
+    {"off", XkbKB_Default},
+    {"permanent", XkbKB_Lock | XkbKB_Permanent},
+    {NULL, 0}
+};
+
+static LookupEntry repeatEntries[] = {
+    {"true", RepeatYes},
+    {"yes", RepeatYes},
+    {"on", RepeatYes},
+    {"false", RepeatNo},
+    {"no", RepeatNo},
+    {"off", RepeatNo},
+    {"default", RepeatUndefined},
+    {NULL, 0}
+};
+
+static LookupEntry rgEntries[] = {
+    {"none", 0},
+    {NULL, 0}
+};
+
+static Bool
+SetSymbolsField(KeyInfo * key,
+                XkbDescPtr xkb,
+                char *field,
+                ExprDef * arrayNdx, ExprDef * value, SymbolsInfo * info)
+{
+    Bool ok = True;
+    ExprResult tmp;
+
+    if (uStrCaseCmp(field, "type") == 0)
+    {
+        ExprResult ndx;
+        if ((!ExprResolveString(value, &tmp, NULL, NULL))
+            && (warningLevel > 0))
+        {
+            WARN("The type field of a key symbol map must be a string\n");
+            ACTION("Ignoring illegal type definition\n");
+        }
+        if (arrayNdx == NULL)
+        {
+            key->dfltType = XkbInternAtom(NULL, tmp.str, False);
+            key->defs.defined |= _Key_Type_Dflt;
+        }
+        else if (!ExprResolveInteger(arrayNdx, &ndx, SimpleLookup,
+                                     (XPointer) groupNames))
+        {
+            ERROR1("Illegal group index for type of key %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Definition with non-integer array index ignored\n");
+            return False;
+        }
+        else if ((ndx.uval < 1) || (ndx.uval > XkbNumKbdGroups))
+        {
+            ERROR2
+                ("Group index for type of key %s is out of range (1..%d)\n",
+                 longText(key->name, XkbMessage), XkbNumKbdGroups + 1);
+            ACTION1("Ignoring type for group %d\n", ndx.uval);
+            return False;
+        }
+        else
+        {
+            key->types[ndx.uval - 1] = XkbInternAtom(NULL, tmp.str, False);
+            key->typesDefined |= (1 << (ndx.uval - 1));
+        }
+    }
+    else if (uStrCaseCmp(field, "symbols") == 0)
+        return AddSymbolsToKey(key, xkb, field, arrayNdx, value, info);
+    else if (uStrCaseCmp(field, "actions") == 0)
+        return AddActionsToKey(key, xkb, field, arrayNdx, value, info);
+    else if ((uStrCaseCmp(field, "vmods") == 0) ||
+             (uStrCaseCmp(field, "virtualmods") == 0) ||
+             (uStrCaseCmp(field, "virtualmodifiers") == 0))
+    {
+        ok = ExprResolveModMask(value, &tmp, LookupVModMask, (XPointer) xkb);
+        if (ok)
+        {
+            key->vmodmap = (tmp.uval >> 8);
+            key->defs.defined |= _Key_VModMap;
+        }
+        else
+        {
+            ERROR1("Expected a virtual modifier mask, found %s\n",
+                   exprOpText(value->op));
+            ACTION1("Ignoring virtual modifiers definition for key %s\n",
+                    longText(key->name, XkbMessage));
+        }
+    }
+    else if ((uStrCaseCmp(field, "locking") == 0)
+             || (uStrCaseCmp(field, "lock") == 0)
+             || (uStrCaseCmp(field, "locks") == 0))
+    {
+        ok = ExprResolveEnum(value, &tmp, lockingEntries);
+        if (ok)
+            key->behavior.type = tmp.uval;
+        key->defs.defined |= _Key_Behavior;
+    }
+    else if ((uStrCaseCmp(field, "radiogroup") == 0) ||
+             (uStrCaseCmp(field, "permanentradiogroup") == 0))
+    {
+        Bool permanent = False;
+        if (uStrCaseCmp(field, "permanentradiogroup") == 0)
+            permanent = True;
+        ok = ExprResolveInteger(value, &tmp, SimpleLookup,
+                                (XPointer) rgEntries);
+        if (!ok)
+        {
+            ERROR1("Illegal radio group specification for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Non-integer radio group ignored\n");
+            return False;
+        }
+        if (tmp.uval == 0)
+        {
+            key->behavior.type = XkbKB_Default;
+            key->behavior.data = 0;
+            return ok;
+        }
+        if ((tmp.uval < 1) || (tmp.uval > XkbMaxRadioGroups))
+        {
+            ERROR1
+                ("Radio group specification for %s out of range (1..32)\n",
+                 longText(key->name, XkbMessage));
+            ACTION1("Illegal radio group %d ignored\n", tmp.uval);
+            return False;
+        }
+        key->behavior.type =
+            XkbKB_RadioGroup | (permanent ? XkbKB_Permanent : 0);
+        key->behavior.data = tmp.uval - 1;
+        if (key->allowNone & (1 << (tmp.uval - 1)))
+            key->behavior.data |= XkbKB_RGAllowNone;
+        key->defs.defined |= _Key_Behavior;
+    }
+    else if (uStrCaseEqual(field, "allownone"))
+    {
+        ok = SetAllowNone(key, arrayNdx, value);
+    }
+    else if (uStrCasePrefix("overlay", field) ||
+             uStrCasePrefix("permanentoverlay", field))
+    {
+        Bool permanent = False;
+        char *which;
+        int overlayNdx;
+        if (uStrCasePrefix("permanent", field))
+        {
+            permanent = True;
+            which = &field[sizeof("permanentoverlay") - 1];
+        }
+        else
+        {
+            which = &field[sizeof("overlay") - 1];
+        }
+        if (sscanf(which, "%d", &overlayNdx) == 1)
+        {
+            if (((overlayNdx < 1) || (overlayNdx > 2)) && (warningLevel > 0))
+            {
+                ERROR2("Illegal overlay %d specified for %s\n",
+                       overlayNdx, longText(key->name, XkbMessage));
+                ACTION("Ignored\n");
+                return False;
+            }
+        }
+        else if (*which == '\0')
+            overlayNdx = 1;
+        else if (warningLevel > 0)
+        {
+            ERROR2("Illegal overlay \"%s\" specified for %s\n",
+                   which, longText(key->name, XkbMessage));
+            ACTION("Ignored\n");
+            return False;
+        }
+        ok = ExprResolveKeyName(value, &tmp, NULL, NULL);
+        if (!ok)
+        {
+            ERROR1("Illegal overlay key specification for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Overlay key must be specified by name\n");
+            return False;
+        }
+        if (overlayNdx == 1)
+            key->behavior.type = XkbKB_Overlay1;
+        else
+            key->behavior.type = XkbKB_Overlay2;
+        if (permanent)
+            key->behavior.type |= XkbKB_Permanent;
+
+        key->behavior.data = 0;
+        key->nameForOverlayKey = KeyNameToLong(tmp.keyName.name);
+        key->defs.defined |= _Key_Behavior;
+    }
+    else if ((uStrCaseCmp(field, "repeating") == 0) ||
+             (uStrCaseCmp(field, "repeats") == 0) ||
+             (uStrCaseCmp(field, "repeat") == 0))
+    {
+        ok = ExprResolveEnum(value, &tmp, repeatEntries);
+        if (!ok)
+        {
+            ERROR1("Illegal repeat setting for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Non-boolean repeat setting ignored\n");
+            return False;
+        }
+        key->repeat = tmp.uval;
+        key->defs.defined |= _Key_Repeat;
+    }
+    else if ((uStrCaseCmp(field, "groupswrap") == 0) ||
+             (uStrCaseCmp(field, "wrapgroups") == 0))
+    {
+        ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
+        if (!ok)
+        {
+            ERROR1("Illegal groupsWrap setting for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Non-boolean value ignored\n");
+            return False;
+        }
+        if (tmp.uval)
+            key->groupInfo = XkbWrapIntoRange;
+        else
+            key->groupInfo = XkbClampIntoRange;
+        key->defs.defined |= _Key_GroupInfo;
+    }
+    else if ((uStrCaseCmp(field, "groupsclamp") == 0) ||
+             (uStrCaseCmp(field, "clampgroups") == 0))
+    {
+        ok = ExprResolveBoolean(value, &tmp, NULL, NULL);
+        if (!ok)
+        {
+            ERROR1("Illegal groupsClamp setting for %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Non-boolean value ignored\n");
+            return False;
+        }
+        if (tmp.uval)
+            key->groupInfo = XkbClampIntoRange;
+        else
+            key->groupInfo = XkbWrapIntoRange;
+        key->defs.defined |= _Key_GroupInfo;
+    }
+    else if ((uStrCaseCmp(field, "groupsredirect") == 0) ||
+             (uStrCaseCmp(field, "redirectgroups") == 0))
+    {
+        if (!ExprResolveInteger
+            (value, &tmp, SimpleLookup, (XPointer) groupNames))
+        {
+            ERROR1("Illegal group index for redirect of key %s\n",
+                   longText(key->name, XkbMessage));
+            ACTION("Definition with non-integer group ignored\n");
+            return False;
+        }
+        if ((tmp.uval < 1) || (tmp.uval > XkbNumKbdGroups))
+        {
+            ERROR2("Out-of-range (1..%d) group for redirect of key %s\n",
+                   XkbNumKbdGroups, longText(key->name, XkbMessage));
+            ERROR1("Ignoring illegal group %d\n", tmp.uval);
+            return False;
+        }
+        key->groupInfo =
+            XkbSetGroupInfo(0, XkbRedirectIntoRange, tmp.uval - 1);
+        key->defs.defined |= _Key_GroupInfo;
+    }
+    else
+    {
+        ERROR1("Unknown field %s in a symbol interpretation\n", field);
+        ACTION("Definition ignored\n");
+        ok = False;
+    }
+    return ok;
+}
+
+static int
+SetGroupName(SymbolsInfo * info, ExprDef * arrayNdx, ExprDef * value)
+{
+    ExprResult tmp, name;
+
+    if ((arrayNdx == NULL) && (warningLevel > 0))
+    {
+        WARN("You must specify an index when specifying a group name\n");
+        ACTION("Group name definition without array subscript ignored\n");
+        return False;
+    }
+    if (!ExprResolveInteger
+        (arrayNdx, &tmp, SimpleLookup, (XPointer) groupNames))
+    {
+        ERROR("Illegal index in group name definition\n");
+        ACTION("Definition with non-integer array index ignored\n");
+        return False;
+    }
+    if ((tmp.uval < 1) || (tmp.uval > XkbNumKbdGroups))
+    {
+        ERROR1
+            ("Attempt to specify name for illegal group (must be 1..%d)\n",
+             XkbNumKbdGroups + 1);
+        ACTION1("Name for group %d ignored\n", tmp.uval);
+        return False;
+    }
+    if (!ExprResolveString(value, &name, NULL, NULL))
+    {
+        ERROR("Group name must be a string\n");
+        ACTION1("Illegal name for group %d ignored\n", tmp.uval);
+        return False;
+    }
+    info->groupNames[tmp.uval - 1 + info->explicit_group] =
+        XkbInternAtom(NULL, name.str, False);
+
+    return True;
+}
+
+static int
+HandleSymbolsVar(VarDef * stmt, XkbDescPtr xkb, SymbolsInfo * info)
+{
+    ExprResult elem, field, tmp;
+    ExprDef *arrayNdx;
+
+    if (ExprResolveLhs(stmt->name, &elem, &field, &arrayNdx) == 0)
+        return 0;               /* internal error, already reported */
+    if (elem.str && (uStrCaseCmp(elem.str, "key") == 0))
+    {
+        return SetSymbolsField(&info->dflt, xkb, field.str, arrayNdx,
+                               stmt->value, info);
+    }
+    else if ((elem.str == NULL) && ((uStrCaseCmp(field.str, "name") == 0) ||
+                                    (uStrCaseCmp(field.str, "groupname") ==
+                                     0)))
+    {
+        return SetGroupName(info, arrayNdx, stmt->value);
+    }
+    else if ((elem.str == NULL)
+             && ((uStrCaseCmp(field.str, "groupswrap") == 0)
+                 || (uStrCaseCmp(field.str, "wrapgroups") == 0)))
+    {
+        if (!ExprResolveBoolean(stmt->value, &tmp, NULL, NULL))
+        {
+            ERROR("Illegal setting for global groupsWrap\n");
+            ACTION("Non-boolean value ignored\n");
+            return False;
+        }
+        if (tmp.uval)
+            info->groupInfo = XkbWrapIntoRange;
+        else
+            info->groupInfo = XkbClampIntoRange;
+        return True;
+    }
+    else if ((elem.str == NULL)
+             && ((uStrCaseCmp(field.str, "groupsclamp") == 0)
+                 || (uStrCaseCmp(field.str, "clampgroups") == 0)))
+    {
+        if (!ExprResolveBoolean(stmt->value, &tmp, NULL, NULL))
+        {
+            ERROR("Illegal setting for global groupsClamp\n");
+            ACTION("Non-boolean value ignored\n");
+            return False;
+        }
+        if (tmp.uval)
+            info->groupInfo = XkbClampIntoRange;
+        else
+            info->groupInfo = XkbWrapIntoRange;
+        return True;
+    }
+    else if ((elem.str == NULL)
+             && ((uStrCaseCmp(field.str, "groupsredirect") == 0)
+                 || (uStrCaseCmp(field.str, "redirectgroups") == 0)))
+    {
+        if (!ExprResolveInteger(stmt->value, &tmp,
+                                SimpleLookup, (XPointer) groupNames))
+        {
+            ERROR("Illegal group index for global groupsRedirect\n");
+            ACTION("Definition with non-integer group ignored\n");
+            return False;
+        }
+        if ((tmp.uval < 1) || (tmp.uval > XkbNumKbdGroups))
+        {
+            ERROR1
+                ("Out-of-range (1..%d) group for global groupsRedirect\n",
+                 XkbNumKbdGroups);
+            ACTION1("Ignoring illegal group %d\n", tmp.uval);
+            return False;
+        }
+        info->groupInfo = XkbSetGroupInfo(0, XkbRedirectIntoRange, tmp.uval);
+        return True;
+    }
+    else if ((elem.str == NULL) && (uStrCaseCmp(field.str, "allownone") == 0))
+    {
+        return SetAllowNone(&info->dflt, arrayNdx, stmt->value);
+    }
+    return SetActionField(xkb, elem.str, field.str, arrayNdx, stmt->value,
+                          &info->action);
+}
+
+static Bool
+HandleSymbolsBody(VarDef * def,
+                  XkbDescPtr xkb, KeyInfo * key, SymbolsInfo * info)
+{
+    Bool ok = True;
+    ExprResult tmp, field;
+    ExprDef *arrayNdx;
+
+    for (; def != NULL; def = (VarDef *) def->common.next)
+    {
+        if ((def->name) && (def->name->type == ExprFieldRef))
+        {
+            ok = HandleSymbolsVar(def, xkb, info);
+            continue;
+        }
+        else
+        {
+            if (def->name == NULL)
+            {
+                if ((def->value == NULL)
+                    || (def->value->op == ExprKeysymList))
+                    field.str = "symbols";
+                else
+                    field.str = "actions";
+                arrayNdx = NULL;
+            }
+            else
+            {
+                ok = ExprResolveLhs(def->name, &tmp, &field, &arrayNdx);
+            }
+            if (ok)
+                ok = SetSymbolsField(key, xkb, field.str, arrayNdx,
+                                     def->value, info);
+        }
+    }
+    return ok;
+}
+
+static Bool
+SetExplicitGroup(SymbolsInfo * info, KeyInfo * key)
+{
+    unsigned group = info->explicit_group;
+
+    if (group == 0)
+        return True;
+
+    if ((key->typesDefined | key->symsDefined | key->actsDefined) & ~1)
+    {
+        int i;
+        WARN1("For the map %s an explicit group specified\n", info->name);
+        WARN1("but key %s has more than one group defined\n",
+              longText(key->name, XkbMessage));
+        ACTION("All groups except first one will be ignored\n");
+        for (i = 1; i < XkbNumKbdGroups; i++)
+        {
+            key->numLevels[i] = 0;
+            if (key->syms[i] != NULL)
+                uFree(key->syms[i]);
+            key->syms[i] = (KeySym *) NULL;
+            if (key->acts[i] != NULL)
+                uFree(key->acts[i]);
+            key->acts[i] = (XkbAction *) NULL;
+            key->types[i] = (Atom) 0;
+        }
+    }
+    key->typesDefined = key->symsDefined = key->actsDefined = 1 << group;
+
+    key->numLevels[group] = key->numLevels[0];
+    key->numLevels[0] = 0;
+    key->syms[group] = key->syms[0];
+    key->syms[0] = (KeySym *) NULL;
+    key->acts[group] = key->acts[0];
+    key->acts[0] = (XkbAction *) NULL;
+    key->types[group] = key->types[0];
+    key->types[0] = (Atom) 0;
+    return True;
+}
+
+static int
+HandleSymbolsDef(SymbolsDef * stmt,
+                 XkbDescPtr xkb, unsigned merge, SymbolsInfo * info)
+{
+    KeyInfo key;
+
+    InitKeyInfo(&key);
+    CopyKeyInfo(&info->dflt, &key, False);
+    key.defs.merge = stmt->merge;
+    key.name = KeyNameToLong(stmt->keyName);
+    if (!HandleSymbolsBody((VarDef *) stmt->symbols, xkb, &key, info))
+    {
+        info->errorCount++;
+        return False;
+    }
+
+    if (!SetExplicitGroup(info, &key))
+    {
+        info->errorCount++;
+        return False;
+    }
+
+    if (!AddKeySymbols(info, &key, xkb))
+    {
+        info->errorCount++;
+        return False;
+    }
+    return True;
+}
+
+static Bool
+HandleModMapDef(ModMapDef * def,
+                XkbDescPtr xkb, unsigned merge, SymbolsInfo * info)
+{
+    ExprDef *key;
+    ModMapEntry tmp;
+    ExprResult rtrn;
+    Bool ok;
+
+    if (!LookupModIndex(NULL, None, def->modifier, TypeInt, &rtrn))
+    {
+        ERROR("Illegal modifier map definition\n");
+        ACTION1("Ignoring map for non-modifier \"%s\"\n",
+                XkbAtomText(NULL, def->modifier, XkbMessage));
+        return False;
+    }
+    ok = True;
+    tmp.modifier = rtrn.uval;
+    for (key = def->keys; key != NULL; key = (ExprDef *) key->common.next)
+    {
+        if ((key->op == ExprValue) && (key->type == TypeKeyName))
+        {
+            tmp.haveSymbol = False;
+            tmp.u.keyName = KeyNameToLong(key->value.keyName);
+        }
+        else if (ExprResolveKeySym(key, &rtrn, NULL, NULL))
+        {
+            tmp.haveSymbol = True;
+            tmp.u.keySym = rtrn.uval;
+        }
+        else
+        {
+            ERROR("Modmap entries may contain only key names or keysyms\n");
+            ACTION1("Illegal definition for %s modifier ignored\n",
+                    XkbModIndexText(tmp.modifier, XkbMessage));
+            continue;
+        }
+
+        ok = AddModMapEntry(info, &tmp) && ok;
+    }
+    return ok;
+}
+
+static void
+HandleSymbolsFile(XkbFile * file,
+                  XkbDescPtr xkb, unsigned merge, SymbolsInfo * info)
+{
+    ParseCommon *stmt;
+
+    info->name = uStringDup(file->name);
+    stmt = file->defs;
+    while (stmt)
+    {
+        switch (stmt->stmtType)
+        {
+        case StmtInclude:
+            if (!HandleIncludeSymbols((IncludeStmt *) stmt, xkb, info,
+                                      HandleSymbolsFile))
+                info->errorCount++;
+            break;
+        case StmtSymbolsDef:
+            if (!HandleSymbolsDef((SymbolsDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        case StmtVarDef:
+            if (!HandleSymbolsVar((VarDef *) stmt, xkb, info))
+                info->errorCount++;
+            break;
+        case StmtVModDef:
+            if (!HandleVModDef((VModDef *) stmt, merge, &info->vmods))
+                info->errorCount++;
+            break;
+        case StmtInterpDef:
+            ERROR("Interpretation files may not include other types\n");
+            ACTION("Ignoring definition of symbol interpretation\n");
+            info->errorCount++;
+            break;
+        case StmtKeycodeDef:
+            ERROR("Interpretation files may not include other types\n");
+            ACTION("Ignoring definition of key name\n");
+            info->errorCount++;
+            break;
+        case StmtModMapDef:
+            if (!HandleModMapDef((ModMapDef *) stmt, xkb, merge, info))
+                info->errorCount++;
+            break;
+        default:
+            WSGO1("Unexpected statement type %d in HandleSymbolsFile\n",
+                  stmt->stmtType);
+            break;
+        }
+        stmt = stmt->next;
+        if (info->errorCount > 10)
+        {
+#ifdef NOISY
+            ERROR("Too many errors\n");
+#endif
+            ACTION1("Abandoning symbols file \"%s\"\n", file->topName);
+            break;
+        }
+    }
+    return;
+}
+
+static Bool
+FindKeyForSymbol(XkbDescPtr xkb, KeySym sym, unsigned int *kc_rtrn)
+{
+    register int i, j;
+    register Bool gotOne;
+
+    j = 0;
+    do
+    {
+        gotOne = False;
+        for (i = xkb->min_key_code; i <= (int) xkb->max_key_code; i++)
+        {
+            if (j < (int) XkbKeyNumSyms(xkb, i))
+            {
+                gotOne = True;
+                if ((XkbKeySym(xkb, i, j) == sym))
+                {
+                    *kc_rtrn = i;
+                    return True;
+                }
+            }
+        }
+        j++;
+    }
+    while (gotOne);
+    return False;
+}
+
+/**
+ * Find the given name in the xkb->map->types and return its index.
+ *
+ * @param name The atom to search for.
+ * @param type_rtrn Set to the index of the name if found.
+ *
+ * @return True if found, False otherwise.
+ */
+static Bool
+FindNamedType(XkbDescPtr xkb, Atom name, unsigned *type_rtrn)
+{
+    register unsigned n;
+
+    if (xkb && xkb->map && xkb->map->types)
+    {
+        for (n = 0; n < xkb->map->num_types; n++)
+        {
+            if (xkb->map->types[n].name == (Atom) name)
+            {
+                *type_rtrn = n;
+                return True;
+            }
+        }
+    }
+    return False;
+}
+
+static Bool
+KSIsLower(KeySym ks)
+{
+    KeySym lower, upper;
+    XConvertCase(ks, &lower, &upper);
+
+    if (lower == upper)
+        return False;
+    return (ks == lower ? True : False);
+}
+
+static Bool
+KSIsUpper(KeySym ks)
+{
+    KeySym lower, upper;
+    XConvertCase(ks, &lower, &upper);
+
+    if (lower == upper)
+        return False;
+    return (ks == upper ? True : False);
+}
+
+/**
+ * Assign a type to the given sym and return the Atom for the type assigned.
+ *
+ * Simple recipe:
+ * - ONE_LEVEL for width 0/1
+ * - ALPHABETIC for 2 shift levels, with lower/upercase
+ * - KEYPAD for keypad keys.
+ * - TWO_LEVEL for other 2 shift level keys.
+ * and the same for four level keys.
+ *
+ * @param width Number of sysms in syms.
+ * @param syms The keysyms for the given key (must be size width).
+ * @param typeNameRtrn Set to the Atom of the type name.
+ *
+ * @returns True if a type could be found, False otherwise.
+ */
+static Bool
+FindAutomaticType(int width, KeySym * syms, Atom * typeNameRtrn,
+                  Bool * autoType)
+{
+    *autoType = False;
+    if ((width == 1) || (width == 0))
+    {
+        *typeNameRtrn = XkbInternAtom(NULL, "ONE_LEVEL", False);
+        *autoType = True;
+    }
+    else if (width == 2)
+    {
+        if (syms && KSIsLower(syms[0]) && KSIsUpper(syms[1]))
+        {
+            *typeNameRtrn = XkbInternAtom(NULL, "ALPHABETIC", False);
+        }
+        else if (syms && (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1])))
+        {
+            *typeNameRtrn = XkbInternAtom(NULL, "KEYPAD", False);
+            *autoType = True;
+        }
+        else
+        {
+            *typeNameRtrn = XkbInternAtom(NULL, "TWO_LEVEL", False);
+            *autoType = True;
+        }
+    }
+    else if (width <= 4)
+    {
+        if (syms && KSIsLower(syms[0]) && KSIsUpper(syms[1]))
+            if (KSIsLower(syms[2]) && KSIsUpper(syms[3]))
+                *typeNameRtrn =
+                    XkbInternAtom(NULL, "FOUR_LEVEL_ALPHABETIC", False);
+            else
+                *typeNameRtrn = XkbInternAtom(NULL,
+                                              "FOUR_LEVEL_SEMIALPHABETIC",
+                                              False);
+
+        else if (syms && (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1])))
+            *typeNameRtrn = XkbInternAtom(NULL, "FOUR_LEVEL_KEYPAD", False);
+        else
+            *typeNameRtrn = XkbInternAtom(NULL, "FOUR_LEVEL", False);
+        /* XXX: why not set autoType here? */
+    }
+    return ((width >= 0) && (width <= 4));
+}
+
+/**
+ * Ensure the given KeyInfo is in a coherent state, i.e. no gaps between the
+ * groups, and reduce to one group if all groups are identical anyway.
+ */
+static void
+PrepareKeyDef(KeyInfo * key)
+{
+    int i, j, width, defined, lastGroup;
+    Bool identical;
+
+    defined = key->symsDefined | key->actsDefined | key->typesDefined;
+    /* get highest group number */
+    for (i = XkbNumKbdGroups - 1; i >= 0; i--)
+    {
+        if (defined & (1 << i))
+            break;
+    }
+    lastGroup = i;
+
+    if (lastGroup == 0)
+        return;
+
+    /* If there are empty groups between non-empty ones fill them with data */
+    /* from the first group. */
+    /* We can make a wrong assumption here. But leaving gaps is worse. */
+    for (i = lastGroup; i > 0; i--)
+    {
+        if (defined & (1 << i))
+            continue;
+        width = key->numLevels[0];
+        if (key->typesDefined & 1)
+        {
+            for (j = 0; j < width; j++)
+            {
+                key->types[i] = key->types[0];
+            }
+            key->typesDefined |= 1 << i;
+        }
+        if ((key->actsDefined & 1) && key->acts[0])
+        {
+            key->acts[i] = uTypedCalloc(width, XkbAction);
+            if (key->acts[i] == NULL)
+                continue;
+            memcpy((void *) key->acts[i], (void *) key->acts[0],
+                   width * sizeof(XkbAction));
+            key->actsDefined |= 1 << i;
+        }
+        if ((key->symsDefined & 1) && key->syms[0])
+        {
+            key->syms[i] = uTypedCalloc(width, KeySym);
+            if (key->syms[i] == NULL)
+                continue;
+            memcpy((void *) key->syms[i], (void *) key->syms[0],
+                   width * sizeof(KeySym));
+            key->symsDefined |= 1 << i;
+        }
+        if (defined & 1)
+        {
+            key->numLevels[i] = key->numLevels[0];
+        }
+    }
+    /* If all groups are completely identical remove them all */
+    /* exept the first one. */
+    identical = True;
+    for (i = lastGroup; i > 0; i--)
+    {
+        if ((key->numLevels[i] != key->numLevels[0]) ||
+            (key->types[i] != key->types[0]))
+        {
+            identical = False;
+            break;
+        }
+        if ((key->syms[i] != key->syms[0]) &&
+            (key->syms[i] == NULL || key->syms[0] == NULL ||
+             memcmp((void *) key->syms[i], (void *) key->syms[0],
+                    sizeof(KeySym) * key->numLevels[0])))
+        {
+            identical = False;
+            break;
+        }
+        if ((key->acts[i] != key->acts[0]) &&
+            (key->acts[i] == NULL || key->acts[0] == NULL ||
+             memcmp((void *) key->acts[i], (void *) key->acts[0],
+                    sizeof(XkbAction) * key->numLevels[0])))
+        {
+            identical = False;
+            break;
+        }
+    }
+    if (identical)
+    {
+        for (i = lastGroup; i > 0; i--)
+        {
+            key->numLevels[i] = 0;
+            if (key->syms[i] != NULL)
+                uFree(key->syms[i]);
+            key->syms[i] = (KeySym *) NULL;
+            if (key->acts[i] != NULL)
+                uFree(key->acts[i]);
+            key->acts[i] = (XkbAction *) NULL;
+            key->types[i] = (Atom) 0;
+        }
+        key->symsDefined &= 1;
+        key->actsDefined &= 1;
+        key->typesDefined &= 1;
+    }
+    return;
+}
+
+/**
+ * Copy the KeyInfo into result.
+ *
+ * This function recurses.
+ */
+static Bool
+CopySymbolsDef(XkbFileInfo * result, KeyInfo * key, int start_from)
+{
+    register int i;
+    unsigned okc, kc, width, tmp, nGroups;
+    XkbKeyTypePtr type;
+    Bool haveActions, autoType, useAlias;
+    KeySym *outSyms;
+    XkbAction *outActs;
+    XkbDescPtr xkb;
+    unsigned types[XkbNumKbdGroups];
+
+    xkb = result->xkb;
+    useAlias = (start_from == 0);
+
+    /* get the keycode for the key. */
+    if (!FindNamedKey(xkb, key->name, &kc, useAlias, CreateKeyNames(xkb),
+                      start_from))
+    {
+        if ((start_from == 0) && (warningLevel >= 5))
+        {
+            WARN2("Key %s not found in %s keycodes\n",
+                  longText(key->name, XkbMessage),
+                  XkbAtomText(NULL, xkb->names->keycodes, XkbMessage));
+            ACTION("Symbols ignored\n");
+        }
+        return False;
+    }
+
+    haveActions = False;
+    for (i = width = nGroups = 0; i < XkbNumKbdGroups; i++)
+    {
+        if (((i + 1) > nGroups)
+            && (((key->symsDefined | key->actsDefined) & (1 << i))
+                || (key->typesDefined) & (1 << i)))
+            nGroups = i + 1;
+        if (key->acts[i])
+            haveActions = True;
+        autoType = False;
+        /* Assign the type to the key, if it is missing. */
+        if (key->types[i] == None)
+        {
+            if (key->dfltType != None)
+                key->types[i] = key->dfltType;
+            else if (FindAutomaticType(key->numLevels[i], key->syms[i],
+                                       &key->types[i], &autoType))
+            {
+            }
+            else
+            {
+                if (warningLevel >= 5)
+                {
+                    WARN1("No automatic type for %d symbols\n",
+                          (unsigned int) key->numLevels[i]);
+                    ACTION3("Using %s for the %s key (keycode %d)\n",
+                            XkbAtomText(NULL, key->types[i],
+                                        XkbMessage),
+                            longText(key->name, XkbMessage), kc);
+                }
+            }
+        }
+        if (FindNamedType(xkb, key->types[i], &types[i]))
+        {
+            if (!autoType || key->numLevels[i] > 2)
+                xkb->server->explicit[kc] |= (1 << i);
+        }
+        else
+        {
+            if (warningLevel >= 3)
+            {
+                WARN1("Type \"%s\" is not defined\n",
+                      XkbAtomText(NULL, key->types[i], XkbMessage));
+                ACTION2("Using TWO_LEVEL for the %s key (keycode %d)\n",
+                        longText(key->name, XkbMessage), kc);
+            }
+            types[i] = XkbTwoLevelIndex;
+        }
+        /* if the type specifies less syms than the key has, shrink the key */
+        type = &xkb->map->types[types[i]];
+        if (type->num_levels < key->numLevels[i])
+        {
+            if (warningLevel > 0)
+            {
+                WARN4
+                    ("Type \"%s\" has %d levels, but %s has %d symbols\n",
+                     XkbAtomText(NULL, type->name, XkbMessage),
+                     (unsigned int) type->num_levels,
+                     longText(key->name, XkbMessage),
+                     (unsigned int) key->numLevels[i]);
+                ACTION("Ignoring extra symbols\n");
+            }
+            key->numLevels[i] = type->num_levels;
+        }
+        if (key->numLevels[i] > width)
+            width = key->numLevels[i];
+        if (type->num_levels > width)
+            width = type->num_levels;
+    }
+
+    /* width is now the largest width found */
+
+    i = width * nGroups;
+    outSyms = XkbResizeKeySyms(xkb, kc, i);
+    if (outSyms == NULL)
+    {
+        WSGO2("Could not enlarge symbols for %s (keycode %d)\n",
+              longText(key->name, XkbMessage), kc);
+        return False;
+    }
+    if (haveActions)
+    {
+        outActs = XkbResizeKeyActions(xkb, kc, i);
+        if (outActs == NULL)
+        {
+            WSGO2("Could not enlarge actions for %s (key %d)\n",
+                  longText(key->name, XkbMessage), kc);
+            return False;
+        }
+        xkb->server->explicit[kc] |= XkbExplicitInterpretMask;
+    }
+    else
+        outActs = NULL;
+    if (key->defs.defined & _Key_GroupInfo)
+        i = key->groupInfo;
+    else
+        i = xkb->map->key_sym_map[kc].group_info;
+
+    xkb->map->key_sym_map[kc].group_info = XkbSetNumGroups(i, nGroups);
+    xkb->map->key_sym_map[kc].width = width;
+    for (i = 0; i < nGroups; i++)
+    {
+        /* assign kt_index[i] to the index of the type in map->types.
+         * kt_index[i] may have been set by a previous run (if we have two
+         * layouts specified). Let's not overwrite it with the ONE_LEVEL
+         * default group if we dont even have keys for this group anyway.
+         *
+         * FIXME: There should be a better fix for this.
+         */
+        if (key->numLevels[i])
+            xkb->map->key_sym_map[kc].kt_index[i] = types[i];
+        if (key->syms[i] != NULL)
+        {
+            /* fill key to "width" symbols*/
+            for (tmp = 0; tmp < width; tmp++)
+            {
+                if (tmp < key->numLevels[i])
+                    outSyms[tmp] = key->syms[i][tmp];
+                else
+                    outSyms[tmp] = NoSymbol;
+                if ((outActs != NULL) && (key->acts[i] != NULL))
+                {
+                    if (tmp < key->numLevels[i])
+                        outActs[tmp] = key->acts[i][tmp];
+                    else
+                        outActs[tmp].type = XkbSA_NoAction;
+                }
+            }
+        }
+        outSyms += width;
+        if (outActs)
+            outActs += width;
+    }
+    switch (key->behavior.type & XkbKB_OpMask)
+    {
+    case XkbKB_Default:
+        break;
+    case XkbKB_Overlay1:
+    case XkbKB_Overlay2:
+        /* find key by name! */
+        if (!FindNamedKey(xkb, key->nameForOverlayKey, &okc, True,
+                          CreateKeyNames(xkb), 0))
+        {
+            if (warningLevel >= 1)
+            {
+                WARN2("Key %s not found in %s keycodes\n",
+                      longText(key->nameForOverlayKey, XkbMessage),
+                      XkbAtomText(NULL, xkb->names->keycodes, XkbMessage));
+                ACTION1("Not treating %s as an overlay key \n",
+                        longText(key->name, XkbMessage));
+            }
+            break;
+        }
+        key->behavior.data = okc;
+    default:
+        xkb->server->behaviors[kc] = key->behavior;
+        xkb->server->explicit[kc] |= XkbExplicitBehaviorMask;
+        break;
+    }
+    if (key->defs.defined & _Key_VModMap)
+    {
+        xkb->server->vmodmap[kc] = key->vmodmap;
+        xkb->server->explicit[kc] |= XkbExplicitVModMapMask;
+    }
+    if (key->repeat != RepeatUndefined)
+    {
+        if (key->repeat == RepeatYes)
+            xkb->ctrls->per_key_repeat[kc / 8] |= (1 << (kc % 8));
+        else
+            xkb->ctrls->per_key_repeat[kc / 8] &= ~(1 << (kc % 8));
+        xkb->server->explicit[kc] |= XkbExplicitAutoRepeatMask;
+    }
+
+    /* do the same thing for the next key */
+    CopySymbolsDef(result, key, kc + 1);
+    return True;
+}
+
+static Bool
+CopyModMapDef(XkbFileInfo * result, ModMapEntry * entry)
+{
+    unsigned kc;
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    if ((!entry->haveSymbol)
+        &&
+        (!FindNamedKey
+         (xkb, entry->u.keyName, &kc, True, CreateKeyNames(xkb), 0)))
+    {
+        if (warningLevel >= 5)
+        {
+            WARN2("Key %s not found in %s keycodes\n",
+                  longText(entry->u.keyName, XkbMessage),
+                  XkbAtomText(NULL, xkb->names->keycodes, XkbMessage));
+            ACTION1("Modifier map entry for %s not updated\n",
+                    XkbModIndexText(entry->modifier, XkbMessage));
+        }
+        return False;
+    }
+    else if (entry->haveSymbol
+             && (!FindKeyForSymbol(xkb, entry->u.keySym, &kc)))
+    {
+        if (warningLevel > 5)
+        {
+            WARN2("Key \"%s\" not found in %s symbol map\n",
+                  XkbKeysymText(entry->u.keySym, XkbMessage),
+                  XkbAtomText(NULL, xkb->names->symbols, XkbMessage));
+            ACTION1("Modifier map entry for %s not updated\n",
+                    XkbModIndexText(entry->modifier, XkbMessage));
+        }
+        return False;
+    }
+    xkb->map->modmap[kc] |= (1 << entry->modifier);
+    return True;
+}
+
+/**
+ * Handle the xkb_symbols section of an xkb file.
+ *
+ * @param file The parsed xkb_symbols section of the xkb file.
+ * @param result Handle to the data to store the result in.
+ * @param merge Merge strategy (e.g. MergeOverride).
+ */
+Bool
+CompileSymbols(XkbFile * file, XkbFileInfo * result, unsigned merge)
+{
+    register int i;
+    SymbolsInfo info;
+    XkbDescPtr xkb;
+
+    xkb = result->xkb;
+    InitSymbolsInfo(&info, xkb);
+    info.dflt.defs.fileID = file->id;
+    info.dflt.defs.merge = merge;
+    HandleSymbolsFile(file, xkb, merge, &info);
+
+    if (info.nKeys == 0)
+        return True;
+    if (info.errorCount == 0)
+    {
+        KeyInfo *key;
+
+        /* alloc memory in the xkb struct */
+        if (XkbAllocNames(xkb, XkbSymbolsNameMask | XkbGroupNamesMask, 0, 0)
+            != Success)
+        {
+            WSGO("Can not allocate names in CompileSymbols\n");
+            ACTION("Symbols not added\n");
+            return False;
+        }
+        if (XkbAllocClientMap(xkb, XkbKeySymsMask | XkbModifierMapMask, 0)
+            != Success)
+        {
+            WSGO("Could not allocate client map in CompileSymbols\n");
+            ACTION("Symbols not added\n");
+            return False;
+        }
+        if (XkbAllocServerMap(xkb, XkbAllServerInfoMask, 32) != Success)
+        {
+            WSGO("Could not allocate server map in CompileSymbols\n");
+            ACTION("Symbols not added\n");
+            return False;
+        }
+        if (XkbAllocControls(xkb, XkbPerKeyRepeatMask) != Success)
+        {
+            WSGO("Could not allocate controls in CompileSymbols\n");
+            ACTION("Symbols not added\n");
+            return False;
+        }
+
+        /* now copy info into xkb. */
+        xkb->names->symbols = XkbInternAtom(xkb->dpy, info.name, False);
+        if (info.aliases)
+            ApplyAliases(xkb, False, &info.aliases);
+        for (i = 0; i < XkbNumKbdGroups; i++)
+        {
+            if (info.groupNames[i] != None)
+                xkb->names->groups[i] = info.groupNames[i];
+        }
+        /* sanitize keys */
+        for (key = info.keys, i = 0; i < info.nKeys; i++, key++)
+        {
+            PrepareKeyDef(key);
+        }
+        /* copy! */
+        for (key = info.keys, i = 0; i < info.nKeys; i++, key++)
+        {
+            if (!CopySymbolsDef(result, key, 0))
+                info.errorCount++;
+        }
+        if (warningLevel > 3)
+        {
+            for (i = xkb->min_key_code; i <= xkb->max_key_code; i++)
+            {
+                if (xkb->names->keys[i].name[0] == '\0')
+                    continue;
+                if (XkbKeyNumGroups(xkb, i) < 1)
+                {
+                    char buf[5];
+                    memcpy(buf, xkb->names->keys[i].name, 4);
+                    buf[4] = '\0';
+                    WARN2
+                        ("No symbols defined for <%s> (keycode %d)\n",
+                         buf, i);
+                }
+            }
+        }
+        if (info.modMap)
+        {
+            ModMapEntry *mm, *next;
+            for (mm = info.modMap; mm != NULL; mm = next)
+            {
+                if (!CopyModMapDef(result, mm))
+                    info.errorCount++;
+                next = (ModMapEntry *) mm->defs.next;
+            }
+        }
+        return True;
+    }
+    return False;
+}
diff --git a/src/xkbcomp/tokens.h b/src/xkbcomp/tokens.h
new file mode 100644 (file)
index 0000000..970f3d4
--- /dev/null
@@ -0,0 +1,104 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+#ifndef TOKENS_H
+#define        TOKENS_H 1
+
+#define        END_OF_FILE     0
+#define        ERROR_TOK       255
+
+#define        XKB_KEYMAP      1
+#define        XKB_KEYCODES    2
+#define        XKB_TYPES       3
+#define        XKB_SYMBOLS     4
+#define        XKB_COMPATMAP   5
+#define        XKB_GEOMETRY    6
+#define        XKB_SEMANTICS   7
+#define        XKB_LAYOUT      8
+
+#define        INCLUDE         10
+#define        OVERRIDE        11
+#define        AUGMENT         12
+#define        REPLACE         13
+#define        ALTERNATE       14
+
+#define        VIRTUAL_MODS    20
+#define        TYPE            21
+#define        INTERPRET       22
+#define        ACTION_TOK      23
+#define        KEY             24
+#define        ALIAS           25
+#define        GROUP           26
+#define        MODIFIER_MAP    27
+#define        INDICATOR       28
+#define        SHAPE           29
+#define        KEYS            30
+#define        ROW             31
+#define        SECTION         32
+#define        OVERLAY         33
+#define        TEXT            34
+#define        OUTLINE         35
+#define        SOLID           36
+#define        LOGO            37
+#define        VIRTUAL         38
+
+#define        EQUALS          40
+#define        PLUS            41
+#define        MINUS           42
+#define        DIVIDE          43
+#define        TIMES           44
+#define        OBRACE          45
+#define        CBRACE          46
+#define        OPAREN          47
+#define        CPAREN          48
+#define        OBRACKET        49
+#define        CBRACKET        50
+#define        DOT             51
+#define        COMMA           52
+#define        SEMI            53
+#define        EXCLAM          54
+#define        INVERT          55
+
+#define        STRING          60
+#define        INTEGER         61
+#define        FLOAT           62
+#define        IDENT           63
+#define        KEYNAME         64
+
+#define        PARTIAL         70
+#define        DEFAULT         71
+#define        HIDDEN          72
+#define        ALPHANUMERIC_KEYS       73
+#define        MODIFIER_KEYS           74
+#define        KEYPAD_KEYS             75
+#define        FUNCTION_KEYS           76
+#define        ALTERNATE_GROUP         77
+
+extern Atom tok_ONE_LEVEL;
+extern Atom tok_TWO_LEVEL;
+extern Atom tok_ALPHABETIC;
+extern Atom tok_KEYPAD;
+
+#endif
diff --git a/src/xkbcomp/utils.c b/src/xkbcomp/utils.c
new file mode 100644 (file)
index 0000000..55efbe1
--- /dev/null
@@ -0,0 +1,434 @@
+
+  /*\
+   *
+   *                          COPYRIGHT 1990
+   *                    DIGITAL EQUIPMENT CORPORATION
+   *                       MAYNARD, MASSACHUSETTS
+   *                        ALL RIGHTS RESERVED.
+   *
+   * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
+   * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
+   * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE 
+   * FOR ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED 
+   * WARRANTY.
+   *
+   * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
+   * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
+   * ADDITION TO THAT SET FORTH ABOVE.
+   *
+   * 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 Digital Equipment Corporation not be
+   * used in advertising or publicity pertaining to distribution of the 
+   * software without specific, written prior permission.
+   \*/
+
+#include       "utils.h"
+#include       <ctype.h>
+#include       <stdlib.h>
+#include       <stdarg.h>
+
+/***====================================================================***/
+
+Opaque
+uAlloc(unsigned size)
+{
+    return ((Opaque) malloc(size));
+}
+
+/***====================================================================***/
+
+Opaque
+uCalloc(unsigned n, unsigned size)
+{
+    return ((Opaque) calloc(n, size));
+}
+
+/***====================================================================***/
+
+Opaque
+uRealloc(Opaque old, unsigned newSize)
+{
+    if (old == NULL)
+        return ((Opaque) malloc(newSize));
+    else
+        return ((Opaque) realloc((char *) old, newSize));
+}
+
+/***====================================================================***/
+
+Opaque
+uRecalloc(Opaque old, unsigned nOld, unsigned nNew, unsigned itemSize)
+{
+    char *rtrn;
+
+    if (old == NULL)
+        rtrn = (char *) calloc(nNew, itemSize);
+    else
+    {
+        rtrn = (char *) realloc((char *) old, nNew * itemSize);
+        if ((rtrn) && (nNew > nOld))
+        {
+            bzero(&rtrn[nOld * itemSize], (nNew - nOld) * itemSize);
+        }
+    }
+    return (Opaque) rtrn;
+}
+
+/***====================================================================***/
+
+void
+uFree(Opaque ptr)
+{
+    if (ptr != (Opaque) NULL)
+        free((char *) ptr);
+    return;
+}
+
+/***====================================================================***/
+/***                  FUNCTION ENTRY TRACKING                           ***/
+/***====================================================================***/
+
+static FILE *entryFile = NULL;
+int uEntryLevel;
+
+Boolean
+uSetEntryFile(char *name)
+{
+    if ((entryFile != NULL) && (entryFile != stderr))
+    {
+        fprintf(entryFile, "switching to %s\n", name ? name : "stderr");
+        fclose(entryFile);
+    }
+    if (name != NullString)
+        entryFile = fopen(name, "w");
+    else
+        entryFile = stderr;
+    if (entryFile == NULL)
+    {
+        entryFile = stderr;
+        return (False);
+    }
+    return (True);
+}
+
+void
+uEntry(int l, char *s, ...)
+{
+    int i;
+    va_list args;
+
+    for (i = 0; i < uEntryLevel; i++)
+    {
+        putc(' ', entryFile);
+    }
+    va_start(args, s);
+    vfprintf(entryFile, s, args);
+    va_end(args);
+    uEntryLevel += l;
+}
+
+void
+uExit(int l, char *rtVal)
+{
+    int i;
+
+    uEntryLevel -= l;
+    if (uEntryLevel < 0)
+        uEntryLevel = 0;
+    for (i = 0; i < uEntryLevel; i++)
+    {
+        putc(' ', entryFile);
+    }
+    fprintf(entryFile, "---> %p\n", rtVal);
+    return;
+}
+
+/***====================================================================***/
+/***                   PRINT FUNCTIONS                                 ***/
+/***====================================================================***/
+
+FILE *uDebugFile = NULL;
+int uDebugIndentLevel = 0;
+int uDebugIndentSize = 4;
+
+Boolean
+uSetDebugFile(char *name)
+{
+    if ((uDebugFile != NULL) && (uDebugFile != stderr))
+    {
+        fprintf(uDebugFile, "switching to %s\n", name ? name : "stderr");
+        fclose(uDebugFile);
+    }
+    if (name != NullString)
+        uDebugFile = fopen(name, "w");
+    else
+        uDebugFile = stderr;
+    if (uDebugFile == NULL)
+    {
+        uDebugFile = stderr;
+        return (False);
+    }
+    return (True);
+}
+
+void
+uDebug(char *s, ...)
+{
+    int i;
+    va_list args;
+
+    for (i = (uDebugIndentLevel * uDebugIndentSize); i > 0; i--)
+    {
+        putc(' ', uDebugFile);
+    }
+    va_start(args, s);
+    vfprintf(uDebugFile, s, args);
+    va_end(args);
+    fflush(uDebugFile);
+}
+
+void
+uDebugNOI(char *s, ...)
+{
+    va_list args;
+
+    va_start(args, s);
+    vfprintf(uDebugFile, s, args);
+    va_end(args);
+    fflush(uDebugFile);
+}
+
+/***====================================================================***/
+
+static FILE *errorFile = NULL;
+static int outCount = 0;
+static char *preMsg = NULL;
+static char *postMsg = NULL;
+static char *prefix = NULL;
+
+Boolean
+uSetErrorFile(char *name)
+{
+    if ((errorFile != NULL) && (errorFile != stderr))
+    {
+        fprintf(errorFile, "switching to %s\n", name ? name : "stderr");
+        fclose(errorFile);
+    }
+    if (name != NullString)
+        errorFile = fopen(name, "w");
+    else
+        errorFile = stderr;
+    if (errorFile == NULL)
+    {
+        errorFile = stderr;
+        return (False);
+    }
+    return (True);
+}
+
+void
+uInformation(const char *s, ...)
+{
+    va_list args;
+
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fflush(errorFile);
+}
+
+/***====================================================================***/
+
+void
+uAction(const char *s, ...)
+{
+    va_list args;
+
+    if (prefix != NULL)
+        fprintf(errorFile, "%s", prefix);
+    fprintf(errorFile, "                  ");
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fflush(errorFile);
+}
+
+/***====================================================================***/
+
+void
+uWarning(const char *s, ...)
+{
+    va_list args;
+
+    if ((outCount == 0) && (preMsg != NULL))
+        fprintf(errorFile, "%s\n", preMsg);
+    if (prefix != NULL)
+        fprintf(errorFile, "%s", prefix);
+    fprintf(errorFile, "Warning:          ");
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fflush(errorFile);
+    outCount++;
+}
+
+/***====================================================================***/
+
+void
+uError(const char *s, ...)
+{
+    va_list args;
+
+    if ((outCount == 0) && (preMsg != NULL))
+        fprintf(errorFile, "%s\n", preMsg);
+    if (prefix != NULL)
+        fprintf(errorFile, "%s", prefix);
+    fprintf(errorFile, "Error:            ");
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fflush(errorFile);
+    outCount++;
+}
+
+/***====================================================================***/
+
+void
+uFatalError(const char *s, ...)
+{
+    va_list args;
+
+    if ((outCount == 0) && (preMsg != NULL))
+        fprintf(errorFile, "%s\n", preMsg);
+    if (prefix != NULL)
+        fprintf(errorFile, "%s", prefix);
+    fprintf(errorFile, "Fatal Error:      ");
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fprintf(errorFile, "                  Exiting\n");
+    fflush(errorFile);
+    outCount++;
+    exit(1);
+    /* NOTREACHED */
+}
+
+/***====================================================================***/
+
+void
+uInternalError(const char *s, ...)
+{
+    va_list args;
+
+    if ((outCount == 0) && (preMsg != NULL))
+        fprintf(errorFile, "%s\n", preMsg);
+    if (prefix != NULL)
+        fprintf(errorFile, "%s", prefix);
+    fprintf(errorFile, "Internal error:   ");
+    va_start(args, s);
+    vfprintf(errorFile, s, args);
+    va_end(args);
+    fflush(errorFile);
+    outCount++;
+}
+
+void
+uSetPreErrorMessage(char *msg)
+{
+    outCount = 0;
+    preMsg = msg;
+    return;
+}
+
+void
+uSetPostErrorMessage(char *msg)
+{
+    postMsg = msg;
+    return;
+}
+
+void
+uSetErrorPrefix(char *pre)
+{
+    prefix = pre;
+    return;
+}
+
+void
+uFinishUp(void)
+{
+    if ((outCount > 0) && (postMsg != NULL))
+        fprintf(errorFile, "%s\n", postMsg);
+    return;
+}
+
+/***====================================================================***/
+
+#ifndef HAVE_STRDUP
+char *
+uStringDup(const char *str)
+{
+    char *rtrn;
+
+    if (str == NULL)
+        return NULL;
+    rtrn = (char *) uAlloc(strlen(str) + 1);
+    strcpy(rtrn, str);
+    return rtrn;
+}
+#endif
+
+#ifndef HAVE_STRCASECMP
+int
+uStrCaseCmp(const char *str1, const char *str2)
+{
+    char buf1[512], buf2[512];
+    char c, *s;
+    register int n;
+
+    for (n = 0, s = buf1; (c = *str1++); n++)
+    {
+        if (isupper(c))
+            c = tolower(c);
+        if (n > 510)
+            break;
+        *s++ = c;
+    }
+    *s = '\0';
+    for (n = 0, s = buf2; (c = *str2++); n++)
+    {
+        if (isupper(c))
+            c = tolower(c);
+        if (n > 510)
+            break;
+        *s++ = c;
+    }
+    *s = '\0';
+    return (strcmp(buf1, buf2));
+}
+
+int
+uStrCasePrefix(const char *my_prefix, char *str)
+{
+    char c1;
+    char c2;
+    while (((c1 = *my_prefix) != '\0') && ((c2 = *str) != '\0'))
+    {
+        if (isupper(c1))
+            c1 = tolower(c1);
+        if (isupper(c2))
+            c2 = tolower(c2);
+        if (c1 != c2)
+            return 0;
+        my_prefix++;
+        str++;
+    }
+    if (c1 != '\0')
+        return 0;
+    return 1;
+}
+
+#endif
diff --git a/src/xkbcomp/utils.h b/src/xkbcomp/utils.h
new file mode 100644 (file)
index 0000000..65e37c8
--- /dev/null
@@ -0,0 +1,381 @@
+#ifndef UTILS_H
+#define        UTILS_H 1
+
+  /*\
+   *
+   *                          COPYRIGHT 1990
+   *                    DIGITAL EQUIPMENT CORPORATION
+   *                       MAYNARD, MASSACHUSETTS
+   *                        ALL RIGHTS RESERVED.
+   *
+   * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
+   * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
+   * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE 
+   * FOR ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED 
+   * WARRANTY.
+   *
+   * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
+   * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
+   * ADDITION TO THAT SET FORTH ABOVE.
+   *
+   * 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 Digital Equipment Corporation not be
+   * used in advertising or publicity pertaining to distribution of the 
+   * software without specific, written prior permission.
+   \*/
+
+/***====================================================================***/
+
+#include       <stdio.h>
+#include       <X11/Xos.h>
+#include       <X11/Xfuncproto.h>
+#include       <X11/Xfuncs.h>
+
+#include <stddef.h>
+#include "config.h"
+
+#ifndef NUL
+#define        NUL     '\0'
+#endif
+
+/***====================================================================***/
+
+#ifndef OPAQUE_DEFINED
+typedef void *Opaque;
+#endif
+#ifndef NullOpaque
+#define        NullOpaque      ((Opaque)NULL)
+#endif
+
+#ifndef BOOLEAN_DEFINED
+typedef char Boolean;
+#endif
+
+#ifndef True
+#define        True    ((Boolean)1)
+#define        False   ((Boolean)0)
+#endif /* ndef True */
+#define        booleanText(b)  ((b)?"True":"False")
+
+#ifndef COMPARISON_DEFINED
+typedef int Comparison;
+
+#define        Greater         ((Comparison)1)
+#define        Equal           ((Comparison)0)
+#define        Less            ((Comparison)-1)
+#define        CannotCompare   ((Comparison)-37)
+#define        comparisonText(c)       ((c)?((c)<0?"Less":"Greater"):"Equal")
+#endif
+
+/***====================================================================***/
+
+extern Opaque uAlloc(unsigned   /* size */
+    );
+extern Opaque uCalloc(unsigned /* n */ ,
+                      unsigned  /* size */
+    );
+extern Opaque uRealloc(Opaque /* old */ ,
+                       unsigned /* newSize */
+    );
+extern Opaque uRecalloc(Opaque /* old */ ,
+                        unsigned /* nOld */ ,
+                        unsigned /* nNew */ ,
+                        unsigned        /* newSize */
+    );
+extern void uFree(Opaque        /* ptr */
+    );
+
+#define        uTypedAlloc(t)          ((t *)uAlloc((unsigned)sizeof(t)))
+#define        uTypedCalloc(n,t)       ((t *)uCalloc((unsigned)n,(unsigned)sizeof(t)))
+#define        uTypedRealloc(pO,n,t)   ((t *)uRealloc((Opaque)pO,((unsigned)n)*sizeof(t)))
+#define        uTypedRecalloc(pO,o,n,t) ((t *)uRecalloc((Opaque)pO,((unsigned)o),((unsigned)n),sizeof(t)))
+#if (defined mdHasAlloca) && (mdHasAlloca)
+#define        uTmpAlloc(n)    ((Opaque)alloca((unsigned)n))
+#define        uTmpFree(p)
+#else
+#define        uTmpAlloc(n)    uAlloc(n)
+#define        uTmpFree(p)     uFree(p)
+#endif
+
+/***====================================================================***/
+
+extern Boolean uSetErrorFile(char *     /* name */
+    );
+
+#define INFO6                  uInformation
+#define INFO5                  uInformation
+#define INFO4                  uInformation
+#define INFO3                  uInformation
+#define INFO2                  uInformation
+#define INFO1                  uInformation
+#define INFO                   uInformation
+
+extern void
+uInformation(const char * /* s */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+#define ACTION6                        uAction
+#define ACTION5                        uAction
+#define ACTION4                        uAction
+#define ACTION3                        uAction
+#define ACTION2                        uAction
+#define ACTION1                        uAction
+#define ACTION                 uAction
+
+     extern void uAction(const char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+#define WARN6                  uWarning
+#define WARN5                  uWarning
+#define WARN4                  uWarning
+#define WARN3                  uWarning
+#define WARN2                  uWarning
+#define WARN1                  uWarning
+#define WARN                   uWarning
+
+     extern void uWarning(const char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+#define ERROR6                 uError
+#define ERROR5                 uError
+#define ERROR4                 uError
+#define ERROR3                 uError
+#define ERROR2                 uError
+#define ERROR1                 uError
+#define ERROR                  uError
+
+     extern void uError(const char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+#define FATAL6                 uFatalError
+#define FATAL5                 uFatalError
+#define FATAL4                 uFatalError
+#define FATAL3                 uFatalError
+#define FATAL2                 uFatalError
+#define FATAL1                 uFatalError
+#define FATAL                  uFatalError
+
+     extern void uFatalError(const char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+/* WSGO stands for "Weird Stuff Going On" */
+#define WSGO6                  uInternalError
+#define WSGO5                  uInternalError
+#define WSGO4                  uInternalError
+#define WSGO3                  uInternalError
+#define WSGO2                  uInternalError
+#define WSGO1                  uInternalError
+#define WSGO                   uInternalError
+
+     extern void uInternalError(const char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+     extern void uSetPreErrorMessage(char *     /* msg */
+    );
+
+     extern void uSetPostErrorMessage(char *    /* msg */
+    );
+
+     extern void uSetErrorPrefix(char * /* void */
+    );
+
+     extern void uFinishUp(void);
+
+
+/***====================================================================***/
+
+#define        NullString      ((char *)NULL)
+
+#define        uStringText(s)          ((s)==NullString?"<NullString>":(s))
+#define        uStringEqual(s1,s2)     (uStringCompare(s1,s2)==Equal)
+#define        uStringPrefix(p,s)      (strncmp(p,s,strlen(p))==0)
+#define        uStringCompare(s1,s2)   (((s1)==NullString||(s2)==NullString)?\
+                                 (s1)!=(s2):strcmp(s1,s2))
+#define        uStrCaseEqual(s1,s2)    (uStrCaseCmp(s1,s2)==0)
+#ifdef HAVE_STRCASECMP
+#define        uStrCaseCmp(s1,s2)      (strcasecmp(s1,s2))
+#define        uStrCasePrefix(p,s)     (strncasecmp(p,s,strlen(p))==0)
+#else
+     extern int uStrCaseCmp(const char * /* s1 */ ,
+                            const char *        /* s2 */
+    );
+     extern int uStrCasePrefix(const char * /* p */ ,
+                               char *   /* str */
+    );
+#endif
+#ifdef HAVE_STRDUP
+#define        uStringDup(s1)          ((s1) ? strdup(s1) : NULL)
+#else
+     extern char *uStringDup(const char *       /* s1 */
+    );
+#endif
+
+/***====================================================================***/
+
+#ifdef ASSERTIONS_ON
+#define        uASSERT(where,why) \
+       {if (!(why)) uFatalError("assertion botched in %s ( why )\n",where);}
+#else
+#define        uASSERT(where,why)
+#endif
+
+/***====================================================================***/
+
+#ifndef DEBUG_VAR
+#define        DEBUG_VAR       debugFlags
+#endif
+
+extern
+     unsigned int DEBUG_VAR;
+
+     extern void uDebug(char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+     extern void uDebugNOI(     /* no indent */
+                              char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 1, 2)))
+#endif
+    ;
+
+     extern Boolean uSetDebugFile(char *name);
+
+     extern FILE *uDebugFile;
+     extern int uDebugIndentLevel;
+     extern int uDebugIndentSize;
+#define        uDebugIndent(l)         (uDebugIndentLevel+=(l))
+#define        uDebugOutdent(l)        (uDebugIndentLevel-=(l))
+#ifdef DEBUG_ON
+#define        uDEBUG(f,s)             { if (DEBUG_VAR&(f)) uDebug(s);}
+#define        uDEBUG1(f,s,a)          { if (DEBUG_VAR&(f)) uDebug(s,a);}
+#define        uDEBUG2(f,s,a,b)        { if (DEBUG_VAR&(f)) uDebug(s,a,b);}
+#define        uDEBUG3(f,s,a,b,c)      { if (DEBUG_VAR&(f)) uDebug(s,a,b,c);}
+#define        uDEBUG4(f,s,a,b,c,d)    { if (DEBUG_VAR&(f)) uDebug(s,a,b,c,d);}
+#define        uDEBUG5(f,s,a,b,c,d,e)  { if (DEBUG_VAR&(f)) uDebug(s,a,b,c,d,e);}
+#define        uDEBUG_NOI(f,s)         { if (DEBUG_VAR&(f)) uDebug(s);}
+#define        uDEBUG_NOI1(f,s,a)      { if (DEBUG_VAR&(f)) uDebugNOI(s,a);}
+#define        uDEBUG_NOI2(f,s,a,b)    { if (DEBUG_VAR&(f)) uDebugNOI(s,a,b);}
+#define        uDEBUG_NOI3(f,s,a,b,c)  { if (DEBUG_VAR&(f)) uDebugNOI(s,a,b,c);}
+#define        uDEBUG_NOI4(f,s,a,b,c,d) { if (DEBUG_VAR&(f)) uDebugNOI(s,a,b,c,d);}
+#define        uDEBUG_NOI5(f,s,a,b,c,d,e) { if (DEBUG_VAR&(f)) uDebugNOI(s,a,b,c,d,e);}
+#else
+#define        uDEBUG(f,s)
+#define        uDEBUG1(f,s,a)
+#define        uDEBUG2(f,s,a,b)
+#define        uDEBUG3(f,s,a,b,c)
+#define        uDEBUG4(f,s,a,b,c,d)
+#define        uDEBUG5(f,s,a,b,c,d,e)
+#define        uDEBUG_NOI(f,s)
+#define        uDEBUG_NOI1(f,s,a)
+#define        uDEBUG_NOI2(f,s,a,b)
+#define        uDEBUG_NOI3(f,s,a,b,c)
+#define        uDEBUG_NOI4(f,s,a,b,c,d)
+#define        uDEBUG_NOI5(f,s,a,b,c,d,e)
+#endif
+
+     extern Boolean uSetEntryFile(char *name);
+     extern void uEntry(int /* l */ ,
+                        char * /* s  */ , ...
+    )
+#if defined(__GNUC__) && \
+    ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 6)))
+    __attribute__ ((format(printf, 2, 3)))
+#endif
+    ;
+
+     extern void uExit(int l, char *rtVal);
+#ifdef ENTRY_TRACKING_ON
+#define        ENTRY_BIT       0x10
+#define        LOW_ENTRY_BIT   0x1000
+#define        ENTER   (DEBUG_VAR&ENTRY_BIT)
+#define        FLAG(fLag)      (DEBUG_VAR&(fLag))
+
+     extern int uEntryLevel;
+
+#define        uENTRY(s)                       { if (ENTER) uEntry(1,s);}
+#define        uENTRY1(s,a)                    { if (ENTER) uEntry(1,s,a);}
+#define        uENTRY2(s,a,b)                  { if (ENTER) uEntry(1,s,a,b);}
+#define        uENTRY3(s,a,b,c)                { if (ENTER) uEntry(1,s,a,b,c);}
+#define        uENTRY4(s,a,b,c,d)              { if (ENTER) uEntry(1,s,a,b,c,d);}
+#define        uENTRY5(s,a,b,c,d,e)            { if (ENTER) uEntry(1,s,a,b,c,d,e);}
+#define        uENTRY6(s,a,b,c,d,e,f)          { if (ENTER) uEntry(1,s,a,b,c,d,e,f);}
+#define        uENTRY7(s,a,b,c,d,e,f,g)        { if (ENTER) uEntry(1,s,a,b,c,d,e,f,g);}
+#define        uRETURN(v)                      { if (ENTER) uEntryLevel--; return(v); }
+#define        uVOIDRETURN                     { if (ENTER) uEntryLevel--; return; }
+
+#define        uFLAG_ENTRY(w,s)                { if (FLAG(w)) uEntry(0,s);}
+#define        uFLAG_ENTRY1(w,s,a)             { if (FLAG(w)) uEntry(0,s,a);}
+#define        uFLAG_ENTRY2(w,s,a,b)           { if (FLAG(w)) uEntry(0,s,a,b);}
+#define        uFLAG_ENTRY3(w,s,a,b,c)         { if (FLAG(w)) uEntry(0,s,a,b,c);}
+#define        uFLAG_ENTRY4(w,s,a,b,c,d)       { if (FLAG(w)) uEntry(0,s,a,b,c,d);}
+#define        uFLAG_ENTRY5(w,s,a,b,c,d,e)     { if (FLAG(w)) uEntry(0,s,a,b,c,d,e);}
+#define        uFLAG_ENTRY6(w,s,a,b,c,d,e,f)   { if (FLAG(w)) uEntry(0,s,a,b,c,d,e,f);}
+#define        uFLAG_ENTRY7(w,s,a,b,c,d,e,f,g) { if(FLAG(w))uEntry(0,s,a,b,c,d,e,f,g);}
+#define        uFLAG_RETURN(v)                 { return(v);}
+#define        uFLAG_VOIDRETURN                { return; }
+#else
+#define        uENTRY(s)
+#define        uENTRY1(s,a)
+#define        uENTRY2(s,a1,a2)
+#define        uENTRY3(s,a1,a2,a3)
+#define        uENTRY4(s,a1,a2,a3,a4)
+#define        uENTRY5(s,a1,a2,a3,a4,a5)
+#define        uENTRY6(s,a1,a2,a3,a4,a5,a6)
+#define        uENTRY7(s,a1,a2,a3,a4,a5,a6,a7)
+#define        uRETURN(v)      { return(v); }
+#define        uVOIDRETURN     { return; }
+
+#define        uFLAG_ENTRY(f,s)
+#define        uFLAG_ENTRY1(f,s,a)
+#define        uFLAG_ENTRY2(f,s,a,b)
+#define        uFLAG_ENTRY3(f,s,a,b,c)
+#define        uFLAG_ENTRY4(f,s,a,b,c,d)
+#define        uFLAG_ENTRY5(f,s,a,b,c,d,e)
+#define        uFLAG_ENTRY6(f,s,a,b,c,d,e,g)
+#define        uFLAG_ENTRY7(f,s,a,b,c,d,e,g,h)
+#define        uFLAG_RETURN(v)                 { return(v);}
+#define        uFLAG_VOIDRETURN                { return; }
+#endif
+
+
+#endif /* UTILS_H */
diff --git a/src/xkbcomp/vmod.c b/src/xkbcomp/vmod.c
new file mode 100644 (file)
index 0000000..5578fd0
--- /dev/null
@@ -0,0 +1,271 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#define        DEBUG_VAR debugFlags
+#include <stdio.h>
+#include "xkbcomp.h"
+#include "tokens.h"
+#include "expr.h"
+#include "misc.h"
+
+#include <X11/extensions/XKB.h>
+#include <X11/extensions/XKBstr.h>
+
+#include "vmod.h"
+
+void
+InitVModInfo(VModInfo * info, XkbDescPtr xkb)
+{
+    ClearVModInfo(info, xkb);
+    info->errorCount = 0;
+    return;
+}
+
+void
+ClearVModInfo(VModInfo * info, XkbDescPtr xkb)
+{
+    register int i;
+
+    if (XkbAllocNames(xkb, XkbVirtualModNamesMask, 0, 0) != Success)
+        return;
+    if (XkbAllocServerMap(xkb, XkbVirtualModsMask, 0) != Success)
+        return;
+    info->xkb = xkb;
+    info->newlyDefined = info->defined = info->available = 0;
+    if (xkb && xkb->names)
+    {
+        register int bit;
+        for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1)
+        {
+            if (xkb->names->vmods[i] != None)
+                info->defined |= bit;
+        }
+    }
+    return;
+}
+
+/***====================================================================***/
+
+/**
+ * Handle one entry in the virtualModifiers line (e.g. NumLock).
+ * If the entry is e.g. NumLock=Mod1, stmt->value is not NULL, and the
+ * XkbServerMap's vmod is set to the given modifier. Otherwise, the vmod is 0.
+ *
+ * @param stmt The statement specifying the name and (if any the value).
+ * @param mergeMode Merge strategy (e.g. MergeOverride)
+ */
+Bool
+HandleVModDef(VModDef * stmt, unsigned mergeMode, VModInfo * info)
+{
+    register int i, bit, nextFree;
+    ExprResult mod;
+    XkbServerMapPtr srv;
+    XkbNamesPtr names;
+    Atom stmtName;
+
+    srv = info->xkb->server;
+    names = info->xkb->names;
+    stmtName =
+        XkbInternAtom(info->xkb->dpy, XkbAtomGetString(NULL, stmt->name),
+                      False);
+    for (i = 0, bit = 1, nextFree = -1; i < XkbNumVirtualMods; i++, bit <<= 1)
+    {
+        if (info->defined & bit)
+        {
+            if (names->vmods[i] == stmtName)
+            {                   /* already defined */
+                info->available |= bit;
+                if (stmt->value == NULL)
+                    return True;
+                else
+                {
+                    char *str1;
+                    const char *str2 = "";
+                    if (!ExprResolveModMask(stmt->value, &mod, NULL, NULL))
+                    {
+                        str1 = XkbAtomText(NULL, stmt->name, XkbMessage);
+                        ACTION1("Declaration of %s ignored\n", str1);
+                        return False;
+                    }
+                    if (mod.uval == srv->vmods[i])
+                        return True;
+
+                    str1 = XkbAtomText(NULL, stmt->name, XkbMessage);
+                    WARN1("Virtual modifier %s multiply defined\n", str1);
+                    str1 = XkbModMaskText(srv->vmods[i], XkbCFile);
+                    if (mergeMode == MergeOverride)
+                    {
+                        str2 = str1;
+                        str1 = XkbModMaskText(mod.uval, XkbCFile);
+                    }
+                    ACTION2("Using %s, ignoring %s\n", str1, str2);
+                    if (mergeMode == MergeOverride)
+                        srv->vmods[i] = mod.uval;
+                    return True;
+                }
+            }
+        }
+        else if (nextFree < 0)
+            nextFree = i;
+    }
+    if (nextFree < 0)
+    {
+        ERROR1("Too many virtual modifiers defined (maximum %d)\n",
+               XkbNumVirtualMods);
+        ACTION("Exiting\n");
+        return False;
+    }
+    info->defined |= (1 << nextFree);
+    info->newlyDefined |= (1 << nextFree);
+    info->available |= (1 << nextFree);
+    names->vmods[nextFree] = stmtName;
+    if (stmt->value == NULL)
+        return True;
+    if (ExprResolveModMask(stmt->value, &mod, NULL, NULL))
+    {
+        srv->vmods[nextFree] = mod.uval;
+        return True;
+    }
+    ACTION1("Declaration of %s ignored\n",
+            XkbAtomText(NULL, stmt->name, XkbMessage));
+    return False;
+}
+
+/**
+ * Returns the index of the given modifier in the xkb->names->vmods array.
+ *
+ * @param priv Pointer to the xkb data structure.
+ * @param elem Must be None, otherwise return False.
+ * @param field The Atom of the modifier's name (e.g. Atom for LAlt)
+ * @param type Must be TypeInt, otherwise return False.
+ * @param val_rtrn Set to the index of the modifier that matches.
+ *
+ * @return True on success, False otherwise. If False is returned, val_rtrn is
+ * undefined.
+ */
+int
+LookupVModIndex(XPointer priv,
+                Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    register int i;
+    register char *fieldStr;
+    register char *modStr;
+    XkbDescPtr xkb;
+
+    xkb = (XkbDescPtr) priv;
+    if ((xkb == NULL) || (xkb->names == NULL) || (elem != None)
+        || (type != TypeInt))
+    {
+        return False;
+    }
+    /* get the actual name */
+    fieldStr = XkbAtomGetString(xkb->dpy, field);
+    if (fieldStr == NULL)
+        return False;
+    /* For each named modifier, get the name and compare it to the one passed
+     * in. If we get a match, return the index of the modifier.
+     * The order of modifiers is the same as in the virtual_modifiers line in
+     * the xkb_types section.
+     */
+    for (i = 0; i < XkbNumVirtualMods; i++)
+    {
+        modStr = XkbAtomGetString(xkb->dpy, xkb->names->vmods[i]);
+        if ((modStr != NULL) && (uStrCaseCmp(fieldStr, modStr) == 0))
+        {
+            val_rtrn->uval = i;
+            return True;
+        }
+    }
+    return False;
+}
+
+/**
+ * Get the mask for the given modifier and set val_rtrn.uval to the mask.
+ * Note that the mask returned is always > 512.
+ *
+ * @param priv Pointer to xkb data structure.
+ * @param val_rtrn Set to the mask returned.
+ *
+ * @return True on success, False otherwise. If False is returned, val_rtrn is
+ * undefined.
+ */
+int
+LookupVModMask(XPointer priv,
+               Atom elem, Atom field, unsigned type, ExprResult * val_rtrn)
+{
+    if (LookupVModIndex(priv, elem, field, type, val_rtrn))
+    {
+        register unsigned ndx = val_rtrn->uval;
+        val_rtrn->uval = (1 << (XkbNumModifiers + ndx));
+        return True;
+    }
+    return False;
+}
+
+int
+FindKeypadVMod(XkbDescPtr xkb)
+{
+    Atom name;
+    ExprResult rtrn;
+
+    name = XkbInternAtom(xkb->dpy, "NumLock", False);
+    if ((xkb) && LookupVModIndex((XPointer) xkb, None, name, TypeInt, &rtrn))
+    {
+        return rtrn.ival;
+    }
+    return -1;
+}
+
+Bool
+ResolveVirtualModifier(ExprDef * def, ExprResult * val_rtrn, VModInfo * info)
+{
+    XkbNamesPtr names;
+
+    names = info->xkb->names;
+    if (def->op == ExprIdent)
+    {
+        register int i, bit;
+        for (i = 0, bit = 1; i < XkbNumVirtualMods; i++, bit <<= 1)
+        {
+            char *str1, *str2;
+            str1 = XkbAtomGetString(info->xkb->dpy, names->vmods[i]);
+            str2 = XkbAtomGetString(NULL, def->value.str);
+            if ((info->available & bit) && (uStrCaseCmp(str1, str2) == Equal))
+            {
+                val_rtrn->uval = i;
+                return True;
+            }
+        }
+    }
+    if (ExprResolveInteger(def, val_rtrn, NULL, NULL))
+    {
+        if (val_rtrn->uval < XkbNumVirtualMods)
+            return True;
+        ERROR2("Illegal virtual modifier %d (must be 0..%d inclusive)\n",
+               val_rtrn->uval, XkbNumVirtualMods - 1);
+    }
+    return False;
+}
diff --git a/src/xkbcomp/vmod.h b/src/xkbcomp/vmod.h
new file mode 100644 (file)
index 0000000..559b9d5
--- /dev/null
@@ -0,0 +1,78 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef VMOD_H
+#define VMOD_H 1
+
+typedef struct _VModInfo
+{
+    XkbDescPtr xkb;
+    unsigned defined;
+    unsigned available;
+    unsigned newlyDefined;
+    int errorCount;
+} VModInfo;
+
+extern void InitVModInfo(VModInfo * /* info */ ,
+                         XkbDescPtr     /* xkb */
+    );
+
+extern void ClearVModInfo(VModInfo * /* info */ ,
+                          XkbDescPtr    /* xkb */
+    );
+
+extern Bool HandleVModDef(VModDef * /* stmt */ ,
+                          unsigned /* mergeMode */ ,
+                          VModInfo *    /* info */
+    );
+
+extern Bool ApplyVModDefs(VModInfo * /* info */ ,
+                          XkbDescPtr    /* xkb */
+    );
+
+extern int LookupVModIndex(XPointer /* priv */ ,
+                           Atom /* elem */ ,
+                           Atom /* field */ ,
+                           unsigned /* type */ ,
+                           ExprResult * /* val_rtrn */
+    );
+
+extern int LookupVModMask(XPointer /* priv */ ,
+                          Atom /* elem */ ,
+                          Atom /* field */ ,
+                          unsigned /* type */ ,
+                          ExprResult *  /* val_rtrn */
+    );
+
+extern int FindKeypadVMod(XkbDescPtr    /* xkb */
+    );
+
+extern Bool ResolveVirtualModifier(ExprDef * /* def */ ,
+                                   ExprResult * /* value_rtrn */ ,
+                                   VModInfo *   /* info */
+    );
+
+#endif /* VMOD_H */
diff --git a/src/xkbcomp/xkbcomp.h b/src/xkbcomp/xkbcomp.h
new file mode 100644 (file)
index 0000000..6e02ed5
--- /dev/null
@@ -0,0 +1,396 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef XKBCOMP_H
+#define        XKBCOMP_H 1
+
+#ifndef DEBUG_VAR
+#define        DEBUG_VAR debugFlags
+#endif
+
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include "utils.h"
+
+#include <X11/extensions/XKM.h>
+#include <X11/extensions/XKBfile.h>
+
+extern char *scanFile;
+
+#define        TypeUnknown     0
+#define        TypeBoolean     1
+#define        TypeInt         2
+#define        TypeFloat       3
+#define        TypeString      4
+#define        TypeAction      5
+#define        TypeKeyName     6
+#define        TypeSymbols     7
+
+#define        StmtUnknown             0
+#define        StmtInclude             1
+#define        StmtKeycodeDef          2
+#define        StmtKeyAliasDef         3
+#define        StmtExpr                4
+#define        StmtVarDef              5
+#define        StmtKeyTypeDef          6
+#define        StmtInterpDef           7
+#define        StmtVModDef             8
+#define        StmtSymbolsDef          9
+#define        StmtModMapDef           10
+#define        StmtGroupCompatDef      11
+#define        StmtIndicatorMapDef     12
+#define        StmtIndicatorNameDef    13
+#define        StmtOutlineDef          14
+#define        StmtShapeDef            15
+#define        StmtKeyDef              16
+#define        StmtRowDef              17
+#define        StmtSectionDef          18
+#define        StmtOverlayKeyDef       19
+#define        StmtOverlayDef          20
+#define        StmtDoodadDef           21
+
+#define        FileSymInterp   100
+
+typedef struct _ParseCommon
+{
+    unsigned stmtType;
+    struct _ParseCommon *next;
+} ParseCommon;
+
+#define        ExprValue       0
+#define        ExprIdent       1
+#define        ExprActionDecl  2
+#define        ExprFieldRef    3
+#define        ExprArrayRef    4
+#define        ExprKeysymList  5
+#define        ExprActionList  6
+#define        ExprCoord       7
+
+#define        OpAdd           20
+#define        OpSubtract      21
+#define        OpMultiply      22
+#define        OpDivide        23
+#define        OpAssign        24
+#define        OpNot           25
+#define        OpNegate        26
+#define        OpInvert        27
+#define        OpUnaryPlus     28
+
+#define        MergeDefault    0
+#define        MergeAugment    1
+#define        MergeOverride   2
+#define        MergeReplace    3
+#define        MergeAltForm    4
+
+#define        AutoKeyNames    (1L <<  0)
+#define        CreateKeyNames(x)       ((x)->flags&AutoKeyNames)
+
+extern unsigned warningLevel;
+extern unsigned optionalParts;
+
+typedef struct _IncludeStmt
+{
+    ParseCommon common;
+    unsigned merge;
+    char *stmt;
+    char *file;
+    char *map;
+    char *modifier;
+    char *path;
+    struct _IncludeStmt *next;
+} IncludeStmt;
+
+typedef struct _Expr
+{
+    ParseCommon common;
+    unsigned op;
+    unsigned type;
+    union
+    {
+        struct
+        {
+            struct _Expr *left;
+            struct _Expr *right;
+        } binary;
+        struct
+        {
+            Atom element;
+            Atom field;
+        } field;
+        struct
+        {
+            Atom element;
+            Atom field;
+            struct _Expr *entry;
+        } array;
+        struct
+        {
+            Atom name;
+            struct _Expr *args;
+        } action;
+        struct
+        {
+            int nSyms;
+            int szSyms;
+            KeySym *syms;
+        } list;
+        struct
+        {
+            int x;
+            int y;
+        } coord;
+        struct _Expr *child;
+        Atom str;
+        unsigned uval;
+        int ival;
+        char keyName[5];
+        Opaque ptr;
+    } value;
+} ExprDef;
+
+typedef struct _VarDef
+{
+    ParseCommon common;
+    unsigned merge;
+    ExprDef *name;
+    ExprDef *value;
+} VarDef;
+
+typedef struct _VModDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom name;
+    ExprDef *value;
+} VModDef;
+
+typedef struct _KeycodeDef
+{
+    ParseCommon common;
+    unsigned merge;
+    char name[5];
+    ExprDef *value;
+} KeycodeDef;
+
+typedef struct _KeyAliasDef
+{
+    ParseCommon common;
+    unsigned merge;
+    char alias[5];
+    char real[5];
+} KeyAliasDef;
+
+typedef struct _KeyTypeDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom name;
+    VarDef *body;
+} KeyTypeDef;
+
+typedef struct _SymbolsDef
+{
+    ParseCommon common;
+    unsigned merge;
+    char keyName[5];
+    ExprDef *symbols;
+} SymbolsDef;
+
+typedef struct _ModMapDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom modifier;
+    ExprDef *keys;
+} ModMapDef;
+
+typedef struct _GroupCompatDef
+{
+    ParseCommon common;
+    unsigned merge;
+    int group;
+    ExprDef *def;
+} GroupCompatDef;
+
+typedef struct _InterpDef
+{
+    ParseCommon common;
+    unsigned merge;
+    KeySym sym;
+    ExprDef *match;
+    VarDef *def;
+} InterpDef;
+
+typedef struct _IndicatorNameDef
+{
+    ParseCommon common;
+    unsigned merge;
+    int ndx;
+    ExprDef *name;
+    Bool virtual;
+} IndicatorNameDef;
+
+typedef struct _OutlineDef
+{
+    ParseCommon common;
+    Atom field;
+    int nPoints;
+    ExprDef *points;
+} OutlineDef;
+
+typedef struct _ShapeDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom name;
+    int nOutlines;
+    OutlineDef *outlines;
+} ShapeDef;
+
+typedef struct _KeyDef
+{
+    ParseCommon common;
+    unsigned defined;
+    char *name;
+    ExprDef *expr;
+} KeyDef;
+
+typedef struct _RowDef
+{
+    ParseCommon common;
+    int nKeys;
+    KeyDef *keys;
+} RowDef;
+
+typedef struct _SectionDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom name;
+    int nRows;
+    RowDef *rows;
+} SectionDef;
+
+typedef struct _OverlayKeyDef
+{
+    ParseCommon common;
+    char over[5];
+    char under[5];
+} OverlayKeyDef;
+
+typedef struct _OverlayDef
+{
+    ParseCommon common;
+    unsigned merge;
+    Atom name;
+    int nKeys;
+    OverlayKeyDef *keys;
+} OverlayDef;
+
+typedef struct _DoodadDef
+{
+    ParseCommon common;
+    unsigned merge;
+    unsigned type;
+    Atom name;
+    VarDef *body;
+} DoodadDef;
+
+/* IndicatorMapDef doesn't use the type field, but the rest of the fields
+   need to be at the same offsets as in DoodadDef.  Use #define to avoid
+   any strict aliasing problems.  */
+#define IndicatorMapDef DoodadDef
+
+typedef struct _XkbFile
+{
+    ParseCommon common;
+    int type;
+    char *topName;
+    char *name;
+    ParseCommon *defs;
+    int id;
+    unsigned flags;
+    Bool compiled;
+} XkbFile;
+
+extern Bool CompileKeymap(XkbFile * /* file */ ,
+                          XkbFileInfo * /* result */ ,
+                          unsigned      /* merge */
+    );
+
+extern Bool CompileKeycodes(XkbFile * /* file */ ,
+                            XkbFileInfo * /* result */ ,
+                            unsigned    /* merge */
+    );
+
+extern Bool CompileGeometry(XkbFile * /* file */ ,
+                            XkbFileInfo * /* result */ ,
+                            unsigned    /* merge */
+    );
+
+extern Bool CompileKeyTypes(XkbFile * /* file */ ,
+                            XkbFileInfo * /* result */ ,
+                            unsigned    /* merge */
+    );
+
+typedef struct _LEDInfo *LEDInfoPtr;
+
+extern Bool CompileCompatMap(XkbFile * /* file */ ,
+                             XkbFileInfo * /* result */ ,
+                             unsigned /* merge */ ,
+                             LEDInfoPtr *       /* unboundLEDs */
+    );
+
+extern Bool CompileSymbols(XkbFile * /* file */ ,
+                           XkbFileInfo * /* result */ ,
+                           unsigned     /* merge */
+    );
+
+#define        WantLongListing (1<<0)
+#define        WantPartialMaps (1<<1)
+#define        WantHiddenMaps  (1<<2)
+#define        WantFullNames   (1<<3)
+#define        ListRecursive   (1<<4)
+
+extern char *rootDir;
+extern unsigned verboseLevel;
+extern unsigned dirsToStrip;
+
+extern Bool AddListing(char * /* file */ ,
+                       char *   /* map */
+    );
+
+extern Bool AddMatchingFiles(char *     /* head_in */
+    );
+
+extern int AddMapOnly(char *    /* map */
+    );
+
+extern int GenerateListing(char *       /* filename */
+    );
+
+#endif /* XKBCOMP_H */
diff --git a/src/xkbcomp/xkbparse.c b/src/xkbcomp/xkbparse.c
new file mode 100644 (file)
index 0000000..b71df99
--- /dev/null
@@ -0,0 +1,3242 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     END_OF_FILE = 0,
+     ERROR_TOK = 255,
+     XKB_KEYMAP = 1,
+     XKB_KEYCODES = 2,
+     XKB_TYPES = 3,
+     XKB_SYMBOLS = 4,
+     XKB_COMPATMAP = 5,
+     XKB_GEOMETRY = 6,
+     XKB_SEMANTICS = 7,
+     XKB_LAYOUT = 8,
+     INCLUDE = 10,
+     OVERRIDE = 11,
+     AUGMENT = 12,
+     REPLACE = 13,
+     ALTERNATE = 14,
+     VIRTUAL_MODS = 20,
+     TYPE = 21,
+     INTERPRET = 22,
+     ACTION_TOK = 23,
+     KEY = 24,
+     ALIAS = 25,
+     GROUP = 26,
+     MODIFIER_MAP = 27,
+     INDICATOR = 28,
+     SHAPE = 29,
+     KEYS = 30,
+     ROW = 31,
+     SECTION = 32,
+     OVERLAY = 33,
+     TEXT = 34,
+     OUTLINE = 35,
+     SOLID = 36,
+     LOGO = 37,
+     VIRTUAL = 38,
+     EQUALS = 40,
+     PLUS = 41,
+     MINUS = 42,
+     DIVIDE = 43,
+     TIMES = 44,
+     OBRACE = 45,
+     CBRACE = 46,
+     OPAREN = 47,
+     CPAREN = 48,
+     OBRACKET = 49,
+     CBRACKET = 50,
+     DOT = 51,
+     COMMA = 52,
+     SEMI = 53,
+     EXCLAM = 54,
+     INVERT = 55,
+     STRING = 60,
+     INTEGER = 61,
+     FLOAT = 62,
+     IDENT = 63,
+     KEYNAME = 64,
+     PARTIAL = 70,
+     DEFAULT = 71,
+     HIDDEN = 72,
+     ALPHANUMERIC_KEYS = 73,
+     MODIFIER_KEYS = 74,
+     KEYPAD_KEYS = 75,
+     FUNCTION_KEYS = 76,
+     ALTERNATE_GROUP = 77
+   };
+#endif
+/* Tokens.  */
+#define END_OF_FILE 0
+#define ERROR_TOK 255
+#define XKB_KEYMAP 1
+#define XKB_KEYCODES 2
+#define XKB_TYPES 3
+#define XKB_SYMBOLS 4
+#define XKB_COMPATMAP 5
+#define XKB_GEOMETRY 6
+#define XKB_SEMANTICS 7
+#define XKB_LAYOUT 8
+#define INCLUDE 10
+#define OVERRIDE 11
+#define AUGMENT 12
+#define REPLACE 13
+#define ALTERNATE 14
+#define VIRTUAL_MODS 20
+#define TYPE 21
+#define INTERPRET 22
+#define ACTION_TOK 23
+#define KEY 24
+#define ALIAS 25
+#define GROUP 26
+#define MODIFIER_MAP 27
+#define INDICATOR 28
+#define SHAPE 29
+#define KEYS 30
+#define ROW 31
+#define SECTION 32
+#define OVERLAY 33
+#define TEXT 34
+#define OUTLINE 35
+#define SOLID 36
+#define LOGO 37
+#define VIRTUAL 38
+#define EQUALS 40
+#define PLUS 41
+#define MINUS 42
+#define DIVIDE 43
+#define TIMES 44
+#define OBRACE 45
+#define CBRACE 46
+#define OPAREN 47
+#define CPAREN 48
+#define OBRACKET 49
+#define CBRACKET 50
+#define DOT 51
+#define COMMA 52
+#define SEMI 53
+#define EXCLAM 54
+#define INVERT 55
+#define STRING 60
+#define INTEGER 61
+#define FLOAT 62
+#define IDENT 63
+#define KEYNAME 64
+#define PARTIAL 70
+#define DEFAULT 71
+#define HIDDEN 72
+#define ALPHANUMERIC_KEYS 73
+#define MODIFIER_KEYS 74
+#define KEYPAD_KEYS 75
+#define FUNCTION_KEYS 76
+#define ALTERNATE_GROUP 77
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 91 "xkbparse.y"
+
+#ifdef DEBUG
+#define        YYDEBUG 1
+#endif
+#define        DEBUG_VAR parseDebug
+#include "parseutils.h"
+#include <X11/keysym.h>
+#include <X11/extensions/XKBgeom.h>
+#include <stdlib.h>
+
+unsigned int parseDebug;
+
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 110 "xkbparse.y"
+{
+       int              ival;
+       unsigned         uval;
+       char            *str;
+       Atom            sval;
+       ParseCommon     *any;
+       ExprDef         *expr;
+       VarDef          *var;
+       VModDef         *vmod;
+       InterpDef       *interp;
+       KeyTypeDef      *keyType;
+       SymbolsDef      *syms;
+       ModMapDef       *modMask;
+       GroupCompatDef  *groupCompat;
+       IndicatorMapDef *ledMap;
+       IndicatorNameDef *ledName;
+       KeycodeDef      *keyName;
+       KeyAliasDef     *keyAlias;
+       ShapeDef        *shape;
+       SectionDef      *section;
+       RowDef          *row;
+       KeyDef          *key;
+       OverlayDef      *overlay;
+       OverlayKeyDef   *olKey;
+       OutlineDef      *outline;
+       DoodadDef       *doodad;
+       XkbFile         *file;
+}
+/* Line 187 of yacc.c.  */
+#line 269 "xkbparse.c"
+       YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 282 "xkbparse.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+            && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+        || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)             \
+      do                                       \
+       {                                       \
+         YYSIZE_T yyi;                         \
+         for (yyi = 0; yyi < (Count); yyi++)   \
+           (To)[yyi] = (From)[yyi];            \
+       }                                       \
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)                                       \
+    do                                                                 \
+      {                                                                        \
+       YYSIZE_T yynewbytes;                                            \
+       YYCOPY (&yyptr->Stack, Stack, yysize);                          \
+       Stack = &yyptr->Stack;                                          \
+       yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+       yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                        \
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  18
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   706
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  65
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  73
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  184
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  335
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   257
+
+#define YYTRANSLATE(YYX)                                               \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     4,     5,     6,     7,     8,     9,    10,    11,     2,
+      12,    13,    14,    15,    16,     2,     2,     2,     2,     2,
+      17,    18,    19,    20,    21,    22,    23,    24,    25,    26,
+      27,    28,    29,    30,    31,    32,    33,    34,    35,     2,
+      36,    37,    38,    39,    40,    41,    42,    43,    44,    45,
+      46,    47,    48,    49,    50,    51,     2,     2,     2,     2,
+      52,    53,    54,    55,    56,     2,     2,     2,     2,     2,
+      57,    58,    59,    60,    61,    62,    63,    64,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     3,     1,     2
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint16 yyprhs[] =
+{
+       0,     0,     3,     5,     7,     9,    12,    14,    22,    24,
+      26,    28,    31,    33,    41,    46,    48,    50,    52,    54,
+      56,    58,    59,    62,    64,    66,    68,    70,    72,    74,
+      76,    78,    80,    83,    84,    87,    90,    93,    96,    99,
+     102,   105,   108,   111,   114,   117,   120,   123,   126,   129,
+     134,   137,   141,   146,   152,   156,   160,   162,   164,   168,
+     175,   179,   181,   184,   186,   193,   200,   204,   206,   207,
+     211,   215,   217,   220,   222,   226,   230,   236,   243,   250,
+     256,   263,   270,   277,   284,   287,   289,   295,   297,   299,
+     301,   303,   306,   308,   314,   316,   320,   322,   324,   328,
+     335,   339,   341,   345,   349,   351,   355,   361,   365,   369,
+     371,   377,   384,   386,   388,   390,   392,   394,   396,   398,
+     400,   402,   404,   406,   408,   410,   412,   414,   416,   418,
+     420,   421,   423,   425,   427,   429,   431,   433,   434,   438,
+     440,   444,   448,   452,   456,   460,   462,   465,   468,   471,
+     474,   476,   481,   483,   487,   491,   493,   498,   500,   504,
+     509,   516,   518,   520,   522,   524,   526,   527,   531,   533,
+     535,   537,   539,   542,   544,   546,   548,   550,   552,   554,
+     556,   558,   560,   562,   563
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int16 yyrhs[] =
+{
+      66,     0,    -1,    67,    -1,    70,    -1,    72,    -1,    67,
+      68,    -1,    68,    -1,    74,    69,   136,    41,    70,    42,
+      49,    -1,     4,    -1,    10,    -1,    11,    -1,    70,    71,
+      -1,    71,    -1,    74,    73,   136,    41,    77,    42,    49,
+      -1,    74,    73,   136,    77,    -1,     5,    -1,     6,    -1,
+       8,    -1,     7,    -1,     9,    -1,    75,    -1,    -1,    75,
+      76,    -1,    76,    -1,    57,    -1,    58,    -1,    59,    -1,
+      60,    -1,    61,    -1,    62,    -1,    63,    -1,    64,    -1,
+      77,    78,    -1,    -1,   116,    79,    -1,   116,    82,    -1,
+     116,    85,    -1,   116,    80,    -1,   116,    81,    -1,   116,
+      88,    -1,   116,    89,    -1,   116,    94,    -1,   116,    93,
+      -1,   116,    95,    -1,   116,    96,    -1,   116,    97,    -1,
+     116,    98,    -1,   116,   112,    -1,   117,    52,    -1,   124,
+      36,   120,    49,    -1,   134,    49,    -1,    50,   134,    49,
+      -1,   133,    36,   120,    49,    -1,    22,   133,    36,   133,
+      49,    -1,    17,    83,    49,    -1,    83,    48,    84,    -1,
+      84,    -1,   134,    -1,   134,    36,   120,    -1,    19,    86,
+      41,    87,    42,    49,    -1,   128,    37,   120,    -1,   128,
+      -1,    87,    79,    -1,    79,    -1,    18,   135,    41,    87,
+      42,    49,    -1,    21,   133,    41,    90,    42,    49,    -1,
+      90,    48,    91,    -1,    91,    -1,    -1,   124,    36,   120,
+      -1,   124,    36,    92,    -1,   134,    -1,    50,   134,    -1,
+      92,    -1,    45,   126,    46,    -1,    45,   122,    46,    -1,
+      23,   132,    36,   120,    49,    -1,    24,   134,    41,   119,
+      42,    49,    -1,    25,   135,    41,    87,    42,    49,    -1,
+      25,   132,    36,   120,    49,    -1,    35,    25,   132,    36,
+     120,    49,    -1,    26,   135,    41,   108,    42,    49,    -1,
+      26,   135,    41,   110,    42,    49,    -1,    29,   135,    41,
+      99,    42,    49,    -1,    99,   100,    -1,   100,    -1,    28,
+      41,   101,    42,    49,    -1,    79,    -1,   112,    -1,    95,
+      -1,   105,    -1,   101,   102,    -1,   102,    -1,    27,    41,
+     103,    42,    49,    -1,    79,    -1,   103,    48,   104,    -1,
+     104,    -1,   133,    -1,    41,   119,    42,    -1,    30,   135,
+      41,   106,    42,    49,    -1,   106,    48,   107,    -1,   107,
+      -1,   133,    36,   133,    -1,   108,    48,   109,    -1,   109,
+      -1,    41,   110,    42,    -1,   134,    36,    41,   110,    42,
+      -1,   134,    36,   120,    -1,   110,    48,   111,    -1,   111,
+      -1,    45,   129,    48,   129,    46,    -1,   113,   135,    41,
+      87,    42,    49,    -1,    31,    -1,    32,    -1,    33,    -1,
+      34,    -1,   134,    -1,   115,    -1,    20,    -1,    19,    -1,
+      18,    -1,    21,    -1,    23,    -1,    24,    -1,    25,    -1,
+      26,    -1,    28,    -1,    29,    -1,    31,    -1,   117,    -1,
+      -1,    12,    -1,    14,    -1,    13,    -1,    15,    -1,    16,
+      -1,   119,    -1,    -1,   119,    48,   120,    -1,   120,    -1,
+     120,    39,   120,    -1,   120,    37,   120,    -1,   120,    38,
+     120,    -1,   120,    40,   120,    -1,   124,    36,   120,    -1,
+     121,    -1,    38,   121,    -1,    37,   121,    -1,    50,   121,
+      -1,    51,   121,    -1,   124,    -1,   114,    43,   118,    44,
+      -1,   125,    -1,    43,   120,    44,    -1,   122,    48,   123,
+      -1,   123,    -1,   114,    43,   118,    44,    -1,   114,    -1,
+     114,    47,   114,    -1,   114,    45,   120,    46,    -1,   114,
+      47,   114,    45,   120,    46,    -1,   135,    -1,   132,    -1,
+     131,    -1,   133,    -1,   127,    -1,    -1,   127,    48,   128,
+      -1,   128,    -1,    55,    -1,    29,    -1,   132,    -1,    38,
+     130,    -1,   130,    -1,    54,    -1,    53,    -1,    54,    -1,
+      53,    -1,    56,    -1,    55,    -1,    58,    -1,    52,    -1,
+     137,    -1,    -1,    52,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   168,   168,   170,   172,   176,   182,   186,   199,   200,
+     201,   204,   213,   217,   229,   238,   239,   240,   241,   242,
+     245,   246,   249,   250,   253,   254,   255,   256,   257,   258,
+     259,   260,   263,   270,   273,   278,   283,   288,   293,   298,
+     303,   308,   313,   318,   323,   328,   333,   338,   343,   358,
+     360,   362,   366,   377,   387,   391,   397,   401,   403,   407,
+     416,   418,   422,   428,   432,   438,   444,   450,   452,   455,
+     457,   459,   461,   463,   467,   469,   473,   477,   481,   485,
+     487,   491,   493,   501,   505,   511,   515,   517,   519,   521,
+     523,   527,   533,   537,   539,   543,   548,   552,   554,   558,
+     562,   569,   573,   577,   583,   587,   589,   591,   595,   601,
+     605,   615,   619,   620,   621,   622,   625,   626,   629,   631,
+     633,   635,   637,   639,   641,   643,   645,   647,   649,   653,
+     654,   657,   658,   659,   660,   661,   664,   665,   668,   674,
+     678,   680,   682,   684,   686,   688,   692,   694,   696,   698,
+     700,   702,   704,   706,   710,   716,   720,   724,   731,   739,
+     748,   759,   766,   773,   780,   791,   792,   795,   797,   801,
+     816,   820,   827,   828,   831,   832,   835,   838,   841,   844,
+     845,   848,   851,   852,   855
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "END_OF_FILE", "error", "$undefined", "ERROR_TOK", "XKB_KEYMAP",
+  "XKB_KEYCODES", "XKB_TYPES", "XKB_SYMBOLS", "XKB_COMPATMAP",
+  "XKB_GEOMETRY", "XKB_SEMANTICS", "XKB_LAYOUT", "INCLUDE", "OVERRIDE",
+  "AUGMENT", "REPLACE", "ALTERNATE", "VIRTUAL_MODS", "TYPE", "INTERPRET",
+  "ACTION_TOK", "KEY", "ALIAS", "GROUP", "MODIFIER_MAP", "INDICATOR",
+  "SHAPE", "KEYS", "ROW", "SECTION", "OVERLAY", "TEXT", "OUTLINE", "SOLID",
+  "LOGO", "VIRTUAL", "EQUALS", "PLUS", "MINUS", "DIVIDE", "TIMES",
+  "OBRACE", "CBRACE", "OPAREN", "CPAREN", "OBRACKET", "CBRACKET", "DOT",
+  "COMMA", "SEMI", "EXCLAM", "INVERT", "STRING", "INTEGER", "FLOAT",
+  "IDENT", "KEYNAME", "PARTIAL", "DEFAULT", "HIDDEN", "ALPHANUMERIC_KEYS",
+  "MODIFIER_KEYS", "KEYPAD_KEYS", "FUNCTION_KEYS", "ALTERNATE_GROUP",
+  "$accept", "XkbFile", "XkbCompMapList", "XkbCompositeMap",
+  "XkbCompositeType", "XkbMapConfigList", "XkbMapConfig", "XkbConfig",
+  "FileType", "OptFlags", "Flags", "Flag", "DeclList", "Decl", "VarDecl",
+  "KeyNameDecl", "KeyAliasDecl", "VModDecl", "VModDefList", "VModDef",
+  "InterpretDecl", "InterpretMatch", "VarDeclList", "KeyTypeDecl",
+  "SymbolsDecl", "SymbolsBody", "SymbolsVarDecl", "ArrayInit",
+  "GroupCompatDecl", "ModMapDecl", "IndicatorMapDecl", "IndicatorNameDecl",
+  "ShapeDecl", "SectionDecl", "SectionBody", "SectionBodyItem", "RowBody",
+  "RowBodyItem", "Keys", "Key", "OverlayDecl", "OverlayKeyList",
+  "OverlayKey", "OutlineList", "OutlineInList", "CoordList", "Coord",
+  "DoodadDecl", "DoodadType", "FieldSpec", "Element", "OptMergeMode",
+  "MergeMode", "OptExprList", "ExprList", "Expr", "Term", "ActionList",
+  "Action", "Lhs", "Terminal", "OptKeySymList", "KeySymList", "KeySym",
+  "SignedNumber", "Number", "Float", "Integer", "KeyName", "Ident",
+  "String", "OptMapName", "MapName", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   255,     1,     2,     3,     4,     5,     6,
+       7,     8,    10,    11,    12,    13,    14,    20,    21,    22,
+      23,    24,    25,    26,    27,    28,    29,    30,    31,    32,
+      33,    34,    35,    36,    37,    38,    40,    41,    42,    43,
+      44,    45,    46,    47,    48,    49,    50,    51,    52,    53,
+      54,    55,    60,    61,    62,    63,    64,    70,    71,    72,
+      73,    74,    75,    76,    77
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    65,    66,    66,    66,    67,    67,    68,    69,    69,
+      69,    70,    70,    71,    72,    73,    73,    73,    73,    73,
+      74,    74,    75,    75,    76,    76,    76,    76,    76,    76,
+      76,    76,    77,    77,    78,    78,    78,    78,    78,    78,
+      78,    78,    78,    78,    78,    78,    78,    78,    78,    79,
+      79,    79,    80,    81,    82,    83,    83,    84,    84,    85,
+      86,    86,    87,    87,    88,    89,    90,    90,    90,    91,
+      91,    91,    91,    91,    92,    92,    93,    94,    95,    96,
+      96,    97,    97,    98,    99,    99,   100,   100,   100,   100,
+     100,   101,   101,   102,   102,   103,   103,   104,   104,   105,
+     106,   106,   107,   108,   108,   109,   109,   109,   110,   110,
+     111,   112,   113,   113,   113,   113,   114,   114,   115,   115,
+     115,   115,   115,   115,   115,   115,   115,   115,   115,   116,
+     116,   117,   117,   117,   117,   117,   118,   118,   119,   119,
+     120,   120,   120,   120,   120,   120,   121,   121,   121,   121,
+     121,   121,   121,   121,   122,   122,   123,   124,   124,   124,
+     124,   125,   125,   125,   125,   126,   126,   127,   127,   128,
+     128,   128,   129,   129,   130,   130,   131,   132,   133,   134,
+     134,   135,   136,   136,   137
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     1,     1,     1,     2,     1,     7,     1,     1,
+       1,     2,     1,     7,     4,     1,     1,     1,     1,     1,
+       1,     0,     2,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     2,     0,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     4,
+       2,     3,     4,     5,     3,     3,     1,     1,     3,     6,
+       3,     1,     2,     1,     6,     6,     3,     1,     0,     3,
+       3,     1,     2,     1,     3,     3,     5,     6,     6,     5,
+       6,     6,     6,     6,     2,     1,     5,     1,     1,     1,
+       1,     2,     1,     5,     1,     3,     1,     1,     3,     6,
+       3,     1,     3,     3,     1,     3,     5,     3,     3,     1,
+       5,     6,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       0,     1,     1,     1,     1,     1,     1,     0,     3,     1,
+       3,     3,     3,     3,     3,     1,     2,     2,     2,     2,
+       1,     4,     1,     3,     3,     1,     4,     1,     3,     4,
+       6,     1,     1,     1,     1,     1,     0,     3,     1,     1,
+       1,     1,     2,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     1,     0,     1
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+      21,    24,    25,    26,    27,    28,    29,    30,    31,     0,
+      21,     6,    21,    12,     4,     0,    20,    23,     1,     5,
+       0,    11,     0,     8,    15,    16,    18,    17,    19,     9,
+      10,   183,   183,    22,   183,   184,     0,   182,    33,     0,
+      21,    33,   130,    21,   130,   131,   133,   132,   134,   135,
+      32,     0,   129,     0,     0,     0,   120,   119,   118,   121,
+       0,   122,   123,   124,   125,   126,   127,   128,   113,   114,
+     115,     0,     0,   179,   178,   180,    34,    37,    38,    35,
+      36,    39,    40,    42,    41,    43,    44,    45,    46,    47,
+       0,   157,   117,     0,     0,   116,    48,     7,    13,     0,
+      56,    57,   181,     0,   170,   177,   169,     0,    61,   171,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,    50,     0,    54,     0,     0,
+       0,     0,    68,     0,     0,     0,     0,     0,     0,     0,
+       0,    51,     0,   120,   119,   121,   122,   123,   124,   125,
+     127,   128,     0,     0,     0,     0,     0,   176,   157,     0,
+     145,   150,   152,   163,   162,   164,   116,   161,   158,     0,
+       0,    55,    58,    63,     0,     0,    60,   166,     0,     0,
+      67,    73,     0,   116,     0,     0,     0,   139,     0,     0,
+       0,     0,     0,   104,     0,   109,     0,   124,   126,     0,
+      87,    89,     0,    85,    90,    88,     0,     0,   147,   150,
+     146,     0,   148,   149,   137,     0,     0,     0,     0,   159,
+       0,     0,    49,    52,     0,    62,     0,   170,   169,     0,
+       0,   155,     0,   165,   168,    72,     0,     0,     0,    53,
+      76,     0,     0,    79,     0,     0,     0,   175,   174,     0,
+     173,     0,     0,     0,     0,     0,     0,     0,     0,    84,
+       0,     0,   153,     0,   136,   141,   142,   140,   143,   144,
+       0,    64,    59,   137,    75,     0,    74,     0,    65,    66,
+      70,    69,    77,   138,    78,   105,   172,     0,    81,   103,
+      82,   108,     0,   107,     0,    94,     0,    92,     0,    83,
+      80,   111,   151,   160,     0,   154,   167,     0,     0,     0,
+       0,    91,     0,   101,     0,   156,   110,   106,     0,     0,
+      96,    97,    86,     0,     0,     0,     0,     0,     0,    99,
+     100,   102,    98,    93,    95
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,     9,    10,    11,    31,    12,    13,    14,    32,    22,
+      16,    17,    42,    50,   173,    77,    78,    79,    99,   100,
+      80,   107,   174,    81,    82,   179,   180,   181,    83,    84,
+     201,    86,    87,    88,   202,   203,   296,   297,   319,   320,
+     204,   312,   313,   192,   193,   194,   195,   205,    90,   158,
+      92,    51,    52,   263,   264,   187,   160,   230,   231,   161,
+     162,   232,   233,   108,   249,   250,   163,   164,   165,   166,
+     167,    36,    37
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -188
+static const yytype_int16 yypact[] =
+{
+     628,  -188,  -188,  -188,  -188,  -188,  -188,  -188,  -188,    12,
+       4,  -188,    58,  -188,  -188,   695,   628,  -188,  -188,  -188,
+     121,  -188,   408,  -188,  -188,  -188,  -188,  -188,  -188,  -188,
+    -188,    -9,    -9,  -188,    -9,  -188,    19,  -188,    45,    45,
+     628,  -188,   174,   620,   137,  -188,  -188,  -188,  -188,  -188,
+    -188,   317,    39,   -15,    50,     1,    59,   -18,  -188,    72,
+      72,   106,     1,   -27,    59,  -188,    59,    62,  -188,  -188,
+    -188,   117,     1,  -188,  -188,  -188,  -188,  -188,  -188,  -188,
+    -188,  -188,  -188,  -188,  -188,  -188,  -188,  -188,  -188,  -188,
+      59,    51,  -188,   125,   146,   134,  -188,  -188,  -188,   132,
+    -188,   163,  -188,   168,  -188,  -188,  -188,   173,   179,  -188,
+     178,   186,   190,   194,   206,   204,   208,   221,   106,   216,
+     225,   265,   640,   265,   265,  -188,     1,  -188,   265,   599,
+     599,   265,   470,    72,   265,   265,   265,   599,   -26,    21,
+     239,  -188,   599,  -188,  -188,  -188,  -188,  -188,  -188,  -188,
+    -188,  -188,   265,   265,   265,   265,   265,  -188,   159,   232,
+    -188,   240,  -188,  -188,  -188,  -188,  -188,  -188,   223,    98,
+     156,  -188,   260,  -188,   485,   513,   260,   617,     1,    30,
+    -188,  -188,   243,    32,   231,   192,    64,   260,   199,   528,
+     236,    35,   127,  -188,   128,  -188,   251,    59,   270,    59,
+    -188,  -188,   422,  -188,  -188,  -188,   265,   556,  -188,  -188,
+    -188,   342,  -188,  -188,   265,   265,   265,   265,   265,  -188,
+     265,   265,  -188,  -188,   263,  -188,   264,   249,   271,   281,
+      61,  -188,   279,   278,  -188,  -188,   280,   470,   340,  -188,
+    -188,   283,   265,  -188,   284,   129,    16,  -188,  -188,   282,
+    -188,   298,   -34,   308,   236,   381,   576,   287,   321,  -188,
+     215,   325,  -188,   332,   336,   158,   158,  -188,  -188,   260,
+     316,  -188,  -188,   265,  -188,   640,  -188,   -18,  -188,  -188,
+    -188,   260,  -188,   260,  -188,  -188,  -188,    35,  -188,  -188,
+    -188,  -188,   236,   260,   346,  -188,   442,  -188,    72,  -188,
+    -188,  -188,  -188,  -188,   344,  -188,  -188,   343,   143,   -28,
+     348,  -188,   176,  -188,   367,  -188,  -188,  -188,   265,   198,
+    -188,  -188,  -188,   359,    72,    72,   202,   362,   -28,  -188,
+    -188,  -188,  -188,  -188,  -188
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -188,  -188,  -188,   410,  -188,   383,    -7,  -188,   399,    38,
+    -188,   409,   385,  -188,   -35,  -188,  -188,  -188,  -188,   301,
+    -188,  -188,   -47,  -188,  -188,  -188,   191,   200,  -188,  -188,
+     378,  -188,  -188,  -188,  -188,   228,  -188,   148,  -188,   130,
+    -188,  -188,   133,  -188,   197,  -187,   205,   423,  -188,    26,
+    -188,  -188,  -188,   203,  -134,    89,   104,  -188,   207,   -29,
+    -188,  -188,  -188,  -175,   188,   233,  -188,   -43,   -51,   -45,
+     -33,   109,  -188
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -180
+static const yytype_int16 yytable[] =
+{
+      94,   186,   234,   245,    -2,    21,    95,   190,   110,   111,
+     101,   104,    18,   318,   109,   190,    76,   113,   112,   191,
+     114,    73,    93,   103,    75,   102,   105,   119,    74,    73,
+     115,   116,    75,   117,    97,   105,    21,   106,    15,   143,
+     144,    58,   145,    35,   146,   147,   197,   149,    20,   198,
+     150,   199,    67,    68,    69,    70,    73,   120,    -3,    75,
+      40,     1,     2,     3,     4,     5,     6,     7,     8,   247,
+     248,    72,   236,   246,   -71,   140,    73,    91,   237,    75,
+     -71,   101,   184,   175,    95,    95,    41,   183,   247,   248,
+     189,    96,    95,   196,    95,   207,   121,    95,   122,    98,
+      93,    93,   306,   182,   200,   308,   241,   274,    93,   275,
+      93,   102,   242,    93,  -112,     1,     2,     3,     4,     5,
+       6,     7,     8,   209,   209,    23,   209,   209,    74,    95,
+      95,    29,    30,   235,   109,   215,   216,   217,   218,   225,
+     225,    38,   118,    39,    95,    93,    93,   222,   168,    45,
+      46,    47,    48,    49,   225,    91,    91,    95,    91,   105,
+      93,   123,    95,    91,   115,    91,   257,   200,    91,   251,
+     253,   285,   225,    93,   -14,   252,   254,   254,    93,    54,
+     126,   127,   124,   125,   326,   317,    45,    46,    47,    48,
+      49,   254,   183,   215,   216,   217,   218,   217,   218,   128,
+      91,    91,   214,   229,   121,   223,   122,   196,   182,   129,
+     159,    95,   169,   170,   130,    91,   131,   172,   323,   132,
+     176,   295,   133,   185,   324,   188,   134,    93,    91,   215,
+     216,   217,   218,    91,   109,   135,   215,   216,   217,   218,
+     327,   240,   136,   211,   332,   137,   328,   314,   243,   138,
+     242,    95,   215,   216,   217,   218,   208,   210,   321,   212,
+     213,   295,   139,    91,   300,   141,   142,    93,   221,   215,
+     216,   217,   218,   314,   331,   206,   220,   321,   219,   238,
+     239,   191,    91,   143,   144,    58,   145,   255,   146,   147,
+     148,   149,  -127,    65,   150,   260,   151,   215,   216,   217,
+     218,   229,   152,   153,   265,   266,   267,   268,   154,   269,
+     270,   256,   271,   272,  -179,   155,   156,   102,   105,   157,
+      73,    74,    91,    75,   273,   276,   277,   281,   298,   278,
+     287,   283,   282,   284,    55,    56,    57,    58,    59,    60,
+      61,    62,    63,    64,   293,    65,    66,   288,    67,    68,
+      69,    70,    71,   215,   216,   217,   218,   290,   143,   144,
+      58,   145,   303,   146,   147,   148,   149,    72,    65,   150,
+     299,   151,    73,    74,   301,    75,   302,   152,   153,   215,
+     216,   217,   218,   154,   242,   177,   262,   309,   315,   316,
+     155,   156,   102,   105,   157,    73,    74,   322,    75,   143,
+     144,    58,   145,   325,   146,   147,   148,   149,   329,    65,
+     150,   333,   151,    24,    25,    26,    27,    28,   152,   153,
+      19,    34,   292,    43,   154,    33,    44,   171,   279,    85,
+     259,   155,   156,   102,   105,   157,    73,    74,   280,    75,
+     143,   144,    58,   145,   311,   146,   147,   197,   149,   289,
+     198,   150,   199,    67,    68,    69,    70,   330,   334,   291,
+     143,   144,    58,   145,   258,   146,   147,   148,   149,   294,
+      65,   150,    72,   151,    89,   307,   304,    73,     0,   286,
+      75,     0,   305,     0,   310,     0,     0,     0,   143,   144,
+      58,   145,    72,   146,   147,   148,   149,    73,    65,   150,
+      75,   151,     0,   143,   144,    58,   145,     0,   146,   147,
+     148,   149,     0,    65,   150,   177,   151,     0,     0,     0,
+     178,     0,     0,     0,     0,    73,     0,   224,    75,     0,
+       0,   143,   144,    58,   145,    72,   146,   147,   148,   149,
+      73,    65,   150,    75,   151,     0,   143,   144,    58,   145,
+       0,   146,   147,   148,   149,   226,    65,   150,     0,   151,
+       0,     0,     0,    72,     0,     0,     0,     0,    73,     0,
+     244,    75,     0,     0,   143,   144,    58,   145,    72,   146,
+     147,   148,   149,    73,    65,   150,    75,   151,     0,     0,
+       0,     0,     0,     0,   143,   144,    58,   145,   261,   146,
+     147,   148,   149,   294,    65,   150,    72,   151,     0,     0,
+       0,    73,     0,     0,    75,     0,     0,   143,   144,    58,
+     145,     0,   146,   147,   148,   149,    72,    65,   150,     0,
+     151,    73,     0,     0,    75,   143,   144,    58,   145,     0,
+     146,   147,   148,   149,     0,    65,   227,     0,   151,    72,
+       0,     0,     0,     0,    73,     0,     0,    75,   143,   144,
+      58,   145,    53,   146,   147,   148,   149,     0,    65,   150,
+     105,   151,   228,     0,     0,    75,     0,     1,     2,     3,
+       4,     5,     6,     7,     8,     1,     2,     3,     4,     5,
+       6,     7,     8,     0,     0,    73,     0,     0,    75,    23,
+      24,    25,    26,    27,    28,    29,    30
+};
+
+static const yytype_int16 yycheck[] =
+{
+      51,   135,   177,   190,     0,    12,    51,    41,    59,    60,
+      55,    29,     0,    41,    57,    41,    51,    62,    61,    45,
+      63,    55,    51,    56,    58,    52,    53,    72,    56,    55,
+      63,    64,    58,    66,    49,    53,    43,    55,     0,    18,
+      19,    20,    21,    52,    23,    24,    25,    26,    10,    28,
+      29,    30,    31,    32,    33,    34,    55,    90,     0,    58,
+      41,    57,    58,    59,    60,    61,    62,    63,    64,    53,
+      54,    50,    42,    38,    42,   118,    55,    51,    48,    58,
+      48,   126,   133,   130,   129,   130,    41,   132,    53,    54,
+     137,    52,   137,   138,   139,   142,    45,   142,    47,    49,
+     129,   130,   277,   132,   139,   292,    42,    46,   137,    48,
+     139,    52,    48,   142,    52,    57,    58,    59,    60,    61,
+      62,    63,    64,   152,   153,     4,   155,   156,    56,   174,
+     175,    10,    11,   178,   177,    37,    38,    39,    40,   174,
+     175,    32,    25,    34,   189,   174,   175,    49,   122,    12,
+      13,    14,    15,    16,   189,   129,   130,   202,   132,    53,
+     189,    36,   207,   137,   197,   139,   199,   202,   142,    42,
+      42,    42,   207,   202,     0,    48,    48,    48,   207,    42,
+      48,    49,    36,    49,   318,    42,    12,    13,    14,    15,
+      16,    48,   237,    37,    38,    39,    40,    39,    40,    36,
+     174,   175,    43,   177,    45,    49,    47,   252,   237,    41,
+     121,   256,   123,   124,    41,   189,    37,   128,    42,    41,
+     131,   256,    36,   134,    48,   136,    36,   256,   202,    37,
+      38,    39,    40,   207,   277,    41,    37,    38,    39,    40,
+      42,    49,    36,   154,    42,    41,    48,   298,    49,    41,
+      48,   296,    37,    38,    39,    40,   152,   153,   309,   155,
+     156,   296,    41,   237,    49,    49,    41,   296,    45,    37,
+      38,    39,    40,   324,   325,    36,    36,   328,    46,    36,
+      49,    45,   256,    18,    19,    20,    21,    36,    23,    24,
+      25,    26,    43,    28,    29,   206,    31,    37,    38,    39,
+      40,   275,    37,    38,   215,   216,   217,   218,    43,   220,
+     221,    41,    49,    49,    43,    50,    51,    52,    53,    54,
+      55,    56,   296,    58,    43,    46,    48,   238,    41,    49,
+      48,   242,    49,    49,    17,    18,    19,    20,    21,    22,
+      23,    24,    25,    26,   255,    28,    29,    49,    31,    32,
+      33,    34,    35,    37,    38,    39,    40,    49,    18,    19,
+      20,    21,    46,    23,    24,    25,    26,    50,    28,    29,
+      49,    31,    55,    56,    49,    58,    44,    37,    38,    37,
+      38,    39,    40,    43,    48,    45,    44,    41,    44,    46,
+      50,    51,    52,    53,    54,    55,    56,    49,    58,    18,
+      19,    20,    21,    36,    23,    24,    25,    26,    49,    28,
+      29,    49,    31,     5,     6,     7,     8,     9,    37,    38,
+      10,    22,    41,    40,    43,    16,    41,   126,   237,    51,
+     202,    50,    51,    52,    53,    54,    55,    56,   238,    58,
+      18,    19,    20,    21,   296,    23,    24,    25,    26,   252,
+      28,    29,    30,    31,    32,    33,    34,   324,   328,   254,
+      18,    19,    20,    21,    42,    23,    24,    25,    26,    27,
+      28,    29,    50,    31,    51,   287,   273,    55,    -1,   246,
+      58,    -1,   275,    -1,    42,    -1,    -1,    -1,    18,    19,
+      20,    21,    50,    23,    24,    25,    26,    55,    28,    29,
+      58,    31,    -1,    18,    19,    20,    21,    -1,    23,    24,
+      25,    26,    -1,    28,    29,    45,    31,    -1,    -1,    -1,
+      50,    -1,    -1,    -1,    -1,    55,    -1,    42,    58,    -1,
+      -1,    18,    19,    20,    21,    50,    23,    24,    25,    26,
+      55,    28,    29,    58,    31,    -1,    18,    19,    20,    21,
+      -1,    23,    24,    25,    26,    42,    28,    29,    -1,    31,
+      -1,    -1,    -1,    50,    -1,    -1,    -1,    -1,    55,    -1,
+      42,    58,    -1,    -1,    18,    19,    20,    21,    50,    23,
+      24,    25,    26,    55,    28,    29,    58,    31,    -1,    -1,
+      -1,    -1,    -1,    -1,    18,    19,    20,    21,    42,    23,
+      24,    25,    26,    27,    28,    29,    50,    31,    -1,    -1,
+      -1,    55,    -1,    -1,    58,    -1,    -1,    18,    19,    20,
+      21,    -1,    23,    24,    25,    26,    50,    28,    29,    -1,
+      31,    55,    -1,    -1,    58,    18,    19,    20,    21,    -1,
+      23,    24,    25,    26,    -1,    28,    29,    -1,    31,    50,
+      -1,    -1,    -1,    -1,    55,    -1,    -1,    58,    18,    19,
+      20,    21,    42,    23,    24,    25,    26,    -1,    28,    29,
+      53,    31,    55,    -1,    -1,    58,    -1,    57,    58,    59,
+      60,    61,    62,    63,    64,    57,    58,    59,    60,    61,
+      62,    63,    64,    -1,    -1,    55,    -1,    -1,    58,     4,
+       5,     6,     7,     8,     9,    10,    11
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    57,    58,    59,    60,    61,    62,    63,    64,    66,
+      67,    68,    70,    71,    72,    74,    75,    76,     0,    68,
+      74,    71,    74,     4,     5,     6,     7,     8,     9,    10,
+      11,    69,    73,    76,    73,    52,   136,   137,   136,   136,
+      41,    41,    77,    70,    77,    12,    13,    14,    15,    16,
+      78,   116,   117,    42,    42,    17,    18,    19,    20,    21,
+      22,    23,    24,    25,    26,    28,    29,    31,    32,    33,
+      34,    35,    50,    55,    56,    58,    79,    80,    81,    82,
+      85,    88,    89,    93,    94,    95,    96,    97,    98,   112,
+     113,   114,   115,   124,   133,   134,    52,    49,    49,    83,
+      84,   134,    52,   135,    29,    53,    55,    86,   128,   132,
+     133,   133,   132,   134,   132,   135,   135,   135,    25,   134,
+     135,    45,    47,    36,    36,    49,    48,    49,    36,    41,
+      41,    37,    41,    36,    36,    41,    36,    41,    41,    41,
+     132,    49,    41,    18,    19,    21,    23,    24,    25,    26,
+      29,    31,    37,    38,    43,    50,    51,    54,   114,   120,
+     121,   124,   125,   131,   132,   133,   134,   135,   114,   120,
+     120,    84,   120,    79,    87,    87,   120,    45,    50,    90,
+      91,    92,   124,   134,   133,   120,   119,   120,   120,    87,
+      41,    45,   108,   109,   110,   111,   134,    25,    28,    30,
+      79,    95,    99,   100,   105,   112,    36,    87,   121,   124,
+     121,   120,   121,   121,    43,    37,    38,    39,    40,    46,
+      36,    45,    49,    49,    42,    79,    42,    29,    55,   114,
+     122,   123,   126,   127,   128,   134,    42,    48,    36,    49,
+      49,    42,    48,    49,    42,   110,    38,    53,    54,   129,
+     130,    42,    48,    42,    48,    36,    41,   135,    42,   100,
+     120,    42,    44,   118,   119,   120,   120,   120,   120,   120,
+     120,    49,    49,    43,    46,    48,    46,    48,    49,    91,
+      92,   120,    49,   120,    49,    42,   130,    48,    49,   109,
+      49,   111,    41,   120,    27,    79,   101,   102,    41,    49,
+      49,    49,    44,    46,   118,   123,   128,   129,   110,    41,
+      42,   102,   106,   107,   133,    44,    46,    42,    41,   103,
+     104,   133,    49,    42,    48,    36,   119,    42,    48,    49,
+     107,   133,    42,    49,   104
+};
+
+#define yyerrok                (yyerrstatus = 0)
+#define yyclearin      (yychar = YYEMPTY)
+#define YYEMPTY                (-2)
+#define YYEOF          0
+
+#define YYACCEPT       goto yyacceptlab
+#define YYABORT                goto yyabortlab
+#define YYERROR                goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL         goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                 \
+do                                                             \
+  if (yychar == YYEMPTY && yylen == 1)                         \
+    {                                                          \
+      yychar = (Token);                                                \
+      yylval = (Value);                                                \
+      yytoken = YYTRANSLATE (yychar);                          \
+      YYPOPSTACK (1);                                          \
+      goto yybackup;                                           \
+    }                                                          \
+  else                                                         \
+    {                                                          \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                 \
+    }                                                          \
+while (YYID (0))
+
+
+#define YYTERROR       1
+#define YYERRCODE      256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)                               \
+    do                                                                 \
+      if (YYID (N))                                                    \
+       {                                                               \
+         (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;        \
+         (Current).first_column = YYRHSLOC (Rhs, 1).first_column;      \
+         (Current).last_line    = YYRHSLOC (Rhs, N).last_line;         \
+         (Current).last_column  = YYRHSLOC (Rhs, N).last_column;       \
+       }                                                               \
+      else                                                             \
+       {                                                               \
+         (Current).first_line   = (Current).last_line   =              \
+           YYRHSLOC (Rhs, 0).last_line;                                \
+         (Current).first_column = (Current).last_column =              \
+           YYRHSLOC (Rhs, 0).last_column;                              \
+       }                                                               \
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)                 \
+     fprintf (File, "%d.%d-%d.%d",                     \
+             (Loc).first_line, (Loc).first_column,     \
+             (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                       \
+do {                                           \
+  if (yydebug)                                 \
+    YYFPRINTF Args;                            \
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                   \
+do {                                                                     \
+  if (yydebug)                                                           \
+    {                                                                    \
+      YYFPRINTF (stderr, "%s ", Title);                                          \
+      yy_symbol_print (stderr,                                           \
+                 Type, Value); \
+      YYFPRINTF (stderr, "\n");                                                  \
+    }                                                                    \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+       break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                           \
+do {                                                           \
+  if (yydebug)                                                 \
+    yy_stack_print ((Bottom), (Top));                          \
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+            yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+                      &(yyvsp[(yyi + 1) - (yynrhs)])
+                                      );
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)         \
+do {                                   \
+  if (yydebug)                         \
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef        YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+\f
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+       switch (*++yyp)
+         {
+         case '\'':
+         case ',':
+           goto do_not_strip_quotes;
+
+         case '\\':
+           if (*++yyp != '\\')
+             goto do_not_strip_quotes;
+           /* Fall through.  */
+         default:
+           if (yyres)
+             yyres[yyn] = *yyp;
+           yyn++;
+           break;
+
+         case '"':
+           if (yyres)
+             yyres[yyn] = '\0';
+           return yyn;
+         }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+        constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+                   + sizeof yyexpecting - 1
+                   + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+                      * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+        YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+       if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+         {
+           if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+             {
+               yycount = 1;
+               yysize = yysize0;
+               yyformat[sizeof yyunexpected - 1] = '\0';
+               break;
+             }
+           yyarg[yycount++] = yytname[yyx];
+           yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+           yysize_overflow |= (yysize1 < yysize);
+           yysize = yysize1;
+           yyfmt = yystpcpy (yyfmt, yyprefix);
+           yyprefix = yyor;
+         }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+       return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+       {
+         /* Avoid sprintf, as that infringes on the user's name space.
+            Don't have undefined behavior even if the translation
+            produced a string with the wrong number of "%s"s.  */
+         char *yyp = yyresult;
+         int yyi = 0;
+         while ((*yyp = *yyf) != '\0')
+           {
+             if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+               {
+                 yyp += yytnamerr (yyp, yyarg[yyi++]);
+                 yyf += 2;
+               }
+             else
+               {
+                 yyp++;
+                 yyf++;
+               }
+           }
+       }
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+\f
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+       break;
+    }
+}
+\f
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol.  */
+int yychar;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  
+  int yystate;
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;            /* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+       /* Give user a chance to reallocate the stack.  Use copies of
+          these so that the &'s don't force the real ones into
+          memory.  */
+       YYSTYPE *yyvs1 = yyvs;
+       yytype_int16 *yyss1 = yyss;
+
+
+       /* Each stack pointer address is followed by the size of the
+          data in use in that stack, in bytes.  This used to be a
+          conditional around just the two extra args, but that might
+          be undefined if yyoverflow is a macro.  */
+       yyoverflow (YY_("memory exhausted"),
+                   &yyss1, yysize * sizeof (*yyssp),
+                   &yyvs1, yysize * sizeof (*yyvsp),
+
+                   &yystacksize);
+
+       yyss = yyss1;
+       yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+       goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+       yystacksize = YYMAXDEPTH;
+
+      {
+       yytype_int16 *yyss1 = yyss;
+       union yyalloc *yyptr =
+         (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+       if (! yyptr)
+         goto yyexhaustedlab;
+       YYSTACK_RELOCATE (yyss);
+       YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+       if (yyss1 != yyssa)
+         YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                 (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+       YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+       goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 169 "xkbparse.y"
+    { (yyval.file)= rtrnValue= (yyvsp[(1) - (1)].file); }
+    break;
+
+  case 3:
+#line 171 "xkbparse.y"
+    { (yyval.file)= rtrnValue= (yyvsp[(1) - (1)].file);  }
+    break;
+
+  case 4:
+#line 173 "xkbparse.y"
+    { (yyval.file)= rtrnValue= (yyvsp[(1) - (1)].file); }
+    break;
+
+  case 5:
+#line 177 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with XkbCompMapList\n");
+                           (yyval.file)= (XkbFile *)AppendStmt(&(yyvsp[(1) - (2)].file)->common,&(yyvsp[(2) - (2)].file)->common);
+                       }
+    break;
+
+  case 6:
+#line 183 "xkbparse.y"
+    { (yyval.file)= (yyvsp[(1) - (1)].file); }
+    break;
+
+  case 7:
+#line 189 "xkbparse.y"
+    {
+                           fprintf(stderr, "Calling CreateXKBFile with "
+                               "XkbCompositeMap type %i defs %p->common\n",
+                               (yyvsp[(2) - (7)].uval), (yyvsp[(5) - (7)].file));
+                           (yyval.file)= CreateXKBFile((yyvsp[(2) - (7)].uval),(yyvsp[(3) - (7)].str),&(yyvsp[(5) - (7)].file)->common,(yyvsp[(1) - (7)].uval));
+                           fprintf(stderr, "Created XkbFile %p, id %i\n",
+                               (yyval.file), (yyval.file)->id);
+                       }
+    break;
+
+  case 8:
+#line 199 "xkbparse.y"
+    { (yyval.uval)= XkmKeymapFile; }
+    break;
+
+  case 9:
+#line 200 "xkbparse.y"
+    { (yyval.uval)= XkmSemanticsFile; }
+    break;
+
+  case 10:
+#line 201 "xkbparse.y"
+    { (yyval.uval)= XkmLayoutFile; }
+    break;
+
+  case 11:
+#line 205 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt(%p->common, %p->common) "
+                               "with XkbMapConfigList\n",
+                               (yyvsp[(1) - (2)].file), (yyvsp[(2) - (2)].file));
+                           (yyval.file)= (XkbFile *)AppendStmt(&(yyvsp[(1) - (2)].file)->common,&(yyvsp[(2) - (2)].file)->common);
+                           fprintf(stderr, "Returned XkbFile %p\n", (yyval.file));
+                       }
+    break;
+
+  case 12:
+#line 214 "xkbparse.y"
+    { (yyval.file)= (yyvsp[(1) - (1)].file); }
+    break;
+
+  case 13:
+#line 220 "xkbparse.y"
+    {
+                           fprintf(stderr, "Calling CreateXKBFile with "
+                               "XkbMapConfig type %i defs %p\n", (yyvsp[(2) - (7)].uval), (yyvsp[(5) - (7)].any));
+                           (yyval.file)= CreateXKBFile((yyvsp[(2) - (7)].uval),(yyvsp[(3) - (7)].str),(yyvsp[(5) - (7)].any),(yyvsp[(1) - (7)].uval));
+                           fprintf(stderr, "Created XkbFile %p, id %i\n",
+                               (yyval.file), (yyval.file)->id);
+                       }
+    break;
+
+  case 14:
+#line 230 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling CreateXKBFile with XkbConfig\n");
+                           (yyval.file)= CreateXKBFile((yyvsp[(2) - (4)].uval),(yyvsp[(3) - (4)].str),(yyvsp[(4) - (4)].any),(yyvsp[(1) - (4)].uval));
+                       }
+    break;
+
+  case 15:
+#line 238 "xkbparse.y"
+    { (yyval.uval)= XkmKeyNamesIndex; }
+    break;
+
+  case 16:
+#line 239 "xkbparse.y"
+    { (yyval.uval)= XkmTypesIndex; }
+    break;
+
+  case 17:
+#line 240 "xkbparse.y"
+    { (yyval.uval)= XkmCompatMapIndex; }
+    break;
+
+  case 18:
+#line 241 "xkbparse.y"
+    { (yyval.uval)= XkmSymbolsIndex; }
+    break;
+
+  case 19:
+#line 242 "xkbparse.y"
+    { (yyval.uval)= XkmGeometryIndex; }
+    break;
+
+  case 20:
+#line 245 "xkbparse.y"
+    { (yyval.uval)= (yyvsp[(1) - (1)].uval); }
+    break;
+
+  case 21:
+#line 246 "xkbparse.y"
+    { (yyval.uval)= 0; }
+    break;
+
+  case 22:
+#line 249 "xkbparse.y"
+    { (yyval.uval)= (((yyvsp[(1) - (2)].uval))|((yyvsp[(2) - (2)].uval))); }
+    break;
+
+  case 23:
+#line 250 "xkbparse.y"
+    { (yyval.uval)= (yyvsp[(1) - (1)].uval); }
+    break;
+
+  case 24:
+#line 253 "xkbparse.y"
+    { (yyval.uval)= XkbLC_Partial; }
+    break;
+
+  case 25:
+#line 254 "xkbparse.y"
+    { (yyval.uval)= XkbLC_Default; }
+    break;
+
+  case 26:
+#line 255 "xkbparse.y"
+    { (yyval.uval)= XkbLC_Hidden; }
+    break;
+
+  case 27:
+#line 256 "xkbparse.y"
+    { (yyval.uval)= XkbLC_AlphanumericKeys; }
+    break;
+
+  case 28:
+#line 257 "xkbparse.y"
+    { (yyval.uval)= XkbLC_ModifierKeys; }
+    break;
+
+  case 29:
+#line 258 "xkbparse.y"
+    { (yyval.uval)= XkbLC_KeypadKeys; }
+    break;
+
+  case 30:
+#line 259 "xkbparse.y"
+    { (yyval.uval)= XkbLC_FunctionKeys; }
+    break;
+
+  case 31:
+#line 260 "xkbparse.y"
+    { (yyval.uval)= XkbLC_AlternateGroup; }
+    break;
+
+  case 32:
+#line 264 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt(%p, %p) with DeclList\n",
+                               (yyvsp[(1) - (2)].any), (yyvsp[(2) - (2)].any));
+                           (yyval.any)= AppendStmt((yyvsp[(1) - (2)].any),(yyvsp[(2) - (2)].any));
+                       }
+    break;
+
+  case 33:
+#line 270 "xkbparse.y"
+    { (yyval.any)= NULL; }
+    break;
+
+  case 34:
+#line 274 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].var)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].var)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].var)->common;
+                       }
+    break;
+
+  case 35:
+#line 279 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].vmod)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].vmod)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].vmod)->common;
+                       }
+    break;
+
+  case 36:
+#line 284 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].interp)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].interp)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].interp)->common;
+                       }
+    break;
+
+  case 37:
+#line 289 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].keyName)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].keyName)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].keyName)->common;
+                       }
+    break;
+
+  case 38:
+#line 294 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].keyAlias)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].keyAlias)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].keyAlias)->common;
+                       }
+    break;
+
+  case 39:
+#line 299 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].keyType)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].keyType)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].keyType)->common;
+                       }
+    break;
+
+  case 40:
+#line 304 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].syms)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].syms)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].syms)->common;
+                       }
+    break;
+
+  case 41:
+#line 309 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].modMask)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].modMask)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].modMask)->common;
+                       }
+    break;
+
+  case 42:
+#line 314 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].groupCompat)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].groupCompat)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].groupCompat)->common;
+                       }
+    break;
+
+  case 43:
+#line 319 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].ledMap)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].ledMap)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].ledMap)->common;
+                       }
+    break;
+
+  case 44:
+#line 324 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].ledName)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].ledName)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].ledName)->common;
+                       }
+    break;
+
+  case 45:
+#line 329 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].shape)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].shape)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].shape)->common;
+                       }
+    break;
+
+  case 46:
+#line 334 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].section)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].section)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].section)->common;
+                       }
+    break;
+
+  case 47:
+#line 339 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (2)].doodad)->merge= StmtSetMerge(&(yyvsp[(2) - (2)].doodad)->common,(yyvsp[(1) - (2)].uval));
+                           (yyval.any)= &(yyvsp[(2) - (2)].doodad)->common;
+                       }
+    break;
+
+  case 48:
+#line 344 "xkbparse.y"
+    {
+                           fprintf(stderr, "Calling IncludeCreate "
+                               "with \"%s\" merge %u\n", scanStr, (yyvsp[(1) - (2)].uval));
+                           if ((yyvsp[(1) - (2)].uval)==MergeAltForm) {
+                               yyerror("cannot use 'alternate' to include other maps");
+                               (yyval.any)= &IncludeCreate(scanStr,MergeDefault)->common;
+                           }
+                           else {
+                               (yyval.any)= &IncludeCreate(scanStr,(yyvsp[(1) - (2)].uval))->common;
+                           }
+                           fprintf(stderr, "Created IncludeStmt %p\n", (yyval.any));
+                        }
+    break;
+
+  case 49:
+#line 359 "xkbparse.y"
+    { (yyval.var)= VarCreate((yyvsp[(1) - (4)].expr),(yyvsp[(3) - (4)].expr)); }
+    break;
+
+  case 50:
+#line 361 "xkbparse.y"
+    { (yyval.var)= BoolVarCreate((yyvsp[(1) - (2)].sval),1); }
+    break;
+
+  case 51:
+#line 363 "xkbparse.y"
+    { (yyval.var)= BoolVarCreate((yyvsp[(2) - (3)].sval),0); }
+    break;
+
+  case 52:
+#line 367 "xkbparse.y"
+    {
+                           KeycodeDef *def;
+
+                           def= KeycodeCreate((yyvsp[(1) - (4)].str),(yyvsp[(3) - (4)].expr));
+                           if ((yyvsp[(1) - (4)].str))
+                               free((yyvsp[(1) - (4)].str));
+                           (yyval.keyName)= def;
+                       }
+    break;
+
+  case 53:
+#line 378 "xkbparse.y"
+    { 
+                           KeyAliasDef *def;
+                           def= KeyAliasCreate((yyvsp[(2) - (5)].str),(yyvsp[(4) - (5)].str)); 
+                           if ((yyvsp[(2) - (5)].str)) free((yyvsp[(2) - (5)].str));   
+                           if ((yyvsp[(4) - (5)].str)) free((yyvsp[(4) - (5)].str));   
+                           (yyval.keyAlias)= def;
+                       }
+    break;
+
+  case 54:
+#line 388 "xkbparse.y"
+    { (yyval.vmod)= (yyvsp[(2) - (3)].vmod); }
+    break;
+
+  case 55:
+#line 392 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with VModDefList\n");
+                           (yyval.vmod)= (VModDef *)AppendStmt(&(yyvsp[(1) - (3)].vmod)->common,&(yyvsp[(3) - (3)].vmod)->common);
+                       }
+    break;
+
+  case 56:
+#line 398 "xkbparse.y"
+    { (yyval.vmod)= (yyvsp[(1) - (1)].vmod); }
+    break;
+
+  case 57:
+#line 402 "xkbparse.y"
+    { (yyval.vmod)= VModCreate((yyvsp[(1) - (1)].sval),NULL); }
+    break;
+
+  case 58:
+#line 404 "xkbparse.y"
+    { (yyval.vmod)= VModCreate((yyvsp[(1) - (3)].sval),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 59:
+#line 410 "xkbparse.y"
+    {
+                           (yyvsp[(2) - (6)].interp)->def= (yyvsp[(4) - (6)].var);
+                           (yyval.interp)= (yyvsp[(2) - (6)].interp);
+                       }
+    break;
+
+  case 60:
+#line 417 "xkbparse.y"
+    { (yyval.interp)= InterpCreate((KeySym)(yyvsp[(1) - (3)].uval),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 61:
+#line 419 "xkbparse.y"
+    { (yyval.interp)= InterpCreate((KeySym)(yyvsp[(1) - (1)].uval),NULL); }
+    break;
+
+  case 62:
+#line 423 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with VarDeclList\n");
+                           (yyval.var)= (VarDef *)AppendStmt(&(yyvsp[(1) - (2)].var)->common,&(yyvsp[(2) - (2)].var)->common);
+                       }
+    break;
+
+  case 63:
+#line 429 "xkbparse.y"
+    { (yyval.var)= (yyvsp[(1) - (1)].var); }
+    break;
+
+  case 64:
+#line 435 "xkbparse.y"
+    { (yyval.keyType)= KeyTypeCreate((yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].var)); }
+    break;
+
+  case 65:
+#line 441 "xkbparse.y"
+    { (yyval.syms)= SymbolsCreate((yyvsp[(2) - (6)].str),(ExprDef *)(yyvsp[(4) - (6)].var)); }
+    break;
+
+  case 66:
+#line 445 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with SymbolsBody\n");
+                           (yyval.var)= (VarDef *)AppendStmt(&(yyvsp[(1) - (3)].var)->common,&(yyvsp[(3) - (3)].var)->common);
+                       }
+    break;
+
+  case 67:
+#line 451 "xkbparse.y"
+    { (yyval.var)= (yyvsp[(1) - (1)].var); }
+    break;
+
+  case 68:
+#line 452 "xkbparse.y"
+    { (yyval.var)= NULL; }
+    break;
+
+  case 69:
+#line 456 "xkbparse.y"
+    { (yyval.var)= VarCreate((yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 70:
+#line 458 "xkbparse.y"
+    { (yyval.var)= VarCreate((yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 71:
+#line 460 "xkbparse.y"
+    { (yyval.var)= BoolVarCreate((yyvsp[(1) - (1)].sval),1); }
+    break;
+
+  case 72:
+#line 462 "xkbparse.y"
+    { (yyval.var)= BoolVarCreate((yyvsp[(2) - (2)].sval),0); }
+    break;
+
+  case 73:
+#line 464 "xkbparse.y"
+    { (yyval.var)= VarCreate(NULL,(yyvsp[(1) - (1)].expr)); }
+    break;
+
+  case 74:
+#line 468 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(2) - (3)].expr); }
+    break;
+
+  case 75:
+#line 470 "xkbparse.y"
+    { (yyval.expr)= ExprCreateUnary(ExprActionList,TypeAction,(yyvsp[(2) - (3)].expr)); }
+    break;
+
+  case 76:
+#line 474 "xkbparse.y"
+    { (yyval.groupCompat)= GroupCompatCreate((yyvsp[(2) - (5)].ival),(yyvsp[(4) - (5)].expr)); }
+    break;
+
+  case 77:
+#line 478 "xkbparse.y"
+    { (yyval.modMask)= ModMapCreate((yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].expr)); }
+    break;
+
+  case 78:
+#line 482 "xkbparse.y"
+    { (yyval.ledMap)= IndicatorMapCreate((yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].var)); }
+    break;
+
+  case 79:
+#line 486 "xkbparse.y"
+    { (yyval.ledName)= IndicatorNameCreate((yyvsp[(2) - (5)].ival),(yyvsp[(4) - (5)].expr),False); }
+    break;
+
+  case 80:
+#line 488 "xkbparse.y"
+    { (yyval.ledName)= IndicatorNameCreate((yyvsp[(3) - (6)].ival),(yyvsp[(5) - (6)].expr),True); }
+    break;
+
+  case 81:
+#line 492 "xkbparse.y"
+    { (yyval.shape)= ShapeDeclCreate((yyvsp[(2) - (6)].sval),(OutlineDef *)&(yyvsp[(4) - (6)].outline)->common); }
+    break;
+
+  case 82:
+#line 494 "xkbparse.y"
+    { 
+                           OutlineDef *outlines;
+                           outlines= OutlineCreate(None,(yyvsp[(4) - (6)].expr));
+                           (yyval.shape)= ShapeDeclCreate((yyvsp[(2) - (6)].sval),outlines);
+                       }
+    break;
+
+  case 83:
+#line 502 "xkbparse.y"
+    { (yyval.section)= SectionDeclCreate((yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].row)); }
+    break;
+
+  case 84:
+#line 506 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with SectionBody\n");
+                           (yyval.row)=(RowDef *)AppendStmt(&(yyvsp[(1) - (2)].row)->common,&(yyvsp[(2) - (2)].row)->common);
+                       }
+    break;
+
+  case 85:
+#line 512 "xkbparse.y"
+    { (yyval.row)= (yyvsp[(1) - (1)].row); }
+    break;
+
+  case 86:
+#line 516 "xkbparse.y"
+    { (yyval.row)= RowDeclCreate((yyvsp[(3) - (5)].key)); }
+    break;
+
+  case 87:
+#line 518 "xkbparse.y"
+    { (yyval.row)= (RowDef *)(yyvsp[(1) - (1)].var); }
+    break;
+
+  case 88:
+#line 520 "xkbparse.y"
+    { (yyval.row)= (RowDef *)(yyvsp[(1) - (1)].doodad); }
+    break;
+
+  case 89:
+#line 522 "xkbparse.y"
+    { (yyval.row)= (RowDef *)(yyvsp[(1) - (1)].ledMap); }
+    break;
+
+  case 90:
+#line 524 "xkbparse.y"
+    { (yyval.row)= (RowDef *)(yyvsp[(1) - (1)].overlay); }
+    break;
+
+  case 91:
+#line 528 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with RowBody\n");
+                           (yyval.key)=(KeyDef *)AppendStmt(&(yyvsp[(1) - (2)].key)->common,&(yyvsp[(2) - (2)].key)->common);
+                       }
+    break;
+
+  case 92:
+#line 534 "xkbparse.y"
+    { (yyval.key)= (yyvsp[(1) - (1)].key); }
+    break;
+
+  case 93:
+#line 538 "xkbparse.y"
+    { (yyval.key)= (yyvsp[(3) - (5)].key); }
+    break;
+
+  case 94:
+#line 540 "xkbparse.y"
+    { (yyval.key)= (KeyDef *)(yyvsp[(1) - (1)].var); }
+    break;
+
+  case 95:
+#line 544 "xkbparse.y"
+    {
+                           fprintf(stderr, "Calling AppendStmt with Keys\n");
+                           (yyval.key)=(KeyDef *)AppendStmt(&(yyvsp[(1) - (3)].key)->common,&(yyvsp[(3) - (3)].key)->common);
+                       }
+    break;
+
+  case 96:
+#line 549 "xkbparse.y"
+    { (yyval.key)= (yyvsp[(1) - (1)].key); }
+    break;
+
+  case 97:
+#line 553 "xkbparse.y"
+    { (yyval.key)= KeyDeclCreate((yyvsp[(1) - (1)].str),NULL); }
+    break;
+
+  case 98:
+#line 555 "xkbparse.y"
+    { (yyval.key)= KeyDeclCreate(NULL,(yyvsp[(2) - (3)].expr)); }
+    break;
+
+  case 99:
+#line 559 "xkbparse.y"
+    { (yyval.overlay)= OverlayDeclCreate((yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].olKey)); }
+    break;
+
+  case 100:
+#line 563 "xkbparse.y"
+    { 
+                           fprintf(stderr,
+                               "Calling AppendStmt with OverlayKeyList\n");
+                           (yyval.olKey)= (OverlayKeyDef *)
+                               AppendStmt(&(yyvsp[(1) - (3)].olKey)->common,&(yyvsp[(3) - (3)].olKey)->common);
+                       }
+    break;
+
+  case 101:
+#line 570 "xkbparse.y"
+    { (yyval.olKey)= (yyvsp[(1) - (1)].olKey); }
+    break;
+
+  case 102:
+#line 574 "xkbparse.y"
+    { (yyval.olKey)= OverlayKeyCreate((yyvsp[(1) - (3)].str),(yyvsp[(3) - (3)].str)); }
+    break;
+
+  case 103:
+#line 578 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with OutlineList\n");
+                           (yyval.outline)=(OutlineDef *)AppendStmt(&(yyvsp[(1) - (3)].outline)->common,&(yyvsp[(3) - (3)].outline)->common);
+                       }
+    break;
+
+  case 104:
+#line 584 "xkbparse.y"
+    { (yyval.outline)= (yyvsp[(1) - (1)].outline); }
+    break;
+
+  case 105:
+#line 588 "xkbparse.y"
+    { (yyval.outline)= OutlineCreate(None,(yyvsp[(2) - (3)].expr)); }
+    break;
+
+  case 106:
+#line 590 "xkbparse.y"
+    { (yyval.outline)= OutlineCreate((yyvsp[(1) - (5)].sval),(yyvsp[(4) - (5)].expr)); }
+    break;
+
+  case 107:
+#line 592 "xkbparse.y"
+    { (yyval.outline)= OutlineCreate((yyvsp[(1) - (3)].sval),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 108:
+#line 596 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with CoordList\n");
+                           (yyval.expr)= (ExprDef *)AppendStmt(&(yyvsp[(1) - (3)].expr)->common,&(yyvsp[(3) - (3)].expr)->common);
+                       }
+    break;
+
+  case 109:
+#line 602 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 110:
+#line 606 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprCoord,TypeUnknown);
+                           expr->value.coord.x= (yyvsp[(2) - (5)].ival);
+                           expr->value.coord.y= (yyvsp[(4) - (5)].ival);
+                           (yyval.expr)= expr;
+                       }
+    break;
+
+  case 111:
+#line 616 "xkbparse.y"
+    { (yyval.doodad)= DoodadCreate((yyvsp[(1) - (6)].uval),(yyvsp[(2) - (6)].sval),(yyvsp[(4) - (6)].var)); }
+    break;
+
+  case 112:
+#line 619 "xkbparse.y"
+    { (yyval.uval)= XkbTextDoodad; }
+    break;
+
+  case 113:
+#line 620 "xkbparse.y"
+    { (yyval.uval)= XkbOutlineDoodad; }
+    break;
+
+  case 114:
+#line 621 "xkbparse.y"
+    { (yyval.uval)= XkbSolidDoodad; }
+    break;
+
+  case 115:
+#line 622 "xkbparse.y"
+    { (yyval.uval)= XkbLogoDoodad; }
+    break;
+
+  case 116:
+#line 625 "xkbparse.y"
+    { (yyval.sval)= (yyvsp[(1) - (1)].sval); }
+    break;
+
+  case 117:
+#line 626 "xkbparse.y"
+    { (yyval.sval)= (yyvsp[(1) - (1)].sval); }
+    break;
+
+  case 118:
+#line 630 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"action",False); }
+    break;
+
+  case 119:
+#line 632 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"interpret",False); }
+    break;
+
+  case 120:
+#line 634 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"type",False); }
+    break;
+
+  case 121:
+#line 636 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"key",False); }
+    break;
+
+  case 122:
+#line 638 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"group",False); }
+    break;
+
+  case 123:
+#line 640 "xkbparse.y"
+    {(yyval.sval)=XkbInternAtom(NULL,"modifier_map",False);}
+    break;
+
+  case 124:
+#line 642 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"indicator",False); }
+    break;
+
+  case 125:
+#line 644 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"shape",False); }
+    break;
+
+  case 126:
+#line 646 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"row",False); }
+    break;
+
+  case 127:
+#line 648 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"section",False); }
+    break;
+
+  case 128:
+#line 650 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"text",False); }
+    break;
+
+  case 129:
+#line 653 "xkbparse.y"
+    { (yyval.uval)= (yyvsp[(1) - (1)].uval); }
+    break;
+
+  case 130:
+#line 654 "xkbparse.y"
+    { (yyval.uval)= MergeDefault; }
+    break;
+
+  case 131:
+#line 657 "xkbparse.y"
+    { (yyval.uval)= MergeDefault; }
+    break;
+
+  case 132:
+#line 658 "xkbparse.y"
+    { (yyval.uval)= MergeAugment; }
+    break;
+
+  case 133:
+#line 659 "xkbparse.y"
+    { (yyval.uval)= MergeOverride; }
+    break;
+
+  case 134:
+#line 660 "xkbparse.y"
+    { (yyval.uval)= MergeReplace; }
+    break;
+
+  case 135:
+#line 661 "xkbparse.y"
+    { (yyval.uval)= MergeAltForm; }
+    break;
+
+  case 136:
+#line 664 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 137:
+#line 665 "xkbparse.y"
+    { (yyval.expr)= NULL; }
+    break;
+
+  case 138:
+#line 669 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with ExprList\n");
+                           (yyval.expr)= (ExprDef *)AppendStmt(&(yyvsp[(1) - (3)].expr)->common,&(yyvsp[(3) - (3)].expr)->common);
+                       }
+    break;
+
+  case 139:
+#line 675 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 140:
+#line 679 "xkbparse.y"
+    { (yyval.expr)= ExprCreateBinary(OpDivide,(yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 141:
+#line 681 "xkbparse.y"
+    { (yyval.expr)= ExprCreateBinary(OpAdd,(yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 142:
+#line 683 "xkbparse.y"
+    { (yyval.expr)= ExprCreateBinary(OpSubtract,(yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 143:
+#line 685 "xkbparse.y"
+    { (yyval.expr)= ExprCreateBinary(OpMultiply,(yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 144:
+#line 687 "xkbparse.y"
+    { (yyval.expr)= ExprCreateBinary(OpAssign,(yyvsp[(1) - (3)].expr),(yyvsp[(3) - (3)].expr)); }
+    break;
+
+  case 145:
+#line 689 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 146:
+#line 693 "xkbparse.y"
+    { (yyval.expr)= ExprCreateUnary(OpNegate,(yyvsp[(2) - (2)].expr)->type,(yyvsp[(2) - (2)].expr)); }
+    break;
+
+  case 147:
+#line 695 "xkbparse.y"
+    { (yyval.expr)= ExprCreateUnary(OpUnaryPlus,(yyvsp[(2) - (2)].expr)->type,(yyvsp[(2) - (2)].expr)); }
+    break;
+
+  case 148:
+#line 697 "xkbparse.y"
+    { (yyval.expr)= ExprCreateUnary(OpNot,TypeBoolean,(yyvsp[(2) - (2)].expr)); }
+    break;
+
+  case 149:
+#line 699 "xkbparse.y"
+    { (yyval.expr)= ExprCreateUnary(OpInvert,(yyvsp[(2) - (2)].expr)->type,(yyvsp[(2) - (2)].expr)); }
+    break;
+
+  case 150:
+#line 701 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr);  }
+    break;
+
+  case 151:
+#line 703 "xkbparse.y"
+    { (yyval.expr)= ActionCreate((yyvsp[(1) - (4)].sval),(yyvsp[(3) - (4)].expr)); }
+    break;
+
+  case 152:
+#line 705 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr);  }
+    break;
+
+  case 153:
+#line 707 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(2) - (3)].expr);  }
+    break;
+
+  case 154:
+#line 711 "xkbparse.y"
+    {
+                           fprintf(stderr,
+                               "Calling AppendStmt with ActionList\n");
+                           (yyval.expr)= (ExprDef *)AppendStmt(&(yyvsp[(1) - (3)].expr)->common,&(yyvsp[(3) - (3)].expr)->common);
+                       }
+    break;
+
+  case 155:
+#line 717 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 156:
+#line 721 "xkbparse.y"
+    { (yyval.expr)= ActionCreate((yyvsp[(1) - (4)].sval),(yyvsp[(3) - (4)].expr)); }
+    break;
+
+  case 157:
+#line 725 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprIdent,TypeUnknown);
+                            expr->value.str= (yyvsp[(1) - (1)].sval);
+                            (yyval.expr)= expr;
+                       }
+    break;
+
+  case 158:
+#line 732 "xkbparse.y"
+    {
+                            ExprDef *expr;
+                            expr= ExprCreate(ExprFieldRef,TypeUnknown);
+                            expr->value.field.element= (yyvsp[(1) - (3)].sval);
+                            expr->value.field.field= (yyvsp[(3) - (3)].sval);
+                            (yyval.expr)= expr;
+                       }
+    break;
+
+  case 159:
+#line 740 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
+                           expr->value.array.element= None;
+                           expr->value.array.field= (yyvsp[(1) - (4)].sval);
+                           expr->value.array.entry= (yyvsp[(3) - (4)].expr);
+                           (yyval.expr)= expr;
+                       }
+    break;
+
+  case 160:
+#line 749 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
+                           expr->value.array.element= (yyvsp[(1) - (6)].sval);
+                           expr->value.array.field= (yyvsp[(3) - (6)].sval);
+                           expr->value.array.entry= (yyvsp[(5) - (6)].expr);
+                           (yyval.expr)= expr;
+                       }
+    break;
+
+  case 161:
+#line 760 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprValue,TypeString);
+                            expr->value.str= (yyvsp[(1) - (1)].sval);
+                            (yyval.expr)= expr;
+                       }
+    break;
+
+  case 162:
+#line 767 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprValue,TypeInt);
+                            expr->value.ival= (yyvsp[(1) - (1)].ival);
+                            (yyval.expr)= expr;
+                       }
+    break;
+
+  case 163:
+#line 774 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprValue,TypeFloat);
+                           expr->value.ival= (yyvsp[(1) - (1)].ival);
+                           (yyval.expr)= expr;
+                       }
+    break;
+
+  case 164:
+#line 781 "xkbparse.y"
+    {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprValue,TypeKeyName);
+                           memset(expr->value.keyName,0,5);
+                           strncpy(expr->value.keyName,(yyvsp[(1) - (1)].str),4);
+                           free((yyvsp[(1) - (1)].str));
+                           (yyval.expr)= expr;
+                       }
+    break;
+
+  case 165:
+#line 791 "xkbparse.y"
+    { (yyval.expr)= (yyvsp[(1) - (1)].expr); }
+    break;
+
+  case 166:
+#line 792 "xkbparse.y"
+    { (yyval.expr)= NULL; }
+    break;
+
+  case 167:
+#line 796 "xkbparse.y"
+    { (yyval.expr)= AppendKeysymList((yyvsp[(1) - (3)].expr),(KeySym)(yyvsp[(3) - (3)].uval)); }
+    break;
+
+  case 168:
+#line 798 "xkbparse.y"
+    { (yyval.expr)= CreateKeysymList((KeySym)(yyvsp[(1) - (1)].uval)); }
+    break;
+
+  case 169:
+#line 802 "xkbparse.y"
+    { 
+                           KeySym sym;
+                           if (LookupKeysym(scanStr,&sym))
+                               (yyval.uval)= sym;
+                           else {
+                               char buf[120];
+                               snprintf(buf, sizeof(buf),
+                                        "expected keysym, got %s",
+                                        uStringText(scanStr));
+                               yyerror(buf);
+                               yynerrs++;
+                               (yyval.uval)= NoSymbol;
+                           }
+                       }
+    break;
+
+  case 170:
+#line 817 "xkbparse.y"
+    {
+                           (yyval.uval)= XK_section;
+                       }
+    break;
+
+  case 171:
+#line 821 "xkbparse.y"
+    {
+                           if ((yyvsp[(1) - (1)].ival)<10)     (yyval.uval)= (yyvsp[(1) - (1)].ival)+'0';      /* XK_0 .. XK_9 */
+                           else        (yyval.uval)= (yyvsp[(1) - (1)].ival);
+                       }
+    break;
+
+  case 172:
+#line 827 "xkbparse.y"
+    { (yyval.ival)= -(yyvsp[(2) - (2)].ival); }
+    break;
+
+  case 173:
+#line 828 "xkbparse.y"
+    { (yyval.ival)= (yyvsp[(1) - (1)].ival); }
+    break;
+
+  case 174:
+#line 831 "xkbparse.y"
+    { (yyval.ival)= scanInt; }
+    break;
+
+  case 175:
+#line 832 "xkbparse.y"
+    { (yyval.ival)= scanInt*XkbGeomPtsPerMM; }
+    break;
+
+  case 176:
+#line 835 "xkbparse.y"
+    { (yyval.ival)= scanInt; }
+    break;
+
+  case 177:
+#line 838 "xkbparse.y"
+    { (yyval.ival)= scanInt; }
+    break;
+
+  case 178:
+#line 841 "xkbparse.y"
+    { (yyval.str)= scanStr; scanStr= NULL; }
+    break;
+
+  case 179:
+#line 844 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,scanStr,False); }
+    break;
+
+  case 180:
+#line 845 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,"default",False); }
+    break;
+
+  case 181:
+#line 848 "xkbparse.y"
+    { (yyval.sval)= XkbInternAtom(NULL,scanStr,False); }
+    break;
+
+  case 182:
+#line 851 "xkbparse.y"
+    { (yyval.str)= (yyvsp[(1) - (1)].str); }
+    break;
+
+  case 183:
+#line 852 "xkbparse.y"
+    { (yyval.str)= NULL; }
+    break;
+
+  case 184:
+#line 855 "xkbparse.y"
+    { (yyval.str)= scanStr; scanStr= NULL; }
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 3008 "xkbparse.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+      {
+       YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+       if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+         {
+           YYSIZE_T yyalloc = 2 * yysize;
+           if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+             yyalloc = YYSTACK_ALLOC_MAXIMUM;
+           if (yymsg != yymsgbuf)
+             YYSTACK_FREE (yymsg);
+           yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+           if (yymsg)
+             yymsg_alloc = yyalloc;
+           else
+             {
+               yymsg = yymsgbuf;
+               yymsg_alloc = sizeof yymsgbuf;
+             }
+         }
+
+       if (0 < yysize && yysize <= yymsg_alloc)
+         {
+           (void) yysyntax_error (yymsg, yystate, yychar);
+           yyerror (yymsg);
+         }
+       else
+         {
+           yyerror (YY_("syntax error"));
+           if (yysize != 0)
+             goto yyexhaustedlab;
+         }
+      }
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+        error, discard it.  */
+
+      if (yychar <= YYEOF)
+       {
+         /* Return failure if at end of input.  */
+         if (yychar == YYEOF)
+           YYABORT;
+       }
+      else
+       {
+         yydestruct ("Error: discarding",
+                     yytoken, &yylval);
+         yychar = YYEMPTY;
+       }
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;     /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+       {
+         yyn += YYTERROR;
+         if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+           {
+             yyn = yytable[yyn];
+             if (0 < yyn)
+               break;
+           }
+       }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+       YYABORT;
+
+
+      yydestruct ("Error: popping",
+                 yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+                yytoken, &yylval);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                 yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 857 "xkbparse.y"
+
+void
+yyerror(const char *s)
+{
+    if (warningLevel>0) {
+       (void)fprintf(stderr,"%s: line %d of %s\n",s,lineNum,
+                                       (scanFile?scanFile:"(unknown)"));
+       if ((scanStr)&&(warningLevel>3))
+           (void)fprintf(stderr,"last scanned symbol is: %s\n",scanStr);
+    }
+    return;
+}
+
+
+int
+yywrap(void)
+{
+   return 1;
+}
+
+
diff --git a/src/xkbcomp/xkbparse.y b/src/xkbcomp/xkbparse.y
new file mode 100644 (file)
index 0000000..63f87bb
--- /dev/null
@@ -0,0 +1,799 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+%token
+       END_OF_FILE     0
+       ERROR_TOK       255
+       XKB_KEYMAP      1
+       XKB_KEYCODES    2
+       XKB_TYPES       3
+       XKB_SYMBOLS     4
+       XKB_COMPATMAP   5
+       XKB_GEOMETRY    6
+       XKB_SEMANTICS   7
+       XKB_LAYOUT      8
+       INCLUDE         10
+       OVERRIDE        11
+       AUGMENT         12
+       REPLACE         13
+       ALTERNATE       14
+       VIRTUAL_MODS    20
+       TYPE            21
+       INTERPRET       22
+       ACTION_TOK      23
+       KEY             24
+       ALIAS           25
+       GROUP           26
+       MODIFIER_MAP    27
+       INDICATOR       28
+       SHAPE           29
+       KEYS            30
+       ROW             31
+       SECTION         32
+       OVERLAY         33
+       TEXT            34
+       OUTLINE         35
+       SOLID           36
+       LOGO            37
+       VIRTUAL         38
+       EQUALS          40
+       PLUS            41
+       MINUS           42
+       DIVIDE          43
+       TIMES           44
+       OBRACE          45
+       CBRACE          46
+       OPAREN          47
+       CPAREN          48
+       OBRACKET        49
+       CBRACKET        50
+       DOT             51
+       COMMA           52
+       SEMI            53
+       EXCLAM          54
+       INVERT          55
+       STRING          60
+       INTEGER         61
+       FLOAT           62
+       IDENT           63
+       KEYNAME         64
+       PARTIAL         70
+       DEFAULT         71
+       HIDDEN          72
+       ALPHANUMERIC_KEYS       73
+       MODIFIER_KEYS           74
+       KEYPAD_KEYS             75
+       FUNCTION_KEYS           76
+       ALTERNATE_GROUP         77
+%{
+#ifdef DEBUG
+#define        YYDEBUG 1
+#endif
+#define        DEBUG_VAR parseDebug
+#include "parseutils.h"
+#include <X11/keysym.h>
+#include <X11/extensions/XKBgeom.h>
+#include <stdlib.h>
+
+unsigned int parseDebug;
+
+%}
+%right EQUALS
+%left  PLUS MINUS
+%left  TIMES DIVIDE
+%left  EXCLAM INVERT
+%left  OPAREN
+%start XkbFile
+%union {
+       int              ival;
+       unsigned         uval;
+       char            *str;
+       Atom            sval;
+       ParseCommon     *any;
+       ExprDef         *expr;
+       VarDef          *var;
+       VModDef         *vmod;
+       InterpDef       *interp;
+       KeyTypeDef      *keyType;
+       SymbolsDef      *syms;
+       ModMapDef       *modMask;
+       GroupCompatDef  *groupCompat;
+       IndicatorMapDef *ledMap;
+       IndicatorNameDef *ledName;
+       KeycodeDef      *keyName;
+       KeyAliasDef     *keyAlias;
+       ShapeDef        *shape;
+       SectionDef      *section;
+       RowDef          *row;
+       KeyDef          *key;
+       OverlayDef      *overlay;
+       OverlayKeyDef   *olKey;
+       OutlineDef      *outline;
+       DoodadDef       *doodad;
+       XkbFile         *file;
+}
+%type <ival>   Number Integer Float SignedNumber
+%type <uval>   XkbCompositeType FileType MergeMode OptMergeMode KeySym
+%type <uval>   DoodadType Flag Flags OptFlags
+%type <str>    KeyName MapName OptMapName
+%type <sval>   FieldSpec Ident Element String 
+%type <any>    DeclList Decl 
+%type <expr>   OptExprList ExprList Expr Term Lhs Terminal ArrayInit
+%type <expr>   OptKeySymList KeySymList Action ActionList Coord CoordList
+%type <var>    VarDecl VarDeclList SymbolsBody SymbolsVarDecl 
+%type <vmod>   VModDecl VModDefList VModDef
+%type <interp> InterpretDecl InterpretMatch
+%type <keyType>        KeyTypeDecl
+%type <syms>   SymbolsDecl
+%type <modMask>        ModMapDecl
+%type <groupCompat> GroupCompatDecl
+%type <ledMap> IndicatorMapDecl
+%type <ledName>        IndicatorNameDecl
+%type <keyName>        KeyNameDecl
+%type <keyAlias> KeyAliasDecl
+%type <shape>  ShapeDecl
+%type <section>        SectionDecl
+%type <row>    SectionBody SectionBodyItem
+%type <key>    RowBody RowBodyItem Keys Key 
+%type <overlay>        OverlayDecl
+%type <olKey>  OverlayKeyList OverlayKey
+%type <outline>        OutlineList OutlineInList
+%type <doodad> DoodadDecl
+%type <file>   XkbFile XkbMapConfigList XkbMapConfig XkbConfig
+%type <file>   XkbCompositeMap XkbCompMapList
+%%
+XkbFile                :       XkbCompMapList
+                       { $$= rtrnValue= $1; }
+               |       XkbMapConfigList 
+                       { $$= rtrnValue= $1;  }
+               |       XkbConfig
+                       { $$= rtrnValue= $1; }
+               ;
+
+XkbCompMapList :       XkbCompMapList XkbCompositeMap
+                       { $$= (XkbFile *)AppendStmt(&$1->common,&$2->common); }
+               |       XkbCompositeMap
+                       { $$= $1; }
+               ;
+
+XkbCompositeMap        :       OptFlags XkbCompositeType OptMapName OBRACE
+                           XkbMapConfigList
+                       CBRACE SEMI
+                       { $$= CreateXKBFile($2,$3,&$5->common,$1); }
+               ;
+
+XkbCompositeType:      XKB_KEYMAP      { $$= XkmKeymapFile; }
+               |       XKB_SEMANTICS   { $$= XkmSemanticsFile; }
+               |       XKB_LAYOUT      { $$= XkmLayoutFile; }
+               ;
+
+XkbMapConfigList :     XkbMapConfigList XkbMapConfig
+                       { $$= (XkbFile *)AppendStmt(&$1->common,&$2->common); }
+               |       XkbMapConfig
+                       { $$= $1; }
+               ;
+
+XkbMapConfig   :       OptFlags FileType OptMapName OBRACE
+                           DeclList
+                       CBRACE SEMI
+                       { $$= CreateXKBFile($2,$3,$5,$1); }
+               ;
+
+XkbConfig      :       OptFlags FileType OptMapName DeclList
+                       { $$= CreateXKBFile($2,$3,$4,$1); }
+               ;
+
+
+FileType       :       XKB_KEYCODES            { $$= XkmKeyNamesIndex; }
+               |       XKB_TYPES               { $$= XkmTypesIndex; }
+               |       XKB_COMPATMAP           { $$= XkmCompatMapIndex; }
+               |       XKB_SYMBOLS             { $$= XkmSymbolsIndex; }
+               |       XKB_GEOMETRY            { $$= XkmGeometryIndex; }
+               ;
+
+OptFlags       :       Flags                   { $$= $1; }
+               |                               { $$= 0; }
+               ;
+
+Flags          :       Flags Flag              { $$= (($1)|($2)); }
+               |       Flag                    { $$= $1; }
+               ;
+
+Flag           :       PARTIAL                 { $$= XkbLC_Partial; }
+               |       DEFAULT                 { $$= XkbLC_Default; }
+               |       HIDDEN                  { $$= XkbLC_Hidden; }
+               |       ALPHANUMERIC_KEYS       { $$= XkbLC_AlphanumericKeys; }
+               |       MODIFIER_KEYS           { $$= XkbLC_ModifierKeys; }
+               |       KEYPAD_KEYS             { $$= XkbLC_KeypadKeys; }
+               |       FUNCTION_KEYS           { $$= XkbLC_FunctionKeys; }
+               |       ALTERNATE_GROUP         { $$= XkbLC_AlternateGroup; }
+               ;
+
+DeclList       :       DeclList Decl
+                       { $$= AppendStmt($1,$2); }
+               |       { $$= NULL; }
+               ;
+
+Decl           :       OptMergeMode VarDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode VModDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode InterpretDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode KeyNameDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode KeyAliasDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode KeyTypeDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode SymbolsDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode ModMapDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode GroupCompatDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode IndicatorMapDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode IndicatorNameDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode ShapeDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode SectionDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       OptMergeMode DoodadDecl
+                       {
+                           $2->merge= StmtSetMerge(&$2->common,$1);
+                           $$= &$2->common;
+                       }
+               |       MergeMode STRING
+                       {
+                           if ($1==MergeAltForm) {
+                               yyerror("cannot use 'alternate' to include other maps");
+                               $$= &IncludeCreate(scanStr,MergeDefault)->common;
+                           }
+                           else {
+                               $$= &IncludeCreate(scanStr,$1)->common;
+                           }
+                        }
+               ;
+
+VarDecl                :       Lhs EQUALS Expr SEMI
+                       { $$= VarCreate($1,$3); }
+               |       Ident SEMI
+                       { $$= BoolVarCreate($1,1); }
+               |       EXCLAM Ident SEMI
+                       { $$= BoolVarCreate($2,0); }
+               ;
+
+KeyNameDecl    :       KeyName EQUALS Expr SEMI
+                        {
+                           KeycodeDef *def;
+
+                           def= KeycodeCreate($1,$3);
+                           if ($1)
+                               free($1);
+                           $$= def;
+                       }
+               ;
+
+KeyAliasDecl   :       ALIAS KeyName EQUALS KeyName SEMI
+                       { 
+                           KeyAliasDef *def;
+                           def= KeyAliasCreate($2,$4); 
+                           if ($2)     free($2);       
+                           if ($4)     free($4);       
+                           $$= def;
+                       }
+               ;
+
+VModDecl       :       VIRTUAL_MODS VModDefList SEMI
+                       { $$= $2; }
+               ;
+
+VModDefList    :       VModDefList COMMA VModDef
+                       { $$= (VModDef *)AppendStmt(&$1->common,&$3->common); }
+               |       VModDef
+                       { $$= $1; }
+               ;
+
+VModDef                :       Ident
+                       { $$= VModCreate($1,NULL); }
+               |       Ident EQUALS Expr
+                       { $$= VModCreate($1,$3); }
+               ;
+
+InterpretDecl  :       INTERPRET InterpretMatch OBRACE
+                           VarDeclList
+                       CBRACE SEMI
+                       {
+                           $2->def= $4;
+                           $$= $2;
+                       }
+               ;
+
+InterpretMatch :       KeySym PLUS Expr        
+                       { $$= InterpCreate((KeySym)$1,$3); }
+               |       KeySym                  
+                       { $$= InterpCreate((KeySym)$1,NULL); }
+               ;
+
+VarDeclList    :       VarDeclList VarDecl
+                       { $$= (VarDef *)AppendStmt(&$1->common,&$2->common); }
+               |       VarDecl
+                       { $$= $1; }
+               ;
+
+KeyTypeDecl    :       TYPE String OBRACE
+                           VarDeclList
+                       CBRACE SEMI
+                       { $$= KeyTypeCreate($2,$4); }
+               ;
+
+SymbolsDecl    :       KEY KeyName OBRACE
+                           SymbolsBody
+                       CBRACE SEMI
+                       { $$= SymbolsCreate($2,(ExprDef *)$4); }
+               ;
+
+SymbolsBody    :       SymbolsBody COMMA SymbolsVarDecl
+                       { $$= (VarDef *)AppendStmt(&$1->common,&$3->common); }
+               |       SymbolsVarDecl
+                       { $$= $1; }
+               |       { $$= NULL; }
+               ;
+
+SymbolsVarDecl :       Lhs EQUALS Expr
+                       { $$= VarCreate($1,$3); }
+               |       Lhs EQUALS ArrayInit
+                       { $$= VarCreate($1,$3); }
+               |       Ident
+                       { $$= BoolVarCreate($1,1); }
+               |       EXCLAM Ident
+                       { $$= BoolVarCreate($2,0); }
+               |       ArrayInit
+                       { $$= VarCreate(NULL,$1); }
+               ;
+
+ArrayInit      :       OBRACKET OptKeySymList CBRACKET
+                       { $$= $2; }
+               |       OBRACKET ActionList CBRACKET
+                       { $$= ExprCreateUnary(ExprActionList,TypeAction,$2); }
+               ;
+
+GroupCompatDecl        :       GROUP Integer EQUALS Expr SEMI
+                       { $$= GroupCompatCreate($2,$4); }
+               ;
+
+ModMapDecl     :       MODIFIER_MAP Ident OBRACE ExprList CBRACE SEMI
+                       { $$= ModMapCreate($2,$4); }
+               ;
+
+IndicatorMapDecl:      INDICATOR String OBRACE VarDeclList CBRACE SEMI
+                       { $$= IndicatorMapCreate($2,$4); }
+               ;
+
+IndicatorNameDecl:     INDICATOR Integer EQUALS Expr SEMI
+                       { $$= IndicatorNameCreate($2,$4,False); }
+               |       VIRTUAL INDICATOR Integer EQUALS Expr SEMI
+                       { $$= IndicatorNameCreate($3,$5,True); }
+               ;
+
+ShapeDecl      :       SHAPE String OBRACE OutlineList CBRACE SEMI
+                       { $$= ShapeDeclCreate($2,(OutlineDef *)&$4->common); }
+               |       SHAPE String OBRACE CoordList CBRACE SEMI
+                       { 
+                           OutlineDef *outlines;
+                           outlines= OutlineCreate(None,$4);
+                           $$= ShapeDeclCreate($2,outlines);
+                       }
+               ;
+
+SectionDecl    :       SECTION String OBRACE SectionBody CBRACE SEMI
+                       { $$= SectionDeclCreate($2,$4); }
+               ;
+
+SectionBody    :       SectionBody SectionBodyItem
+                       { $$=(RowDef *)AppendStmt(&$1->common,&$2->common);}
+               |       SectionBodyItem
+                       { $$= $1; }
+               ;
+
+SectionBodyItem        :       ROW OBRACE RowBody CBRACE SEMI
+                       { $$= RowDeclCreate($3); }
+               |       VarDecl
+                       { $$= (RowDef *)$1; }
+               |       DoodadDecl
+                       { $$= (RowDef *)$1; }
+               |       IndicatorMapDecl
+                       { $$= (RowDef *)$1; }
+               |       OverlayDecl
+                       { $$= (RowDef *)$1; }
+               ;
+
+RowBody                :       RowBody RowBodyItem
+                       { $$=(KeyDef *)AppendStmt(&$1->common,&$2->common);}
+               |       RowBodyItem
+                       { $$= $1; }
+               ;
+
+RowBodyItem    :       KEYS OBRACE Keys CBRACE SEMI
+                       { $$= $3; }
+               |       VarDecl
+                       { $$= (KeyDef *)$1; }
+               ;
+
+Keys           :       Keys COMMA Key
+                       { $$=(KeyDef *)AppendStmt(&$1->common,&$3->common);}
+               |       Key
+                       { $$= $1; }
+               ;
+
+Key            :       KeyName
+                       { $$= KeyDeclCreate($1,NULL); }
+               |       OBRACE ExprList CBRACE
+                       { $$= KeyDeclCreate(NULL,$2); }
+               ;
+
+OverlayDecl    :       OVERLAY String OBRACE OverlayKeyList CBRACE SEMI
+                       { $$= OverlayDeclCreate($2,$4); }
+               ;
+
+OverlayKeyList :       OverlayKeyList COMMA OverlayKey
+                       { 
+                           $$= (OverlayKeyDef *)
+                               AppendStmt(&$1->common,&$3->common);
+                       }
+               |       OverlayKey
+                       { $$= $1; }
+               ;
+
+OverlayKey     :       KeyName EQUALS KeyName
+                       { $$= OverlayKeyCreate($1,$3); }
+               ;
+
+OutlineList    :       OutlineList COMMA OutlineInList
+                       { $$=(OutlineDef *)AppendStmt(&$1->common,&$3->common);}
+               |       OutlineInList
+                       { $$= $1; }
+               ;
+
+OutlineInList  :       OBRACE CoordList CBRACE
+                       { $$= OutlineCreate(None,$2); }
+               |       Ident EQUALS OBRACE CoordList CBRACE
+                       { $$= OutlineCreate($1,$4); }
+               |       Ident EQUALS Expr
+                       { $$= OutlineCreate($1,$3); }
+               ;
+
+CoordList      :       CoordList COMMA Coord
+                       { $$= (ExprDef *)AppendStmt(&$1->common,&$3->common); }
+               |       Coord
+                       { $$= $1; }
+               ;
+
+Coord          :       OBRACKET SignedNumber COMMA SignedNumber CBRACKET
+                       {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprCoord,TypeUnknown);
+                           expr->value.coord.x= $2;
+                           expr->value.coord.y= $4;
+                           $$= expr;
+                       }
+               ;
+
+DoodadDecl     :       DoodadType String OBRACE VarDeclList CBRACE SEMI
+                       { $$= DoodadCreate($1,$2,$4); }
+               ;
+
+DoodadType     :       TEXT                    { $$= XkbTextDoodad; }
+               |       OUTLINE                 { $$= XkbOutlineDoodad; }
+               |       SOLID                   { $$= XkbSolidDoodad; }
+               |       LOGO                    { $$= XkbLogoDoodad; }
+               ;
+
+FieldSpec      :       Ident                   { $$= $1; }
+               |       Element                 { $$= $1; }
+               ;
+
+Element                :       ACTION_TOK              
+                       { $$= XkbInternAtom(NULL,"action",False); }
+               |       INTERPRET
+                       { $$= XkbInternAtom(NULL,"interpret",False); }
+               |       TYPE
+                       { $$= XkbInternAtom(NULL,"type",False); }
+               |       KEY
+                       { $$= XkbInternAtom(NULL,"key",False); }
+               |       GROUP
+                       { $$= XkbInternAtom(NULL,"group",False); }
+               |       MODIFIER_MAP
+                       {$$=XkbInternAtom(NULL,"modifier_map",False);}
+               |       INDICATOR
+                       { $$= XkbInternAtom(NULL,"indicator",False); }
+               |       SHAPE   
+                       { $$= XkbInternAtom(NULL,"shape",False); }
+               |       ROW     
+                       { $$= XkbInternAtom(NULL,"row",False); }
+               |       SECTION 
+                       { $$= XkbInternAtom(NULL,"section",False); }
+               |       TEXT
+                       { $$= XkbInternAtom(NULL,"text",False); }
+               ;
+
+OptMergeMode   :       MergeMode               { $$= $1; }
+               |                               { $$= MergeDefault; }
+               ;
+
+MergeMode      :       INCLUDE                 { $$= MergeDefault; }
+               |       AUGMENT                 { $$= MergeAugment; }
+               |       OVERRIDE                { $$= MergeOverride; }
+               |       REPLACE                 { $$= MergeReplace; }
+               |       ALTERNATE               { $$= MergeAltForm; }
+               ;
+
+OptExprList    :       ExprList                        { $$= $1; }
+               |                               { $$= NULL; }
+               ;
+
+ExprList       :       ExprList COMMA Expr
+                       { $$= (ExprDef *)AppendStmt(&$1->common,&$3->common); }
+               |       Expr
+                       { $$= $1; }
+               ;
+
+Expr           :       Expr DIVIDE Expr
+                       { $$= ExprCreateBinary(OpDivide,$1,$3); }
+               |       Expr PLUS Expr
+                       { $$= ExprCreateBinary(OpAdd,$1,$3); }
+               |       Expr MINUS Expr
+                       { $$= ExprCreateBinary(OpSubtract,$1,$3); }
+               |       Expr TIMES Expr
+                       { $$= ExprCreateBinary(OpMultiply,$1,$3); }
+               |       Lhs EQUALS Expr
+                       { $$= ExprCreateBinary(OpAssign,$1,$3); }
+               |       Term
+                       { $$= $1; }
+               ;
+
+Term           :       MINUS Term
+                       { $$= ExprCreateUnary(OpNegate,$2->type,$2); }
+               |       PLUS Term
+                       { $$= ExprCreateUnary(OpUnaryPlus,$2->type,$2); }
+               |       EXCLAM Term
+                       { $$= ExprCreateUnary(OpNot,TypeBoolean,$2); }
+               |       INVERT Term
+                       { $$= ExprCreateUnary(OpInvert,$2->type,$2); }
+               |       Lhs
+                       { $$= $1;  }
+               |       FieldSpec OPAREN OptExprList CPAREN %prec OPAREN
+                       { $$= ActionCreate($1,$3); }
+               |       Terminal
+                       { $$= $1;  }
+               |       OPAREN Expr CPAREN
+                       { $$= $2;  }
+               ;
+
+ActionList     :       ActionList COMMA Action
+                       { $$= (ExprDef *)AppendStmt(&$1->common,&$3->common); }
+               |       Action
+                       { $$= $1; }
+               ;
+
+Action         :       FieldSpec OPAREN OptExprList CPAREN
+                       { $$= ActionCreate($1,$3); }
+               ;
+
+Lhs            :       FieldSpec
+                       {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprIdent,TypeUnknown);
+                            expr->value.str= $1;
+                            $$= expr;
+                       }
+               |       FieldSpec DOT FieldSpec
+                        {
+                            ExprDef *expr;
+                            expr= ExprCreate(ExprFieldRef,TypeUnknown);
+                            expr->value.field.element= $1;
+                            expr->value.field.field= $3;
+                            $$= expr;
+                       }
+               |       FieldSpec OBRACKET Expr CBRACKET
+                       {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
+                           expr->value.array.element= None;
+                           expr->value.array.field= $1;
+                           expr->value.array.entry= $3;
+                           $$= expr;
+                       }
+               |       FieldSpec DOT FieldSpec OBRACKET Expr CBRACKET
+                       {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprArrayRef,TypeUnknown);
+                           expr->value.array.element= $1;
+                           expr->value.array.field= $3;
+                           expr->value.array.entry= $5;
+                           $$= expr;
+                       }
+               ;
+
+Terminal       :       String
+                       {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprValue,TypeString);
+                            expr->value.str= $1;
+                            $$= expr;
+                       }
+               |       Integer
+                       {
+                           ExprDef *expr;
+                            expr= ExprCreate(ExprValue,TypeInt);
+                            expr->value.ival= $1;
+                            $$= expr;
+                       }
+               |       Float
+                       {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprValue,TypeFloat);
+                           expr->value.ival= $1;
+                           $$= expr;
+                       }
+               |       KeyName
+                       {
+                           ExprDef *expr;
+                           expr= ExprCreate(ExprValue,TypeKeyName);
+                           memset(expr->value.keyName,0,5);
+                           strncpy(expr->value.keyName,$1,4);
+                           free($1);
+                           $$= expr;
+                       }
+               ;
+
+OptKeySymList  :       KeySymList                      { $$= $1; }
+               |                                       { $$= NULL; }
+               ;
+
+KeySymList     :       KeySymList COMMA KeySym
+                       { $$= AppendKeysymList($1,(KeySym)$3); }
+               |       KeySym
+                       { $$= CreateKeysymList((KeySym)$1); }
+               ;
+
+KeySym         :       IDENT
+                       { 
+                           KeySym sym;
+                           if (LookupKeysym(scanStr,&sym))
+                               $$= sym;
+                           else {
+                               char buf[120];
+                               snprintf(buf, sizeof(buf),
+                                        "expected keysym, got %s",
+                                        uStringText(scanStr));
+                               yyerror(buf);
+                               yynerrs++;
+                               $$= NoSymbol;
+                           }
+                       }
+               |       SECTION
+                       {
+                           $$= XK_section;
+                       }
+               |       Integer         
+                       {
+                           if ($1<10)  $$= $1+'0';     /* XK_0 .. XK_9 */
+                           else        $$= $1;
+                       }
+               ;
+
+SignedNumber   :       MINUS Number    { $$= -$2; }
+               |       Number              { $$= $1; }
+               ;
+
+Number         :       FLOAT           { $$= scanInt; }
+               |       INTEGER         { $$= scanInt*XkbGeomPtsPerMM; }
+               ;
+
+Float          :       FLOAT           { $$= scanInt; }
+               ;
+
+Integer                :       INTEGER         { $$= scanInt; }
+               ;
+
+KeyName                :       KEYNAME         { $$= scanStr; scanStr= NULL; }
+               ;
+
+Ident          :       IDENT   { $$= XkbInternAtom(NULL,scanStr,False); }
+               |       DEFAULT { $$= XkbInternAtom(NULL,"default",False); }
+               ;
+
+String         :       STRING  { $$= XkbInternAtom(NULL,scanStr,False); }
+               ;
+
+OptMapName     :       MapName { $$= $1; }
+               |               { $$= NULL; }
+               ;
+
+MapName                :       STRING  { $$= scanStr; scanStr= NULL; }
+               ;
+%%
+void
+yyerror(const char *s)
+{
+    if (warningLevel>0) {
+       (void)fprintf(stderr,"%s: line %d of %s\n",s,lineNum,
+                                       (scanFile?scanFile:"(unknown)"));
+       if ((scanStr)&&(warningLevel>3))
+           (void)fprintf(stderr,"last scanned symbol is: %s\n",scanStr);
+    }
+    return;
+}
+
+
+int
+yywrap(void)
+{
+   return 1;
+}
+
diff --git a/src/xkbcomp/xkbpath.c b/src/xkbcomp/xkbpath.c
new file mode 100644 (file)
index 0000000..6802012
--- /dev/null
@@ -0,0 +1,420 @@
+/************************************************************
+ 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/Xlib.h>
+#include <X11/XKBlib.h>
+
+#define        DEBUG_VAR debugFlags
+#include "utils.h"
+#include <stdlib.h>
+#include <X11/extensions/XKM.h>
+#include "xkbpath.h"
+
+#ifndef DFLT_XKB_CONFIG_ROOT
+#define DFLT_XKB_CONFIG_ROOT   "/usr/lib/X11/xkb"
+#endif
+
+#ifndef PATH_MAX
+#define        PATH_MAX 1024
+#endif
+
+#define        PATH_CHUNK      8       /* initial szPath */
+
+static Bool noDefaultPath = False;
+static int szPath;         /* number of entries allocated for includePath */
+static int nPathEntries;   /* number of actual entries in includePath */
+static char **includePath; /* Holds all directories we might be including data from */
+
+/**
+ * Extract the first token from an include statement.
+ * @param str_inout Input statement, modified in-place. Can be passed in
+ * repeatedly. If str_inout is NULL, the parsing has completed.
+ * @param file_rtrn Set to the include file to be used.
+ * @param map_rtrn Set to whatever comes after ), if any.
+ * @param nextop_rtrn Set to the next operation in the complete statement.
+ * @param extra_data Set to the string between ( and ), if any.
+ *
+ * @return True if parsing was succcessful, False for an illegal string.
+ *
+ * Example: "evdev+aliases(qwerty)"
+ *      str_inout = aliases(qwerty)
+ *      nextop_retrn = +
+ *      extra_data = NULL
+ *      file_rtrn = evdev
+ *      map_rtrn = NULL
+ *
+ * 2nd run with "aliases(qwerty)"
+ *      str_inout = NULL
+ *      file_rtrn = aliases
+ *      map_rtrn = qwerty
+ *      extra_data = NULL
+ *      nextop_retrn = ""
+ *
+ */
+Bool
+XkbParseIncludeMap(char **str_inout, char **file_rtrn, char **map_rtrn,
+                   char *nextop_rtrn, char **extra_data)
+{
+    char *tmp, *str, *next;
+
+    str = *str_inout;
+    if ((*str == '+') || (*str == '|'))
+    {
+        *file_rtrn = *map_rtrn = NULL;
+        *nextop_rtrn = *str;
+        next = str + 1;
+    }
+    else if (*str == '%')
+    {
+        *file_rtrn = *map_rtrn = NULL;
+        *nextop_rtrn = str[1];
+        next = str + 2;
+    }
+    else
+    {
+        /* search for tokens inside the string */
+        next = strpbrk(str, "|+");
+        if (next)
+        {
+            /* set nextop_rtrn to \0, next to next character */
+            *nextop_rtrn = *next;
+            *next++ = '\0';
+        }
+        else
+        {
+            *nextop_rtrn = '\0';
+            next = NULL;
+        }
+        /* search for :, store result in extra_data */
+        tmp = strchr(str, ':');
+        if (tmp != NULL)
+        {
+            *tmp++ = '\0';
+            *extra_data = uStringDup(tmp);
+        }
+        else
+        {
+            *extra_data = NULL;
+        }
+        tmp = strchr(str, '(');
+        if (tmp == NULL)
+        {
+            *file_rtrn = uStringDup(str);
+            *map_rtrn = NULL;
+        }
+        else if (str[0] == '(')
+        {
+            uFree(*extra_data);
+            return False;
+        }
+        else
+        {
+            *tmp++ = '\0';
+            *file_rtrn = uStringDup(str);
+            str = tmp;
+            tmp = strchr(str, ')');
+            if ((tmp == NULL) || (tmp[1] != '\0'))
+            {
+                uFree(*file_rtrn);
+                uFree(*extra_data);
+                return False;
+            }
+            *tmp++ = '\0';
+            *map_rtrn = uStringDup(str);
+        }
+    }
+    if (*nextop_rtrn == '\0')
+        *str_inout = NULL;
+    else if ((*nextop_rtrn == '|') || (*nextop_rtrn == '+'))
+        *str_inout = next;
+    else
+        return False;
+    return True;
+}
+
+/**
+ * Init memory for include paths.
+ */
+Bool
+XkbInitIncludePath(void)
+{
+    szPath = PATH_CHUNK;
+    includePath = (char **) calloc(szPath, sizeof(char *));
+    if (includePath == NULL)
+        return False;
+    return True;
+}
+
+void
+XkbAddDefaultDirectoriesToPath(void)
+{
+    if (noDefaultPath)
+        return;
+    XkbAddDirectoryToPath(DFLT_XKB_CONFIG_ROOT);
+}
+
+/**
+ * Remove all entries from the global includePath.
+ */
+void
+XkbClearIncludePath(void)
+{
+    register int i;
+
+    if (szPath > 0)
+    {
+        for (i = 0; i < nPathEntries; i++)
+        {
+            if (includePath[i] != NULL)
+            {
+                uFree(includePath[i]);
+                includePath[i] = NULL;
+            }
+        }
+        nPathEntries = 0;
+    }
+    noDefaultPath = True;
+    return;
+}
+
+/**
+ * Add the given path to the global includePath variable.
+ * If dir is NULL, the includePath is emptied.
+ */
+Bool
+XkbAddDirectoryToPath(const char *dir)
+{
+    int len;
+    if ((dir == NULL) || (dir[0] == '\0'))
+    {
+        XkbClearIncludePath();
+        return True;
+    }
+#ifdef __UNIXOS2__
+    dir = (char *) __XOS2RedirRoot(dir);
+#endif
+    len = strlen(dir);
+    if (len + 2 >= PATH_MAX)
+    {                           /* allow for '/' and at least one character */
+        ERROR2("Path entry (%s) too long (maxiumum length is %d)\n",
+               dir, PATH_MAX - 3);
+        return False;
+    }
+    if (nPathEntries >= szPath)
+    {
+        szPath += PATH_CHUNK;
+        includePath = (char **) realloc(includePath, szPath * sizeof(char *));
+        if (includePath == NULL)
+        {
+            WSGO("Allocation failed (includePath)\n");
+            return False;
+        }
+    }
+    includePath[nPathEntries] =
+        (char *) calloc(strlen(dir) + 1, sizeof(char));
+    if (includePath[nPathEntries] == NULL)
+    {
+        WSGO1("Allocation failed (includePath[%d])\n", nPathEntries);
+        return False;
+    }
+    strcpy(includePath[nPathEntries++], dir);
+    return True;
+}
+
+/***====================================================================***/
+
+/**
+ * Return the xkb directory based on the type.
+ * Do not free the memory returned by this function.
+ */
+char *
+XkbDirectoryForInclude(unsigned type)
+{
+    static char buf[32];
+
+    switch (type)
+    {
+    case XkmSemanticsFile:
+        strcpy(buf, "semantics");
+        break;
+    case XkmLayoutFile:
+        strcpy(buf, "layout");
+        break;
+    case XkmKeymapFile:
+        strcpy(buf, "keymap");
+        break;
+    case XkmKeyNamesIndex:
+        strcpy(buf, "keycodes");
+        break;
+    case XkmTypesIndex:
+        strcpy(buf, "types");
+        break;
+    case XkmSymbolsIndex:
+        strcpy(buf, "symbols");
+        break;
+    case XkmCompatMapIndex:
+        strcpy(buf, "compat");
+        break;
+    case XkmGeometryFile:
+    case XkmGeometryIndex:
+        strcpy(buf, "geometry");
+        break;
+    default:
+        strcpy(buf, "");
+        break;
+    }
+    return buf;
+}
+
+/***====================================================================***/
+
+typedef struct _FileCacheEntry
+{
+    char *name;
+    unsigned type;
+    char *path;
+    void *data;
+    struct _FileCacheEntry *next;
+} FileCacheEntry;
+static FileCacheEntry *fileCache;
+
+/**
+ * Add the file with the given name to the internal cache to avoid opening and
+ * parsing the file multiple times. If a cache entry for the same name + type
+ * is already present, the entry is overwritten and the data belonging to the
+ * previous entry is returned.
+ *
+ * @parameter name The name of the file (e.g. evdev).
+ * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...)
+ * @parameter path The full path to the file.
+ * @parameter data Already parsed data.
+ *
+ * @return The data from the overwritten file or NULL.
+ */
+void *
+XkbAddFileToCache(char *name, unsigned type, char *path, void *data)
+{
+    FileCacheEntry *entry;
+
+    for (entry = fileCache; entry != NULL; entry = entry->next)
+    {
+        if ((type == entry->type) && (uStringEqual(name, entry->name)))
+        {
+            void *old = entry->data;
+            WSGO2("Replacing file cache entry (%s/%d)\n", name, type);
+            entry->path = path;
+            entry->data = data;
+            return old;
+        }
+    }
+    entry = uTypedAlloc(FileCacheEntry);
+    if (entry != NULL)
+    {
+        entry->name = name;
+        entry->type = type;
+        entry->path = path;
+        entry->data = data;
+        entry->next = fileCache;
+        fileCache = entry;
+    }
+    return NULL;
+}
+
+/**
+ * Search for the given name + type in the cache.
+ *
+ * @parameter name The name of the file (e.g. evdev).
+ * @parameter type Type of the file (XkbTypesIdx, ... or XkbSemanticsFile, ...)
+ * @parameter pathRtrn Set to the full path of the given entry.
+ *
+ * @return the data from the cache entry or NULL if no matching entry was found.
+ */
+void *
+XkbFindFileInCache(char *name, unsigned type, char **pathRtrn)
+{
+    FileCacheEntry *entry;
+
+    for (entry = fileCache; entry != NULL; entry = entry->next)
+    {
+        if ((type == entry->type) && (uStringEqual(name, entry->name)))
+        {
+            *pathRtrn = entry->path;
+            return entry->data;
+        }
+    }
+    return NULL;
+}
+
+/***====================================================================***/
+
+/**
+ * Search for the given file name in the include directories.
+ *
+ * @param type one of XkbTypesIndex, XkbCompatMapIndex, ..., or
+ * XkbSemanticsFile, XkmKeymapFile, ...
+ * @param pathReturn is set to the full path of the file if found.
+ *
+ * @return an FD to the file or NULL. If NULL is returned, the value of
+ * pathRtrn is undefined.
+ */
+FILE *
+XkbFindFileInPath(char *name, unsigned type, char **pathRtrn)
+{
+    register int i;
+    FILE *file = NULL;
+    int nameLen, typeLen, pathLen;
+    char buf[PATH_MAX], *typeDir;
+
+    typeDir = XkbDirectoryForInclude(type);
+    nameLen = strlen(name);
+    typeLen = strlen(typeDir);
+    for (i = 0; i < nPathEntries; i++)
+    {
+        pathLen = strlen(includePath[i]);
+        if (typeLen < 1)
+            continue;
+
+        if ((nameLen + typeLen + pathLen + 2) >= PATH_MAX)
+        {
+            ERROR3("File name (%s/%s/%s) too long\n", includePath[i],
+                   typeDir, name);
+            ACTION("Ignored\n");
+            continue;
+        }
+        snprintf(buf, sizeof(buf), "%s/%s/%s", includePath[i], typeDir, name);
+        file = fopen(buf, "r");
+        if (file != NULL)
+            break;
+    }
+
+    if ((file != NULL) && (pathRtrn != NULL))
+    {
+        *pathRtrn = (char *) calloc(strlen(buf) + 1, sizeof(char));
+        if (*pathRtrn != NULL)
+            strcpy(*pathRtrn, buf);
+    }
+    return file;
+}
diff --git a/src/xkbcomp/xkbpath.h b/src/xkbcomp/xkbpath.h
new file mode 100644 (file)
index 0000000..66c3ab7
--- /dev/null
@@ -0,0 +1,65 @@
+/************************************************************
+ 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.
+
+ ********************************************************/
+
+#ifndef _XKBPATH_H_
+#define _XKBPATH_H_ 1
+
+extern Bool XkbInitIncludePath(void);
+
+extern void XkbClearIncludePath(void);
+
+extern void XkbAddDefaultDirectoriesToPath(void);
+
+extern Bool XkbAddDirectoryToPath(const char *  /* dir */
+    );
+
+extern char *XkbDirectoryForInclude(unsigned    /* type */
+    );
+
+extern FILE *XkbFindFileInPath(char * /* name */ ,
+                               unsigned /* type */ ,
+                               char **  /* pathRtrn */
+    );
+
+extern void *XkbAddFileToCache(char * /* name */ ,
+                               unsigned /* type */ ,
+                               char * /* path */ ,
+                               void *   /* data */
+    );
+
+extern void *XkbFindFileInCache(char * /* name */ ,
+                                unsigned /* type */ ,
+                                char ** /* pathRtrn */
+    );
+
+extern Bool XkbParseIncludeMap(char ** /* str_inout */ ,
+                               char ** /* file_rtrn */ ,
+                               char ** /* map_rtrn */ ,
+                               char * /* nextop_rtrn */ ,
+                               char **  /* extra_data */
+    );
+
+#endif /* _XKBPATH_H_ */
diff --git a/src/xkbcomp/xkbscan.c b/src/xkbcomp/xkbscan.c
new file mode 100644 (file)
index 0000000..31cafd4
--- /dev/null
@@ -0,0 +1,723 @@
+/************************************************************
+ 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 <stdio.h>
+#include <ctype.h>
+#include <X11/Xos.h>
+#include <X11/Xlib.h>
+#include <X11/XKBlib.h>
+
+#include "tokens.h"
+#define        DEBUG_VAR       scanDebug
+#include "utils.h"
+#include "parseutils.h"
+
+unsigned int scanDebug;
+
+FILE *yyin = NULL;
+
+static char scanFileBuf[1024] = {0};
+char *scanFile = scanFileBuf;
+int lineNum = 0;
+
+int scanInt;
+
+char *scanStr = NULL;
+static int scanStrLine = 0;
+
+#define        BUFSIZE 512
+static int nInBuf = 0;
+static char buf[BUFSIZE];
+
+#ifdef DEBUG
+static char *
+tokText(int tok)
+{
+    static char buf[32];
+
+    switch (tok)
+    {
+    case END_OF_FILE:
+        snprintf(buf, sizeof(buf), "END_OF_FILE");
+        break;
+    case ERROR_TOK:
+        snprintf(buf, sizeof(buf), "ERROR");
+        break;
+
+    case XKB_KEYMAP:
+        snprintf(buf, sizeof(buf), "XKB_KEYMAP");
+        break;
+    case XKB_KEYCODES:
+        snprintf(buf, sizeof(buf), "XKB_KEYCODES");
+        break;
+    case XKB_TYPES:
+        snprintf(buf, sizeof(buf), "XKB_TYPES");
+        break;
+    case XKB_SYMBOLS:
+        snprintf(buf, sizeof(buf), "XKB_SYMBOLS");
+        break;
+    case XKB_COMPATMAP:
+        snprintf(buf, sizeof(buf), "XKB_COMPATMAP");
+        break;
+    case XKB_GEOMETRY:
+        snprintf(buf, sizeof(buf), "XKB_GEOMETRY");
+        break;
+    case XKB_SEMANTICS:
+        snprintf(buf, sizeof(buf), "XKB_SEMANTICS");
+        break;
+    case XKB_LAYOUT:
+        snprintf(buf, sizeof(buf), "XKB_LAYOUT");
+        break;
+
+    case INCLUDE:
+        snprintf(buf, sizeof(buf), "INCLUDE");
+        break;
+    case OVERRIDE:
+        snprintf(buf, sizeof(buf), "OVERRIDE");
+        break;
+    case AUGMENT:
+        snprintf(buf, sizeof(buf), "AUGMENT");
+        break;
+    case REPLACE:
+        snprintf(buf, sizeof(buf), "REPLACE");
+        break;
+    case ALTERNATE:
+        snprintf(buf, sizeof(buf), "ALTERNATE");
+        break;
+
+    case VIRTUAL_MODS:
+        snprintf(buf, sizeof(buf), "VIRTUAL_MODS");
+        break;
+    case TYPE:
+        snprintf(buf, sizeof(buf), "TYPE");
+        break;
+    case INTERPRET:
+        snprintf(buf, sizeof(buf), "INTERPRET");
+        break;
+    case ACTION_TOK:
+        snprintf(buf, sizeof(buf), "ACTION");
+        break;
+    case KEY:
+        snprintf(buf, sizeof(buf), "KEY");
+        break;
+    case ALIAS:
+        snprintf(buf, sizeof(buf), "ALIAS");
+        break;
+    case GROUP:
+        snprintf(buf, sizeof(buf), "GROUP");
+        break;
+    case MODIFIER_MAP:
+        snprintf(buf, sizeof(buf), "MODIFIER_MAP");
+        break;
+    case INDICATOR:
+        snprintf(buf, sizeof(buf), "INDICATOR");
+        break;
+    case SHAPE:
+        snprintf(buf, sizeof(buf), "SHAPE");
+        break;
+    case KEYS:
+        snprintf(buf, sizeof(buf), "KEYS");
+        break;
+    case ROW:
+        snprintf(buf, sizeof(buf), "ROW");
+        break;
+    case SECTION:
+        snprintf(buf, sizeof(buf), "SECTION");
+        break;
+    case OVERLAY:
+        snprintf(buf, sizeof(buf), "OVERLAY");
+        break;
+    case TEXT:
+        snprintf(buf, sizeof(buf), "TEXT");
+        break;
+    case OUTLINE:
+        snprintf(buf, sizeof(buf), "OUTLINE");
+        break;
+    case SOLID:
+        snprintf(buf, sizeof(buf), "SOLID");
+        break;
+    case LOGO:
+        snprintf(buf, sizeof(buf), "LOGO");
+        break;
+    case VIRTUAL:
+        snprintf(buf, sizeof(buf), "VIRTUAL");
+        break;
+
+    case EQUALS:
+        snprintf(buf, sizeof(buf), "EQUALS");
+        break;
+    case PLUS:
+        snprintf(buf, sizeof(buf), "PLUS");
+        break;
+    case MINUS:
+        snprintf(buf, sizeof(buf), "MINUS");
+        break;
+    case DIVIDE:
+        snprintf(buf, sizeof(buf), "DIVIDE");
+        break;
+    case TIMES:
+        snprintf(buf, sizeof(buf), "TIMES");
+        break;
+    case OBRACE:
+        snprintf(buf, sizeof(buf), "OBRACE");
+        break;
+    case CBRACE:
+        snprintf(buf, sizeof(buf), "CBRACE");
+        break;
+    case OPAREN:
+        snprintf(buf, sizeof(buf), "OPAREN");
+        break;
+    case CPAREN:
+        snprintf(buf, sizeof(buf), "CPAREN");
+        break;
+    case OBRACKET:
+        snprintf(buf, sizeof(buf), "OBRACKET");
+        break;
+    case CBRACKET:
+        snprintf(buf, sizeof(buf), "CBRACKET");
+        break;
+    case DOT:
+        snprintf(buf, sizeof(buf), "DOT");
+        break;
+    case COMMA:
+        snprintf(buf, sizeof(buf), "COMMA");
+        break;
+    case SEMI:
+        snprintf(buf, sizeof(buf), "SEMI");
+        break;
+    case EXCLAM:
+        snprintf(buf, sizeof(buf), "EXCLAM");
+        break;
+    case INVERT:
+        snprintf(buf, sizeof(buf), "INVERT");
+        break;
+
+    case STRING:
+        snprintf(buf, sizeof(buf), "STRING (%s)", scanStr);
+        break;
+    case INTEGER:
+        snprintf(buf, sizeof(buf), "INTEGER (0x%x)", scanInt);
+        break;
+    case FLOAT:
+        snprintf(buf, sizeof(buf), "FLOAT (%d.%d)",
+                scanInt / XkbGeomPtsPerMM, scanInt % XkbGeomPtsPerMM);
+        break;
+    case IDENT:
+        snprintf(buf, sizeof(buf), "IDENT (%s)", scanStr);
+        break;
+    case KEYNAME:
+        snprintf(buf, sizeof(buf), "KEYNAME (%s)", scanStr);
+        break;
+
+    case PARTIAL:
+        snprintf(buf, sizeof(buf), "PARTIAL");
+        break;
+    case DEFAULT:
+        snprintf(buf, sizeof(buf), "DEFAULT");
+        break;
+    case HIDDEN:
+        snprintf(buf, sizeof(buf), "HIDDEN");
+        break;
+
+    case ALPHANUMERIC_KEYS:
+        snprintf(buf, sizeof(buf), "ALPHANUMERIC_KEYS");
+        break;
+    case MODIFIER_KEYS:
+        snprintf(buf, sizeof(buf), "MODIFIER_KEYS");
+        break;
+    case KEYPAD_KEYS:
+        snprintf(buf, sizeof(buf), "KEYPAD_KEYS");
+        break;
+    case FUNCTION_KEYS:
+        snprintf(buf, sizeof(buf), "FUNCTION_KEYS");
+        break;
+    case ALTERNATE_GROUP:
+        snprintf(buf, sizeof(buf), "ALTERNATE_GROUP");
+        break;
+
+    default:
+        snprintf(buf, sizeof(buf), "UNKNOWN");
+        break;
+    }
+    return buf;
+}
+#endif
+
+int
+setScanState(char *file, int line)
+{
+    if (file != NULL)
+        strncpy(scanFile, file, 1024);
+    if (line >= 0)
+        lineNum = line;
+    return 1;
+}
+
+static int
+yyGetString(void)
+{
+    int ch;
+
+    nInBuf = 0;
+    while (((ch = getc(yyin)) != EOF) && (ch != '"'))
+    {
+        if (ch == '\\')
+        {
+            if ((ch = getc(yyin)) != EOF)
+            {
+                if (ch == 'n')
+                    ch = '\n';
+                else if (ch == 't')
+                    ch = '\t';
+                else if (ch == 'v')
+                    ch = '\v';
+                else if (ch == 'b')
+                    ch = '\b';
+                else if (ch == 'r')
+                    ch = '\r';
+                else if (ch == 'f')
+                    ch = '\f';
+                else if (ch == 'e')
+                    ch = '\033';
+                else if (ch == '0')
+                {
+                    int tmp, stop;
+                    ch = stop = 0;
+                    if (((tmp = getc(yyin)) != EOF) && (isdigit(tmp))
+                        && (tmp != '8') && (tmp != '9'))
+                    {
+                        ch = (ch * 8) + (tmp - '0');
+                    }
+                    else
+                    {
+                        stop = 1;
+                        ungetc(tmp, yyin);
+                    }
+                    if (!stop)
+                    {
+                        if (((tmp = getc(yyin)) != EOF)
+                            && (isdigit(tmp)) && (tmp != '8') && (tmp != '9'))
+                        {
+                            ch = (ch * 8) + (tmp - '0');
+                        }
+                        else
+                        {
+                            stop = 1;
+                            ungetc(tmp, yyin);
+                        }
+                    }
+                    if (!stop)
+                    {
+                        if (((tmp = getc(yyin)) != EOF)
+                            && (isdigit(tmp)) && (tmp != '8') && (tmp != '9'))
+                        {
+                            ch = (ch * 8) + (tmp - '0');
+                        }
+                        else
+                        {
+                            stop = 1;
+                            ungetc(tmp, yyin);
+                        }
+                    }
+                }
+            }
+            else
+                return ERROR_TOK;
+        }
+        if (nInBuf < BUFSIZE - 1)
+            buf[nInBuf++] = ch;
+    }
+    if (ch == '"')
+    {
+        buf[nInBuf++] = '\0';
+        if (scanStr)
+            uFree(scanStr);
+        scanStr = (char *) uStringDup(buf);
+        scanStrLine = lineNum;
+        return STRING;
+    }
+    return ERROR_TOK;
+}
+
+static int
+yyGetKeyName(void)
+{
+    int ch;
+
+    nInBuf = 0;
+    while (((ch = getc(yyin)) != EOF) && (ch != '>'))
+    {
+        if (ch == '\\')
+        {
+            if ((ch = getc(yyin)) != EOF)
+            {
+                if (ch == 'n')
+                    ch = '\n';
+                else if (ch == 't')
+                    ch = '\t';
+                else if (ch == 'v')
+                    ch = '\v';
+                else if (ch == 'b')
+                    ch = '\b';
+                else if (ch == 'r')
+                    ch = '\r';
+                else if (ch == 'f')
+                    ch = '\f';
+                else if (ch == 'e')
+                    ch = '\033';
+                else if (ch == '0')
+                {
+                    int tmp, stop;
+                    ch = stop = 0;
+                    if (((tmp = getc(yyin)) != EOF) && (isdigit(tmp))
+                        && (tmp != '8') && (tmp != '9'))
+                    {
+                        ch = (ch * 8) + (tmp - '0');
+                    }
+                    else
+                    {
+                        stop = 1;
+                        ungetc(tmp, yyin);
+                    }
+                    if ((!stop) && ((tmp = getc(yyin)) != EOF)
+                        && (isdigit(tmp)) && (tmp != '8') && (tmp != '9'))
+                    {
+                        ch = (ch * 8) + (tmp - '0');
+                    }
+                    else
+                    {
+                        stop = 1;
+                        ungetc(tmp, yyin);
+                    }
+                    if ((!stop) && ((tmp = getc(yyin)) != EOF)
+                        && (isdigit(tmp)) && (tmp != '8') && (tmp != '9'))
+                    {
+                        ch = (ch * 8) + (tmp - '0');
+                    }
+                    else
+                    {
+                        stop = 1;
+                        ungetc(tmp, yyin);
+                    }
+                }
+            }
+            else
+                return ERROR_TOK;
+        }
+
+        if (nInBuf < BUFSIZE - 1)
+            buf[nInBuf++] = ch;
+    }
+    if ((ch == '>') && (nInBuf < 5))
+    {
+        buf[nInBuf++] = '\0';
+        if (scanStr)
+            uFree(scanStr);
+        scanStr = (char *) uStringDup(buf);
+        scanStrLine = lineNum;
+        return KEYNAME;
+    }
+    return ERROR_TOK;
+}
+
+static struct _Keyword
+{
+    const char *keyword;
+    int token;
+} keywords[] =
+{
+    {
+    "xkb_keymap", XKB_KEYMAP},
+    {
+    "xkb_keycodes", XKB_KEYCODES},
+    {
+    "xkb_types", XKB_TYPES},
+    {
+    "xkb_symbols", XKB_SYMBOLS},
+    {
+    "xkb_compat", XKB_COMPATMAP},
+    {
+    "xkb_compat_map", XKB_COMPATMAP},
+    {
+    "xkb_compatibility", XKB_COMPATMAP},
+    {
+    "xkb_compatibility_map", XKB_COMPATMAP},
+    {
+    "xkb_geometry", XKB_GEOMETRY},
+    {
+    "xkb_semantics", XKB_SEMANTICS},
+    {
+    "xkb_layout", XKB_LAYOUT},
+    {
+    "include", INCLUDE},
+    {
+    "override", OVERRIDE},
+    {
+    "augment", AUGMENT},
+    {
+    "replace", REPLACE},
+    {
+    "alternate", ALTERNATE},
+    {
+    "partial", PARTIAL},
+    {
+    "default", DEFAULT},
+    {
+    "hidden", HIDDEN},
+    {
+    "virtual_modifiers", VIRTUAL_MODS},
+    {
+    "type", TYPE},
+    {
+    "interpret", INTERPRET},
+    {
+    "action", ACTION_TOK},
+    {
+    "key", KEY},
+    {
+    "alias", ALIAS},
+    {
+    "group", GROUP},
+    {
+    "modmap", MODIFIER_MAP},
+    {
+    "mod_map", MODIFIER_MAP},
+    {
+    "modifier_map", MODIFIER_MAP},
+    {
+    "indicator", INDICATOR},
+    {
+    "shape", SHAPE},
+    {
+    "row", ROW},
+    {
+    "keys", KEYS},
+    {
+    "section", SECTION},
+    {
+    "overlay", OVERLAY},
+    {
+    "text", TEXT},
+    {
+    "outline", OUTLINE},
+    {
+    "solid", SOLID},
+    {
+    "logo", LOGO},
+    {
+    "virtual", VIRTUAL},
+    {
+    "alphanumeric_keys", ALPHANUMERIC_KEYS},
+    {
+    "modifier_keys", MODIFIER_KEYS},
+    {
+    "keypad_keys", KEYPAD_KEYS},
+    {
+    "function_keys", FUNCTION_KEYS},
+    {
+    "alternate_group", ALTERNATE_GROUP}
+};
+static int numKeywords = sizeof(keywords) / sizeof(struct _Keyword);
+
+static int
+yyGetIdent(int first)
+{
+    int ch, i, found;
+    int rtrn = IDENT;
+
+    buf[0] = first;
+    nInBuf = 1;
+    while (((ch = getc(yyin)) != EOF) && (isalnum(ch) || (ch == '_')))
+    {
+        if (nInBuf < BUFSIZE - 1)
+            buf[nInBuf++] = ch;
+    }
+    buf[nInBuf++] = '\0';
+    found = 0;
+
+    for (i = 0; (!found) && (i < numKeywords); i++)
+    {
+        if (uStrCaseCmp(buf, keywords[i].keyword) == 0)
+        {
+            rtrn = keywords[i].token;
+            found = 1;
+        }
+    }
+    if (!found)
+    {
+        if (scanStr)
+            uFree(scanStr);
+        scanStr = (char *) uStringDup(buf);
+        scanStrLine = lineNum;
+        rtrn = IDENT;
+    }
+
+    if ((ch != EOF) && (!isspace(ch)))
+        ungetc(ch, yyin);
+    else if (ch == '\n')
+        lineNum++;
+
+    return rtrn;
+}
+
+static int
+yyGetNumber(int ch)
+{
+    int isFloat = 0;
+
+    buf[0] = ch;
+    nInBuf = 1;
+    while (((ch = getc(yyin)) != EOF)
+           && (isxdigit(ch) || ((nInBuf == 1) && (ch == 'x'))))
+    {
+        buf[nInBuf++] = ch;
+    }
+    if (ch == '.')
+    {
+        isFloat = 1;
+        buf[nInBuf++] = ch;
+        while (((ch = getc(yyin)) != EOF) && (isxdigit(ch)))
+        {
+            buf[nInBuf++] = ch;
+        }
+    }
+    buf[nInBuf++] = '\0';
+    if ((ch != EOF) && (!isspace(ch)))
+        ungetc(ch, yyin);
+
+    if (isFloat)
+    {
+        float tmp;
+        if (sscanf(buf, "%g", &tmp) == 1)
+        {
+            scanInt = tmp * XkbGeomPtsPerMM;
+            return FLOAT;
+        }
+    }
+    else if (sscanf(buf, "%i", &scanInt) == 1)
+        return INTEGER;
+    fprintf(stderr, "Malformed number %s\n", buf);
+    return ERROR_TOK;
+}
+
+int
+yylex(void)
+{
+    int ch;
+    int rtrn;
+
+    do
+    {
+        ch = getc(yyin);
+        if (ch == '\n')
+        {
+            lineNum++;
+        }
+        else if (ch == '#')
+        {                       /* handle shell style '#' comments */
+            do
+            {
+                ch = getc(yyin);
+            }
+            while ((ch != '\n') && (ch != EOF));
+            lineNum++;
+        }
+        else if (ch == '/')
+        {                       /* handle C++ style double-/ comments */
+            int newch = getc(yyin);
+            if (newch == '/')
+            {
+                do
+                {
+                    ch = getc(yyin);
+                }
+                while ((ch != '\n') && (ch != EOF));
+                lineNum++;
+            }
+            else if (newch != EOF)
+            {
+                ungetc(newch, yyin);
+            }
+        }
+    }
+    while ((ch != EOF) && (isspace(ch)));
+    if (ch == '=')
+        rtrn = EQUALS;
+    else if (ch == '+')
+        rtrn = PLUS;
+    else if (ch == '-')
+        rtrn = MINUS;
+    else if (ch == '/')
+        rtrn = DIVIDE;
+    else if (ch == '*')
+        rtrn = TIMES;
+    else if (ch == '{')
+        rtrn = OBRACE;
+    else if (ch == '}')
+        rtrn = CBRACE;
+    else if (ch == '(')
+        rtrn = OPAREN;
+    else if (ch == ')')
+        rtrn = CPAREN;
+    else if (ch == '[')
+        rtrn = OBRACKET;
+    else if (ch == ']')
+        rtrn = CBRACKET;
+    else if (ch == '.')
+        rtrn = DOT;
+    else if (ch == ',')
+        rtrn = COMMA;
+    else if (ch == ';')
+        rtrn = SEMI;
+    else if (ch == '!')
+        rtrn = EXCLAM;
+    else if (ch == '~')
+        rtrn = INVERT;
+    else if (ch == '"')
+        rtrn = yyGetString();
+    else if (ch == '<')
+        rtrn = yyGetKeyName();
+    else if (isalpha(ch) || (ch == '_'))
+        rtrn = yyGetIdent(ch);
+    else if (isdigit(ch))
+        rtrn = yyGetNumber(ch);
+    else if (ch == EOF)
+        rtrn = END_OF_FILE;
+    else
+    {
+#ifdef DEBUG
+        if (debugFlags)
+            fprintf(stderr,
+                    "Unexpected character %c (%d) in input stream\n", ch, ch);
+#endif
+        rtrn = ERROR_TOK;
+    }
+#ifdef DEBUG
+    if (debugFlags & 0x2)
+        fprintf(stderr, "scan: %s\n", tokText(rtrn));
+#endif
+    return rtrn;
+}