/*
 * Electric(tm) VLSI Design System
 *
 * File: iobinaryi.c
 * Input/output aid: binary format input
 * 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 "database.h"
#include "eio.h"
#include "tecgen.h"
#include "tecschem.h"
#include "edialogs.h"

#define	REPORTINC   2000			/* bytes between graphical updates */

typedef struct
{
	INTSML       aidbitsmessed;		/* 0: aid order is correct */
	INTBIG       aabcount;			/* number of aidbits for old files */
	INTBIG       bytecount;			/* position within input file */
	char       **realname;			/* variable names */
	INTBIG       namecount;			/* number of variable names */
	INTBIG       filelength;		/* length of the file */
	INTBIG       reported;			/* reported progress in file */
	char         sizeofsmall;		/* number of bytes in file for in INTSML */
	char         sizeofbig;			/* number of bytes in file for in INTBIG */

	/* totals of objects in the file */
	INTBIG       magic;				/* file's magic number */
	INTBIG       aacount;			/* number of aids */
	INTBIG       techcount;			/* number of technologies */
	INTBIG       nodepprotoindex;	/* number of primitive node prototypes */
	INTBIG       portpprotoindex;	/* number of primitive port prototypes */
	INTBIG       arcprotoindex;		/* number of arc prototypes */
	INTBIG       nodeprotoindex;	/* number of facets */
	INTBIG       nodeindex;			/* number of node instances */
	INTBIG       portprotoindex;	/* number of port prototypes */
	INTBIG       arcindex;			/* number of arc instances */
	INTBIG       geomindex;			/* number of geometry modules */
	INTBIG       cellindex;			/* number of cells */
	INTBIG       curnodeproto;		/* current facet */

	/* input error message arrays */
	char       **techerror;			/* name of unknown technologies */
	char       **aiderror;			/* name of unknown aids */
	INTSML      *nodepprotoerror;	/* flag for unknown prim. NODEPROTOs */
	char       **nodepprotoorig;	/* name of unknown prim. NODEPROTOs */
	INTBIG      *nodepprototech;	/* tech. assoc. with prim. NODEPROTOs */
	char       **portpprotoerror;	/* name of unknown PORTPROTOs */
	char       **arcprotoerror;		/* name of unknown ARCPROTOs */
	INTSML       swap_bytes;		/* are the bytes swapped? 1 if so */
	INTBIG       swapped_floats;	/* number of swappped floating-point variables */
	INTBIG       swapped_doubles;	/* number of swappped double-precision variables */
	INTBIG       clipped_integers;	/* number of clipped integers */

	FILE        *filein;			/* channel for input */
	INTBIG      *newnames;			/* for adding new names to global namespace */
	jmp_buf      filerror;			/* nonlocal jump when I/O fails */
	NODEINST   **nodelist;			/* list of nodes for readin */
	INTBIG      *nodecount;			/* number of nodeinsts in each facet */
	ARCINST    **arclist;			/* list of arcs for readin */
	INTBIG      *arccount;			/* number of arcinsts in each facet */
	NODEPROTO  **nodeprotolist;		/* list of facets for readin */
	CELL       **celllist;			/* list of cells for readin */
	TECHNOLOGY **techlist;			/* list of technologies for readin */
	INTBIG      *aidlist;			/* list of aids for readin */
	PORTPROTO  **portprotolist;		/* list of portprotos for readin */
	INTBIG      *portcount;			/* number of portprotos in each facet */
	PORTPROTO  **portpprotolist;	/* list of primitive portprotos for readin */
	NODEPROTO  **nodepprotolist;	/* list of primitive nodeprotos for readin */
	ARCPROTO   **arcprotolist;		/* list of arcprotos for readin */
} BININPUTDATA;

static BININPUTDATA io_binindata;				/* all data pertaining to reading a file */
static char        *io_tempstring;				/* for reading temporary strings */
static INTSML       io_tempstringlength = 0;	/* length of temporary string */

/* prototypes for local routines */
char       *io_doreadlibrary(LIBRARY*);
INTSML      io_readnodeproto(NODEPROTO*);
INTSML      io_readnodeinst(NODEINST*);
void        io_readgeom(INTBIG*, INTBIG*);
INTSML      io_readarcinst(ARCINST*);
INTSML      io_readnamespace(void);
void        io_ignorevariables(void);
INTSML      io_readvariables(INTBIG, INTBIG);
INTSML      io_getinvar(INTBIG*, INTBIG);
INTBIG      io_arrangeaidbits(INTBIG);
char       *io_convertnodeproto(NODEPROTO**);
void        io_convertportproto(PORTPROTO**);
void        io_convertarcproto(ARCPROTO**);
ARCPROTO   *io_getarcprotolist(INTSML);
PORTPROTO  *io_getportpprotolist(INTBIG);
NODEPROTO  *io_getnodepprotolist(INTBIG);
TECHNOLOGY *io_gettechlist(INTSML);
void        io_getin(void *, INTSML, INTSML, INTSML);
char       *io_getstring(CLUSTER*);
char       *io_gettempstring(void);
void        io_getinbig(void *data);
void        io_getinubig(void *data);
void        io_getinsmall(void *data);
void        io_getin1byte(void *data);
void        io_readexternalnodeproto(LIBRARY *lib, INTBIG i);
void        io_ensureallports(LIBRARY *lib);
void        io_binfindallports(LIBRARY *lib, PORTPROTO *pp, INTBIG *lx, INTBIG *hx,
				INTBIG *ly, INTBIG *hy, INTSML *first, ARCPROTO **onlyarc, XARRAY trans);

/*
 * Routine to free all memory associated with this module.
 */
void io_freebininmemory(void)
{
	if (io_tempstringlength != 0) efree(io_tempstring);
}

INTSML io_readbinlibrary(LIBRARY *lib)
{
	return(io_doreadbinlibrary(lib, 1));
}

INTSML io_doreadbinlibrary(LIBRARY *lib, INTSML newprogress)
{
	REGISTER char *result;
	char *filename;
	extern DIALOG us_eprogressdialog;

	/* find the library file */
	result = truepath(lib->libfile);
	io_binindata.filein = xopen(result, FILETYPEBLIB, el_libdir, &filename);
	if (io_binindata.filein == NULL)
	{
		ttyputerr("File '%s' not found", result);
		return(1);
	}

	/* prepare status dialog */
	if (io_verbose < 0)
	{
		io_binindata.filelength = filesize(io_binindata.filein);
		if (io_binindata.filelength > 0)
		{
			if (newprogress != 0)
			{
				if (DiaInitDialog(&us_eprogressdialog) != 0)
				{
					xclose(io_binindata.filein);
					return(1);
				}
			}
			DiaPercent(1, 0);
			(void)initinfstr();
			(void)addstringtoinfstr("Reading library ");
			(void)addstringtoinfstr(lib->libname);
			DiaSetText(3, returninfstr());
			DiaSetText(2, "Initializing...");
		}
	} else io_binindata.filelength = 0;
	io_binindata.reported = 0;
	io_binindata.bytecount = 0;

	/* assume regular format */
	io_binindata.swap_bytes = 0;
	io_binindata.swapped_floats = 0;
	io_binindata.swapped_doubles = 0;
	io_binindata.clipped_integers = 0;

	/* now read the file */
	result = io_doreadlibrary(lib);

	/* clean up */
	xclose(io_binindata.filein);
	if (io_verbose < 0 && io_binindata.filelength > 0 && newprogress != 0)
		DiaDoneDialog();
	if (io_binindata.swapped_floats != 0)
		ttyputverbose("Read %ld swapped float variables; may be incorrect",
			io_binindata.swapped_floats);
	if (io_binindata.swapped_doubles != 0)
		ttyputverbose("Read %ld swapped double variables; may be incorrect",
			io_binindata.swapped_doubles);
	if (io_binindata.clipped_integers != 0)
		ttyputverbose("Read %ld clipped integers; may be incorrect",
			io_binindata.clipped_integers);
	if (*result == 0) return(0);
	ttyputerr("%s", result);
	return(1);
}

