/*
 * Electric(tm) VLSI Design System
 *
 * File: iobinaryo.c
 * Input/output aid: binary format output
 * Written by: Steven M. Rubin, Static Free Software
 *
 * Copyright (c) 2000 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "global.h"
#include "eio.h"
#include "usr.h"

static INTSML  io_outputstage;		/* progress of database (aborting) */
static char   *io_namesave;			/* for saving the global namespace */

/* prototypes for local routines */
void io_cleanup(void);
void io_writenodeproto(NODEPROTO*, INTSML);
void io_writenodeinst(NODEINST*);
void io_writearcinst(ARCINST*);
void io_writenamespace(void);
void io_restorenamespace(void);
void io_writevariables(VARIABLE*, INTSML);
void io_putoutvar(INTBIG*, INTBIG);
void io_putout(void*, INTSML);
void io_putoutbig(void*);
void io_putstring(char*);

INTSML io_writebinlibrary(LIBRARY *lib, INTSML nobackup)
{
	INTBIG i, a, n, p, magic, aacount, techcount, nodepprotoindex,
		portpprotoindex, arcprotoindex, nodeprotoindex, nodeindex,
		portprotoindex, arcindex, geomindex, curnodeproto, cellindex,
		*curstate;
	UINTBIG olddate;
	INTSML save, year, month, mday, hour, minute, second;
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap;
	REGISTER PORTPROTO *pp;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *olib;
	REGISTER TECHNOLOGY *tech;
	REGISTER char *rename, *name, *istrue, *dotaddr, *msg;
	char *truename, *arg[3], datesuffix[30];
	unsigned char wordsize;
	REGISTER CELL *c;
	REGISTER VIEW *v;

	io_outputstage = 0;
	i = initinfstr();
	istrue = &lib->libfile[strlen(lib->libfile)-5];
	if (strcmp(istrue, ".elib") == 0)
	{
		dotaddr = istrue;
		*istrue = 0;
	} else dotaddr = 0;
	istrue = truepath(lib->libfile);
	i += addstringtoinfstr(istrue);
	i += addstringtoinfstr(".elib");
	if (dotaddr != 0) *dotaddr = '.';
	if (i != 0)
	{
		ttyputerr(_("No memory! saving to 'PANIC'"));
		name = _("PANIC");
	} else name = returninfstr();

	curstate = io_getstatebits();
	if ((curstate[0]&CHECKATWRITE) != 0)
	{
		ttyputmsg(_("Checking all libraries..."));
		us_checkdatabase(0);
	}
	if (nobackup != 0) curstate[0] &= ~(BINOUTBACKUP|CHECKATWRITE);

	rename = 0;
	if ((lib->userbits&READFROMDISK) == 0) msg = _("Library File"); else
	{
		msg = 0;

		/* if the file already exists, rename it first */
		if (fileexistence(name) == 1)
			rename = (char *)emalloc((strlen(name)+15), el_tempcluster);
		if (rename != 0)
		{
			switch (curstate[0]&BINOUTBACKUP)
			{
				case BINOUTNOBACK:
					(void)strcpy(rename, name);
					(void)strcat(rename, ".XXXXXX");
					emaketemp(rename);
					if (erename(name, rename) != 0)
					{
						efree(rename);
						rename = 0;
					}
					break;
				case BINOUTONEBACK:
					(void)strcpy(rename, name);
					(void)strcat(rename, "~");
					if (fileexistence(rename) == 1) (void)eunlink(rename);
					(void)erename(name, rename);
					efree(rename);
					rename = 0;
					break;
				case BINOUTFULLBACK:
					olddate = filedate(name);
					parsetime(olddate, &year, &month, &mday, &hour, &minute, &second);
					for(i=0; i<1000; i++)
					{
						if (i == 0) sprintf(datesuffix, "-%d-%d-%d", year, month+1, mday); else
							sprintf(datesuffix, "-%d-%d-%d--%d", year, month+1, mday, i);
						(void)strcpy(rename, name);
						rename[strlen(rename)-5] = 0;
						(void)strcat(rename, datesuffix);
						(void)strcat(rename, ".elib");
						if (fileexistence(rename) == 0) break;
					}
					erename(name, rename);
					efree(rename);
					rename = 0;
					break;
			}
		}
	}

	/* create the new library file */
	io_fileout = xcreate(name, io_filetypeblib, msg, &truename);
	if (io_fileout == NULL)
	{
		if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
		if (rename != 0) efree(rename);
		return(1);
	}

	/* if the user got a dialog, replace the library and file name */
	if (msg != 0)
	{
		arg[0] = lib->libname;
		arg[1] = truename;
		arg[2] = "library";
		us_rename(3, arg);
		name = truename;
	}

	if (setjmp(io_filerror))
	{
		ttyputerr(_("Error writing %s"), name);
		xclose(io_fileout);
		io_cleanup();
		if (rename != 0)
		{
			ttyputmsg(_("Old binary file saved in %s"), rename);
			efree(rename);
		}
		return(1);
	}

	/* first flush all changes so that database is clean */
	noundoallowed();

	/* initialize the file (writing version 10) */
	magic = MAGIC10;
	io_putout(&magic, 4);
	wordsize = SIZEOFINTSML;   io_putout(&wordsize, 1);
	wordsize = SIZEOFINTBIG;   io_putout(&wordsize, 1);
	aacount = el_maxaid;
	io_putoutbig(&aacount);
	techcount = el_maxtech;
	io_putoutbig(&techcount);

	/* count the number of objects in the database */
	nodeindex = portprotoindex = nodeprotoindex = arcindex = nodepprotoindex =
		portpprotoindex = arcprotoindex = cellindex = 0;

	/* prepare to locate references to facets in other libraries */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->temp2 = 0;
		for(c = olib->firstcell; c != NOCELL; c = c->nextcell)
			c->temp2 = 0;
	}
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto->primindex != 0) continue;
			ni->proto->temp2 = 1;
			ni->proto->cell->temp2 = 1;
		}
	}

	/* count and number the cells, facets, nodes, arcs, and ports in this library */
	for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
		c->temp1 = cellindex++;
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		np->temp1 = nodeprotoindex++;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			pp->temp1 = portprotoindex++;
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			ai->temp1 = arcindex++;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			ni->temp1 = nodeindex++;
	}

	/* count and number the facets and cells in other libraries */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		if (olib == lib) continue;
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp2 == 0) continue;
			np->temp1 = nodeprotoindex++;
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				pp->temp1 = portprotoindex++;
		}
		for(c = olib->firstcell; c != NOCELL; c = c->nextcell)
		{
			if (c->temp2 != 0) c->temp1 = cellindex++;
		}
	}

	/* count and number the primitive node and port prototypes */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			np->temp1 = -2 - nodepprotoindex++;
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				pp->temp1 = -2 - portpprotoindex++;
		}
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			ap->temp1 = -2 - arcprotoindex++;
	}

	/* write number of objects */
	io_putoutbig(&nodepprotoindex);
	io_putoutbig(&portpprotoindex);
	io_putoutbig(&arcprotoindex);
	io_putoutbig(&nodeprotoindex);
	io_putoutbig(&nodeindex);
	io_putoutbig(&portprotoindex);
	io_putoutbig(&arcindex);
	io_putoutbig(&geomindex);
	io_putoutbig(&cellindex);

	/* write the current facet */
	if (lib->curnodeproto == NONODEPROTO) curnodeproto = -1; else
		curnodeproto = lib->curnodeproto->temp1;
	io_putoutbig(&curnodeproto);

	if (io_verbose > 0)
	{
		ttyputmsg(_("Writing %ld aids, %ld technologies"), aacount, techcount);
		ttyputmsg(_("        %ld prim nodes, %ld prim ports, %ld arc protos"),
			nodepprotoindex, portpprotoindex, arcprotoindex);
		ttyputmsg(_("        %ld cells %ld facets, %ld ports, %ld nodes, %ld arcs"),
			cellindex, nodeprotoindex, portprotoindex, nodeindex, arcindex);
	}

	/* write the version number */
	io_putstring(el_version);

	/* number the views and write nonstandard ones */
	for(v = el_views; v != NOVIEW; v = v->nextview) v->temp1 = 0;
	el_unknownview->temp1 = -1;
	el_layoutview->temp1 = -2;
	el_schematicview->temp1 = -3;
	el_iconview->temp1 = -4;
	el_simsnapview->temp1 = -5;
	el_skeletonview->temp1 = -6;
	el_vhdlview->temp1 = -7;
	el_netlistview->temp1 = -8;
	el_docview->temp1 = -9;
	el_netlistnetlispview->temp1 = -10;
	el_netlistalsview->temp1 = -11;
	el_netlistquiscview->temp1 = -12;
	el_netlistrsimview->temp1 = -13;
	el_netlistsilosview->temp1 = -14;
	el_verilogview->temp1 = -15;
	i = 1;
	for(v = el_views; v != NOVIEW; v = v->nextview)
		if (v->temp1 == 0) v->temp1 = i++;
	i--;
	io_putoutbig(&i);
	for(v = el_views; v != NOVIEW; v = v->nextview)
	{
		if (v->temp1 < 0) continue;
		io_putstring(v->viewname);
		io_putstring(v->sviewname);
	}

	/* write total number of arcinsts, nodeinsts, and ports in each facet */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(a = 0, ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) a++;
		io_putoutbig(&a);
		for(n = 0, ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) n++;
		io_putoutbig(&n);
		for(p = 0, pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) p++;
		io_putoutbig(&p);
		if (io_verbose > 0) ttyputmsg(_("Facet %s has %ld arcs, %ld nodes, %ld ports"),
			describenodeproto(np), a, n, p);
	}

	/* write dummy numbers of arcinsts and nodeinst; count ports for external facets */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		if (olib == lib) continue;
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp2 == 0) continue;
			a = -1;
			io_putoutbig(&a);
			io_putoutbig(&a);
			for(p = 0, pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) p++;
			io_putoutbig(&p);
		}
	}

	/* write the names of technologies and primitive prototypes */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		/* write the technology name */
		io_putstring(tech->techname);

		/* count and write the number of primitive node prototypes */
		for(np = tech->firstnodeproto, i=0; np != NONODEPROTO; np = np->nextnodeproto) i++;
		io_putoutbig(&i);

		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			/* write the primitive node prototype name */
			io_putstring(np->primname);
			for(pp = np->firstportproto, i=0; pp != NOPORTPROTO; pp = pp->nextportproto) i++;
			io_putoutbig(&i);
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				io_putstring(pp->protoname);
		}

		/* count and write the number of arc prototypes */
		for(ap = tech->firstarcproto, i=0; ap != NOARCPROTO; ap = ap->nextarcproto) i++;
		io_putoutbig(&i);

		/* write the primitive arc prototype names */
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			io_putstring(ap->protoname);
	}

	/* write the names of the aids */
	for(i=0; i<el_maxaid; i++) io_putstring(el_aids[i].aidname);

	/* write the userbits for the library */
	io_putoutbig(&lib->userbits);

	/* write the aid lambda values */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		io_putoutbig(&lib->lambda[tech->techindex]);

	/* write the global namespace */
	io_writenamespace();
	io_outputstage = 1;

	/* write the library variables */
	io_writevariables(lib->firstvar, lib->numvar);

	/* write the aid variables */
	for(i=0; i<el_maxaid; i++)
		io_writevariables(el_aids[i].firstvar, el_aids[i].numvar);

	/* write the variables on technologies */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		io_writevariables(tech->firstvar, tech->numvar);

	/* write the arcproto variables */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
			io_writevariables(ap->firstvar, ap->numvar);

	/* write the variables on primitive node prototypes */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			io_writevariables(np->firstvar, np->numvar);

	/* write the variables on primitive port prototypes */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				io_writevariables(pp->firstvar, pp->numvar);

	/* write the view variables */
	i = 0;
	for(v = el_views; v != NOVIEW; v = v->nextview) i++;
	io_putoutbig(&i);
	for(v = el_views; v != NOVIEW; v = v->nextview)
	{
		io_putoutbig(&v->temp1);
		io_writevariables(v->firstvar, v->numvar);
	}

	/* write all of the cell names in this library */
	for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
	{
		if (stopping(STOPREASONBINARY)) longjmp(io_filerror, 1);

		/* write the cell name */
		io_putstring(c->cellname);

		/* write variable information */
		io_writevariables(c->firstvar, c->numvar);

		if (io_verbose > 0) ttyputmsg(_("Wrote cell %s"), c->cellname);
	}

	/* write all of the cell names in external libraries */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		if (olib == lib) continue;
		for(c = olib->firstcell; c != NOCELL; c = c->nextcell)
		{
			if (c->temp2 == 0) continue;

			/* write the cell name */
			io_putstring(c->cellname);

			/* write dummy variable information */
			save = c->numvar;   c->numvar = 0;
			io_writevariables(c->firstvar, c->numvar);
			c->numvar = save;
		}
	}

	/* write all of the facets in this library */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (stopping(STOPREASONBINARY)) longjmp(io_filerror, 1);
		io_writenodeproto(np, 1);
	}

	/* write all of the facets in external libraries */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		if (olib == lib) continue;
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (np->temp2 == 0) continue;
			io_writenodeproto(np, 0);
		}
	}

	/* write all of the arcs and nodes in this library */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (stopping(STOPREASONBINARY)) longjmp(io_filerror, 1);
		a = n = 0;
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			io_writearcinst(ai);
			a++;
		}
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			io_writenodeinst(ni);
			n++;
		}
		if (io_verbose > 0) ttyputmsg(_("Wrote %ld arcs and %ld nodes in facet %s"),
			a, n, describenodeproto(np));
	}

	/* close the output file */
	xclose(io_fileout);

	/* restore any damage to the database */
	io_cleanup();

	/* if the file was renamed, kill the backup copy */
	if (rename != 0)
	{
		if (eunlink(rename) < 0) ttyputerr(_("Cannot delete backup file %s"), rename);
		efree(rename);
	}

	ttyputmsg(_("%s written (%ld %s)"), name, nodeprotoindex,
		makeplural(_("facet"), nodeprotoindex));
	lib->userbits = (lib->userbits & ~(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) | READFROMDISK;
	return(0);
}

