/* $Xorg: xkmout.c,v 1.3 2000/08/17 19:46:44 cpqbld Exp $ */
/************************************************************
 Copyright (c) 1994 by Silicon Graphics Computer Systems, Inc.

 Permission to use, copy, modify, and distribute this
 software and its documentation for any purpose and without
 fee is hereby granted, provided that the above copyright
 notice appear in all copies and that both that copyright
 notice and this permission notice appear in supporting
 documentation, and that the name of Silicon Graphics not be 
 used in advertising or publicity pertaining to distribution 
 of the software without specific prior written permission.
 Silicon Graphics makes no representation about the suitability 
 of this software for any purpose. It is provided "as is"
 without any express or implied warranty.
 
 SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS 
 SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 
 AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
 GRAPHICS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL 
 DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 
 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 
 OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
 THE USE OR PERFORMANCE OF THIS SOFTWARE.

 ********************************************************/
/* $XFree86: xc/lib/xkbfile/xkmout.c,v 1.5 2001/07/25 15:04:58 dawes Exp $ */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <X11/Xfuncs.h>
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XKBgeom.h>

#include "XKMformat.h"
#include "XKBfileInt.h"

typedef struct _XkmInfo {
     unsigned short	bound_vmods;
     unsigned short	named_vmods;
     unsigned char	num_bound;
     unsigned char	group_compat;
     unsigned short	num_group_compat;
     unsigned short	num_leds;
     int		total_vmodmaps;
} XkmInfo;

/***====================================================================***/

#define	xkmPutCARD8(f,v)	(putc(v,f),1)

static int
xkmPutCARD16(FILE *file,unsigned val)
{
CARD16	tmp= val;

    fwrite(&tmp,2,1,file);
    return 2;
}

static int
xkmPutCARD32(FILE *file,unsigned long val)
{
CARD32 tmp= val;

    fwrite(&tmp,4,1,file);
    return 4;
}

static int
xkmPutPadding(FILE *file,unsigned pad)
{
int	i;
    for (i=0;i<pad;i++) {
	putc('\0',file);
    }
    return pad;
}

static int
xkmPutCountedBytes(FILE *file,char *ptr,unsigned count)
{
register int nOut;
register unsigned pad;

    if (count==0)
	return xkmPutCARD32(file,(unsigned long)0);

    xkmPutCARD16(file,count);
    nOut= fwrite(ptr,1,count,file);
    if (nOut<0)
	return 2;
    nOut= count+2;
    pad= XkbPaddedSize(nOut)-nOut;
    if (pad)
	xkmPutPadding(file,pad);
    return nOut+pad;
}

static unsigned
xkmSizeCountedString(char *str)
{
    if (str==NULL)
	return 4;
    return XkbPaddedSize(strlen(str)+2);
}

static int
xkmPutCountedString(FILE *file,char *str)
{
    if (str==NULL)
	 return xkmPutCARD32(file,(unsigned long)0);
    return xkmPutCountedBytes(file,str,strlen(str));
}

#define	xkmSizeCountedAtomString(d,a)	\
	xkmSizeCountedString(XkbAtomGetString((d),(a)))

#define	xkmPutCountedAtomString(d,f,a)	\
	xkmPutCountedString((f),XkbAtomGetString((d),(a)))

/***====================================================================***/