char *io_doreadlibrary(LIBRARY *lib)
{
	REGISTER INTSML te, imosconv, oldunit;
	REGISTER INTBIG i, thisone, *geomtype, *geommoreup, top,
		bot, look, nodeindex, arcindex, geomindex;
	INTBIG num, den, j, count, cou, arcinstpos, nodeinstpos, portprotopos;
	REGISTER char *name, *sname;
	REGISTER NODEPROTO *np, *onp, *lastnp, *ent;
	REGISTER NODEINST *ni;
	GEOM *geom;
	REGISTER ARCPROTO *ap;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER TECHNOLOGY *tech;
	REGISTER PORTEXPINST **portexpinstlist;
	REGISTER PORTARCINST **portarcinstlist;
	char *version;
	unsigned char chmagic[4];
	REGISTER VIEW *v;
	REGISTER CELL *c, *lastcell;
	REGISTER LIBRARY *savelib;

	if (setjmp(io_binindata.filerror)) return("error reading file");

	/* read magic number */
	io_getin(&io_binindata.magic, 4, SIZEOFINTBIG, 0);
	if (io_binindata.magic != MAGIC1 && io_binindata.magic != MAGIC2 &&
		io_binindata.magic != MAGIC3 && io_binindata.magic != MAGIC4 &&
		io_binindata.magic != MAGIC5 && io_binindata.magic != MAGIC6 &&
		io_binindata.magic != MAGIC7 && io_binindata.magic != MAGIC8 &&
		io_binindata.magic != MAGIC9 && io_binindata.magic != MAGIC10)
	{
		/* try swapping the bytes */
		chmagic[0] = io_binindata.magic & 0xFF;
		chmagic[1] = (io_binindata.magic >> 8) & 0xFF;
		chmagic[2] = (io_binindata.magic >> 16) & 0xFF;
		chmagic[3] = (io_binindata.magic >> 24) & 0xFF;
		io_binindata.magic = (chmagic[0] << 24) | (chmagic[1] << 16) |
			(chmagic[2] << 8) | chmagic[3];
		if (io_binindata.magic != MAGIC1 && io_binindata.magic != MAGIC2 &&
			io_binindata.magic != MAGIC3 && io_binindata.magic != MAGIC4 &&
			io_binindata.magic != MAGIC5 && io_binindata.magic != MAGIC6 &&
			io_binindata.magic != MAGIC7 && io_binindata.magic != MAGIC8 &&
			io_binindata.magic != MAGIC9 && io_binindata.magic != MAGIC10)
				return("Bad file format"); else
		{
			io_binindata.swap_bytes = 1;
			ttyputverbose("File has swapped bytes in the header. Will attempt to read it.");
		}
	}
	if (io_verbose > 0) switch (io_binindata.magic)
	{
		case MAGIC1: ttyputmsg("This library is 9 versions old");   break;
		case MAGIC2: ttyputmsg("This library is 8 versions old");   break;
		case MAGIC3: ttyputmsg("This library is 7 versions old");   break;
		case MAGIC4: ttyputmsg("This library is 6 versions old");   break;
		case MAGIC5: ttyputmsg("This library is 5 versions old");   break;
		case MAGIC6: ttyputmsg("This library is 4 versions old");   break;
		case MAGIC7: ttyputmsg("This library is 3 versions old");   break;
		case MAGIC8: ttyputmsg("This library is 2 versions old");   break;
		case MAGIC9: ttyputmsg("This library is 1 version old");    break;
	}

	/* determine size of "big" and "small" integers on disk */
	if (io_binindata.magic <= MAGIC10)
	{
		io_getin(&io_binindata.sizeofsmall, 1, 1, 0);
		io_getin(&io_binindata.sizeofbig, 1, 1, 0);
	} else
	{
		io_binindata.sizeofsmall = 2;
		io_binindata.sizeofbig = 4;
	}
	if (io_binindata.sizeofsmall > SIZEOFINTSML || io_binindata.sizeofbig > SIZEOFINTBIG)
		ttyputmsg("Warning: file has larger integers than memory: clipping may occur.");

	/* get count of objects in the file */
	io_getinbig(&io_binindata.aacount);
	io_getinbig(&io_binindata.techcount);
	io_getinbig(&io_binindata.nodepprotoindex);
	io_getinbig(&io_binindata.portpprotoindex);
	io_getinbig(&io_binindata.arcprotoindex);
	io_getinbig(&io_binindata.nodeprotoindex);
	io_getinbig(&io_binindata.nodeindex);
	io_getinbig(&io_binindata.portprotoindex);
	io_getinbig(&io_binindata.arcindex);
	io_getinbig(&io_binindata.geomindex);
	if (io_binindata.magic <= MAGIC9) io_getinbig(&io_binindata.cellindex); else
		io_binindata.cellindex = io_binindata.nodeprotoindex;
	io_getinbig(&io_binindata.curnodeproto);
	if (io_verbose > 0)
	{
		ttyputmsg("Reading %ld aids, %ld technologies", io_binindata.aacount,
			io_binindata.techcount);
		ttyputmsg("        %ld prim nodes, %ld prim ports, %ld arc protos",
			io_binindata.nodepprotoindex, io_binindata.portpprotoindex,
			io_binindata.arcprotoindex);
		ttyputmsg("        %ld cells, %ld facets, %ld ports, %ld nodes, %ld arcs",
			io_binindata.cellindex, io_binindata.nodeprotoindex,
			io_binindata.portprotoindex, io_binindata.nodeindex,
			io_binindata.arcindex);
	}

	/* get the Electric version (version 8 and later) */
	if (io_binindata.magic <= MAGIC8) version = io_getstring(el_tempcluster); else
		(void)allocstring(&version, "3.35", el_tempcluster);

	/* get the newly created views (version 9 and later) */
	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;
	if (io_binindata.magic <= MAGIC9)
	{
		io_getinbig(&j);
		for(i=0; i<j; i++)
		{
			name = io_getstring(db_cluster);
			sname = io_getstring(db_cluster);
			v = getview(name);
			if (v == NOVIEW)
			{
				v = allocview();
				if (v == NOVIEW) return("No memory");
				v->viewname = name;
				v->sviewname = sname;
				if (namesamen(name, "Schematic-Page-", 15) == 0)
					v->viewstate |= MULTIPAGEVIEW;
				v->nextview = el_views;
				el_views = v;
			} else
			{
				efree(name);
				efree(sname);
			}
			v->temp1 = i + 1;
		}
	}

	/* get the number of aidbits to ignore */
	if (io_binindata.magic <= MAGIC3 && io_binindata.magic >= MAGIC6)
	{
		/* versions 3, 4, 5, and 6 find this in the file */
		io_getinbig(&io_binindata.aabcount);
	} else
	{
		/* versions 1 and 2 compute this (versions 7 and later ignore it) */
		io_binindata.aabcount = io_binindata.aacount;
	}

	/* erase the current database */
	if (io_verbose > 0) ttyputmsg("Erasing old library");
	eraselibrary(lib);

	/* allocate pointers */
	io_binindata.nodelist = (NODEINST **)emalloc(((sizeof (NODEINST *)) * io_binindata.nodeindex), el_tempcluster);
	if (io_binindata.nodelist == 0) return("No memory");
	io_binindata.nodecount = emalloc((SIZEOFINTBIG * io_binindata.nodeprotoindex), el_tempcluster);
	if (io_binindata.nodecount == 0) return("No memory");
	io_binindata.nodeprotolist = (NODEPROTO **)emalloc(((sizeof (NODEPROTO *)) * io_binindata.nodeprotoindex), el_tempcluster);
	if (io_binindata.nodeprotolist == 0) return("No memory");
	io_binindata.portprotolist = (PORTPROTO **)emalloc(((sizeof (PORTPROTO *)) * io_binindata.portprotoindex), el_tempcluster);
	if (io_binindata.portprotolist == 0) return("No memory");
	io_binindata.portcount = emalloc((SIZEOFINTBIG * io_binindata.nodeprotoindex), el_tempcluster);
	if (io_binindata.portcount == 0) return("No memory");
	io_binindata.portpprotolist = (PORTPROTO **)emalloc(((sizeof (PORTPROTO *)) * io_binindata.portpprotoindex), el_tempcluster);
	if (io_binindata.portpprotolist == 0) return("No memory");
	io_binindata.portpprotoerror = (char **)emalloc(((sizeof (char *)) * io_binindata.portpprotoindex), el_tempcluster);
	if (io_binindata.portpprotoerror == 0) return("No memory");
	portexpinstlist = (PORTEXPINST **)emalloc(((sizeof (PORTEXPINST *)) * io_binindata.portprotoindex), el_tempcluster);
	if (portexpinstlist == 0) return("No memory");
	portarcinstlist = (PORTARCINST **)emalloc(((sizeof (PORTARCINST *)) * io_binindata.arcindex * 2), el_tempcluster);
	if (portarcinstlist == 0) return("No memory");
	io_binindata.arclist = (ARCINST **)emalloc(((sizeof (ARCINST *)) * io_binindata.arcindex), el_tempcluster);
	if (io_binindata.arclist == 0) return("No memory");
	io_binindata.arccount = emalloc((SIZEOFINTBIG * io_binindata.nodeprotoindex), el_tempcluster);
	if (io_binindata.arccount == 0) return("No memory");
	io_binindata.nodepprotolist = (NODEPROTO **)emalloc(((sizeof (NODEPROTO *)) * io_binindata.nodepprotoindex), el_tempcluster);
	if (io_binindata.nodepprotolist == 0) return("No memory");
	io_binindata.nodepprototech = emalloc((SIZEOFINTBIG * io_binindata.nodepprotoindex), el_tempcluster);
	if (io_binindata.nodepprototech == 0) return("No memory");
	io_binindata.nodepprotoerror = (INTSML *)emalloc((SIZEOFINTSML * io_binindata.nodepprotoindex), el_tempcluster);
	if (io_binindata.nodepprotoerror == 0) return("No memory");
	io_binindata.nodepprotoorig = (char **)emalloc(((sizeof (char *)) * io_binindata.nodepprotoindex), el_tempcluster);
	if (io_binindata.nodepprotoorig == 0) return("No memory");
	io_binindata.arcprotolist = (ARCPROTO **)emalloc(((sizeof (ARCPROTO *)) * io_binindata.arcprotoindex), el_tempcluster);
	if (io_binindata.arcprotolist == 0) return("No memory");
	io_binindata.arcprotoerror = (char **)emalloc(((sizeof (char *)) * io_binindata.arcprotoindex), el_tempcluster);
	if (io_binindata.arcprotoerror == 0) return("No memory");
	io_binindata.techlist = (TECHNOLOGY **)emalloc(((sizeof (TECHNOLOGY *)) * io_binindata.techcount), el_tempcluster);
	if (io_binindata.techlist == 0) return("No memory");
	io_binindata.techerror = (char **)emalloc(((sizeof (char *)) * io_binindata.techcount), el_tempcluster);
	if (io_binindata.techerror == 0) return("No memory");
	io_binindata.aidlist = emalloc((SIZEOFINTBIG * io_binindata.aacount), el_tempcluster);
	if (io_binindata.aidlist == 0) return("No memory");
	io_binindata.aiderror = (char **)emalloc(((sizeof (char *)) * io_binindata.aacount), el_tempcluster);
	if (io_binindata.aiderror == 0) return("No memory");

	/* versions 9 and later allocate cell pointers */
	if (io_binindata.magic <= MAGIC9)
	{
		io_binindata.celllist = (CELL **)emalloc(((sizeof (CELL *)) * io_binindata.cellindex), el_tempcluster);
		if (io_binindata.celllist == 0) return("No memory");
	}

	/* versions 4 and earlier allocate geometric pointers */
	if (io_binindata.magic > MAGIC5)
	{
		geomtype = emalloc((SIZEOFINTBIG * io_binindata.geomindex), el_tempcluster);
		if (geomtype == 0) return("No memory");
		geommoreup = emalloc((SIZEOFINTBIG * io_binindata.geomindex), el_tempcluster);
		if (geommoreup == 0) return("No memory");
		if (geomtype == 0 || geommoreup == 0) return("No memory");
	}

	/* get number of arcinsts and nodeinsts in each facet */
	if (io_binindata.magic != MAGIC1)
	{
		/* versions 2 and later find this in the file */
		nodeinstpos = arcinstpos = portprotopos = 0;
		for(i=0; i<io_binindata.nodeprotoindex; i++)
		{
			io_getinbig(&io_binindata.arccount[i]);
			io_getinbig(&io_binindata.nodecount[i]);
			io_getinbig(&io_binindata.portcount[i]);
			if (io_binindata.arccount[i] >= 0 || io_binindata.nodecount[i] >= 0)
			{
				arcinstpos += io_binindata.arccount[i];
				nodeinstpos += io_binindata.nodecount[i];
			}
			portprotopos += io_binindata.portcount[i];
		}

		/* verify that the number of node instances is equal to the total in the file */
		if (nodeinstpos != io_binindata.nodeindex)
		{
			ttyputerr("Error: facets have %ld nodes but library has %ld",
				nodeinstpos, io_binindata.nodeindex);
			return("Bad file");
		}
		if (arcinstpos != io_binindata.arcindex)
		{
			ttyputerr("Error: facets have %ld arcs but library has %ld",
				arcinstpos, io_binindata.arcindex);
			return("Bad file");
		}
		if (portprotopos != io_binindata.portprotoindex)
		{
			ttyputerr("Error: facets have %ld ports but library has %ld",
				portprotopos, io_binindata.portprotoindex);
			return("Bad file");
		}
	} else
	{
		/* version 1 computes this information */
		io_binindata.arccount[0] = io_binindata.arcindex;
		io_binindata.nodecount[0] = io_binindata.nodeindex;
		io_binindata.portcount[0] = io_binindata.portprotoindex;
		for(i=1; i<io_binindata.nodeprotoindex; i++)
			io_binindata.arccount[i] = io_binindata.nodecount[i] = io_binindata.portcount[i] = 0;
	}

	/* allocate all cells in the library */
	lib->firstcell = NOCELL;
	if (io_binindata.magic <= MAGIC9)
	{
		/* versions 9 and later allocate cells now */
		for(i=0; i<io_binindata.cellindex; i++)
		{
			io_binindata.celllist[i] = alloccell(lib->cluster);
			if (io_binindata.celllist[i] == NOCELL) return("No memory");
			io_binindata.celllist[i]->lib = lib;
			if (i > 0) io_binindata.celllist[i-1]->nextcell = io_binindata.celllist[i]; else
				lib->firstcell = io_binindata.celllist[0];
		}
	}

	/* allocate all facets in the library */
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		if (io_binindata.arccount[i] < 0 && io_binindata.nodecount[i] < 0)
		{
			/* this facet is from an external library */
			io_binindata.nodeprotolist[i] = NONODEPROTO;
		} else
		{
			io_binindata.nodeprotolist[i] = allocnodeproto(lib->cluster);
			if (io_binindata.nodeprotolist[i] == NONODEPROTO) return("No memory");
			io_binindata.nodeprotolist[i]->cellview = el_unknownview;
			io_binindata.nodeprotolist[i]->newestversion = io_binindata.nodeprotolist[i];

			/* versions 8 and earlier assume each facet is a cell */
			if (io_binindata.magic >= MAGIC8)
			{
				c = alloccell(lib->cluster);
				if (c == NOCELL) return("No memory");
				io_binindata.nodeprotolist[i]->cell = c;
				c->firstincell = io_binindata.nodeprotolist[i];
				c->lib = lib;
				if (i > 0) io_binindata.nodeprotolist[i-1]->cell->nextcell = c; else
					lib->firstcell = io_binindata.nodeprotolist[0]->cell;
			}
		}
	}

	/* allocate the nodes, arcs, and ports in each facet */
	nodeinstpos = arcinstpos = portprotopos = 0;
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		np = io_binindata.nodeprotolist[i];
		if (np == NONODEPROTO)
		{
			/* for external references, clear the port proto list */
			for(j=0; j<io_binindata.portcount[i]; j++)
				io_binindata.portprotolist[portprotopos+j] = NOPORTPROTO;
			portprotopos += io_binindata.portcount[i];
			continue;
		}

		/* allocate node instances in this facet */
		for(j=0; j<io_binindata.nodecount[i]; j++)
		{
			io_binindata.nodelist[nodeinstpos+j] = allocnodeinst(lib->cluster);
			if (io_binindata.nodelist[nodeinstpos+j] == NONODEINST) return("No memory");
		}
		if (io_binindata.nodecount[i] == 0) np->firstnodeinst = NONODEINST; else
			np->firstnodeinst = io_binindata.nodelist[nodeinstpos];
		for(j=0; j<io_binindata.nodecount[i]; j++)
		{
			ni = io_binindata.nodelist[j+nodeinstpos];
			geom = allocgeom(lib->cluster);
			if (geom == NOGEOM) return("No memory");
			ni->geom = geom;

			/* compute linked list of nodes in this facet */
			if (j == 0) ni->lastnodeinst = NONODEINST; else
				ni->lastnodeinst = io_binindata.nodelist[j+nodeinstpos-1];
			if (j+1 == io_binindata.nodecount[i]) ni->nextnodeinst = NONODEINST; else
				ni->nextnodeinst = io_binindata.nodelist[j+nodeinstpos+1];
		}
		nodeinstpos += io_binindata.nodecount[i];

		/* allocate port prototypes in this facet */
		for(j=0; j<io_binindata.portcount[i]; j++)
		{
			io_binindata.portprotolist[portprotopos+j] = allocportproto(lib->cluster);
			if (io_binindata.portprotolist[portprotopos+j] == NOPORTPROTO) return("No memory");
			portexpinstlist[portprotopos+j] = allocportexpinst(lib->cluster);
			if (portexpinstlist[portprotopos+j] == NOPORTEXPINST) return("No memory");
		}

		for(j=0; j<io_binindata.portcount[i]; j++)
		{
			thisone = j + portprotopos;
			if (portexpinstlist[thisone] == NOPORTEXPINST ||
				io_binindata.portprotolist[thisone] == NOPORTPROTO) return("No memory");
			io_binindata.portprotolist[thisone]->subportexpinst = portexpinstlist[thisone];
		}
		portprotopos += io_binindata.portcount[i];

		/* allocate arc instances and port arc instances in this facet */
		for(j=0; j<io_binindata.arccount[i]; j++)
		{
			io_binindata.arclist[arcinstpos+j] = allocarcinst(lib->cluster);
			if (io_binindata.arclist[arcinstpos+j] == NOARCINST) return("No memory");
		}
		for(j=0; j<io_binindata.arccount[i]*2; j++)
		{
			portarcinstlist[arcinstpos*2+j] = allocportarcinst(lib->cluster);
			if (portarcinstlist[arcinstpos*2+j] == NOPORTARCINST) return("No memory");
		}
		if (io_binindata.arccount[i] == 0) np->firstarcinst = NOARCINST; else
			np->firstarcinst = io_binindata.arclist[arcinstpos];
		for(j=0; j<io_binindata.arccount[i]; j++)
		{
			thisone = j + arcinstpos;
			ai = io_binindata.arclist[thisone];
			geom = allocgeom(lib->cluster);
			if (geom == NOGEOM) return("No memory");
			ai->geom = geom;
			ai->end[0].portarcinst = portarcinstlist[thisone*2];
			ai->end[1].portarcinst = portarcinstlist[thisone*2+1];

			/* compute linked list of arcs in this facet */
			if (j == 0) ai->lastarcinst = NOARCINST; else
				ai->lastarcinst = io_binindata.arclist[j+arcinstpos-1];
			if (j+1 == io_binindata.arccount[i]) ai->nextarcinst = NOARCINST; else
				ai->nextarcinst = io_binindata.arclist[j+arcinstpos+1];
		}
		arcinstpos += io_binindata.arccount[i];

		if (io_verbose > 0)
			ttyputmsg("Allocated %ld arcs, %ld nodes, %ld ports for facet %ld",
				io_binindata.arccount[i], io_binindata.nodecount[i], io_binindata.portcount[i], i);
	}

	efree((char *)portarcinstlist);
	efree((char *)portexpinstlist);

	/* setup pointers for technologies and primitives */
	io_binindata.nodepprotoindex = 0;
	io_binindata.portpprotoindex = 0;
	io_binindata.arcprotoindex = 0;
	for(te=0; te<io_binindata.techcount; te++)
	{
		/* associate the technology name with the true technology */
		name = io_gettempstring();
		if (name == 0) return("No memory");
		tech = gettechnology(name);

		/* conversion code for old technologies */
		imosconv = 0;
		if (tech == NOTECHNOLOGY)
		{
			if (namesame(name, "imos") == 0)
			{
				tech = gettechnology("mocmos");
				if (tech != NOTECHNOLOGY) imosconv++;
			} else if (namesame(name, "logic") == 0) tech = sch_tech;
		}

		if (tech == NOTECHNOLOGY)
		{
			tech = el_technologies;
			(void)allocstring(&io_binindata.techerror[te], name, el_tempcluster);
		} else io_binindata.techerror[te] = 0;
		io_binindata.techlist[te] = tech;

		/* get the number of primitive node prototypes */
		io_getinbig(&count);
		for(j=0; j<count; j++)
		{
			io_binindata.nodepprotoorig[io_binindata.nodepprotoindex] = 0;
			io_binindata.nodepprotoerror[io_binindata.nodepprotoindex] = 0;
			name = io_gettempstring();
			if (name == 0) return("No memory");
			if (imosconv != 0) name += 6;
			for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				if (strcmp(np->primname, name) == 0) break;
			if (np == NONODEPROTO)
			{
				if (io_verbose > 0)
					ttyputmsg("No node exactly named %s in technology %s", name, tech->techname);

				/* look for substring name match at start of name */
				i = strlen(name);
				for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
					if (namesamen(np->primname, name, mini(strlen(np->primname), i)) == 0)
						break;

				/* look for substring match at end of name */
				if (np == NONODEPROTO)
				{
					for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
					{
						thisone = strlen(np->primname);
						if (i >= thisone) continue;
						if (namesame(&np->primname[thisone-i], name) == 0) break;
					}
				}

				/* special cases: convert "message" and "cell-center" nodes */
				if (np == NONODEPROTO)
				{
					if (namesame(name, "Cell-Center") == 0) np = gen_facetcenterprim; else
					if (namesame(name, "Message") == 0 ||
						namesame(name, "Centered-Message") == 0 ||
						namesame(name, "Left-Message") == 0 ||
						namesame(name, "Right-Message") == 0) np = gen_invispinprim;
				}

				/* give up and use first primitive in this technology */
				if (np == NONODEPROTO) np = tech->firstnodeproto;

				/* construct the error message */
				(void)initinfstr();
				if (io_binindata.techerror[te] != 0) (void)addstringtoinfstr(io_binindata.techerror[te]); else
					(void)addstringtoinfstr(tech->techname);
				(void)addtoinfstr(':');
				(void)addstringtoinfstr(name);
				(void)allocstring(&io_binindata.nodepprotoorig[io_binindata.nodepprotoindex], returninfstr(), el_tempcluster);
				io_binindata.nodepprotoerror[io_binindata.nodepprotoindex] = 1;
			}
			io_binindata.nodepprototech[io_binindata.nodepprotoindex] = te;
			io_binindata.nodepprotolist[io_binindata.nodepprotoindex] = np;

			/* get the number of primitive port prototypes */
			io_getinbig(&cou);
			for(i=0; i<cou; i++)
			{
				io_binindata.portpprotoerror[io_binindata.portpprotoindex] = 0;
				name = io_gettempstring();
				if (name == 0) return("No memory");
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					if (strcmp(pp->protoname, name) == 0) break;

				/* convert special port names */
				if (pp == NOPORTPROTO)
					pp = io_convertoldportname(name, np);

				if (pp == NOPORTPROTO)
				{
					pp = np->firstportproto;
					if (io_binindata.nodepprotoerror[io_binindata.nodepprotoindex] == 0)
					{
						(void)initinfstr();
						(void)addstringtoinfstr(name);
						(void)addstringtoinfstr(" on ");
						if (io_binindata.nodepprotoorig[io_binindata.nodepprotoindex] != 0)
							(void)addstringtoinfstr(io_binindata.nodepprotoorig[io_binindata.nodepprotoindex]); else
						{
							if (io_binindata.techerror[te] != 0)
								(void)addstringtoinfstr(io_binindata.techerror[te]); else
									(void)addstringtoinfstr(tech->techname);
							(void)addtoinfstr(':');
							(void)addstringtoinfstr(np->primname);
						}
						(void)allocstring(&io_binindata.portpprotoerror[io_binindata.portpprotoindex],
							returninfstr(), el_tempcluster);
					}
				}
				io_binindata.portpprotolist[io_binindata.portpprotoindex++] = pp;
			}
			io_binindata.nodepprotoindex++;
		}

		/* get the number of arc prototypes */
		io_getinbig(&count);
		for(j=0; j<count; j++)
		{
			io_binindata.arcprotoerror[io_binindata.arcprotoindex] = 0;
			name = io_gettempstring();
			if (name == 0) return("No memory");
			if (imosconv != 0) name += 6;
			for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
				if (strcmp(ap->protoname, name) == 0) break;
			if (ap == NOARCPROTO)
			{
				ap = tech->firstarcproto;
				(void)initinfstr();
				if (io_binindata.techerror[te] != 0)
					(void)addstringtoinfstr(io_binindata.techerror[te]); else
						(void)addstringtoinfstr(tech->techname);
				(void)addtoinfstr(':');
				(void)addstringtoinfstr(name);
				(void)allocstring(&io_binindata.arcprotoerror[io_binindata.arcprotoindex], returninfstr(), el_tempcluster);
			}
			io_binindata.arcprotolist[io_binindata.arcprotoindex++] = ap;
		}
	}

	/* setup pointers for tools */
	io_binindata.aidbitsmessed = 0;
	for(i=0; i<io_binindata.aacount; i++)
	{
		name = io_gettempstring();
		if (name == 0) return("No memory");
		io_binindata.aiderror[i] = 0;
		for(j=0; j<el_maxaid; j++)
			if (strcmp(name, el_aids[j].aidname) == 0) break;
		if (j >= el_maxaid)
		{
			(void)allocstring(&io_binindata.aiderror[i], name, el_tempcluster);
			j = -1;
		}
		if (i != j) io_binindata.aidbitsmessed++;
		io_binindata.aidlist[i] = j;
	}
	if (io_binindata.magic <= MAGIC3 && io_binindata.magic >= MAGIC6)
	{
		/* versions 3, 4, 5, and 6 must ignore aidbits associations */
		for(i=0; i<io_binindata.aabcount; i++) (void)io_gettempstring();
	}

	/* get the library userbits */
	if (io_binindata.magic <= MAGIC7)
	{
		/* version 7 and later simply read the relevant data */
		io_getinubig(&lib->userbits);
	} else
	{
		/* version 6 and earlier must sift through the information */
		if (io_binindata.aabcount >= 1) io_getinubig(&lib->userbits);
		for(i=1; i<io_binindata.aabcount; i++) io_getinubig(&j);
	}
	lib->userbits &= ~(LIBCHANGEDMAJOR | LIBCHANGEDMINOR);
	lib->userbits |= READFROMDISK;

	/* set the lambda values in the library */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		lib->lambda[tech->techindex] = tech->deflambda;
	for(i=0; i<io_binindata.techcount; i++)
	{
		io_getinbig(&j);
		if (io_binindata.techerror[i] != 0) continue;
		tech = io_binindata.techlist[i];

		/* for version 4 or earlier, scale lambda by 20 */
		if (atoi(version) <= 4) j *= 20;

		/* account for any differences of internal unit in this library */
		oldunit = (INTSML)(((lib->userbits & LIBUNITS) >> LIBUNITSSH) << INTERNALUNITSSH);
		if (oldunit != (el_units&INTERNALUNITS))
		{
			db_getinternalunitscale(&num, &den, el_units, oldunit);
			j = muldiv(j, den, num);
		}

		/* if this is to be the current library, adjust technologies */
		if (lib == el_curlib) changelambda(tech->deflambda, j, tech);
		lib->lambda[tech->techindex] = j;
	}

	/* read the global namespace */
	io_binindata.realname = 0;
	io_binindata.newnames = 0;
	if (io_readnamespace() != 0) return("No memory");

	/* read the library variables */
	if (io_verbose > 0) ttyputmsg("Reading library variables");
	if (io_readvariables((INTBIG)lib, VLIBRARY) < 0) return("No memory");

	/* read the aid variables */
	if (io_verbose > 0) ttyputmsg("Reading aid variables");
	for(i=0; i<io_binindata.aacount; i++)
	{
		j = io_binindata.aidlist[i];
		if (j < 0) io_ignorevariables(); else
			if (io_readvariables((INTBIG)&el_aids[j], VAID) < 0)
				return("No memory");
	}

	/* read the technology variables */
	if (io_verbose > 0) ttyputmsg("Reading technology/primitive variables");
	for(te=0; te<io_binindata.techcount; te++)
	{
		tech = io_binindata.techlist[te];
		j = io_readvariables((INTBIG)tech, VTECHNOLOGY);
		if (j < 0) return("No memory");
		if (j > 0) (void)io_gettechlist(te);
	}

	/* read the arcproto variables */
	for(i=0; i<io_binindata.arcprotoindex; i++)
	{
		ap = io_binindata.arcprotolist[i];
		j = io_readvariables((INTBIG)ap, VARCPROTO);
		if (j < 0) return("No memory");
		if (j > 0) (void)io_getarcprotolist((INTSML)i);
	}

	/* read the primitive nodeproto variables */
	for(i=0; i<io_binindata.nodepprotoindex; i++)
	{
		np = io_binindata.nodepprotolist[i];
		j = io_readvariables((INTBIG)np, VNODEPROTO);
		if (j < 0) return("No memory");
		if (j > 0) (void)io_getnodepprotolist(i);
	}

	/* read the primitive portproto variables */
	for(i=0; i<io_binindata.portpprotoindex; i++)
	{
		pp = io_binindata.portpprotolist[i];
		j = io_readvariables((INTBIG)pp, VPORTPROTO);
		if (j < 0) return("No memory");
		if (j > 0) (void)io_getportpprotolist(i);
	}

	/* read the view variables (version 9 and later) */
	if (io_binindata.magic <= MAGIC9)
	{
		io_getinbig(&count);
		for(i=0; i<count; i++)
		{
			io_getinbig(&j);
			for(v = el_views; v != NOVIEW; v = v->nextview)
				if (v->temp1 == j) break;
			if (v == NOVIEW)
			{
				ttyputmsg("View index %ld not found", j);
				io_ignorevariables();
				continue;
			}
			if (io_readvariables((INTBIG)v, VVIEW) < 0) return("No memory");
		}
	}

	/* read the cells (version 9 and later) */
	if (io_binindata.magic <= MAGIC9)
	{
		for(i=0; i<io_binindata.cellindex; i++)
		{
			c = io_binindata.celllist[i];
			c->lib = lib;
			c->cellname = io_getstring(lib->cluster);

			/* read variable information */
			if (io_readvariables((INTBIG)c, VCELL) < 0) return("No memory");
		}
	}

	/* clear bit that indicates that a cell name has been modified (when external) */
	for(i=0; i<io_binindata.cellindex; i++)
		io_binindata.celllist[i]->temp1 = 0;

	/* read the facets */
	io_binindata.portprotoindex = 0;
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		np = io_binindata.nodeprotolist[i];
		if (np == NONODEPROTO) continue;
		if (io_readnodeproto(np) != 0) return("No memory");
		if (i == 0) np->lastnodeproto = NONODEPROTO; else
		{
			np->lastnodeproto = io_binindata.nodeprotolist[i-1];
			np->lastnodeproto->nextnodeproto = np;
		}
		np->nextnodeproto = NONODEPROTO;
	}
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		np = io_binindata.nodeprotolist[i];
		if (np != NONODEPROTO) continue;
		io_readexternalnodeproto(lib, i);
	}
	if (io_binindata.nodeprotoindex <= 0) lib->firstnodeproto = NONODEPROTO; else
		lib->firstnodeproto = io_binindata.nodeprotolist[0];

	/* convert port references in external facets */
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		np = io_binindata.nodeprotolist[i];
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (pp->temp2 == 0) continue;
			io_convertportproto(&pp->subportproto);
		}
	}
	if (io_verbose > 0) ttyputmsg("Done reading facets");

	/* read the facet contents: arcs and nodes */
	nodeindex = arcindex = geomindex = 0;
	for(i=0; i<io_binindata.nodeprotoindex; i++)
	{
		if (stopping("Binary input") != 0) return("Library incomplete");
		np = io_binindata.nodeprotolist[i];
		if (io_verbose != 0)
		{
			savelib = el_curlib;   el_curlib = lib;
			if (io_verbose < 0 && io_binindata.filelength > 0)
			{
				(void)initinfstr();
				(void)addstringtoinfstr("Reading ");
				(void)addstringtoinfstr(describenodeproto(np));
				DiaSetText(2, returninfstr());
			} else ttyputmsg("Reading %s", describenodeproto(np));
			el_curlib = savelib;
		}
		if (io_binindata.magic > MAGIC5)
		{
			/* versions 4 and earlier must read some geometric information */
			j = geomindex;
			io_readgeom(&geomtype[j], &geommoreup[j]);   j++;
			io_readgeom(&geomtype[j], &geommoreup[j]);   j++;
			top = j;   io_readgeom(&geomtype[j], &geommoreup[j]);   j++;
			bot = j;   io_readgeom(&geomtype[j], &geommoreup[j]);   j++;
			for(;;)
			{
				io_readgeom(&geomtype[j], &geommoreup[j]);   j++;
				if (geommoreup[j-1] == top) break;
			}
			geomindex = j;
			for(look = bot; look != top; look = geommoreup[look])
				if (geomtype[look] == OBJARCINST)
			{
				io_binindata.arclist[arcindex]->parent = np;
				if (io_readarcinst(io_binindata.arclist[arcindex]) != 0) return("No memory");
				arcindex++;
			} else if (geomtype[look] == OBJNODEINST)
			{
				io_binindata.nodelist[nodeindex]->parent = np;
				if (io_readnodeinst(io_binindata.nodelist[nodeindex]) != 0) return("No memory");
				nodeindex++;
			}
		} else
		{
			/* version 5 and later find the arcs and nodes in linear order */
			for(j=0; j<io_binindata.arccount[i]; j++)
			{
				io_binindata.arclist[arcindex]->parent = np;
				if (io_readarcinst(io_binindata.arclist[arcindex]) != 0) return("No memory");
				arcindex++;
			}
			for(j=0; j<io_binindata.nodecount[i]; j++)
			{
				io_binindata.nodelist[nodeindex]->parent = np;
				if (io_readnodeinst(io_binindata.nodelist[nodeindex]) != 0) return("No memory");
				nodeindex++;
			}
		}
	}

	if (io_verbose < 0 && io_binindata.filelength > 0)
	{
		DiaPercent(1, 100);
		DiaSetText(2, "Cleaning up...");
	}

	/* transform indices to pointers */
	if (io_binindata.curnodeproto != -1 && io_binindata.curnodeproto < io_binindata.nodeprotoindex)
		lib->curnodeproto = io_binindata.nodeprotolist[io_binindata.curnodeproto]; else
			lib->curnodeproto = NONODEPROTO;

	/* create proper cell lists (version 9 and later) */
	if (io_binindata.magic <= MAGIC9)
	{
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			c = np->cell;

			/* see if there is already a facet with this view */
			lastnp = NONODEPROTO;
			for(onp = c->firstincell; onp != NONODEPROTO; onp = onp->nextincell)
			{
				if (onp->cellview == np->cellview) break;
				lastnp = onp;
			}
			if (onp != NONODEPROTO)
			{
				/* new version of a cell/view, find place in list */
				if (np->version >= onp->version)
				{
					/* place on top of list */
					if (lastnp == NONODEPROTO) c->firstincell = np; else
						lastnp->nextincell = np;
					np->nextincell = onp->nextincell;
					onp->nextincell = NONODEPROTO;
					np->lastversion = onp;
					for(; onp != NONODEPROTO; onp = onp->lastversion)
						onp->newestversion = np;
					np->newestversion = np;
				} else
				{
					/* find place in list of versions */
					np->newestversion = onp;
					ent = NONODEPROTO;
					for(; onp != NONODEPROTO; onp = onp->lastversion)
					{
						if (np->version >= onp->version) break;
						ent = onp;
					}
					ent->lastversion = np;
					np->lastversion = onp;
				}
			} else
			{
				/* this view is new */
				np->lastversion = NONODEPROTO;
				np->newestversion = np;
				np->nextincell = c->firstincell;
				c->firstincell = np;
			}
		}

		/* remove cells that were external references */
		lastcell = NOCELL;
		for(i=0; i<io_binindata.cellindex; i++)
		{
			c = io_binindata.celllist[i];
			if (c->firstincell != NONODEPROTO) lastcell = c; else
			{
				if (lastcell == NOCELL) lib->firstcell = c->nextcell; else
					lastcell->nextcell = c->nextcell;
				efree((char *)c->cellname);
				efree((char *)c);
			}
		}
	}

	/* link up the nodes and arcs to their geometry modules */
	for(i=0; i<io_binindata.nodeindex; i++)
	{
		ni = io_binindata.nodelist[i];
		geom = ni->geom;
		geom->entrytype = OBJNODEINST;  geom->entryaddr.ni = ni;
		linkgeom(geom, ni->parent);
	}
	for(i=0; i<io_binindata.arcindex; i++)
	{
		ai = io_binindata.arclist[i];
		(void)setshrinkvalue(ai, 0);
		geom = ai->geom;
		geom->entrytype = OBJARCINST;  geom->entryaddr.ai = ai;
		linkgeom(geom, ai->parent);
	}

	/* create clusters for the cells */
	for(c = lib->firstcell; c != NOCELL; c = c->nextcell)
	{
		c->cluster = alloccluster(c->cellname);
		if (c->cluster == NOCLUSTER) return("No memory");
	}

	/* create ports on facets that were not found in other libraries */
	io_ensureallports(lib);

	/* store the version number in the library */
	nextvarchangequiet();
	(void)setvalkey((INTBIG)lib, VLIBRARY, makekey("LIB_former_version"),
		(INTBIG)version, VSTRING|VDONTSAVE);
	efree(version);

	/* create those pointers not on the disk file */
	if (io_verbose < 0 && io_binindata.filelength > 0)
		io_fixnewlib(lib, 2); else
			io_fixnewlib(lib, 0);

	/* look for any database changes that did not matter */
	for(te=0; te<io_binindata.techcount; te++)
		if (io_binindata.techerror[te] != 0)
	{
		if (io_verbose > 0)
			ttyputmsg("Unknown technology %s not used so all is well",
				io_binindata.techerror[te]);
		efree(io_binindata.techerror[te]);
	}
	for(i=0; i<io_binindata.aacount; i++)
		if (io_binindata.aiderror[i] != 0)
	{
		if (io_verbose > 0)
			ttyputmsg("Unknown aid %s not used so all is well",
				io_binindata.aiderror[i]);
		efree(io_binindata.aiderror[i]);
	}
	for(i=0; i<io_binindata.nodepprotoindex; i++)
		if (io_binindata.nodepprotoorig[i] != 0)
	{
		if (io_verbose > 0 && io_binindata.nodepprotoerror[i] != 0)
			ttyputmsg("Unknown node %s not used so all is well",
				io_binindata.nodepprotoorig[i]);
		efree(io_binindata.nodepprotoorig[i]);
	}
	for(i=0; i<io_binindata.portpprotoindex; i++)
		if (io_binindata.portpprotoerror[i] != 0)
	{
		if (io_verbose > 0) ttyputmsg("Unknown port %s not used so all is well",
			io_binindata.portpprotoerror[i]);
		efree(io_binindata.portpprotoerror[i]);
	}
	for(i=0; i<io_binindata.arcprotoindex; i++)
		if (io_binindata.arcprotoerror[i] != 0)
	{
		if (io_verbose > 0) ttyputmsg("Unknown arc %s not used so all is well",
			io_binindata.arcprotoerror[i]);
		efree(io_binindata.arcprotoerror[i]);
	}

	/* free the memory used here */
	if (io_binindata.realname != 0)
	{
		for(i=0; i<io_binindata.namecount; i++) efree(io_binindata.realname[i]);
		efree((char *)io_binindata.realname);
	}
	if (io_binindata.newnames != 0) efree((char *)io_binindata.newnames);
	if (io_binindata.magic > MAGIC5)
	{
		/* versions 4 and earlier used geometric data */
		efree((char *)geomtype);
		efree((char *)geommoreup);
	}
	efree((char *)io_binindata.nodelist);
	efree((char *)io_binindata.nodecount);
	efree((char *)io_binindata.nodeprotolist);
	efree((char *)io_binindata.arclist);
	efree((char *)io_binindata.arccount);
	efree((char *)io_binindata.nodepprotolist);
	efree((char *)io_binindata.nodepprototech);
	efree((char *)io_binindata.portprotolist);
	efree((char *)io_binindata.portcount);
	efree((char *)io_binindata.portpprotolist);
	efree((char *)io_binindata.arcprotolist);
	efree((char *)io_binindata.techlist);
	efree((char *)io_binindata.aidlist);
	efree((char *)io_binindata.techerror);
	efree((char *)io_binindata.aiderror);
	efree((char *)io_binindata.nodepprotoerror);
	efree((char *)io_binindata.nodepprotoorig);
	efree((char *)io_binindata.portpprotoerror);
	efree((char *)io_binindata.arcprotoerror);
	if (io_binindata.magic <= MAGIC9) efree((char *)io_binindata.celllist);

	return("");
}