/*
 * routine to clean-up the internal database after it has been modified
 * for binary output
 */
void io_cleanup(void)
{
	/* if binary output didn't get far, no cleanup needed */
	if (io_outputstage == 0) return;

	/* restore damage to the global namespace */
	io_restorenamespace();
}

/******************** COMPONENT OUTPUT ********************/

void io_writenodeproto(NODEPROTO *np, INTSML thislib)
{
	INTBIG i;
	REGISTER PORTPROTO *pp;
	REGISTER LIBRARY *instlib;

	/* write facet information */
	io_putoutbig(&np->cell->temp1);
	io_putoutbig(&np->cellview->temp1);
	i = np->version;   io_putoutbig(&i);
	io_putoutbig(&np->creationdate);
	io_putoutbig(&np->revisiondate);

	/* write the nodeproto bounding box */
	io_putoutbig(&np->lowx);
	io_putoutbig(&np->highx);
	io_putoutbig(&np->lowy);
	io_putoutbig(&np->highy);

	if (thislib == 0)
	{
		instlib = np->cell->lib;
		io_putstring(instlib->libfile);
	}

	/* write the number of portprotos on this nodeproto */
	i = 0;
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) i++;
	io_putoutbig(&i);

	/* write the portprotos on this nodeproto */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (thislib != 0)
		{
			/* write the connecting subnodeinst for this portproto */
			if (pp->subnodeinst == NONODEINST) i = -1; else
				i = pp->subnodeinst->temp1;
			io_putoutbig(&i);

			/* write the portproto index in the subnodeinst */
			if (pp->subnodeinst == NONODEINST || pp->subportproto == NOPORTPROTO) i = -1; else
				i = pp->subportproto->temp1;
			io_putoutbig(&i);
		}

		/* write the portproto name */
		io_putstring(pp->protoname);

		if (thislib != 0)
		{
			/* write the text descriptor */
			io_putoutbig(&pp->textdescript);

			/* write the portproto aid information */
			io_putoutbig(&pp->userbits);

			/* write variable information */
			io_writevariables(pp->firstvar, pp->numvar);
		}
	}

	if (thislib != 0)
	{
		/* write aid information */
		io_putoutbig(&np->adirty);
		io_putoutbig(&np->userbits);

		/* write variable information */
		io_writevariables(np->firstvar, np->numvar);
	}

	if (io_verbose > 0) ttyputmsg(_("Wrote facet %s"), describenodeproto(np));
}

