/* $XConsortium: geometry.c /main/7 1996/12/27 21:16:35 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.

 ********************************************************/

#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 *
#if NeedFunctionPrototypes
ddText(Display *dpy,DoodadInfo *di)
#else
ddText(dpy,di)
    Display *		dpy;
    DoodadInfo *	di;
#endif
{
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
#if NeedFunctionPrototypes
InitPropertyInfo(PropertyInfo *pi,GeometryInfo *info)
#else
InitPropertyInfo(pi,info)
    PropertyInfo *	pi;
    GeometryInfo *	info;
#endif
{
    pi->defs.defined= 0;
    pi->defs.fileID= info->fileID;
    pi->defs.merge= info->merge;
    pi->name= pi->value= NULL;
    return;
}

static void
#if NeedFunctionPrototypes
FreeProperties(PropertyInfo *pi,GeometryInfo *info)
#else
FreeProperties(pi,info)
    PropertyInfo *	pi;
    GeometryInfo *	info;
#endif
{
PropertyInfo *	tmp;
PropertyInfo *	next;

    if (info->props==pi) {
	info->props= NULL;
	info->nProps= 0;
    }
    for (tmp=pi;tmp!=NULL;tmp=next) {
	if (pi->name)
	    uFree(pi->name);
	if (pi->value)
	    uFree(pi->value);
	pi->name= pi->value=NULL;
	next= (PropertyInfo *)tmp->defs.next;
	uFree(tmp);
    }
    return;
}

static void
#if NeedFunctionPrototypes
InitKeyInfo(KeyInfo *key,RowInfo *row,GeometryInfo *info)
#else
InitKeyInfo(key,row,info)
    KeyInfo *		key;
    RowInfo *		row;
    GeometryInfo *	info;
#endif
{

    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
#if NeedFunctionPrototypes
ClearKeyInfo(KeyInfo *key)
#else
ClearKeyInfo(key)
    KeyInfo *		key;
#endif
{
    key->defs.defined&= ~_GK_Default;
    strcpy(key->name,"default");
    key->gap= 0;
    key->shape= None;
    key->color= None;
    return;
}

static void
#if NeedFunctionPrototypes
FreeKeys(KeyInfo *key,RowInfo *row,GeometryInfo *info)
#else
FreeKeys(key,row,info)
    KeyInfo *		key;
    RowInfo *		row;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
InitRowInfo(RowInfo *row,SectionInfo *section,GeometryInfo *info)
#else
InitRowInfo(row,section,info)
    RowInfo *		row;
    SectionInfo *	section;
    GeometryInfo *	info;
#endif
{
    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
#if NeedFunctionPrototypes
ClearRowInfo(RowInfo *row,GeometryInfo *info)
#else
ClearRowInfo(row,info)
    RowInfo *		row;
    GeometryInfo *	info;
#endif
{
    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
#if NeedFunctionPrototypes
FreeRows(RowInfo *row,SectionInfo *section,GeometryInfo *info)
#else
FreeRows(row,section,info)
    RowInfo *		row;
    SectionInfo *	section;
    GeometryInfo *	info;
#endif
{
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 *
#if NeedFunctionPrototypes
FindDoodadByType(DoodadInfo *di,unsigned type)
#else
FindDoodadByType(di,type)
    DoodadInfo *	di;
    unsigned		type;
#endif
{
    while (di) {
	if (di->type==type)
	    return di;
	di= (DoodadInfo *)di->defs.next;
    }
    return NULL;
}

static DoodadInfo *
#if NeedFunctionPrototypes
FindDoodadByName(DoodadInfo *di,Atom name)
#else
FindDoodadByName(di,name)
    DoodadInfo *	di;
    Atom		name;
#endif
{
    while (di) {
	if (di->name==name)
	    return di;
	di= (DoodadInfo *)di->defs.next;
    }
    return NULL;
}

static void
#if NeedFunctionPrototypes
InitDoodadInfo(DoodadInfo *di,unsigned type,SectionInfo *si,GeometryInfo *info)
#else
InitDoodadInfo(di,type,si,info)
    DoodadInfo *	di;
    unsigned		type;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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 (si->nextDoodadPriority>XkbGeomMaxPriority)
	    si->nextDoodadPriority= XkbGeomMaxPriority;
    }
    else {
	di->priority= info->nextPriority++;
	if (info->nextPriority>XkbGeomMaxPriority)
	    info->nextPriority= XkbGeomMaxPriority;
    }
    return;
}

static void
#if NeedFunctionPrototypes
ClearDoodadInfo(DoodadInfo *di)
#else
ClearDoodadInfo(di)
    DoodadInfo *	di;
#endif
{
CommonInfo	defs;

    defs= di->defs;
    bzero(di,sizeof(DoodadInfo));
    di->defs= defs;
    di->defs.defined= 0;
    return;
}

static void
#if NeedFunctionPrototypes
ClearOverlayInfo(OverlayInfo *ol)
#else
ClearOverlayInfo(ol)
    OverlayInfo *	ol;
#endif
{
    if (ol && ol->keys) {
	ol->keys= (OverlayKeyInfo *)ClearCommonInfo(&ol->keys->defs);
	ol->nKeys= 0;
    }
    return;
}

static void
#if NeedFunctionPrototypes
FreeDoodads(DoodadInfo *di,SectionInfo *si,GeometryInfo *info)
#else
FreeDoodads(di,si,info)
    DoodadInfo *	di;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
InitSectionInfo(SectionInfo *si,GeometryInfo *info)
#else
InitSectionInfo(si,info)
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
    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
#if NeedFunctionPrototypes
DupSectionInfo(SectionInfo *into,SectionInfo *from,GeometryInfo *info)
#else
DupSectionInfo(into,from,info)
    SectionInfo *	into;
    SectionInfo *	from;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
ClearSectionInfo(SectionInfo *si,GeometryInfo *info)
#else
ClearSectionInfo(si,info)
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{

    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
#if NeedFunctionPrototypes
FreeSections(SectionInfo *si,GeometryInfo *info)
#else
FreeSections(si,info)
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
FreeShapes(ShapeInfo *si,GeometryInfo *info)
#else
FreeShapes(si,info)
    ShapeInfo *		si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
InitGeometryInfo(GeometryInfo *info,unsigned fileID,unsigned merge)
#else
InitGeometryInfo(info,fileID,merge)
    GeometryInfo *	info;
    unsigned		fileID;
    unsigned		merge;
#endif
{
    bzero(info,sizeof(GeometryInfo));
    info->fileID= fileID;
    info->merge= merge;
    InitSectionInfo(&info->dfltSection,info);
    info->dfltSection.defs.defined= _GS_Default;
    return;
}

static void
#if NeedFunctionPrototypes
ClearGeometryInfo(GeometryInfo *info)
#else
ClearGeometryInfo(info)
    GeometryInfo *	info;
#endif
{
    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 *
#if NeedFunctionPrototypes
NextProperty(GeometryInfo *info)
#else
NextProperty(info)
    GeometryInfo *	info;
#endif
{
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 *
#if NeedFunctionPrototypes
FindProperty(GeometryInfo *info,char *name)
#else
FindProperty(info,name)
    GeometryInfo *	info;
    char *		name;
#endif
{
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
#if NeedFunctionPrototypes
AddProperty(GeometryInfo *info,PropertyInfo *new)
#else
AddProperty(info,new)
    GeometryInfo *	info;
    PropertyInfo *	new;
#endif
{
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 *
#if NeedFunctionPrototypes
NextShape(GeometryInfo *info)
#else
NextShape(info)
    GeometryInfo *	info;
#endif
{
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 *
#if NeedFunctionPrototypes
FindShape(GeometryInfo *info,Atom name,char *type,char *which)
#else
FindShape(info,name,type,which)
    GeometryInfo *	info;
    Atom		name;
    char *		type;
    char *		which;
#endif
{
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
#if NeedFunctionPrototypes
AddShape(GeometryInfo *info,ShapeInfo *new)
#else
AddShape(info,new)
    GeometryInfo *	info;
    ShapeInfo *		new;
#endif
{
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
#if NeedFunctionPrototypes
ReplaceDoodad(DoodadInfo *into,DoodadInfo *from)
#else
ReplaceDoodad(into,from)
    DoodadInfo *	into;
    DoodadInfo *	from;
#endif
{
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 *
#if NeedFunctionPrototypes
NextDfltDoodad(SectionInfo *si,GeometryInfo *info)
#else
NextDfltDoodad(si,info)
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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 *
#if NeedFunctionPrototypes
NextDoodad(SectionInfo *si,GeometryInfo *info)
#else
NextDoodad(si,info)
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
AddDoodad(SectionInfo *si,GeometryInfo *info,DoodadInfo *new)
#else
AddDoodad(si,info,new)
    SectionInfo *	si;
    GeometryInfo *	info;
    DoodadInfo *	new;
#endif
{
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 *
#if NeedFunctionPrototypes
FindDfltDoodadByTypeName(char *name,SectionInfo *si,GeometryInfo *info)
#else
FindDfltDoodadByTypeName(name,si,info)
    char *		name;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
AddOverlay(SectionInfo	*si,GeometryInfo *info,OverlayInfo *new)
#else
AddOverlay(si,info,new)
    SectionInfo		*si;
    GeometryInfo	*info;
    OverlayInfo		*new;
#endif
{
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 *
#if NeedFunctionPrototypes
NextSection(GeometryInfo *info)
#else
NextSection(info)
    GeometryInfo *	info;
#endif
{
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 *
#if NeedFunctionPrototypes
FindMatchingSection(GeometryInfo *info,SectionInfo *new)
#else
FindMatchingSection(info,new)
    GeometryInfo *	info;
    SectionInfo *		new;
#endif
{
SectionInfo *	old;

    for (old=info->sections;old!=NULL;old=(SectionInfo *)old->defs.next) {
	if (new->name==old->name)
	    return old;
    }
    return NULL;
}

static Bool
#if NeedFunctionPrototypes
AddSection(GeometryInfo *info,SectionInfo *new)
#else
AddSection(info,new)
    GeometryInfo *	info;
    SectionInfo *		new;
#endif
{
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 *
#if NeedFunctionPrototypes
NextRow(SectionInfo *si)
#else
NextRow(si)
    SectionInfo *	si;
#endif
{
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
#if NeedFunctionPrototypes
AddRow(SectionInfo *si,RowInfo *new)
#else
AddRow(si,new)
    SectionInfo *	si;
    RowInfo *		new;
#endif
{
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 *
#if NeedFunctionPrototypes
NextKey(RowInfo *row)
#else
NextKey(row)
    RowInfo *	row;
#endif
{
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
#if NeedFunctionPrototypes
AddKey(RowInfo *row,KeyInfo *new)
#else
AddKey(row,new)
    RowInfo *	row;
    KeyInfo *	new;
#endif
{
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
#if NeedFunctionPrototypes
MergeIncludedGeometry(GeometryInfo *into,GeometryInfo *from,unsigned merge)
#else
MergeIncludedGeometry(into,from,merge)
    GeometryInfo *	into;
    GeometryInfo *	from;
    unsigned		merge;
#endif
{
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)(
#if NeedFunctionPrototypes
	XkbFile *	/* file */,
	XkbDescPtr	/* xkb */,
	unsigned	/* merge */,
	GeometryInfo *	/* info */
#endif
);

static Bool
#if NeedFunctionPrototypes
HandleIncludeGeometry(IncludeStmt *stmt,XkbDescPtr xkb,GeometryInfo *info,
							FileHandler hndlr)
#else
HandleIncludeGeometry(stmt,xkb,info,hndlr)
    IncludeStmt	*	  stmt;
    XkbDescPtr		  xkb;
    GeometryInfo *	  info;
    FileHandler		  hndlr;
#endif
{
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
#if NeedFunctionPrototypes
SetShapeField(	ShapeInfo *	si,
		char *		field,
		ExprDef *	arrayNdx,
		ExprDef *	value,
		GeometryInfo *	info)
#else
SetShapeField(si,field,arrayNdx,value,info)
    ShapeInfo *		si;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetShapeDoodadField(	DoodadInfo *	di,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SectionInfo *	si,
			GeometryInfo *	info)
#else
SetShapeDoodadField(di,field,arrayNdx,value,si,info)
    DoodadInfo *	di;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
ExprResult	tmp;
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
#if NeedFunctionPrototypes
SetTextDoodadField(	DoodadInfo *	di,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SectionInfo *	si,
			GeometryInfo *	info)
#else
SetTextDoodadField(di,field,arrayNdx,value,si,info)
    DoodadInfo *	di;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetIndicatorDoodadField(	DoodadInfo *	di,
				char *		field,
				ExprDef *	arrayNdx,
				ExprDef *	value,
				SectionInfo *	si,
				GeometryInfo *	info)
#else
SetIndicatorDoodadField(di,field,arrayNdx,value,si,info)
    DoodadInfo *	di;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetLogoDoodadField(	DoodadInfo *	di,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			SectionInfo *	si,
			GeometryInfo *	info)
#else
SetLogoDoodadField(di,field,arrayNdx,value,si,info)
    DoodadInfo *	di;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetDoodadField(	DoodadInfo *	di,
		char *		field,
		ExprDef *	arrayNdx,
		ExprDef *	value,
		SectionInfo *	si,
		GeometryInfo *	info)
#else
SetDoodadField(di,field,arrayNdx,value,si,info)
    DoodadInfo *	di;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetSectionField(	SectionInfo *	si,
			char *		field,
			ExprDef *	arrayNdx,
			ExprDef *	value,
			GeometryInfo *	info)
#else
SetSectionField(si,field,arrayNdx,value,info)
    SectionInfo *	si;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetRowField(	RowInfo *	row,
		char *		field,
		ExprDef *	arrayNdx,
		ExprDef *	value,
		GeometryInfo *	info)
#else
SetRowField(row,field,arrayNdx,value,info)
    RowInfo *		row;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetKeyField(	KeyInfo *key,
		char *field,
		ExprDef *arrayNdx,
		ExprDef *value,
		GeometryInfo *info)
#else
SetKeyField(key,field,arrayNdx,value,info)
    KeyInfo *		key;
    char *		field;
    ExprDef *		arrayNdx;
    ExprDef *		value;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
SetGeometryProperty(GeometryInfo *info,char *property,ExprDef *value)
#else
SetGeometryProperty(info,property,value)
    GeometryInfo *	info;
    char *		property;
    ExprDef *		value;
#endif
{
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
#if NeedFunctionPrototypes
HandleGeometryVar(VarDef *stmt,XkbDescPtr xkb,GeometryInfo *info)
#else
HandleGeometryVar(stmt,xkb,info)
    VarDef *		stmt;
    XkbDescPtr		xkb;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleShapeBody(ShapeDef *def,ShapeInfo *si,unsigned merge,GeometryInfo *info)
#else
HandleShapeBody(def,si,merge,info)
    ShapeDef *		def;
    ShapeInfo *		si;
    unsigned 		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleShapeDef(ShapeDef *def,XkbDescPtr xkb,unsigned merge,GeometryInfo *info)
#else
HandleShapeDef(def,xkb,merge,info)
    ShapeDef *		def;
    XkbDescPtr		xkb;
    unsigned 		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleDoodadDef(	DoodadDef *def,
			unsigned merge,
			SectionInfo *si,
			GeometryInfo *info)
#else
HandleDoodadDef(def,merge,si,info)
    DoodadDef *		def;
    unsigned		merge;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
ExprResult	elem,field;
ExprDef *	ndx;
DoodadInfo	new;
VarDef *	var;
    
    if (def->common.stmtType==StmtIndicatorMapDef) {
	IndicatorMapDef *imap= (IndicatorMapDef *)def;
	Atom		name= imap->name;
	VarDef *	body= imap->body;
	def->common.stmtType= StmtDoodadDef;
	def->type= XkbIndicatorDoodad;
	def->name= name;
	def->body= body;
    }
    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
#if NeedFunctionPrototypes
HandleOverlayDef(	OverlayDef *	def,
			unsigned 	merge,
			SectionInfo *	si,
			GeometryInfo *	info)
#else
HandleOverlayDef(def,merge,si,info)
    OverlayDef *	def;
    unsigned		merge;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleComplexKey(KeyDef *def,KeyInfo *key,GeometryInfo *info)
#else
HandleComplexKey(def,key,info)
    KeyDef *		def;
    KeyInfo *		key;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleRowBody(RowDef *def,RowInfo *row,unsigned merge,GeometryInfo *info)
#else
HandleRowBody(def,row,merge,info)
    RowDef *		def;
    RowInfo *		row;
    unsigned 		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleSectionBody(	SectionDef *	def,
			SectionInfo *	si,
			unsigned 	merge,
			GeometryInfo *	info)
#else
HandleSectionBody(def,si,merge,info)
    SectionDef *	def;
    SectionInfo *	si;
    unsigned 		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleSectionDef(	SectionDef *	def,
			XkbDescPtr 	xkb,
			unsigned 	merge,
			GeometryInfo *	info)
#else
HandleSectionDef(def,xkb,merge,info)
    SectionDef *	def;
    XkbDescPtr		xkb;
    unsigned 		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
HandleGeometryFile(	XkbFile *	file,
			XkbDescPtr 	xkb,
			unsigned 	merge,
			GeometryInfo *	info)
#else
HandleGeometryFile(file,xkb,merge,info)
    XkbFile *		file;
    XkbDescPtr 		xkb;
    unsigned		merge;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
CopyShapeDef(Display *dpy,XkbGeometryPtr geom,ShapeInfo *si)
#else
CopyShapeDef(dpy,geom,si)
    Display *		dpy;
    XkbGeometryPtr	geom;
    ShapeInfo *		si;
#endif
{
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
#if NeedFunctionPrototypes
VerifyDoodadInfo(DoodadInfo *di,GeometryInfo *info)
#else
VerifyDoodadInfo(di,info)
    DoodadInfo *	di;
    GeometryInfo *	info;
#endif
{
    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 *
#if NeedFunctionPrototypes
FontFromParts(	Atom	fontTok,
    		Atom	weightTok,
		Atom	slantTok,
		Atom	setWidthTok,
		Atom	varTok,
		int	size,
		Atom	encodingTok)
#else
FontFromParts(fontTok,weightTok,slantTok,setWidthTok,varTok,size,encodingTok)
    Atom	fontTok;
    Atom	weightTok;
    Atom	slantTok;
    Atom	setWidthTok;
    Atom	varTok;
    int		size;
    Atom	encodingTok;
#endif
{
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
#if NeedFunctionPrototypes
CopyDoodadDef(	XkbGeometryPtr	geom,
    		XkbSectionPtr	section,
    		DoodadInfo *	di,
    		GeometryInfo *	info)
#else
CopyDoodadDef(geom,section,di,info)
    XkbGeometryPtr	geom;
    XkbSectionPtr	section;
    DoodadInfo *	di;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
VerifyOverlayInfo(	XkbGeometryPtr	geom,
			XkbSectionPtr	section,
			OverlayInfo *	oi,
			GeometryInfo *	info,
			short		rowMap[256],
			short		rowSize[256])
#else
VerifyOverlayInfo(geom,section,oi,info,rowMap,rowSize)
    XkbGeometryPtr	geom;
    XkbSectionPtr	section;
    OverlayInfo *	oi;
    GeometryInfo *	info;
    short		rowMap[256];
    short		rowSize[256];
#endif
{
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
#if NeedFunctionPrototypes
CopyOverlayDef(	XkbGeometryPtr	geom,
		XkbSectionPtr	section,
		OverlayInfo *	oi,
		GeometryInfo *	info)
#else
CopyOverlayDef(geom,section,oi,info)
    XkbGeometryPtr	geom;
    XkbSectionPtr	section;
    OverlayInfo *	oi;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
CopySectionDef(XkbGeometryPtr geom,SectionInfo *si,GeometryInfo *info)
#else
CopySectionDef(geom,si,info)
    XkbGeometryPtr	geom;
    SectionInfo *	si;
    GeometryInfo *	info;
#endif
{
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
#if NeedFunctionPrototypes
CompileGeometry(XkbFile *file,XkbFileInfo *result,unsigned merge)
#else
CompileGeometry(file,result,merge)
    XkbFile *		file;
    XkbFileInfo *	result;
    unsigned	 	merge;
#endif
{
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;
}