/******************** COMPONENT INPUT ********************/

/* routine to read node prototype.  returns nonzero upon error */
INTSML io_readnodeproto(NODEPROTO *np)
{
	INTBIG i, portcount, k;
	REGISTER INTSML j;
	REGISTER PORTPROTO *pp, *lpt;
	REGISTER VIEW *v;

	/* read the cell information (version 9 and later) */
	if (io_binindata.magic <= MAGIC9)
	{
		io_getinbig(&np->cell);
		np->cell = io_binindata.celllist[(INTBIG)np->cell];
		io_getinbig(&np->cellview);
		for(v = el_views; v != NOVIEW; v = v->nextview)
			if (v->temp1 == (INTBIG)np->cellview) break;
		if (v == NOVIEW) v = el_unknownview;
		np->cellview = v;
		io_getinbig(&i);
		np->version = (INTSML)i;
		io_getinubig(&np->creationdate);
		io_getinubig(&np->revisiondate);
	}

	/* versions 8 and earlier read a cell name */
	if (io_binindata.magic >= MAGIC8)
	{
		np->cell->cellname = io_getstring(np->cell->lib->cluster);
		if (np->cell->cellname == 0) return(1);
		if (io_verbose > 0) ttyputmsg("Reading cell %s", np->cell->cellname);
	}

	/* set the nodeproto primitive index */
	np->primindex = 0;

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

	/* reset the first nodeinst index */
	np->firstinst = NONODEINST;

	/* zap the technology field */
	np->tech = NOTECHNOLOGY;

	/* read the library list of nodeproto indices (versions 5 or older) */
	if (io_binindata.magic >= MAGIC5)
	{
		io_getinbig(&np->lastnodeproto);
		(void)io_convertnodeproto(&np->lastnodeproto);
		io_getinbig(&np->nextnodeproto);
		(void)io_convertnodeproto(&np->nextnodeproto);
	}

	/* read the number of portprotos on this nodeproto */
	io_getinbig(&portcount);

	/* read the portprotos on this nodeproto */
	lpt = NOPORTPROTO;
	for(j=0; j<portcount; j++)
	{
		/* set pointers to portproto */
		pp = io_binindata.portprotolist[io_binindata.portprotoindex++];
		if (lpt == NOPORTPROTO) np->firstportproto = pp; else
			lpt->nextportproto = pp;
		lpt = pp;

		/* set the parent pointer */
		pp->parent = np;

		/* read the connecting subnodeinst for this portproto */
		io_getinbig(&i);
		if (i >= 0 && i < io_binindata.nodeindex)
			pp->subnodeinst = io_binindata.nodelist[i]; else
		{
			ttyputerr("Warning: corrupt data on port.  Do a 'Check and Repair Library'");
			pp->subnodeinst = NONODEINST;
		}			

		/* read the sub-port prototype of the subnodeinst */
		io_getinbig(&i);
		pp->subportproto = (PORTPROTO *)i;
		if (i >= 0 && io_binindata.portprotolist[i] == NOPORTPROTO)
		{
			pp->temp2 = 1;
		} else
		{
			io_convertportproto(&pp->subportproto);
			pp->temp2 = 0;
		}

		/* read the portproto name and text descriptor */
		pp->protoname = io_getstring(np->cell->lib->cluster);
		if (pp->protoname == 0) return(1);
		if (io_binindata.magic <= MAGIC9) io_getinubig(&pp->textdescript);

		/* ignore the "seen" bits (versions 8 and older) */
		if (io_binindata.magic > MAGIC9) io_getinubig(&k);

		/* read the portproto aid information */
		if (io_binindata.magic <= MAGIC7)
		{
			/* version 7 and later simply read the relevant data */
			io_getinubig(&pp->userbits);

			/* versions 7 and 8 ignore net number */
			if (io_binindata.magic >= MAGIC8) io_getinbig(&k);
		} else
		{
			/* version 6 and earlier must sift through the information */
			if (io_binindata.aabcount >= 1) io_getinubig(&pp->userbits);
			for(i=1; i<io_binindata.aabcount; i++) io_getinubig(&k);
		}

		/* read variable information */
		if (io_readvariables((INTBIG)pp, VPORTPROTO) < 0) return(1);
	}

	/* set final pointer in portproto list */
	if (lpt == NOPORTPROTO) np->firstportproto = NOPORTPROTO; else
		lpt->nextportproto = NOPORTPROTO;

	/* read the facet's geometry modules */
	if (io_binindata.magic > MAGIC5)
	{
		/* versions 4 and older have geometry module pointers (ignore it) */
		io_getinbig(&i);
		io_getinbig(&i);
		io_getinbig(&i);
		io_getinbig(&i);
		io_getinbig(&i);
	}

	/* read aid information */
	io_getinubig(&np->adirty);
	np->adirty = io_arrangeaidbits((INTBIG)np->adirty);
	if (io_binindata.magic <= MAGIC7)
	{
		/* version 7 and later simply read the relevant data */
		io_getinubig(&np->userbits);

		/* versions 7 and 8 ignore net number */
		if (io_binindata.magic >= MAGIC8) io_getinbig(&k);
	} else
	{
		/* version 6 and earlier must sift through the information */
		if (io_binindata.aabcount >= 1) io_getinubig(&np->userbits);
		for(i=1; i<io_binindata.aabcount; i++) io_getinubig(&k);
	}

	/* build the dummy geometric structure for this facet */
	if (geomstructure(np) != 0) return(1);

	/* read variable information */
	if (io_readvariables((INTBIG)np, VNODEPROTO) < 0) return(1);
	return(0);
}