void io_writenodeinst(NODEINST *ni)
{
	INTBIG i;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;

	/* write the nodeproto pointer */
	io_putoutbig(&ni->proto->temp1);

	/* write descriptive information */
	io_putoutbig(&ni->lowx);
	io_putoutbig(&ni->lowy);
	io_putoutbig(&ni->highx);
	io_putoutbig(&ni->highy);
	i = ni->transpose;   io_putoutbig(&i);
	i = ni->rotation;    io_putoutbig(&i);
	io_putoutbig(&ni->textdescript);

	/* count the arc ports */
	i = 0;
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst) i++;
	io_putoutbig(&i);

	/* write the arc ports */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		/* write the arcinst index (and the particular end on that arc) */
		ai = pi->conarcinst;
		if (ai->end[0].portarcinst == pi) i = 0; else i = 1;
		i = (ai->temp1 << 1) + (ai->end[0].portarcinst == pi ? 0 : 1);
		io_putoutbig(&i);

		/* write the portinst prototype */
		io_putoutbig(&pi->proto->temp1);

		/* write the variable information */
		io_writevariables(pi->firstvar, pi->numvar);
	}

	/* count the exports */
	i = 0;
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst) i++;
	io_putoutbig(&i);

	/* write the exports */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
	{
		io_putoutbig(&pe->exportproto->temp1);

		/* write the portinst prototype */
		io_putoutbig(&pe->proto->temp1);

		/* write the variable information */
		io_writevariables(pe->firstvar, pe->numvar);
	}

	/* write the aid information */
	io_putoutbig(&ni->userbits);

	/* write variable information */
	io_writevariables(ni->firstvar, ni->numvar);
}

