/* $TOG: symbols.c /main/15 1997/06/14 06:35:50 kaleb $ */
/************************************************************
 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.

 ********************************************************/
/* $XFree86: xc/programs/xkbcomp/symbols.c,v 3.5.2.1 1997/06/22 10:32:40 dawes Exp $ */

#include "xkbcomp.h"
#include "tokens.h"
#include "expr.h"

#include <X11/keysym.h>
#include <X11/Xutil.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;
    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;

static void
#if NeedFunctionPrototypes
InitKeyInfo(KeyInfo *info)
#else
InitKeyInfo(info)
    KeyInfo *	info;
#endif
{
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;
}

static void
#if NeedFunctionPrototypes
FreeKeyInfo(KeyInfo *info)
#else
FreeKeyInfo(info)
    KeyInfo *	info;
#endif
{
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;
    return;
}

static Bool
#if NeedFunctionPrototypes
CopyKeyInfo(KeyInfo *old,KeyInfo *new,Bool clearOld)
#else
CopyKeyInfo(old,new,clearOld)
    KeyInfo *	old;
    KeyInfo *	new;
    Bool	clearOld;
#endif
{
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;
    int				errorCount;
    unsigned			fileID;
    unsigned			merge;
    unsigned			groupInfo;
    unsigned			szKeys;
    unsigned			nKeys;
    KeyInfo *			keys;
    KeyInfo			dflt;
    VModInfo			vmods;
    ActionInfo *		action;
    Atom			groupNames[XkbNumKbdGroups];

    ModMapEntry *		modMap;
    AliasInfo *			aliases;
} SymbolsInfo;

static void
#if NeedFunctionPrototypes
InitSymbolsInfo(SymbolsInfo *info,XkbDescPtr xkb)
#else
InitSymbolsInfo(info,xkb)
    SymbolsInfo *	info;
    XkbDescPtr		xkb;
#endif
{
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->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
#if NeedFunctionPrototypes
FreeSymbolsInfo(SymbolsInfo *info)
#else
FreeSymbolsInfo(info)
    SymbolsInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
ResizeKeyGroup(	KeyInfo *	key,
		unsigned	group,
		unsigned 	atLeastSize,
		Bool 		forceActions)
#else
ResizeKeyGroup(key,group,atLeastSize,forceActions)
    KeyInfo *		key;
    unsigned		group;
    unsigned		atLeastSize;
    Bool		forceActions;
#endif
{
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
#if NeedFunctionPrototypes
MergeKeyGroups(	SymbolsInfo *	info,
		KeyInfo *	into,
		KeyInfo *	from,
		unsigned	group)
#else
MergeKeyGroups(info,into,from,group)
    SymbolsInfo *	info;
    KeyInfo *		into;
    KeyInfo *		from;
    unsigned		group;
#endif
{
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]);
    into->numLevels[group]= resultWidth;
    into->syms[group]= resultSyms;
    from->syms[group]= NULL;
    into->symsDefined|= (1<<group);
    from->symsDefined&= ~(1<<group);
    return True;
}

static Bool
#if NeedFunctionPrototypes
MergeKeys(SymbolsInfo *info,KeyInfo *into,KeyInfo *from)
#else
MergeKeys(info,into,from)
    SymbolsInfo *	info;
    KeyInfo *		into;
    KeyInfo *		from;