/* routine to read node prototype for external references */
void io_readexternalnodeproto(LIBRARY *lib, INTBIG i)
{
	INTBIG portcount, k, vers, lowx, highx, lowy, highy, len, insx, insy;
	UINTBIG creation, revision;
	REGISTER INTSML j, newfacet, filetype;
	REGISTER LIBRARY *elib;
	REGISTER char *protoname, *libname, *pt;
	char *filename, *libfile, *oldline2, *oldline3;
	FILE *io;
	REGISTER PORTPROTO *pp, *lpt;
	REGISTER NODEPROTO *np, *onp;
	REGISTER NODEINST *ni;
	REGISTER VIEW *v;
	REGISTER CELL *c;
	BININPUTDATA savebinindata;

	/* read the cell information */
	io_getinbig(&k);
	c = io_binindata.celllist[k];
	io_getinbig(&k);
	for(v = el_views; v != NOVIEW; v = v->nextview)
		if (v->temp1 == k) break;
	if (v == NOVIEW) v = el_unknownview;
	io_getinbig(&vers);
	io_getinubig(&creation);
	io_getinubig(&revision);

	/* read the nodeproto bounding box */
	io_getinbig(&lowx);
	io_getinbig(&highx);
	io_getinbig(&lowy);
	io_getinbig(&highy);

	/* get the path to the library file */
	libfile = io_getstring(el_tempcluster);

	/* see if this library is already read in */
	(void)initinfstr();
	(void)addstringtoinfstr(skippath(libfile));
	libname = returninfstr();
	len = strlen(libname);
	filetype = FILETYPEBLIB;
	if (len > 5 && namesame(&libname[len-5], ".elib") == 0) libname[len-5] = 0; else
	{
		if (len > 4 && namesame(&libname[len-4], ".txt") == 0)
		{
			libname[len-4] = 0;
			filetype = FILETYPETLIB;
		}
	}
	elib = getlibrary(libname);
	if (elib == NOLIBRARY)
	{
		/* library does not exist: see if file is there */
		io = xopen(truepath(libfile), filetype, el_libdir, &filename);
		if (io != 0)
		{
			xclose(io);
			ttyputmsg("Reading referenced library %s", libfile);
		} else
		{
			(void)initinfstr();
			(void)addstringtoinfstr("Reference library '");
			(void)addstringtoinfstr(libname);
			(void)addstringtoinfstr("'");
			pt = fileselect(returninfstr(), filetype, "");
			if (pt != 0)
				(void)reallocstring(&libfile, pt, el_tempcluster);
		}
		elib = newlibrary(libname, libfile);
		if (elib == NOLIBRARY) { efree(libfile);   return; }

		/* read the external library */
		savebinindata = io_binindata;
		if (io_verbose < 0 && io_binindata.filelength > 0)
		{
			(void)allocstring(&oldline2, DiaGetText(2), el_tempcluster);
			(void)allocstring(&oldline3, DiaGetText(3), el_tempcluster);
		}

		len = strlen(libfile);
		if (len > 4 && namesame(&libfile[len-4], ".txt") == 0)
		{
			/* ends in ".txt", presume text file */
			k = io_doreadtextlibrary(elib, 0);
		} else
		{
			/* all other endings: presume binary file */
			k = io_doreadbinlibrary(elib, 0);
		}
		for(k=0; k<el_maxaid; k++)
			if (el_aids[k].readlibrary != 0)
				(*el_aids[k].readlibrary)(elib);
		io_binindata = savebinindata;
		if (io_verbose < 0 && io_binindata.filelength > 0)
		{
			DiaPercent(1, io_binindata.bytecount*100/io_binindata.filelength);
			DiaSetText(2, oldline2);
			DiaSetText(3, oldline3);
			efree(oldline2);
			efree(oldline3);
		}
	}

	/* find this facet in the external library */
	for(np = elib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (namesame(np->cell->cellname, c->cellname) != 0) continue;
		if (np->cellview != v) continue;
		if (np->version != vers) continue;
		break;
	}
	if (np == NONODEPROTO)
	{
		/* facet not found in library: issue warning */
		(void)initinfstr();
		(void)addstringtoinfstr(c->cellname);
		if (v != el_unknownview)
		{
			(void)addtoinfstr('{');
			(void)addstringtoinfstr(v->sviewname);
			(void)addtoinfstr('}');
		}
		ttyputerr("Cannot find facet %s in library %s..creating dummy verision",
			returninfstr(), elib->libname);
	} else
	{
		/* facet found: make sure it is valid */
		if (np->revisiondate != revision || np->lowx != lowx || np->highx != highx ||
			np->lowy != lowy || np->highy != highy)
		{
			ttyputerr("Warning: facet %s in library %s has been modified",
				describenodeproto(np), elib->libname);
			np = NONODEPROTO;
		}
	}
	if (np != NONODEPROTO) newfacet = 0; else
	{
		/* create a facet that meets these specs */
		newfacet = 1;
		np = allocnodeproto(lib->cluster);
		if (np == NONODEPROTO) return;
		np->primindex = 0;
		np->lowx = lowx;
		np->highx = highx;
		np->lowy = lowy;
		np->highy = highy;
		np->firstinst = NONODEINST;
		np->firstnodeinst = NONODEINST;
		np->firstarcinst = NOARCINST;
		np->tech = NOTECHNOLOGY;
		np->firstportproto = NOPORTPROTO;
		np->adirty = 0;
		np->cell = c;
		np->cellview = v;
		np->creationdate = creation;
		np->revisiondate = revision;

		/* rename the cell */
		if (c->temp1 == 0)
		{
			c->temp1 = 1;
			(void)initinfstr();
			(void)addstringtoinfstr(c->cellname);
			(void)addstringtoinfstr("FROM");
			(void)addstringtoinfstr(elib->libname);
			(void)reallocstring(&c->cellname, returninfstr(), lib->cluster);
		}

		/* determine version number of this facet */
		np->version = 1;
		for(onp = c->firstincell; onp != NONODEPROTO; onp = onp->nextincell)
			if (onp->cellview == v) np->version = onp->version + 1;

		/* insert in the library and facet structures */
		if (i != 0)
		{
			np->lastnodeproto = io_binindata.nodeprotolist[i-1];
			np->lastnodeproto->nextnodeproto = np;
		}

		/* create initial R-tree data */
		if (geomstructure(np) != 0) return;

		/* create two Invisible Pins to define the facet size */
		insx = gen_invispinprim->highx - gen_invispinprim->lowx;
		insy = gen_invispinprim->highy - gen_invispinprim->lowy;
		ni = allocnodeinst(lib->cluster);
		ni->proto = gen_invispinprim;
		ni->parent = np;
		ni->nextnodeinst = np->firstnodeinst;
		np->firstnodeinst = ni;
		ni->lowx = lowx;   ni->highx = lowx + insx;
		ni->lowy = lowy;   ni->highy = lowy + insy;
		ni->geom = allocgeom(lib->cluster);
		ni->geom->entrytype = OBJNODEINST;   ni->geom->entryaddr.ni = ni;
		linkgeom(ni->geom, np);

		ni = allocnodeinst(lib->cluster);
		ni->proto = gen_invispinprim;
		ni->parent = np;
		ni->nextnodeinst = np->firstnodeinst;
		np->firstnodeinst = ni;
		ni->highx = highx;   ni->lowx = highx - insx;
		ni->highy = highy;   ni->lowy = highy - insy;
		ni->geom = allocgeom(lib->cluster);
		ni->geom->entrytype = OBJNODEINST;   ni->geom->entryaddr.ni = ni;
		linkgeom(ni->geom, np);
	}
	io_binindata.nodeprotolist[i] = np;

	/* read the number of portprotos on this nodeproto */
	io_getinbig(&portcount);

	/* read the portprotos on this nodeproto */
	lpt = NOPORTPROTO;
	for(j=0; j<portcount; j++)
	{
		/* read the portproto name */
		protoname = io_gettempstring();
		if (protoname == 0) return;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (namesame(protoname, pp->protoname) == 0) break;
		if (pp == NOPORTPROTO)
		{
			if (newfacet == 0) ttyputerr("Cannot find port %s on facet %s in library %s", 
				protoname, describenodeproto(np), elib->libname);
			pp = allocportproto(lib->cluster);
			(void)allocstring(&pp->protoname, protoname, lib->cluster);
			pp->parent = np;

			if (lpt == NOPORTPROTO) np->firstportproto = pp; else
				lpt->nextportproto = pp;
			lpt = pp;
		}
		pp->temp2 = 0;
		io_binindata.portprotolist[io_binindata.portprotoindex] = pp;
		io_binindata.portprotoindex++;
	}
	efree(libfile);
}