void io_writearcinst(ARCINST *arcinst)
{
	INTBIG i;

	/* write the arcproto pointer */
	io_putoutbig(&arcinst->proto->temp1);

	/* write basic arcinst information */
	io_putoutbig(&arcinst->width);

	/* write the arcinst end information */
	for(i=0; i<2; i++)
	{
		io_putoutbig(&arcinst->end[i].xpos);
		io_putoutbig(&arcinst->end[i].ypos);

		/* write the arcinst's connecting nodeinst index */
		io_putoutbig(&arcinst->end[i].nodeinst->temp1);
	}

	/* write the arcinst's aid information */
	io_putoutbig(&arcinst->userbits);

	/* write variable information */
	io_writevariables(arcinst->firstvar, arcinst->numvar);
}

/******************** VARIABLE ROUTINES ********************/

/* routine to write the global namespace */
void io_writenamespace(void)
{
	REGISTER INTSML i;
	INTBIG val;

	val = el_numnames;
	io_putoutbig(&val);
	if (el_numnames == 0) return;

	for(i=0; i<el_numnames; i++) io_putstring(el_namespace[i]);

	io_namesave = (char *)emalloc((el_numnames*2), el_tempcluster);
	if (io_namesave == 0)
	{
		ttyputerr(_("No memory! destroying internal variables to save file"));
		ttyputerr(_("Saved file is good, memory NOT: quit when save finishes!"));
		for(i=0; i<el_numnames; i++) *((INTSML *)el_namespace[i]) = i;
	} else for(i=0; i<el_numnames; i++)
	{
		io_namesave[2*i] = el_namespace[i][0];
		io_namesave[2*i+1] = el_namespace[i][1];
		*((INTSML *)el_namespace[i]) = i;
	}
}