#endif
{
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
#if NeedFunctionPrototypes
AddKeySymbols(SymbolsInfo *info,KeyInfo *key)
#else
AddKeySymbols(info,key)
    SymbolsInfo *	  info;
    KeyInfo *		  key;
#endif
{
register int i;

    for (i=0;i<info->nKeys;i++) {
	if (info->keys[i].name==key->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
#if NeedFunctionPrototypes
AddModMapEntry(SymbolsInfo *info,ModMapEntry *new)
#else
AddModMapEntry(info,new)
    SymbolsInfo *	info;
    ModMapEntry *	new;
#endif
{
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
#if NeedFunctionPrototypes
MergeIncludedSymbols(SymbolsInfo *into,SymbolsInfo *from,unsigned merge)
#else
MergeIncludedSymbols(into,from,merge)
    SymbolsInfo *	into;
    SymbolsInfo *	from;
    unsigned		merge;
#endif
{
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))
	    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)(
#if NeedFunctionPrototypes
	XkbFile *	/* rtrn */,
	XkbDescPtr	/* xkb */,
	unsigned	/* merge */,
	SymbolsInfo *	/* included */
#endif
);

static Bool
#if NeedFunctionPrototypes
HandleIncludeSymbols(	IncludeStmt *	stmt,
			XkbDescPtr	xkb,
			SymbolsInfo *	info,
			FileHandler	hndlr)
#else
HandleIncludeSymbols(stmt,xkb,info,hndlr)
    IncludeStmt	*	  stmt;
    XkbDescPtr		  xkb;
    SymbolsInfo *	  info;
    void		(*hndlr)();
#endif
{
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;
	(*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);
		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;
		(*hndlr)(rtrn,xkb,MergeOverride,&next_incl);
		MergeIncludedSymbols(&included,&next_incl,op);
		FreeSymbolsInfo(&next_incl);
	    }
	    else {
		info->errorCount+= 10;
		return False;
	    }
	}
    }
    if (haveSelf)
	*info= included;
    else {
	MergeIncludedSymbols(info,&included,newMerge);
	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
#if NeedFunctionPrototypes
GetGroupIndex(	KeyInfo *	key,
		ExprDef *	arrayNdx,
		unsigned	what,
		unsigned *	ndx_rtrn)
#else
GetGroupIndex(key,arrayNdx,what,ndx_rtrn)
    KeyInfo *	key;
    ExprDef *	arrayNdx;
    unsigned	what;
    unsigned *	ndx_rtrn;
#endif
{
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
#if NeedFunctionPrototypes
AddSymbolsToKey(	KeyInfo *	key,
			XkbDescPtr	xkb,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SymbolsInfo *	info)
#else
AddSymbolsToKey(key,xkb,field,arrayNdx,value,info)
    KeyInfo *		key;
    XkbDescPtr		xkb;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SymbolsInfo *	info;
#endif
{
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));
    uFree(value->value.list.syms);
    value->value.list.syms= NULL;
    for (i=key->numLevels[ndx]-1;(i>=0)&&(key->syms[ndx][i]==NoSymbol);i--) {
	key->numLevels[ndx]--;
    }
    return True;
}

static Bool
#if NeedFunctionPrototypes
AddActionsToKey(	KeyInfo *	key,
			XkbDescPtr	xkb,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SymbolsInfo *	info)
#else
AddActionsToKey(key,xkb,field,arrayNdx,value,info)
    KeyInfo *		key;
    XkbDescPtr		xkb;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SymbolsInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetAllowNone(KeyInfo *key,ExprDef *arrayNdx,ExprDef *value)
#else
SetAllowNone(key,arrayNdx,value)
    KeyInfo *	key;
    ExprDef *	arrayNdx;
    ExprDef *	value;
#endif
{
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
#if NeedFunctionPrototypes
SetSymbolsField(	KeyInfo *	key,
			XkbDescPtr	xkb,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SymbolsInfo *	info)
#else
SetSymbolsField(key,xkb,field,arrayNdx,value,info)
    KeyInfo *		key;
    XkbDescPtr		xkb;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SymbolsInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetGroupName(SymbolsInfo *info,ExprDef *arrayNdx,ExprDef *value)
#else
SetGroupName(info,arrayNdx,value)
    SymbolsInfo *	info;
    ExprDef *		arrayNdx;
    ExprDef *		value;
#endif
{
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]= XkbInternAtom(NULL,name.str,False);
    return True;
}

static int
#if NeedFunctionPrototypes
HandleSymbolsVar(VarDef *stmt,XkbDescPtr xkb,SymbolsInfo *info)
#else
HandleSymbolsVar(stmt,xkb,info)
    VarDef *		stmt;
    XkbDescPtr		xkb;
    SymbolsInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleSymbolsBody(	VarDef *	def,
			XkbDescPtr	xkb,
			KeyInfo *	key,
			SymbolsInfo *	info)
#else
HandleSymbolsBody(def,xkb,key,info)
    VarDef *		def;
    XkbDescPtr		xkb;
    KeyInfo *		key;
    SymbolsInfo *	info;
#endif
{
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 int
#if NeedFunctionPrototypes
HandleSymbolsDef(	SymbolsDef *	stmt,
			XkbDescPtr 	xkb,
			unsigned 	merge,
			SymbolsInfo *	info)
#else
HandleSymbolsDef(stmt,xkb,merge,info)
    SymbolsDef *	stmt;
    XkbDescPtr		xkb;
    unsigned 		merge;
    SymbolsInfo *	info;
#endif
{
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 (!AddKeySymbols(info,&key)) {
	info->errorCount++;
	return False;
    }
    return True;
}

static Bool
#if NeedFunctionPrototypes
HandleModMapDef(	ModMapDef *	def,
			XkbDescPtr	xkb,
			unsigned	merge,
			SymbolsInfo *	info)
#else
HandleModMapDef(def,xkb,merge,info)
    ModMapDef *		def;
    XkbDescPtr		xkb;
    unsigned		merge;
    SymbolsInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleSymbolsFile(	XkbFile	*	file,
			XkbDescPtr	xkb,
			unsigned	merge,
			SymbolsInfo *	info)
#else
HandleSymbolsFile(file,xkb,merge,info)
    XkbFile	*	file;
    XkbDescPtr	 	xkb;
    unsigned		merge;
    SymbolsInfo	*	info;
#endif
{
ParseCommon	*stmt;

    info->name= 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
#if NeedFunctionPrototypes
FindKeyForSymbol(XkbDescPtr xkb,KeySym sym,unsigned int *kc_rtrn)
#else
FindKeyForSymbol(xkb,sym,kc_rtrn)
    XkbDescPtr		xkb;
    KeySym		sym;
    unsigned int *	kc_rtrn;
#endif
{
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;
}

static Bool
#if NeedFunctionPrototypes
FindNamedType(XkbDescPtr xkb,Atom name,unsigned *type_rtrn)
#else
FindNamedType(xkb,name,type_rtrn)
    XkbDescPtr		xkb;
    Atom		name;
    unsigned *		type_rtrn;
#endif
{
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
#if NeedFunctionPrototypes
FindAutomaticType(int width,KeySym *syms,Atom *typeNameRtrn)
#else
FindAutomaticType(width,syms,typeNameRtrn)
    int		width;
    KeySym *	syms;
    Atom *	typeNameRtrn;
#endif
{
    if ((width==1)||(width==0))
	 *typeNameRtrn= XkbInternAtom(NULL,"ONE_LEVEL",False);
    else if ( syms && XkbKSIsLower(syms[0]) && XkbKSIsUpper(syms[1]) )
	 *typeNameRtrn= XkbInternAtom(NULL,"ALPHABETIC",False);
    else if ( syms && (XkbKSIsKeypad(syms[0]) || XkbKSIsKeypad(syms[1])) ) 
	 *typeNameRtrn= XkbInternAtom(NULL,"KEYPAD",False);
    else *typeNameRtrn= XkbInternAtom(NULL,"TWO_LEVEL",False);
    return ((width>=0)&&(width<=2));
}

Bool
#if NeedFunctionPrototypes
CopySymbolsDef(XkbFileInfo *result,KeyInfo *key,int start_from)
#else
CopySymbolsDef(result,key,start_from)
    XkbFileInfo *	result;
    KeyInfo *		key;
    int			start_from;
#endif
{
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);
    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;
	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= True;
	    }
	    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)
		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;
	}
	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;
    }

    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++) {
	xkb->map->key_sym_map[kc].kt_index[i]= types[i];
	if (key->syms[i]!=NULL) {
	    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;
    }
    CopySymbolsDef(result,key,kc+1);
    return True;
}

static Bool
#if NeedFunctionPrototypes
CopyModMapDef(XkbFileInfo *result,ModMapEntry *entry)
#else
CopyModMapDef(result,entry)
    XkbFileInfo *	result;
    ModMapEntry *	entry;
#endif
{
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;
}

Bool
#if NeedFunctionPrototypes
CompileSymbols(XkbFile *file,XkbFileInfo *result,unsigned merge)
#else
CompileSymbols(file,result,merge)
    XkbFile *		file;
    XkbFileInfo *	result;
    unsigned	 	merge;
#endif
{
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.errorCount==0) {
	KeyInfo *key;
	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;
	}
	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];
	}
	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;
}