/* routine to read a node instance.  returns nonzero upon error */
INTSML io_readnodeinst(NODEINST *ni)
{
	INTBIG i, k;
	REGISTER INTSML j;
	REGISTER char *inst;
	REGISTER PORTARCINST *pi, *lpo;
	REGISTER PORTEXPINST *pe, *lpe;
	PORTPROTO *pp;
	REGISTER ARCINST *ai;
	static INTBIG orignodename = 0;

	/* read the nodeproto index */
	io_getinbig(&ni->proto);
	inst = io_convertnodeproto(&ni->proto);
	if (inst != 0)
	{
		if (orignodename == 0) orignodename = makekey("NODE_original_name");
		nextvarchangequiet();
		(void)setvalkey((INTBIG)ni, VNODEINST, orignodename, (INTBIG)inst, VSTRING|VDONTSAVE);
	}

	/* read the descriptive information */
	io_getinbig(&ni->lowx);
	io_getinbig(&ni->lowy);
	io_getinbig(&ni->highx);
	io_getinbig(&ni->highy);
	io_getinbig(&i);
	ni->transpose = (INTSML)i;
	io_getinbig(&i);
	ni->rotation = (INTSML)i;

	/* versions 9 and later get text descriptor for facet name */
	if (io_binindata.magic <= MAGIC9) io_getinubig(&ni->textdescript);

	/* read the nodeinst name (versions 1, 2, or 3 only) */
	if (io_binindata.magic >= MAGIC3)
	{
		inst = io_getstring(el_tempcluster);
		if (inst == 0) return(1);
		if (*inst != 0)
		{
			nextvarchangequiet();
			if (setvalkey((INTBIG)ni, VNODEINST, el_node_name,
				(INTBIG)inst, VSTRING|VDISPLAY) == NOVARIABLE) return(1);
		}
		efree(inst);
	}

	/* ignore the geometry index (versions 4 or older) */
	if (io_binindata.magic > MAGIC5) io_getinbig(&i);

	/* read the arc ports */
	io_getinbig(&i);
	lpo = NOPORTARCINST;
	for(j=0; j<i; j++)
	{
		/* read the arcinst information (and the particular end on the arc) */
		io_getinbig(&k);
		ai = io_binindata.arclist[k>>1];
		pi = ai->end[k&1].portarcinst;
		pi->conarcinst = ai;

		/* link in the portarcinst */
		if (j == 0) ni->firstportarcinst = pi; else lpo->nextportarcinst = pi;
		lpo = pi;

		/* read the arcinst index */
		io_getinbig(&pi->proto);
		io_convertportproto(&pi->proto);

		/* read variable information */
		if (io_readvariables((INTBIG)pi, VPORTARCINST) < 0) return(1);
	}

	/* setup the last portinst pointer */
	if (i == 0) ni->firstportarcinst = NOPORTARCINST; else
		lpo->nextportarcinst = NOPORTARCINST;

	/* read the exported ports */
	io_getinbig(&i);
	lpe = NOPORTEXPINST;
	for(j=0; j<i; j++)
	{
		/* read the exported port prototype index */
		io_getinbig(&pp);
		io_convertportproto(&pp);
		pe = pp->subportexpinst;
		pe->exportproto = pp;

		if (j == 0) ni->firstportexpinst = pe; else lpe->nextportexpinst = pe;
		lpe = pe;

		/* read the export prototype */
		io_getinbig(&pe->proto);
		io_convertportproto(&pe->proto);

		/* read variable information */
		if (io_readvariables((INTBIG)pe, VPORTEXPINST) < 0) return(1);
	}

	/* setup the last portinst pointer */
	if (i == 0) ni->firstportexpinst = NOPORTEXPINST; else
		lpe->nextportexpinst = NOPORTEXPINST;

	/* ignore the "seen" bits (versions 8 and older) */
	if (io_binindata.magic > MAGIC9) io_getinubig(&k);

	/* read the aid information */
	if (io_binindata.magic <= MAGIC7)
	{
		/* version 7 and later simply read the relevant data */
		io_getinubig(&ni->userbits);
	} else
	{
		/* version 6 and earlier must sift through the information */
		if (io_binindata.aabcount >= 1) io_getinubig(&ni->userbits);
		for(i=1; i<io_binindata.aabcount; i++) io_getinubig(&k);
	}

	/* read variable information */
	if (io_readvariables((INTBIG)ni, VNODEINST) < 0) return(1);

	return(0);
}