void io_restorenamespace(void)
{
	REGISTER INTSML i;

	if (el_numnames == 0) return;
	for(i=0; i<el_numnames; i++)
	{
		el_namespace[i][0] = io_namesave[2*i];
		el_namespace[i][1] = io_namesave[2*i+1];
	}
	efree(io_namesave);
}

void io_writevariables(VARIABLE *firstvar, INTSML numvar)
{
	REGISTER INTSML i, j, datasize;
	INTBIG num, len, addr, ty;

	for(num=i=0; i<numvar; i++)
		if ((firstvar[i].type&VDONTSAVE) == 0) num++;
	io_putoutbig(&num);
	for(i=0; i<numvar; i++)
	{
		if ((firstvar[i].type&VDONTSAVE) != 0) continue;
		io_putout((void *)firstvar[i].key, SIZEOFINTSML);
		ty = firstvar[i].type;
		io_putoutbig(&ty);
		if ((ty&VDISPLAY) != 0) io_putoutbig(&firstvar[i].textdescript);
		addr = firstvar[i].addr;
		if ((ty&VISARRAY) != 0)
		{
			len = (ty&VLENGTH) >> VLENGTHSH;
			if ((ty&VTYPE) == VCHAR)
			{
				datasize = 1;
				if (len == 0)
					for(len=0; ((((char *)addr)[len])&0377) != 0377; len++)
						;
			} else if ((ty&VTYPE) == VDOUBLE)
			{
				datasize = SIZEOFINTBIG*2;
				if (len == 0)
					for(len=0; ((INTBIG *)addr)[len*2] != -1 &&
						((INTBIG *)addr)[len*2+1] != -1; len++)
							;
			} else if ((ty&VTYPE) == VSHORT)
			{
				datasize = 2;
				if (len == 0)
					for(len=0; ((INTSML *)addr)[len] != -1; len++)
						;
			} else
			{
				datasize = SIZEOFINTBIG;
				if (len == 0) for(len=0; ((INTBIG *)addr)[len] != -1; len++)
					;
			}
			io_putoutbig(&len);
			if ((ty&VTYPE) == VGENERAL)
			{
				for(j=0; j<len; j += 2)
				{
					io_putoutbig(((INTBIG *)(addr + (j+1)*datasize)));
					io_putoutvar((INTBIG *)(addr + j*datasize), *(INTBIG *)(addr + (j+1)*datasize));
				}
			} else
			{
				for(j=0; j<len; j++) io_putoutvar((INTBIG *)(addr + j*datasize), ty);
			}
		} else io_putoutvar(&addr, ty);
	}
}