static unsigned
SizeXKMVirtualMods(	XkbFileInfo *		result,
			XkmInfo *		info,
			xkmSectionInfo *	toc,
			int *			offset_inout)
{
Display *	dpy;
XkbDescPtr	xkb;
unsigned	nBound,bound;
unsigned	nNamed,named,szNames;
register unsigned	i,bit;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->names)||(!xkb->server)) {
	_XkbLibError(_XkbErrMissingVMods,"SizeXKMVirtualMods",0);
	return 0;
    }
    bound=named=0;
    for (i=nBound=nNamed=szNames=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
	if (xkb->server->vmods[i]!=XkbNoModifierMask) {
	    bound|= bit;
	    nBound++;
	}
	if (xkb->names->vmods[i]!=None) {
	    named|= bit;
	    szNames+= xkmSizeCountedAtomString(dpy,xkb->names->vmods[i]);
	    nNamed++;
	}
    }
    info->num_bound= nBound;
    info->bound_vmods= bound;
    info->named_vmods= named;
    if ((nBound==0)&&(nNamed==0))
	return 0;
    toc->type= 		XkmVirtualModsIndex;
    toc->format= 	MSBFirst;
    toc->size= 		4+XkbPaddedSize(nBound)+szNames+SIZEOF(xkmSectionInfo);
    toc->offset= 	*offset_inout;
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMVirtualMods(FILE *file,XkbFileInfo *result,XkmInfo *info)
{
register unsigned int i,bit;
XkbDescPtr	xkb;
Display *	dpy;
unsigned	size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    size+= xkmPutCARD16(file,info->bound_vmods);
    size+= xkmPutCARD16(file,info->named_vmods);
    for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
	if (info->bound_vmods&bit)
	    size+= xkmPutCARD8(file,xkb->server->vmods[i]);
    }
    if ((i= XkbPaddedSize(info->num_bound)-info->num_bound)>0)
	size+= xkmPutPadding(file,i);
    for (i=0,bit=1;i<XkbNumVirtualMods;i++,bit<<=1) {
	if (info->named_vmods&bit) {
	    register char *name;
	    name= XkbAtomGetString(dpy,xkb->names->vmods[i]);
	    size+= xkmPutCountedString(file,name);
	}
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMKeycodes(XkbFileInfo *result,xkmSectionInfo *toc,int *offset_inout)
{
XkbDescPtr	xkb;
Atom		kcName;
int		size=0;
Display *	dpy;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->names)||(!xkb->names->keys)) {
	_XkbLibError(_XkbErrMissingNames,"SizeXKMKeycodes",0);
	return 0;
    }
    kcName= xkb->names->keycodes;
    size+= 4;	/* min and max keycode */
    size+= xkmSizeCountedAtomString(dpy,kcName);
    size+= XkbNumKeys(xkb)*sizeof(XkbKeyNameRec);
    if (xkb->names->num_key_aliases>0) {
	if (xkb->names->key_aliases!=NULL)
	     size+= xkb->names->num_key_aliases*sizeof(XkbKeyAliasRec);
	else xkb->names->num_key_aliases= 0;
    }
    toc->type= 		XkmKeyNamesIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMKeycodes(FILE *file,XkbFileInfo *result)
{
XkbDescPtr	xkb;
Atom	 	kcName;
char 		*start;
Display *	dpy;
unsigned	tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    kcName= xkb->names->keycodes;
    start= xkb->names->keys[xkb->min_key_code].name;
    
    size+= xkmPutCountedString(file,XkbAtomGetString(dpy,kcName));
    size+= xkmPutCARD8(file,xkb->min_key_code);
    size+= xkmPutCARD8(file,xkb->max_key_code);
    size+= xkmPutCARD8(file,xkb->names->num_key_aliases);
    size+= xkmPutPadding(file,1);
    tmp= fwrite(start,sizeof(XkbKeyNameRec),XkbNumKeys(xkb),file);
    size+= tmp*sizeof(XkbKeyNameRec);
    if (xkb->names->num_key_aliases>0) {
	tmp= fwrite((char *)xkb->names->key_aliases,
			sizeof(XkbKeyAliasRec),xkb->names->num_key_aliases,
			file);
	size+= tmp*sizeof(XkbKeyAliasRec);
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMKeyTypes(XkbFileInfo *result,xkmSectionInfo *toc,int *offset_inout)
{
register unsigned	i,n,size;
XkbKeyTypePtr		type;
XkbDescPtr		xkb;
Display *		dpy;
char *			name;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->map)||(!xkb->map->types)) {
	_XkbLibError(_XkbErrMissingTypes,"SizeXKBKeyTypes",0);
	return 0;
    }
    if (xkb->map->num_types<XkbNumRequiredTypes) {
	_XkbLibError(_XkbErrMissingReqTypes,"SizeXKBKeyTypes",0);
	return 0;
    }
    if (xkb->names)	name= XkbAtomGetString(dpy,xkb->names->types);
    else		name= NULL;
    size= xkmSizeCountedString(name);
    size+= 4;	/* room for # of key types + padding */
    for (i=0,type=xkb->map->types;i<xkb->map->num_types;i++,type++) {
	size+= SIZEOF(xkmKeyTypeDesc);
	size+= SIZEOF(xkmKTMapEntryDesc)*type->map_count;
	size+= xkmSizeCountedAtomString(dpy,type->name);
	if (type->preserve)
	    size+= SIZEOF(xkmModsDesc)*type->map_count;
	if (type->level_names) {
	    Atom *names;
	    names= type->level_names;
	    for (n=0;n<(unsigned)type->num_levels;n++) {
		size+= xkmSizeCountedAtomString(dpy,names[n]);
	    }
	}
    }
    toc->type= 		XkmTypesIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMKeyTypes(FILE *file,XkbFileInfo *result)
{
register unsigned	i,n;
XkbDescPtr		xkb;
XkbKeyTypePtr		type;
xkmKeyTypeDesc		wire;
XkbKTMapEntryPtr	entry;
xkmKTMapEntryDesc	wire_entry;
Atom *			names;
Display *		dpy;
unsigned		tmp,size= 0;
char *			name;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if (xkb->names)	name= XkbAtomGetString(dpy,xkb->names->types);
    else		name= NULL;
    size+= xkmPutCountedString(file,name);
    size+= xkmPutCARD16(file,xkb->map->num_types);
    size+= xkmPutPadding(file,2);
    type= xkb->map->types;
    for (i=0;i<xkb->map->num_types;i++,type++) {
	wire.realMods= type->mods.real_mods;
	wire.virtualMods= type->mods.vmods;
	wire.numLevels= type->num_levels;
	wire.nMapEntries= type->map_count;
	wire.preserve= (type->preserve!=NULL);
	if (type->level_names!=NULL)
	     wire.nLevelNames= type->num_levels;
	else wire.nLevelNames= 0;
	tmp= fwrite(&wire,SIZEOF(xkmKeyTypeDesc),1,file);
	size+= tmp*SIZEOF(xkmKeyTypeDesc);
	for (n=0,entry= type->map;n<type->map_count;n++,entry++) {
	    wire_entry.level= entry->level;
	    wire_entry.realMods= entry->mods.real_mods;
	    wire_entry.virtualMods= entry->mods.vmods;
	    tmp= fwrite(&wire_entry,SIZEOF(xkmKTMapEntryDesc),1,file);
	    size+= tmp*SIZEOF(xkmKTMapEntryDesc);
	}
	size+= xkmPutCountedString(file,XkbAtomGetString(dpy,type->name));
	if (type->preserve) {
	    xkmModsDesc	p_entry;
	    XkbModsPtr	pre;
	    for (n=0,pre=type->preserve;n<type->map_count;n++,pre++) {
		p_entry.realMods= pre->real_mods;
		p_entry.virtualMods= pre->vmods;
		tmp= fwrite(&p_entry,SIZEOF(xkmModsDesc),1,file);
		size+= tmp*SIZEOF(xkmModsDesc);
	    }
	}
	if (type->level_names!=NULL) {
	    names= type->level_names;
	    for (n=0;n<wire.nLevelNames;n++) {
		size+= xkmPutCountedString(file,XkbAtomGetString(dpy,names[n]));
	    }
	}
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMCompatMap(	XkbFileInfo *		result,
			XkmInfo *		info,
			xkmSectionInfo *	toc,
			int *			offset_inout)
{
XkbDescPtr	xkb;
char *		name;
int		size;
register int	i;
unsigned 	groups,nGroups;
Display *	dpy;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->compat)||(!xkb->compat->sym_interpret)) {
	_XkbLibError(_XkbErrMissingCompatMap,"SizeXKMCompatMap",0);
	return 0;
    }
    if (xkb->names)	name= XkbAtomGetString(dpy,xkb->names->compat);
    else		name= NULL;

    for (i=groups=nGroups=0;i<XkbNumKbdGroups;i++) {
	if ((xkb->compat->groups[i].real_mods!=0)||
			(xkb->compat->groups[i].vmods!=0)) {
	    groups|= (1<<i);
	    nGroups++;
	}
    }
    info->group_compat= groups;
    info->num_group_compat= nGroups;
    size= 4; /* room for num_si and group_compat mask */
    size+= xkmSizeCountedString(name);
    size+= (SIZEOF(xkmSymInterpretDesc)*xkb->compat->num_si);
    size+= (SIZEOF(xkmModsDesc)*nGroups);
    toc->type= 		XkmCompatMapIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMCompatMap(FILE *file,XkbFileInfo *result,XkmInfo *info)
{
register unsigned	i;
char *			name;
XkbDescPtr		xkb;
XkbSymInterpretPtr	interp;
xkmSymInterpretDesc	wire;
Display *		dpy;
unsigned		tmp,size=0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if (xkb->names)	name= XkbAtomGetString(dpy,xkb->names->compat);
    else		name= NULL;
    size+= xkmPutCountedString(file,name);
    size+= xkmPutCARD16(file,xkb->compat->num_si);
    size+= xkmPutCARD8(file,info->group_compat);
    size+= xkmPutPadding(file,1);
    interp= xkb->compat->sym_interpret;
    for (i=0;i<xkb->compat->num_si;i++,interp++) {
	wire.sym= interp->sym;
	wire.mods= interp->mods;
	wire.match= interp->match;
	wire.virtualMod= interp->virtual_mod;
	wire.flags= interp->flags;
	wire.actionType= interp->act.type;
	wire.actionData[0]= interp->act.data[0];
	wire.actionData[1]= interp->act.data[1];
	wire.actionData[2]= interp->act.data[2];
	wire.actionData[3]= interp->act.data[3];
	wire.actionData[4]= interp->act.data[4];
	wire.actionData[5]= interp->act.data[5];
	wire.actionData[6]= interp->act.data[6];
	tmp= fwrite(&wire,SIZEOF(xkmSymInterpretDesc),1,file);
	size+= tmp*SIZEOF(xkmSymInterpretDesc);
    }
    if (info->group_compat) {
	register unsigned 	bit;
	xkmModsDesc 		modsWire;
	for (i=0,bit=1;i<XkbNumKbdGroups;i++,bit<<=1) {
	    if (info->group_compat&bit) {
		modsWire.realMods= xkb->compat->groups[i].real_mods;
		modsWire.virtualMods= xkb->compat->groups[i].vmods;
		fwrite(&modsWire,SIZEOF(xkmModsDesc),1,file);
		size+= SIZEOF(xkmModsDesc);
	    }
	}
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMSymbols(	XkbFileInfo *		result,
		XkmInfo *		info,
		xkmSectionInfo *	toc,
		int *			offset_inout)
{
Display *	dpy;
XkbDescPtr	xkb;
unsigned 	size;
register int	i,nSyms;
char *		name;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->map)||((!xkb->map->syms))) {
	_XkbLibError(_XkbErrMissingSymbols,"SizeXKMSymbols",0);
	return 0;
    }
    if (xkb->names && (xkb->names->symbols!=None))
	 name= XkbAtomGetString(dpy,xkb->names->symbols);
    else name= NULL;
    size= xkmSizeCountedString(name);
    size+= 4;	/* min and max keycode, group names mask */
    for (i=0;i<XkbNumKbdGroups;i++) {
	if (xkb->names->groups[i]!=None)
	    size+= xkmSizeCountedAtomString(dpy,xkb->names->groups[i]);
    }
    info->total_vmodmaps= 0;
    for (i=xkb->min_key_code;i<=(int)xkb->max_key_code;i++) {
	nSyms= XkbKeyNumSyms(xkb,i);
	size+= SIZEOF(xkmKeySymMapDesc)+(nSyms*4);
	if (xkb->server) {
	    if (xkb->server->explicit[i]&XkbExplicitKeyTypesMask) {
		register int g;
		for (g=XkbKeyNumGroups(xkb,i)-1;g>=0;g--) {
		    if (xkb->server->explicit[i]&(1<<g)) {
			XkbKeyTypePtr	type;
			char *		name;
			type= XkbKeyKeyType(xkb,i,g);
			name= XkbAtomGetString(dpy,type->name);
			if (name!=NULL)
			    size+= xkmSizeCountedString(name);
		    }
		}
	    }
	    if (XkbKeyHasActions(xkb,i))
		size+= nSyms*SIZEOF(xkmActionDesc);
	    if (xkb->server->behaviors[i].type!=XkbKB_Default)
		size+= SIZEOF(xkmBehaviorDesc);
	    if (xkb->server->vmodmap && (xkb->server->vmodmap[i]!=0))
		info->total_vmodmaps++;
	}
    }
    size+= info->total_vmodmaps*SIZEOF(xkmVModMapDesc);
    toc->type= 		XkmSymbolsIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMSymbols(FILE *file,XkbFileInfo *result,XkmInfo *info)
{
Display *		dpy;
XkbDescPtr		xkb;
register int		i,n;
xkmKeySymMapDesc 	wireMap;
char *			name;
unsigned		tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if (xkb->names && (xkb->names->symbols!=None))
	 name= XkbAtomGetString(dpy,xkb->names->symbols);
    else name= NULL;
    size+= xkmPutCountedString(file,name);
    for (tmp=i=0;i<XkbNumKbdGroups;i++) {
	if (xkb->names->groups[i]!=None)
	    tmp|= (1<<i);
    }
    size+= xkmPutCARD8(file,xkb->min_key_code);
    size+= xkmPutCARD8(file,xkb->max_key_code);
    size+= xkmPutCARD8(file,tmp);
    size+= xkmPutCARD8(file,info->total_vmodmaps);
    for (i=0,n=1;i<XkbNumKbdGroups;i++,n<<=1) {
	if ((tmp&n)==0)
	    continue;
	size+= xkmPutCountedAtomString(dpy,file,xkb->names->groups[i]);
    }
    for (i=xkb->min_key_code;i<=(int)xkb->max_key_code;i++)  {
	char *typeName[XkbNumKbdGroups];
	wireMap.width= XkbKeyGroupsWidth(xkb,i);
	wireMap.num_groups= XkbKeyGroupInfo(xkb,i);
	if (xkb->map && xkb->map->modmap)
	     wireMap.modifier_map= xkb->map->modmap[i];
	else wireMap.modifier_map= 0;
	wireMap.flags= 0;
	bzero((char *)typeName,XkbNumKbdGroups*sizeof(char *));
	if (xkb->server) {
	    if (xkb->server->explicit[i]&XkbExplicitKeyTypesMask) {
		register int g;
		for (g=0;g<XkbKeyNumGroups(xkb,i);g++) {
		    if (xkb->server->explicit[i]&(1<<g)) {
			XkbKeyTypePtr	type;
			type= XkbKeyKeyType(xkb,i,g);
			typeName[g]= XkbAtomGetString(dpy,type->name);
			if (typeName[g]!=NULL)
			    wireMap.flags|= (1<<g);
		    }
		}
	    }
	    if (XkbKeyHasActions(xkb,i))
		wireMap.flags|= XkmKeyHasActions;
	    if (xkb->server->behaviors[i].type!=XkbKB_Default)
		wireMap.flags|= XkmKeyHasBehavior;
	    if ((xkb->server->explicit[i]&XkbExplicitAutoRepeatMask)&&
		(xkb->ctrls!=NULL)) {
		if (xkb->ctrls->per_key_repeat[(i/8)]&(1<<(i%8)))
		     wireMap.flags|= XkmRepeatingKey;
		else wireMap.flags|= XkmNonRepeatingKey;
	    }
	}
	tmp= fwrite(&wireMap,SIZEOF(xkmKeySymMapDesc),1,file);
	size+= tmp*SIZEOF(xkmKeySymMapDesc);
	if (xkb->server->explicit[i]&XkbExplicitKeyTypesMask) {
	    register int g;
	    for (g=0;g<XkbNumKbdGroups;g++) {
		if (typeName[g]!=NULL)
		    size+= xkmPutCountedString(file,typeName[g]);
	    }
	}
	if (XkbNumGroups(wireMap.num_groups)>0) {
	    KeySym	*sym;
	    sym= XkbKeySymsPtr(xkb,i);
	    for (n=XkbKeyNumSyms(xkb,i);n>0;n--,sym++) {
		size+= xkmPutCARD32(file,(CARD32)*sym);
	    }
	    if (wireMap.flags&XkmKeyHasActions) {
		XkbAction *	act;
		act= XkbKeyActionsPtr(xkb,i);
		for (n=XkbKeyNumActions(xkb,i);n>0;n--,act++) {
		    tmp= fwrite(act,SIZEOF(xkmActionDesc),1,file);
		    size+= tmp*SIZEOF(xkmActionDesc);
		}
	    }
	}
	if (wireMap.flags&XkmKeyHasBehavior) {
	    xkmBehaviorDesc	b;
	    b.type= xkb->server->behaviors[i].type;
	    b.data= xkb->server->behaviors[i].data;
	    tmp= fwrite(&b,SIZEOF(xkmBehaviorDesc),1,file);
	    size+= tmp*SIZEOF(xkmBehaviorDesc);
	}
    }
    if (info->total_vmodmaps>0) {
	xkmVModMapDesc	v;
	for (i=xkb->min_key_code;i<=xkb->max_key_code;i++) {
	    if (xkb->server->vmodmap[i]!=0) {
		v.key= i;
		v.vmods= xkb->server->vmodmap[i];
		tmp= fwrite(&v,SIZEOF(xkmVModMapDesc),1,file);
		size+= tmp*SIZEOF(xkmVModMapDesc);
	    }
	}
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMIndicators(XkbFileInfo *result,XkmInfo *info,xkmSectionInfo *toc,
							int *offset_inout)
{
Display *		dpy;
XkbDescPtr		xkb;
unsigned 		size;
register unsigned	i,nLEDs;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((xkb==NULL)||(xkb->indicators==NULL)) {
/*	_XkbLibError(_XkbErrMissingIndicators,"SizeXKMIndicators",0);*/
	return 0;
    }
    nLEDs=0;
    size= 8;	/* number of indicator maps/physical indicators */
    if (xkb->indicators!=NULL) {
	for (i=0;i<XkbNumIndicators;i++) {
	    XkbIndicatorMapPtr map= &xkb->indicators->maps[i];
	    if ((map->flags!=0)||(map->which_groups!=0)||(map->groups!=0)||
		(map->which_mods!=0)||
		(map->mods.real_mods!=0)||(map->mods.vmods!=0)||
		(map->ctrls!=0) || 
		(xkb->names && (xkb->names->indicators[i]!=None))) {
		char *name;
		if (xkb->names && xkb->names->indicators[i]!=None) {
		     name= XkbAtomGetString(dpy,xkb->names->indicators[i]);
		}
		else name= NULL;
		size+= xkmSizeCountedString(name);
		size+= SIZEOF(xkmIndicatorMapDesc);
		nLEDs++;
	    }
	}
    }
    info->num_leds= nLEDs;
    toc->type= 		XkmIndicatorsIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMIndicators(FILE *file,XkbFileInfo *result,XkmInfo *info)
{
Display *		dpy;
XkbDescPtr		xkb;
register unsigned	i;
xkmIndicatorMapDesc	wire;
unsigned		tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    size+= xkmPutCARD8(file,info->num_leds);
    size+= xkmPutPadding(file,3);
    size+= xkmPutCARD32(file,xkb->indicators->phys_indicators);
    if (xkb->indicators!=NULL) {
	for (i=0;i<XkbNumIndicators;i++) {
	    XkbIndicatorMapPtr map= &xkb->indicators->maps[i];
	    if ((map->flags!=0)||(map->which_groups!=0)||(map->groups!=0)||
		(map->which_mods!=0)||
		(map->mods.real_mods!=0)||(map->mods.vmods!=0)||
		(map->ctrls!=0) ||
		(xkb->names && (xkb->names->indicators[i]!=None))) {
		char *name;
		if (xkb->names && xkb->names->indicators[i]!=None) {
		     name= XkbAtomGetString(dpy,xkb->names->indicators[i]);
		}
		else name= NULL;
		size+= xkmPutCountedString(file,name);
		wire.indicator= i+1;
		wire.flags= map->flags;
		wire.which_mods= map->which_mods;
		wire.real_mods= map->mods.real_mods;
		wire.vmods= map->mods.vmods;
		wire.which_groups= map->which_groups;
		wire.groups= map->groups;
		wire.ctrls= map->ctrls;
		tmp= fwrite(&wire,SIZEOF(xkmIndicatorMapDesc),1,file);
		size+= tmp*SIZEOF(xkmIndicatorMapDesc);
	    }
	}
    }
    return size;
}

/***====================================================================***/

static unsigned
SizeXKMGeomDoodad(XkbFileInfo *result,XkbDoodadPtr doodad)
{
unsigned	size;

    size= SIZEOF(xkmAnyDoodadDesc);
    size+= xkmSizeCountedAtomString(result->xkb->dpy,doodad->any.name);
    if (doodad->any.type==XkbTextDoodad) {
	size+= xkmSizeCountedString(doodad->text.text);
	size+= xkmSizeCountedString(doodad->text.font);
    }
    else if (doodad->any.type==XkbLogoDoodad) {
	size+= xkmSizeCountedString(doodad->logo.logo_name);
    }
    return size;
}

static unsigned
SizeXKMGeomSection(XkbFileInfo *result,XkbSectionPtr section)
{
register int i;
unsigned size;

    size= SIZEOF(xkmSectionDesc);
    size+= xkmSizeCountedAtomString(result->xkb->dpy,section->name);
    if (section->rows) {
	XkbRowPtr	row;
	for (row=section->rows,i=0;i<section->num_rows;i++,row++) {
	    size+= SIZEOF(xkmRowDesc);
	    size+= row->num_keys*SIZEOF(xkmKeyDesc);
	}
    }
    if (section->doodads) {
	XkbDoodadPtr	doodad;
	for (doodad=section->doodads,i=0;i<section->num_doodads;i++,doodad++) {
	    size+= SizeXKMGeomDoodad(result,doodad);
	}
    }
    if (section->overlays) {
	XkbOverlayPtr	ol;
	for (ol=section->overlays,i=0;i<section->num_overlays;i++,ol++) {
	    register int r;
	    XkbOverlayRowPtr	row;
	    size+= xkmSizeCountedAtomString(result->xkb->dpy,ol->name);
	    size+= SIZEOF(xkmOverlayDesc);
	    for (r=0,row=ol->rows;r<ol->num_rows;r++,row++) {
		size+= SIZEOF(xkmOverlayRowDesc);
		size+= row->num_keys*SIZEOF(xkmOverlayKeyDesc);
	    }
	}
    }
    return size;
}

static unsigned
SizeXKMGeometry(XkbFileInfo *result,xkmSectionInfo *toc,int *offset_inout)
{
register int	i;
Display *	dpy;
XkbDescPtr	xkb;
XkbGeometryPtr	geom;
unsigned	size;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->geom))
	return 0;
    geom= xkb->geom;
    size= xkmSizeCountedAtomString(dpy,geom->name);
    size+= SIZEOF(xkmGeometryDesc);
    size+= xkmSizeCountedString(geom->label_font);
    if (geom->properties) {
	XkbPropertyPtr	prop;
	for (i=0,prop=geom->properties;i<geom->num_properties;i++,prop++) {
	    size+= xkmSizeCountedString(prop->name);
	    size+= xkmSizeCountedString(prop->value);
	}
    }
    if (geom->colors) {
	XkbColorPtr	color;
	for (i=0,color=geom->colors;i<geom->num_colors;i++,color++) {
	    size+= xkmSizeCountedString(color->spec);
	}
    }
    if (geom->shapes) {
	XkbShapePtr	shape;
	for (i=0,shape=geom->shapes;i<geom->num_shapes;i++,shape++) {
	    register int n;
	    register XkbOutlinePtr	ol;
	    size+= xkmSizeCountedAtomString(dpy,shape->name);
	    size+= SIZEOF(xkmShapeDesc);
	    for (n=0,ol=shape->outlines;n<shape->num_outlines;n++,ol++) {
		size+= SIZEOF(xkmOutlineDesc);
		size+= ol->num_points*SIZEOF(xkmPointDesc);
	    }
	}
    }
    if (geom->sections) {
	XkbSectionPtr	section;
	for (i=0,section=geom->sections;i<geom->num_sections;i++,section++) {
	    size+= SizeXKMGeomSection(result,section);
	}
    }
    if  (geom->doodads) {
	XkbDoodadPtr	doodad;
	for (i=0,doodad=geom->doodads;i<geom->num_doodads;i++,doodad++) {
	    size+= SizeXKMGeomDoodad(result,doodad);
	}
    }
    if (geom->key_aliases) {
	size+= geom->num_key_aliases*(XkbKeyNameLength*2);
    }
    toc->type= 		XkmGeometryIndex;
    toc->format= 	MSBFirst;
    toc->size= 		size+SIZEOF(xkmSectionInfo);
    toc->offset= 	(*offset_inout);
    (*offset_inout)+= 	toc->size;
    return 1;
}

static unsigned
WriteXKMGeomDoodad(FILE *file,XkbFileInfo *result,XkbDoodadPtr doodad)
{
Display *	dpy;
XkbDescPtr	xkb;
xkmDoodadDesc	doodadWire;
unsigned	tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    bzero((char *)&doodadWire,sizeof(doodadWire));
    doodadWire.any.type= doodad->any.type;
    doodadWire.any.priority= doodad->any.priority;
    doodadWire.any.top= doodad->any.top;
    doodadWire.any.left= doodad->any.left;
    switch (doodad->any.type) {
	case XkbOutlineDoodad:
	case XkbSolidDoodad:
	    doodadWire.shape.angle= doodad->shape.angle;
	    doodadWire.shape.color_ndx= doodad->shape.color_ndx;
	    doodadWire.shape.shape_ndx= doodad->shape.shape_ndx;
	    break;
	case XkbTextDoodad:
	    doodadWire.text.angle= doodad->text.angle;
	    doodadWire.text.width= doodad->text.width;
	    doodadWire.text.height= doodad->text.height;
	    doodadWire.text.color_ndx= doodad->text.color_ndx;
	    break;
	case XkbIndicatorDoodad:
	    doodadWire.indicator.shape_ndx= doodad->indicator.shape_ndx;
	    doodadWire.indicator.on_color_ndx= doodad->indicator.on_color_ndx;
	    doodadWire.indicator.off_color_ndx= doodad->indicator.off_color_ndx;
	    break;
	case XkbLogoDoodad:
	    doodadWire.logo.angle= doodad->logo.angle;
	    doodadWire.logo.color_ndx= doodad->logo.color_ndx;
	    doodadWire.logo.shape_ndx= doodad->logo.shape_ndx;
	    break;
	default:
	    _XkbLibError(_XkbErrIllegalDoodad,"WriteXKMGeomDoodad",
	    						doodad->any.type);
	    return 0;
    }
    size+= xkmPutCountedAtomString(dpy,file,doodad->any.name);
    tmp= fwrite(&doodadWire,SIZEOF(xkmDoodadDesc),1,file);
    size+= tmp*SIZEOF(xkmDoodadDesc);
    if (doodad->any.type==XkbTextDoodad) {
	size+= xkmPutCountedString(file,doodad->text.text);
	size+= xkmPutCountedString(file,doodad->text.font);
    }
    else if (doodad->any.type==XkbLogoDoodad) {
	size+= xkmPutCountedString(file,doodad->logo.logo_name);
    }
    return size;
}

static unsigned
WriteXKMGeomOverlay(FILE *file,XkbFileInfo *result,XkbOverlayPtr ol)
{
register int		r,k;
Display *		dpy;
XkbDescPtr		xkb;
XkbOverlayRowPtr	row;
xkmOverlayDesc		olWire;
xkmOverlayRowDesc	rowWire;
xkmOverlayKeyDesc	keyWire;
unsigned		tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    bzero((char *)&olWire,sizeof(olWire));
    bzero((char *)&rowWire,sizeof(rowWire));
    bzero((char *)&keyWire,sizeof(keyWire));
    size+= xkmPutCountedAtomString(dpy,file,ol->name);
    olWire.num_rows= ol->num_rows;
    tmp= fwrite(&olWire,SIZEOF(xkmOverlayDesc),1,file);
    size+= tmp*SIZEOF(xkmOverlayDesc);
    for (r=0,row=ol->rows;r<ol->num_rows;r++,row++) {
	XkbOverlayKeyPtr	key;
	rowWire.row_under= row->row_under;
	rowWire.num_keys= row->num_keys;
	tmp= fwrite(&rowWire,SIZEOF(xkmOverlayRowDesc),1,file);
	size+= tmp*SIZEOF(xkmOverlayRowDesc);
	for (k=0,key=row->keys;k<row->num_keys;k++,key++) {
	    memcpy(keyWire.over,key->over.name,XkbKeyNameLength);
	    memcpy(keyWire.under,key->under.name,XkbKeyNameLength);
	    tmp= fwrite(&keyWire,SIZEOF(xkmOverlayKeyDesc),1,file);
	    size+= tmp*SIZEOF(xkmOverlayKeyDesc);
	}
    }
    return size;    
}

static unsigned
WriteXKMGeomSection(FILE *file,XkbFileInfo *result,XkbSectionPtr section)
{
register int	i;
Display *	dpy;
XkbDescPtr	xkb;
xkmSectionDesc	sectionWire;
unsigned	tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    size+= xkmPutCountedAtomString(dpy,file,section->name);
    sectionWire.top= section->top;
    sectionWire.left= section->left;
    sectionWire.width= section->width;
    sectionWire.height= section->height;
    sectionWire.angle= section->angle;
    sectionWire.priority= section->priority;
    sectionWire.num_rows= section->num_rows;
    sectionWire.num_doodads= section->num_doodads;
    sectionWire.num_overlays= section->num_overlays;
    tmp= fwrite(&sectionWire,SIZEOF(xkmSectionDesc),1,file);
    size+= tmp*SIZEOF(xkmSectionDesc);
    if (section->rows) {
	register unsigned k;
	XkbRowPtr	row;
	xkmRowDesc	rowWire;
	XkbKeyPtr	key;
	xkmKeyDesc	keyWire;
	for (i=0,row=section->rows;i<section->num_rows;i++,row++) {
	    rowWire.top= row->top;
	    rowWire.left= row->left;
	    rowWire.num_keys= row->num_keys;
	    rowWire.vertical= row->vertical;
	    tmp= fwrite(&rowWire,SIZEOF(xkmRowDesc),1,file);
	    size+= tmp*SIZEOF(xkmRowDesc);
	    for (k=0,key=row->keys;k<row->num_keys;k++,key++) {
		memcpy(keyWire.name,key->name.name,XkbKeyNameLength);
		keyWire.gap= key->gap;
		keyWire.shape_ndx= key->shape_ndx;
		keyWire.color_ndx= key->color_ndx;
		tmp= fwrite(&keyWire,SIZEOF(xkmKeyDesc),1,file);
		size+= tmp*SIZEOF(xkmKeyDesc);
	    }
	}
    }
    if (section->doodads) {
	XkbDoodadPtr	doodad;
	for (i=0,doodad=section->doodads;i<section->num_doodads;i++,doodad++) {
	    size+= WriteXKMGeomDoodad(file,result,doodad);
	}
    }
    if (section->overlays) {
	XkbOverlayPtr	ol;
	for (i=0,ol=section->overlays;i<section->num_overlays;i++,ol++) {
	    size+= WriteXKMGeomOverlay(file,result,ol);
	}
    }
    return size;
}

static unsigned
WriteXKMGeometry(FILE *file,XkbFileInfo *result)
{
register int	i;
Display *	dpy;
XkbDescPtr	xkb;
XkbGeometryPtr	geom;
xkmGeometryDesc	wire;
unsigned	tmp,size= 0;

    xkb= result->xkb;
    dpy= xkb->dpy;
    if ((!xkb)||(!xkb->geom))
	return 0;
    geom= xkb->geom;
    wire.width_mm= geom->width_mm;
    wire.height_mm= geom->height_mm;
    wire.base_color_ndx= XkbGeomColorIndex(geom,geom->base_color);
    wire.label_color_ndx= XkbGeomColorIndex(geom,geom->label_color);
    wire.num_properties= geom->num_properties;
    wire.num_colors= geom->num_colors;
    wire.num_shapes= geom->num_shapes;
    wire.num_sections= geom->num_sections;
    wire.num_doodads= geom->num_doodads;
    wire.num_key_aliases= geom->num_key_aliases;
    size+= xkmPutCountedAtomString(dpy,file,geom->name);
    tmp= fwrite(&wire,SIZEOF(xkmGeometryDesc),1,file);
    size+= tmp*SIZEOF(xkmGeometryDesc);
    size+= xkmPutCountedString(file,geom->label_font);
    if (geom->properties) {
	XkbPropertyPtr	prop;
	for (i=0,prop=geom->properties;i<geom->num_properties;i++,prop++) {
	    size+= xkmPutCountedString(file,prop->name);
	    size+= xkmPutCountedString(file,prop->value);
	}
    }
    if (geom->colors) {
	XkbColorPtr	color;
	for (i=0,color=geom->colors;i<geom->num_colors;i++,color++) {
	    size+= xkmPutCountedString(file,color->spec);
	}
    }
    if (geom->shapes) {
	XkbShapePtr	shape;
	xkmShapeDesc	shapeWire;

	for (i=0,shape=geom->shapes;i<geom->num_shapes;i++,shape++) {
	    register int 	n;
	    XkbOutlinePtr	ol;
	    xkmOutlineDesc	olWire;
	    bzero((char *)&shapeWire,sizeof(xkmShapeDesc));
	    size+= xkmPutCountedAtomString(dpy,file,shape->name);
	    shapeWire.num_outlines= shape->num_outlines;
	    if (shape->primary!=NULL)
		 shapeWire.primary_ndx= XkbOutlineIndex(shape,shape->primary);
	    else shapeWire.primary_ndx= XkbNoShape;
	    if (shape->approx!=NULL)
		 shapeWire.approx_ndx= XkbOutlineIndex(shape,shape->approx);
	    else shapeWire.approx_ndx= XkbNoShape;
	    tmp= fwrite(&shapeWire,SIZEOF(xkmShapeDesc),1,file);
	    size+= tmp*SIZEOF(xkmShapeDesc);
	    for (n=0,ol=shape->outlines;n<shape->num_outlines;n++,ol++) {
		register int	p;
		XkbPointPtr	pt;
		xkmPointDesc	ptWire;
		olWire.num_points= ol->num_points;
		olWire.corner_radius= ol->corner_radius;
		tmp= fwrite(&olWire,SIZEOF(xkmOutlineDesc),1,file);
		size+= tmp*SIZEOF(xkmOutlineDesc);
		for (p=0,pt=ol->points;p<ol->num_points;p++,pt++) {
		    ptWire.x= pt->x;
		    ptWire.y= pt->y;
		    tmp= fwrite(&ptWire,SIZEOF(xkmPointDesc),1,file);
		    size+= tmp*SIZEOF(xkmPointDesc);
		}
	    }
	}
    }
    if (geom->sections) {
	XkbSectionPtr	section;
	for (i=0,section=geom->sections;i<geom->num_sections;i++,section++) {
	    size+= WriteXKMGeomSection(file,result,section);
	}
    }
    if (geom->doodads) {
	XkbDoodadPtr	doodad;
	for (i=0,doodad=geom->doodads;i<geom->num_doodads;i++,doodad++) {
	    size+= WriteXKMGeomDoodad(file,result,doodad);
	}
    }
    if (geom->key_aliases) {
	tmp= fwrite(geom->key_aliases,2*XkbKeyNameLength,geom->num_key_aliases,
									file);
	size+= tmp*(2*XkbKeyNameLength);
    }
    return size;
}

/***====================================================================***/

/*ARGSUSED*/
static int
GetXKMKeyNamesTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMKeycodes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMIndicators(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMTypesTOC(	XkbFileInfo *	result,
		XkmInfo *	info,
		int		max_toc,
		xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMVirtualMods(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeyTypes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMCompatMapTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMVirtualMods(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMCompatMap(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMIndicators(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMSemanticsTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMVirtualMods(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeyTypes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMCompatMap(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMIndicators(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMLayoutTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMVirtualMods(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeycodes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeyTypes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMSymbols(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMIndicators(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMGeometry(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMKeymapTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMVirtualMods(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeycodes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMKeyTypes(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMCompatMap(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMSymbols(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMIndicators(result,info,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    if (SizeXKMGeometry(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

/*ARGSUSED*/
static int
GetXKMGeometryTOC(	XkbFileInfo *	result,
			XkmInfo *	info,
			int		max_toc,
			xkmSectionInfo *toc_rtrn)
{
int	num_toc;
int	total_size;

    total_size= num_toc=0;
    if (SizeXKMGeometry(result,&toc_rtrn[num_toc],&total_size))
	num_toc++;
    return num_toc;
}

static Bool
WriteXKMFile(	FILE *		file,
		XkbFileInfo *	result,
		int		num_toc,
		xkmSectionInfo *toc,
		XkmInfo *	info)
{
register int 	i;
unsigned	tmp,size,total= 0;
    
    for (i=0;i<num_toc;i++) {
	tmp= fwrite(&toc[i],SIZEOF(xkmSectionInfo),1,file);
	total+= tmp*SIZEOF(xkmSectionInfo);
	switch (toc[i].type) {
	    case XkmTypesIndex:
		size= WriteXKMKeyTypes(file,result);
		break;
	    case XkmCompatMapIndex:
		size= WriteXKMCompatMap(file,result,info);
		break;
	    case XkmSymbolsIndex:
		size= WriteXKMSymbols(file,result,info);
		break;
	    case XkmIndicatorsIndex:
		size= WriteXKMIndicators(file,result,info);
		break;
	    case XkmKeyNamesIndex:
		size= WriteXKMKeycodes(file,result);
		break;
	    case XkmGeometryIndex:
		size= WriteXKMGeometry(file,result);
		break;
	    case XkmVirtualModsIndex:
		size= WriteXKMVirtualMods(file,result,info);
		break;
	    default:
		_XkbLibError(_XkbErrIllegalTOCType,"WriteXKMFile",toc[i].type);
		return False;
	}
	size+= SIZEOF(xkmSectionInfo);
	if (size!=toc[i].size) {
	    _XkbLibError(_XkbErrBadLength,XkbConfigText(toc[i].type,XkbMessage),
	    						size-toc[i].size);
	    return False;
	}
    }
    return True;
}


#define	MAX_TOC	16

Bool
XkbWriteXKMFile(FILE *out,XkbFileInfo *result)
{
Bool	 		ok;
XkbDescPtr		xkb;
XkmInfo			info;
int			size_toc,i;
unsigned		hdr,present;
xkmFileInfo		fileInfo;
xkmSectionInfo		toc[MAX_TOC];
int			(*getTOC)(
	XkbFileInfo *	/* result */,
	XkmInfo *	/* info */,
	int		/* max_to */,
	xkmSectionInfo */* toc_rtrn */
);

    switch (result->type) {
	case XkmKeyNamesIndex:
	    getTOC= GetXKMKeyNamesTOC;
	    break;
	case XkmTypesIndex:
	    getTOC= GetXKMTypesTOC;
	    break;
	case XkmCompatMapIndex:
	    getTOC= GetXKMCompatMapTOC;
	    break;
	case XkmSemanticsFile:
	    getTOC= GetXKMSemanticsTOC;
	    break;
	case XkmLayoutFile:
	    getTOC= GetXKMLayoutTOC;
	    break;
	case XkmKeymapFile:
	    getTOC= GetXKMKeymapTOC;
	    break;
	case XkmGeometryFile:
	case XkmGeometryIndex:
	    getTOC= GetXKMGeometryTOC;
	    break;
	default:
	    _XkbLibError(_XkbErrIllegalContents,
	    			XkbConfigText(result->type,XkbMessage),0);
	    return False;
    }
    xkb= result->xkb;

    bzero((char *)&info,sizeof(XkmInfo));
    size_toc= (*getTOC)(result,&info,MAX_TOC,toc);
    if (size_toc<1) {
	_XkbLibError(_XkbErrEmptyFile,"XkbWriteXKMFile",0);
	return False;
    }
    if (out==NULL) {
	_XkbLibError(_XkbErrFileCannotOpen,"XkbWriteXKMFile",0);
	return False;
    }
    for (i=present=0;i<size_toc;i++) {
	toc[i].offset+= 4+SIZEOF(xkmFileInfo);
	toc[i].offset+= (size_toc*SIZEOF(xkmSectionInfo));
	if (toc[i].type<=XkmLastIndex) {
	    present|= (1<<toc[i].type);
	}
#ifdef DEBUG
	else {
	    fprintf(stderr,"Illegal section type %d\n",toc[i].type);
	    fprintf(stderr,"Ignored\n");
	}
#endif
    }
    hdr= (('x'<<24)|('k'<<16)|('m'<<8)|XkmFileVersion);
    xkmPutCARD32(out,(unsigned long)hdr);
    fileInfo.type= result->type;
    fileInfo.min_kc= xkb->min_key_code;
    fileInfo.max_kc= xkb->max_key_code;
    fileInfo.num_toc= size_toc;
    fileInfo.present= present;
    fileInfo.pad= 0;
    fwrite(&fileInfo,SIZEOF(xkmFileInfo),1,out);
    fwrite(toc,SIZEOF(xkmSectionInfo),size_toc,out);
    ok= WriteXKMFile(out,result,size_toc,toc,&info);
    return ok;
}