/* routine to read (and mostly ignore) a geometry module */
void io_readgeom(INTBIG *type, INTBIG *moreup)
{
	INTBIG i;

	io_getinbig(type);				/* read entrytype */
	if (*type != 0) io_getinbig(&i);/* skip entryaddr */
	io_getinbig(&i);				/* skip lowx */
	io_getinbig(&i);				/* skip highx */
	io_getinbig(&i);				/* skip lowy */
	io_getinbig(&i);				/* skip highy */
	io_getinbig(&i);				/* skip moreleft */
	io_getinbig(&i);				/* skip ll */
	io_getinbig(&i);				/* skip moreright */
	io_getinbig(&i);				/* skip lr */
	io_getinbig(moreup);			/* read moreup */
	io_getinbig(&i);				/* skip lu */
	io_getinbig(&i);				/* skip moredown */
	io_getinbig(&i);				/* skip ld */
	io_ignorevariables();			/* skip variables */
}

INTSML io_readarcinst(ARCINST *ai)
{
	INTBIG i, j, index;
	REGISTER char *inst;

	/* read the arcproto pointer */
	io_getinbig(&ai->proto);
	io_convertarcproto(&ai->proto);

	/* read the arc length (versions 5 or older) */
	if (io_binindata.magic >= MAGIC5) io_getinbig(&ai->length);

	/* read the arc width */
	io_getinbig(&ai->width);

	/* ignore the signals value (versions 6, 7, or 8) */
	if (io_binindata.magic <= MAGIC6 && io_binindata.magic >= MAGIC8)
		io_getinbig(&i);

	/* read the arcinst name (versions 3 or older) */
	if (io_binindata.magic >= MAGIC3)
	{
		inst = io_getstring(el_tempcluster);
		if (inst == 0) return(1);
		if (*inst != 0)
		{
			nextvarchangequiet();
			if (setvalkey((INTBIG)ai, VARCINST, el_arc_name,
				(INTBIG)inst, VSTRING|VDISPLAY) == NOVARIABLE) return(1);
		}
		efree(inst);
	}

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

		/* read the arcinst's connecting nodeinst index */
		io_getinbig(&ai->end[i].nodeinst);
		index = (INTBIG)ai->end[i].nodeinst;
		if (index >= 0 && index < io_binindata.nodeindex)
			ai->end[i].nodeinst = io_binindata.nodelist[index]; else
		{
			ttyputerr("Warning: corrupt data on arc.  Do a 'Check and Repair Library'");
			ai->end[i].nodeinst = NONODEINST;
		}			
	}

	/* compute the arc length (versions 6 or newer) */
	if (io_binindata.magic <= MAGIC6) ai->length = computedistance(ai->end[0].xpos,
		ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);

	/* ignore the geometry index (versions 4 or older) */
	if (io_binindata.magic > MAGIC5) io_getinbig(&i);

	/* ignore the "seen" bits (versions 8 and older) */
	if (io_binindata.magic > MAGIC9) io_getinubig(&i);

	/* read the arcinst's aid information */
	if (io_binindata.magic <= MAGIC7)
	{
		/* version 7 and later simply read the relevant data */
		io_getinubig(&ai->userbits);

		/* versions 7 and 8 ignore net number */
		if (io_binindata.magic >= MAGIC8) io_getinbig(&j);
	} else
	{
		/* version 6 and earlier must sift through the information */
		if (io_binindata.aabcount >= 1) io_getinubig(&ai->userbits);
		for(i=1; i<io_binindata.aabcount; i++) io_getinubig(&j);
	}

	/* read variable information */
	if (io_readvariables((INTBIG)ai, VARCINST) < 0) return(1);
	return(0);
}

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

/* routine to read the global namespace.  returns nonzero upon error */
INTSML io_readnamespace(void)
{
	REGISTER INTSML i;

	io_getinbig(&io_binindata.namecount);
	if (io_verbose > 0) ttyputmsg("Reading %ld variable names", io_binindata.namecount);
	if (io_binindata.namecount == 0) return(0);

	/* read in the namespace */
	io_binindata.newnames = (INTBIG *)emalloc((SIZEOFINTBIG * io_binindata.namecount), el_tempcluster);
	if (io_binindata.newnames == 0) return(1);
	io_binindata.realname = (char **)emalloc(((sizeof (char *)) * io_binindata.namecount), el_tempcluster);
	if (io_binindata.realname == 0) return(1);
	for(i=0; i<io_binindata.namecount; i++)
	{
		io_binindata.realname[i] = io_getstring(el_tempcluster);
		if (io_binindata.realname[i] == 0) return(1);
		io_binindata.newnames[i] = 0;
	}
	return(0);
}

/*
 * routine to ignore one set of object variables on readin
 */