void io_putoutvar(INTBIG *addr, INTBIG ty)
{
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER GEOM *geom;
	INTBIG j;
	static INTBIG nullptr = -1;

	if ((ty&(VCODE1|VCODE2)) != 0) ty = VSTRING;
	switch (ty&VTYPE)
	{
		case VADDRESS:
		case VFRACT:
		case VINTEGER:
			io_putoutbig(addr);
			break;
		case VFLOAT:
			io_putout(addr, sizeof(float));
			break;
		case VDOUBLE:
			io_putout(addr, sizeof(double));
			break;
		case VCHAR:
			io_putout(addr, sizeof (char));
			break;
		case VSTRING:
			io_putstring((char *)*addr);
			break;
		case VTECHNOLOGY:
			if ((TECHNOLOGY *)*addr != NOTECHNOLOGY)
			{
				j = ((TECHNOLOGY *)*addr)->techindex;
				io_putoutbig(&j);
			} else io_putoutbig(&nullptr);
			break;
		case VNODEINST:
			if ((NODEINST *)*addr != NONODEINST)
				io_putoutbig(&((NODEINST *)*addr)->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VNODEPROTO:
			if ((NODEPROTO *)*addr != NONODEPROTO)
				io_putoutbig(&((NODEPROTO *)*addr)->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VARCPROTO:
			if ((ARCPROTO *)*addr != NOARCPROTO)
				io_putoutbig(&((ARCPROTO *)*addr)->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VPORTPROTO:
			if ((PORTPROTO *)*addr != NOPORTPROTO)
				io_putoutbig(&((PORTPROTO *)*addr)->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VARCINST:
			if ((ARCINST *)*addr != NOARCINST)
				io_putoutbig(&((ARCINST *)*addr)->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VGEOM:
			geom = (GEOM *)*addr;
			io_putoutbig(&geom->entrytype);
			if ((INTBIG)geom->entryaddr.blind == -1) io_putoutbig(&nullptr); else
			{
				if (geom->entrytype == OBJNODEINST)
					io_putoutbig(&geom->entryaddr.ni->temp1); else
						if (geom->entrytype == OBJARCINST)
							io_putoutbig(&geom->entryaddr.ai->temp1);
			}
			break;
		case VPORTARCINST:
			pi = (PORTARCINST *)*addr;
			if (pi != NOPORTARCINST)
			{
				ai = pi->conarcinst;
				j = (ai->temp1 << 1) + (ai->end[0].portarcinst == pi ? 0 : 1);
				io_putoutbig(&j);
			} else io_putoutbig(&nullptr);
			break;
		case VPORTEXPINST:
			if ((PORTEXPINST *)*addr != NOPORTEXPINST)
				io_putoutbig(&((PORTEXPINST *)*addr)->exportproto->temp1); else
					io_putoutbig(&nullptr);
			break;
		case VLIBRARY:
			if ((LIBRARY *)*addr != NOLIBRARY)
				io_putstring(((LIBRARY *)*addr)->libname); else
					io_putstring("noname");
			break;
		case VAID:
			if ((AIDENTRY *)*addr != NOAID)
				io_putoutbig(&((AIDENTRY *)*addr)->aidindex); else
					io_putoutbig(&nullptr);
			break;
		case VSHORT:
			io_putout(addr, SIZEOFINTSML);
			break;
	}
}

/******************** I/O ROUTINES ********************/

void io_putoutbig(void *data)
{
	io_putout(data, SIZEOFINTBIG);
}

void io_putout(void *data, INTSML size)
{
	REGISTER INTBIG ret;

	if (size == 0)
	{
		ttyputmsg(_("Warning: null length data item; database may be bad"));
		return;
	}
	ret = xfwrite((char *)data, size, 1, io_fileout);
	if (ret == 0) longjmp(io_filerror, 1);
}

void io_putstring(char *name)
{
	INTBIG len;

	len = strlen(name);
	io_putoutbig(&len);
	if (len != 0) io_putout(name, (INTSML)len);
}