void io_ignorevariables(void)
{
	REGISTER NODEINST *ni;

	ni = dummynode();
	(void)io_readvariables((INTBIG)ni, VNODEINST);

	/* this next line is not strictly legal!!! */
	if (ni->numvar != 0) db_freevars(&ni->firstvar, &ni->numvar);
}

/*
 * routine to read a set of object variables.  returns negative upon error and
 * otherwise returns the number of variables read
 */
INTSML io_readvariables(INTBIG addr, INTBIG type)
{
	REGISTER INTSML i, j, datasize;
	REGISTER INTBIG ty;
	REGISTER VARIABLE *var;
	INTBIG count, newtype, len, newaddr, cou, newdescript;
	INTSML key;

	io_getinbig(&count);
	if (io_verbose > 0 && count > 0)
		 ttyputmsg("Reading %ld variables on %ld object", count, type);
	for(i=0; i<count; i++)
	{
		io_getin(&key, io_binindata.sizeofsmall, SIZEOFINTSML, 0);
		io_getinbig(&newtype);
		ty = newtype;

		/* version 9 and later reads text description on displayable variables */
		if ((ty&VDISPLAY) != 0 && io_binindata.magic <= MAGIC9)
			io_getinubig(&newdescript); else
				newdescript = defaulttextdescript(NOGEOM);
		if ((ty&VISARRAY) != 0)
		{
			io_getinbig(&len);
			cou = len;
			if ((ty&VLENGTH) == 0) cou++;
			if ((ty&VTYPE) == VCHAR) datasize = 1; else
				if ((ty&VTYPE) == VDOUBLE) datasize = SIZEOFINTBIG*2; else
					if ((ty&VTYPE) == VSHORT) datasize = 2; else
						datasize = SIZEOFINTBIG;
			newaddr = (INTBIG)emalloc((cou*datasize), el_tempcluster);
			if (newaddr == 0) return(-1);
			if ((ty&VTYPE) == VGENERAL)
			{
				for(j=0; j<len; j += 2)
				{
					io_getinbig((INTBIG *)(newaddr + (j+1)*datasize));
					if (io_getinvar((INTBIG *)(newaddr + j*datasize),
						*(INTBIG *)(newaddr + (j+1)*datasize)) != 0) return(-1);
				}
			} else
			{
				for(j=0; j<len; j++)
					if (io_getinvar((INTBIG *)(newaddr + j*datasize), ty) != 0) return(-1);
			}
			if ((ty&VLENGTH) == 0)
				for(j=0; j<datasize; j++)
					((char *)newaddr)[len*datasize+j] = -1;
		} else if (io_getinvar(&newaddr, ty) != 0) return(-1);

		/* copy this variable into database */
		if (io_binindata.newnames[key] == 0) io_binindata.newnames[key] = makekey(io_binindata.realname[key]);
		nextvarchangequiet();
		var = setvalkey(addr, type, io_binindata.newnames[key], newaddr, newtype);
		if (var == NOVARIABLE) return(-1);
		var->textdescript = newdescript;

		/* free the memory allocated for the creation of this variable */
		if ((ty&VTYPE) == VSTRING || (ty&(VCODE1|VCODE2)) != 0)
		{
			if ((ty&VISARRAY) == 0) efree((char *)newaddr); else
				for(j=0; j<len; j++) efree((char *)((INTBIG *)newaddr)[j]);
		}
		if ((ty&VISARRAY) != 0) efree((char *)newaddr);
	}
	return((INTSML)count);
}

INTSML io_getinvar(INTBIG *addr, INTBIG ty)
{
	INTBIG i;
	REGISTER char *pp;
	REGISTER ARCINST *ai;
	PORTPROTO *ppr;

	if ((ty&(VCODE1|VCODE2)) != 0) ty = VSTRING;
	switch (ty&VTYPE)
	{
		case VADDRESS:
		case VINTEGER:
		case VFRACT:
			io_getinbig(addr);
			if ((ty&VTYPE) == VFLOAT)
			{
				if (io_binindata.swap_bytes != 0) io_binindata.swapped_floats++;
			}
			break;
		case VFLOAT:
			io_getin(addr, sizeof(float), sizeof(float), 0);
			if (io_binindata.swap_bytes != 0) io_binindata.swapped_doubles++;
			break;
		case VDOUBLE:
			io_getin(addr, sizeof(double), sizeof(double), 0);
			if (io_binindata.swap_bytes != 0) io_binindata.swapped_doubles++;
			break;
		case VSHORT:
			io_getin(addr, io_binindata.sizeofsmall, SIZEOFINTSML, 1);
			break;
		case VCHAR:
			io_getin(addr, 1, 1, 0);
			break;
		case VSTRING:
			pp = io_getstring(el_tempcluster);
			if (pp == 0) return(1);
			*addr = (INTBIG)pp;
			break;
		case VNODEINST:
			io_getinbig(addr);
			if (*addr < 0 || *addr >= io_binindata.nodeindex) *addr = -1; else
				*addr = (INTBIG)io_binindata.nodelist[*addr];
			break;
		case VNODEPROTO:
			io_getinbig(addr);
			(void)io_convertnodeproto((NODEPROTO **)addr);
			break;
		case VARCPROTO:
			io_getinbig(addr);
			io_convertarcproto((ARCPROTO **)addr);
			break;
		case VPORTPROTO:
			io_getinbig(addr);
			io_convertportproto((PORTPROTO **)addr);
			break;
		case VARCINST:
			io_getinbig(addr);
			if (*addr < 0 || *addr >= io_binindata.arcindex) *addr = -1; else
				*addr = (INTBIG)io_binindata.arclist[*addr];
			break;
		case VGEOM:
			*addr = -1;
			io_getinbig(&i);
			if (io_binindata.magic <= MAGIC5)
			{
				/* versions 5 and later store extra information */
				if (i == OBJNODEINST)
				{
					io_getinbig(addr);
					if (*addr < 0 || *addr >= io_binindata.nodeindex) *addr = -1;
						else *addr = (INTBIG)io_binindata.nodelist[*addr]->geom;
				} else if (i == OBJARCINST)
				{
					io_getinbig(addr);
					if (*addr < 0 || *addr >= io_binindata.arcindex) *addr = -1;
						else *addr = (INTBIG)io_binindata.arclist[*addr]->geom;
				}
			}
			break;
		case VTECHNOLOGY:
			io_getinbig(addr);
			if (*addr != -1) *addr = (INTBIG)io_gettechlist((INTSML)(*addr));
			break;
		case VPORTARCINST:
			io_getinbig(addr);
			if (*addr != -1)
			{
				ai = io_binindata.arclist[(*addr) >> 1];
				i = (*addr) & 1;
				*addr = (INTBIG)ai->end[i].portarcinst;
			}
			break;
		case VPORTEXPINST:
			io_getinbig(addr);
			if (*addr != -1)
			{
				ppr = (PORTPROTO *)*addr;
				io_convertportproto(&ppr);
				*addr = (INTBIG)ppr->subportexpinst;
			}
			break;
		case VLIBRARY:
			pp = io_getstring(el_tempcluster);
			if (pp == 0) return(1);
			*addr = (INTBIG)getlibrary(pp);
			break;
		case VAID:
			io_getinbig(addr);
			if (*addr < 0 || *addr >= io_binindata.aacount) *addr = -1; else
			{
				i = io_binindata.aidlist[*addr];
				if (i < 0 || i >= el_maxaid)
				{
					i = 0;
					if (io_binindata.aiderror[*addr] != 0)
					{
						ttyputerr("WARNING: no aid called '%s', using 'user'", io_binindata.aiderror[*addr]);
						efree(io_binindata.aiderror[*addr]);
						io_binindata.aiderror[*addr] = 0;
					}
				}
				*addr = (INTBIG)&el_aids[i];
			}
			break;
		case VRTNODE:
			*addr = -1;
			break;
   }
   return(0);
}

/******************** INPUT HELPER ROUTINES ********************/

/*
 * routine to return the proper aid bits word given the arrangement
 * of aids.  These words have one bit per aid according to
 * the aid's position which may get re-arranged.
 */
INTBIG io_arrangeaidbits(INTBIG val)
{
	REGISTER INTBIG out, i, j;

	/* if the bits are not re-arranged, result is simple */
	if (io_binindata.aidbitsmessed == 0) return(val);

	/* re-arrange the bits in the word */
	out = 0;
	for(i=0; i<io_binindata.aacount; i++)
	{
		j = io_binindata.aidlist[i];
		if (j < 0) continue;
		if ((val & (1 << i)) != 0) out |= 1 << j;
	}
	return(out);
}

/*
 * routine to convert the nodeproto index at "np" to a true nodeproto pointer.
 * returns a string with the original name of this prototype (if there were
 * conversion problems in the file).  Returns zero if the pointer is fine.
 */
char *io_convertnodeproto(NODEPROTO **np)
{
	REGISTER INTBIG i, nindex;
	REGISTER char *origname;

	if (*np == NONODEPROTO) return(0);
	i = (INTBIG)*np;
	origname = 0;
	if (i < 0)
	{
		nindex = -i - 2;
		if (nindex >= io_binindata.nodepprotoindex)
		{
			ttyputerr("Error: want primitive node index %ld when limit is %ld",
				nindex, io_binindata.nodepprotoindex);
			nindex = 0;
		}
		*np = io_getnodepprotolist(nindex);
		origname = io_binindata.nodepprotoorig[nindex];
	} else
	{
		if (i >= io_binindata.nodeprotoindex)
		{
			ttyputerr("Error: want facet index %ld when limit is %ld",
				i, io_binindata.nodeprotoindex);
			*np = io_getnodepprotolist(0);
		} else
		{
			*np = io_binindata.nodeprotolist[i];
		}
	}
	return(origname);
}

void io_convertportproto(PORTPROTO **pp)
{
	REGISTER INTBIG i, pindex;

	if (*pp == NOPORTPROTO) return;
	i = (INTBIG)*pp;
	if (i < 0)
	{
		pindex = -i - 2;
		if (pindex >= io_binindata.portpprotoindex)
		{
			ttyputerr("Error: want primitive port index %ld when limit is %ld",
				pindex, io_binindata.portpprotoindex);
			pindex = 0;
		}
		*pp = io_getportpprotolist(pindex);
	} else *pp = io_binindata.portprotolist[i];
}

void io_convertarcproto(ARCPROTO **ap)
{
	REGISTER INTBIG i, aindex;

	if (*ap == NOARCPROTO) return;
	i = (INTBIG)*ap;
	aindex = -i - 2;
	if (aindex >= io_binindata.arcprotoindex || aindex < 0)
	{
		ttyputerr("Want primitive arc index %ld when range is 0 to %ld", aindex, io_binindata.arcprotoindex);
		aindex = 0;
	}
	*ap = io_getarcprotolist((INTSML)aindex);
}

/*
 * Routine to ensure that all ports in library "lib" are valid.  Invalid ports are
 * caused when references to facets outside of this library cannot be found.  When
 * that happens, dummy facets are generated with invalid ports.  At this point, the
 * ports can be defined by examining their usage.
 */
void io_ensureallports(LIBRARY *lib)
{
	REGISTER NODEPROTO *np, *onp, *pin;
	REGISTER PORTPROTO *pp;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER ARCPROTO *ap;
	ARCPROTO *onlyarc;
	REGISTER TECHNOLOGY *tech;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER INTBIG thisend;
	INTBIG lx, hx, ly, hy, x, y;
	INTSML first;
	XARRAY rot, trn, trans;

	/* look at every port in the library */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (pp->subnodeinst != NONODEINST) continue;

			/* undefined port: figure out what connects to it */
			for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
				for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
					ap->temp1 = 0;
			first = 1;
			onlyarc = NOARCPROTO;
			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
			{
				for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				{
					if (ni->proto != np) continue;
					makerotI(ni, rot);
					maketransI(ni, trn);
					transmult(rot, trn, trans);

					/* found an instance of the external facet */
					for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					{
						if (pi->proto != pp) continue;
						ai = pi->conarcinst;
						if (first == 0 && ai->proto != onlyarc) onlyarc = NOARCPROTO;
						if (first != 0) onlyarc = ai->proto;
						ai->proto->temp1 = 1;
						if (ai->end[0].portarcinst == pi) thisend = 0; else thisend = 1;
						x = ai->end[thisend].xpos;
						y = ai->end[thisend].ypos;
						xform(x, y, &x, &y, trans);
						if (first != 0)
						{
							lx = hx = x;
							ly = hy = y;
							first = 0;
						} else
						{
							if (x < lx) lx = x;
							if (x > hx) hx = x;
							if (y < ly) ly = y;
							if (y > hy) hy = y;
						}
					}
					for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
					{
						if (pe->proto != pp) continue;
						io_binfindallports(lib, pe->exportproto, &lx, &hx, &ly, &hy, &first,
							&onlyarc, trans);
					}
				}
			}

			if (first != 0)
			{
				/* nothing found to tell where the port should go: put it in the middle */
				lx = hx = (np->lowx + np->highx) / 2;
				ly = hy = (np->lowy + np->highy) / 2;
			}

			/* found where the port should go: create it */
			if (onlyarc == NOARCPROTO) pin = gen_univpinprim; else
				pin = getpinproto(onlyarc);
			lx -= (pin->highx-pin->lowx) / 2;
			hx += (pin->highx-pin->lowx) / 2;
			ly -= (pin->highy-pin->lowy) / 2;
			hy += (pin->highy-pin->lowy) / 2;

			ni = allocnodeinst(lib->cluster);
			ni->proto = pin;
			ni->parent = np;
			ni->nextnodeinst = np->firstnodeinst;
			np->firstnodeinst = ni;
			ni->lowx = lx;   ni->highx = hx;
			ni->lowy = ly;   ni->highy = hy;
			ni->geom = allocgeom(lib->cluster);
			ni->geom->entrytype = OBJNODEINST;   ni->geom->entryaddr.ni = ni;
			linkgeom(ni->geom, np);

			pe = allocportexpinst(np->cell->cluster);
			pe->exportproto = pp;
			pe->proto = ni->proto->firstportproto;
			ni->firstportexpinst = pe;
			pp->subnodeinst = ni;
			pp->subportexpinst = pe;
			pp->connects = pe->proto->connects;
			pp->subportproto = pe->proto;
			pp->userbits = pe->proto->userbits;
			pp->textdescript = defaulttextdescript(NOGEOM);
		}
	}
}

/*
 * Helper routine for "io_ensureallports" to find all exported ports in library "lib" attached
 * to port "pp" and gather the bounding box of all arc connections in "lx", "hx", "ly", and
 * "hy".  Sets "first" to zero once a wire is found, and sets "onlyarc" to the type of arc
 * found.  Uses "prevtrans" as the transformation matrix to this point.
 */
void io_binfindallports(LIBRARY *lib, PORTPROTO *pp, INTBIG *lx, INTBIG *hx, INTBIG *ly, INTBIG *hy,
	INTSML *first, ARCPROTO **onlyarc, XARRAY prevtrans)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER ARCINST *ai;
	REGISTER INTBIG thisend;
	INTBIG x, y;
	XARRAY rot, trn, thistran, trans;

	/* look at all nodes that use the instance with this port */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto != pp->parent) continue;

			/* find an arc at this port */
			makerotI(ni, rot);
			maketransI(ni, trn);
			transmult(rot, trn, thistran);
			transmult(thistran, prevtrans, trans);
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
			{
				if (pi->proto != pp) continue;
				ai = pi->conarcinst;
				if (*first == 0 && ai->proto != *onlyarc) *onlyarc = NOARCPROTO;
				if (*first != 0) *onlyarc = ai->proto;
				ai->proto->temp1 = 1;
				if (ai->end[0].portarcinst == pi) thisend = 0; else thisend = 1;
				x = ai->end[thisend].xpos;
				y = ai->end[thisend].ypos;
				xform(x, y, &x, &y, trans);
				if (*first != 0)
				{
					*lx = *hx = x;
					*ly = *hy = y;
					*first = 0;
				} else
				{
					if (x < *lx) *lx = x;
					if (x > *hx) *hx = x;
					if (y < *ly) *ly = y;
					if (y > *hy) *hy = y;
				}
			}
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
			{
				if (pe->proto != pp) continue;
				io_binfindallports(lib, pe->exportproto, lx, hx, ly, hy, first,
					onlyarc, trans);
			}
		}
	}
}

/******************** DATA ACCESS ROUTINES ********************/

ARCPROTO *io_getarcprotolist(INTSML i)
{
	REGISTER ARCPROTO *ap;
	REGISTER char *name;

	if (io_binindata.arcprotoerror[i] != 0)
	{
		ap = NOARCPROTO;
		while (ap == NOARCPROTO)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("Cannot find arc '");
			(void)addstringtoinfstr(io_binindata.arcprotoerror[i]);
			(void)addstringtoinfstr("', use which instead [");
			(void)addstringtoinfstr(io_binindata.arcprotolist[i]->protoname);
			(void)addstringtoinfstr("] ");
			name = ttygetline(returninfstr());
			if (name == 0 || *name == 0) break;
			for(ap = io_binindata.arcprotolist[i]->tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
				if (namesame(ap->protoname, name) == 0) break;
		}
		if (ap != NOARCPROTO) io_binindata.arcprotolist[i] = ap;
		efree(io_binindata.arcprotoerror[i]);
		io_binindata.arcprotoerror[i] = 0;
	}
	return(io_binindata.arcprotolist[i]);
}

PORTPROTO *io_getportpprotolist(INTBIG i)
{
	if (io_binindata.portpprotoerror[i] != 0)
	{
		ttyputerr("WARNING: port %s not found, using %s", io_binindata.portpprotoerror[i],
			io_binindata.portpprotolist[i]->protoname);
		efree(io_binindata.portpprotoerror[i]);
		io_binindata.portpprotoerror[i] = 0;
	}
	return(io_binindata.portpprotolist[i]);
}

NODEPROTO *io_getnodepprotolist(INTBIG i)
{
	REGISTER NODEPROTO *np;
	REGISTER char *name;

	(void)io_gettechlist((INTSML)(io_binindata.nodepprototech[i]));
	if (io_binindata.nodepprotoerror[i] != 0)
	{
		np = NONODEPROTO;
		while (np == NONODEPROTO)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("Cannot find primitive '");
			(void)addstringtoinfstr(io_binindata.nodepprotoorig[i]);
			(void)addstringtoinfstr("', use which instead [");
			(void)addstringtoinfstr(io_binindata.nodepprotolist[i]->primname);
			(void)addstringtoinfstr("] ");
			name = ttygetline(returninfstr());
			if (name == 0 || *name == 0) break;
			for(np = io_binindata.nodepprotolist[i]->tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				if (namesame(np->primname, name) == 0) break;
		}
		if (np != NONODEPROTO) io_binindata.nodepprotolist[i] = np;
		io_binindata.nodepprotoerror[i] = 0;
	}
	return(io_binindata.nodepprotolist[i]);
}

TECHNOLOGY *io_gettechlist(INTSML i)
{
	extern COMCOMP us_noyesp;
	REGISTER INTSML count;
	char *pars[10];

	if (io_binindata.techerror[i] != 0)
	{
		ttyputerr("WARNING: technology '%s' does not exist, using '%s'",
			io_binindata.techerror[i], io_binindata.techlist[i]->techname);
		efree(io_binindata.techerror[i]);
		io_binindata.techerror[i] = 0;
		count = ttygetparam("Is this OK? [n] ", &us_noyesp, 2, pars);
		if (count <= 0 || namesamen(pars[0], "yes", strlen(pars[0])) != 0)
			longjmp(io_binindata.filerror, 1);
		ttyputerr("WARNING: saving this library with a substitute technology may corrupt it!");
	}
	return(io_binindata.techlist[i]);
}

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

void io_getinbig(void *data)
{
	io_getin(data, io_binindata.sizeofbig, SIZEOFINTBIG, 1);
}

void io_getinubig(void *data)
{
	io_getin(data, io_binindata.sizeofbig, SIZEOFINTBIG, 0);
}

/*
 * routine to read "disksize" bytes of data from disk and store them in the "memorysize"-
 * long object at "data".  If "signextend" is nonzero, do sign-extension if the
 * memory object is larger.
 */
void io_getin(void *data, INTSML disksize, INTSML memorysize, INTSML signextend)
{
	REGISTER INTBIG ret;
	char swapbyte;
	char buf[128];
	INTBIG i;

	/* sanity check */
	if (disksize == 0)
	{
		ttyputmsg("Warning: null length data; database may be bad (byte %ld)",
			io_binindata.bytecount);
		return;
	}

	/* check for direct transfer */
	if (disksize == memorysize && io_binindata.swap_bytes == 0)
	{
		/* just peel it off the disk */
		ret = xfread((char *)data, 1, disksize, io_binindata.filein);
		if (ret == 0) longjmp(io_binindata.filerror, 1);
	} else
	{
		/* not a simple read, use a buffer */
		ret = xfread((char *)buf, 1, disksize, io_binindata.filein);
		if (ret == 0) longjmp(io_binindata.filerror, 1);
		if (io_binindata.swap_bytes > 0)
		{
			switch (disksize)
			{
				case 2:
					swapbyte = buf[0]; buf[0] = buf[1]; buf[1] = swapbyte;
					break;
				case 4:
					swapbyte = buf[3]; buf[3] = buf[0]; buf[0] = swapbyte;
					swapbyte = buf[2]; buf[2] = buf[1]; buf[1] = swapbyte;
					break;
				case 8:
					swapbyte = buf[7]; buf[7] = buf[0]; buf[0] = swapbyte;
					swapbyte = buf[6]; buf[6] = buf[1]; buf[1] = swapbyte;
					swapbyte = buf[5]; buf[5] = buf[2]; buf[2] = swapbyte;
					swapbyte = buf[4]; buf[4] = buf[3]; buf[3] = swapbyte;
					break;
			}
		}
		if (disksize == memorysize)
		{
			for(i=0; i<memorysize; i++) ((char *)data)[i] = buf[i];
		} else
		{
			if (disksize > memorysize)
			{
				/* trouble! disk has more bits than memory.  check for clipping */
				for(i=0; i<memorysize; i++) ((char *)data)[i] = buf[i];
				for(i=memorysize; i<disksize; i++)
					if (buf[i] != 0 && buf[i] != (char)0xFF)
						io_binindata.clipped_integers++;
			} else
			{
				/* disk has smaller integer */
				if (signextend == 0 || (buf[disksize-1] & 0x80) == 0)
				{
					for(i=disksize; i<memorysize; i++) buf[i] = 0;
				} else
				{
					for(i=disksize; i<memorysize; i++) buf[i] = (char)0xFF;
				}
				for(i=0; i<memorysize; i++) ((char *)data)[i] = buf[i];
			}
		}
	}
	io_binindata.bytecount += disksize;
	if (io_verbose < 0 && io_binindata.filelength > 0)
	{
		if (io_binindata.bytecount > io_binindata.reported + REPORTINC)
		{
			DiaPercent(1, io_binindata.bytecount*100/io_binindata.filelength);
			io_binindata.reported = io_binindata.bytecount;
		}
	}
}

char *io_getstring(CLUSTER *cluster)
{
	INTBIG len;
	char *name;

	io_getinbig(&len);
	name = (char *)emalloc((len+1), cluster);
	if (name == 0) return(0);
	if (io_binindata.swap_bytes != 0 && len != 0) io_binindata.swap_bytes = -1;     /* prevent swapping */
	if (len != 0) io_getin(name, (INTSML)len, (INTSML)len, 0);
	name[len] = 0;
	if (io_binindata.swap_bytes != 0) io_binindata.swap_bytes = 1;
	return(name);
}

char *io_gettempstring(void)
{
	INTBIG len;

	io_getinbig(&len);
	if (len+1 > io_tempstringlength)
	{
		if (io_tempstringlength != 0) efree(io_tempstring);
		io_tempstringlength = len+1;
		io_tempstring = (char *)emalloc(io_tempstringlength, io_aid->cluster);
		if (io_tempstring == 0) { io_tempstringlength = 0; return(0); }
	}
	if (io_binindata.swap_bytes != 0 && len != 0) io_binindata.swap_bytes = -1;
	if (len != 0) io_getin(io_tempstring, (INTSML)len, (INTSML)len, 0);
	io_tempstring[len] = 0;
	if (io_binindata.swap_bytes != 0) io_binindata.swap_bytes = 1;
	return(io_tempstring);
}
