/*
 * Electric(tm) VLSI Design System
 *
 * File: io.c
 * Input/output aid: controller module
 * 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 "efunction.h"
#include "dbcontour.h"
#include "eio.h"
#include "usredtec.h"
#include "edialogs.h"
#include "tecart.h"
#include "tecschem.h"
#include <math.h>

/* the command parsing table */
#if IOCIF
static KEYWORD iocifoopt[] =
{
	{"fully-instantiated",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"exactly-as-displayed",  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"individual-boxes",      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"merge-boxes",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"include-cloak-layer",   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"ignore-cloak-layer",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"centered",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not-centered",          0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"exactly-as-displayed",  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"instantiate-top",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"dont-instantiate-top",  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_cifop = {iocifoopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "style of CIF output", 0};
static KEYWORD iocifiopt[] =
{
	{"rounded-wires",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"squared-wires",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iocifip = {iocifiopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "style of CIF input", 0};
static KEYWORD iocifopt[] =
{
	{"input",     1,{&iocifip,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"output",    1,{&io_cifop,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_cifp = {iocifopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "control of CIF", 0};
#endif

#if IODXF
static KEYWORD iodxfaopt[] =
{
	{"all",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"restrict",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP iodxfap = {iodxfaopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "DXF layer restriction options", 0};
static KEYWORD iodxfopt[] =
{
	{"acceptable-layers",   1,{&iodxfap,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"flatten-input",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not-flatten-input",   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_dxfp = {iodxfopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "DXF options", 0};
#endif

#if IOGDS
static COMCOMP iogdsoarp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "maximum number of degrees per arc segment", 0};
static COMCOMP iogdsoasp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "maximum sag distance for an arc segment", 0};
static KEYWORD iogdsoopt[] =
{
	{"individual-boxes",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"merge-boxes",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"include-cloak-layer",      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"ignore-cloak-layer",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"arc-resolution",           1,{&iogdsoarp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"arc-sag",                  1,{&iogdsoasp,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iogdsop = {iogdsoopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "style of GDS output", 0};
static KEYWORD iogdsiopt[] =
{
	{"text",                     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"expand",                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"arrays",                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"unknown-layers",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-text",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-expand",                0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-arrays",                0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-unknown-layers",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iogdsiop = {iogdsiopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "style of GDS input", 0};
static KEYWORD iogdsopt[] =
{
	{"output",   1,{&iogdsop,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"input",    1,{&iogdsiop,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_gdsp = {iogdsopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "control of GDS", 0};
#endif

#if IOEDIF
static KEYWORD ioedifopt[] =
{
	{"schematic",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"netlist",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_edifp = {ioedifopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "EDIF output option", 0};
#endif

static KEYWORD ioplotnopt[] =
{
	{"focus",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"include-date",0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP ioplotnp = {ioplotnopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "negating control of plot output", 0};
static KEYWORD ioplotopt[] =
{
	{"not",         1,{&ioplotnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"focus",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"include-date",0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP ioplotp = {ioplotopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "control of plot output", 0};

static KEYWORD ioveropt[] =
{
	{"off",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"on",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"graphical",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP ioverp = {ioveropt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "verbose option", 0};

static COMCOMP iopostsynch = {NOKEYWORD, topoffile, nextfile, NOPARAMS, NOBACKUP,
	NOFILL|INPUTOPT, " \t", "PostScript synchronization file name", 0};
static COMCOMP iopostplotwid = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "PostScript plotter width", 0};
static COMCOMP iopostprintwid = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "PostScript printer width", 0};
static COMCOMP iopostprinthei = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "PostScript printer height", 0};
static COMCOMP iopostmargin = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "PostScript margin", 0};
static KEYWORD iopostopt[] =
{
	{"plain",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"encapsulated",     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"rotate",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-rotate",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"color",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"gray-scale",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"printer",          2,{&iopostprintwid,&iopostprinthei,NOKEY,NOKEY,NOKEY}},
	{"plotter",          1,{&iopostplotwid,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"margin",           1,{&iopostmargin,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"unsynchronize",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"synchronize",      1,{&iopostsynch,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iopostp = {iopostopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "control of PostScript format", 0};

static COMCOMP iohpgl2sp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "internal units per pixel", 0};
static KEYWORD iohpgl2opt[] =
{
	{"scale",           1,{&iohpgl2sp,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iohpgl2p = {iohpgl2opt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "HPGL/2 scaling option", 0};
static KEYWORD iohpglopt[] =
{
	{"1",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"2",               1,{&iohpgl2p,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iohpglp = {iohpglopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "HPGL version", 0};

static COMCOMP iobloatlp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", "Layer name to bloat", 0};
static COMCOMP iobloatap = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	0, " \t", "amount to bloat layer (in internal units)", 0};

static KEYWORD ioopt[] =
{
#if IOCIF
	{"cif",            1,{&io_cifp,NOKEY,NOKEY,NOKEY,NOKEY}},
#endif
#if IODXF
	{"dxf",            1,{&io_dxfp,NOKEY,NOKEY,NOKEY,NOKEY}},
#endif
#if IOGDS
	{"gds",            1,{&io_gdsp,NOKEY,NOKEY,NOKEY,NOKEY}},
#endif
#if IOEDIF
	{"edif",           1,{&io_edifp,NOKEY,NOKEY,NOKEY,NOKEY}},
#endif
	{"plot",           1,{&ioplotp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"verbose",        1,{&ioverp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"postscript",     1,{&iopostp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"hpgl",           1,{&iohpglp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"bloat-output",   2,{&iobloatlp,&iobloatap,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP io_iop = {ioopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
		0, " \t", "Input/Output action", 0};

struct
{
	char *name;
	INTSML required, bits;
} io_formatlist[] =
{
	{"binary",             1, FBINARY},
#if IOCIF
	{"cif",                1, FCIF},
#endif
#if IODEF
	{"def",                2, FDEF},
#endif
#if IODXF
	{"dxf",                2, FDXF},
#endif
#if IOEAGLE
	{"eagle",              2, FEAGLE},
#endif
#if IOEDIF
	{"edif",               2, FEDIF},
#endif
#if IOGDS
	{"gds",                1, FGDS},
#endif
	{"hpgl",               1, FHPGL},
#if IOL
	{"l",                  1, FL},
#endif
#if IOLEF
	{"lef",                2, FLEF},
#endif
#if IOPADS
	{"pads",               2, FPADS},
#endif
	{"postscript",         2, FPOSTSCRIPT},
	{"printed-postscript", 2, FPRINTEDPOSTSCRIPT},
#ifdef MACOS
	{"quickdraw",          1, FQUICKDRAW},
#endif
#if IOSDF
	{"sdf",                2, FSDF},
#endif
#if IOSKILL
	{"skill",              2, FSKILL},
#endif
#if IOSUE
	{"sue",                2, FSUE},
#endif
	{"text",               1, FTEXT},
#if VHDLAID
	{"vhdl",               1, FVHDL},
#endif
	{NULL, 0, 0}
};

#define	NOBLOAT ((BLOAT *)-1)

typedef struct Ibloat
{
	char  *layer;
	INTBIG  amount;
	struct Ibloat *nextbloat;
} BLOAT;

BLOAT *io_curbloat = NOBLOAT;

/* working memory for "io_setuptechorder()" */
static INTSML      io_maxlayers;
static INTSML      io_mostlayers = 0;
static INTSML     *io_overlaporder;

/* miscellaneous */
FILE        *io_fileout;		/* channel for output */
jmp_buf      io_filerror;		/* nonlocal jump when I/O fails */
INTSML       io_cifbase;		/* index used when writing CIF */
INTBIG       io_state;			/* key for "IO_state" */
INTBIG       io_postscriptfilename;/* key for "IO_postscript_filename" */
INTBIG       io_postscriptfiledate;/* key for "IO_postscript_filedate" */
INTBIG       io_postscriptepsscale;/* key for "IO_postscript_EPS_scale" */
INTSML       io_verbose;		/* 0: silent  1:chattier  -1:graphical */
AIDENTRY    *io_aid;			/* the I/O aid object */

/* prototypes for local routines */
double io_calc_angle(double r, double dx, double dy);
void   io_compute_center(INTBIG xc, INTBIG yc, INTBIG x1, INTBIG y1,
		INTBIG x2, INTBIG y2, INTBIG *cx, INTBIG *cy);
void   io_fixrtree(RTNODE*);
void   io_fixtechlayers(void);

void io_init(INTBIG  *argc, char *argv[], AIDENTRY *thisaid)
{
	/* nothing for pass 2 or 3 initialization */
	if (thisaid == NOAID || thisaid == 0) return;

	/* pass 1 initialization */
	io_aid = thisaid;
	io_state = makekey("IO_state");
	io_postscriptfilename = makekey("IO_postscript_filename");
	io_postscriptfiledate = makekey("IO_postscript_filedate");
	io_postscriptepsscale = makekey("IO_postscript_EPS_scale");
	nextvarchangequiet();
	(void)setvalkey((INTBIG)io_aid, VAID, io_state, DXFFLATTENINPUT|GDSINARRAYS, VINTEGER);
	io_verbose = 0;

#if IOCIF
	io_initcif();
#endif
#if IODEF
	io_initdef();
#endif
#if IODXF
	io_initdxf();
#endif
#if IOEDIF
	io_initedif();
#endif
#if IOGDS
	io_initgds();
#endif
#if IOSKILL
	io_initskill();
#endif
}

void io_done(void)
{
	if (io_mostlayers > 0) efree((char *)io_overlaporder);
	io_freebininmemory();
	io_freepostscriptmemory();
	io_freetextinmemory();
#if IOCIF
	io_freecifinmemory();
	io_freecifparsmemory();
	io_freecifoutmemory();
#endif
#if IODEF
	io_freedefimemory();
#endif
#if IODXF
	io_freedxfmemory();
#endif
#if IOEDIF
	io_freeedifinmemory();
#endif
#if IOGDS
	io_freegdsoutmemory();
#endif
#if IOLEF
	io_freelefimemory();
#endif
#if IOSDF
	io_freesdfimemory();
#endif
#if IOSUE
	io_freesuememory();
#endif
}

void io_slice(void)
{
	ttyputmsg("Input and Output are performed with the 'library' command");
	ttyputmsg("...I/O aid turned off");
	aidturnoff(io_aid, 0);
}

INTSML io_set(INTSML count, char *par[])
{
	REGISTER INTSML l;
	REGISTER INTBIG curstate, scale, wid, hei;
	REGISTER NODEPROTO *np;
	REGISTER char *pp;
	REGISTER VARIABLE *var;
	INTBIG arcres, arcsag;

	if (count == 0)
	{
		count = ttygetparam("IO option:", &io_iop, MAXPARS, par);
		if (count == 0)
		{
			ttyputerr("Aborted");
			return(1);
		}
	}
	l = strlen(pp = par[0]);

	/* get current state of I/O aid */
	var = getvalkey((INTBIG)io_aid, VAID, VINTEGER, io_state);
	if (var == NOVARIABLE) curstate = 0; else curstate = var->addr;

	/* check for bloating specifications */
	if (namesamen(pp, "bloat-output", l) == 0)
	{
		if (count <= 1)
		{
			io_setoutputbloat("", 0);
			return(0);
		}
		if (count < 3)
		{
			ttyputerr("Usage: tellaid io bloat-output LAYER AMOUNT");
			return(1);
		}
		io_setoutputbloat(par[1], myatoi(par[2]));
		return(0);
	}

	/* check for format specifications */
	if (namesamen(pp, "plot", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&PLOTFOCUS) != 0)
				ttyputmsg("Plot output will focus on highlighted area"); else
					ttyputmsg("Plot output will display entire facet");
			if ((curstate&PLOTDATES) != 0)
				ttyputmsg("Plot output will include dates"); else
					ttyputmsg("Plot output will not include dates");
			return(0);
		}

		l = strlen(pp = par[1]);
		if (namesamen(pp, "not", l) == 0)
		{
			if (count <= 2)
			{
				ttyputerr("Usage: tellaid io plot not OPTION");
				return(1);
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "focus", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state, curstate & ~PLOTFOCUS, VINTEGER);
				ttyputverbose("Plot output will display entire facet");
				return(0);
			}
			if (namesamen(pp, "include-date", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state, curstate & ~PLOTDATES, VINTEGER);
				ttyputverbose("Plot output will not include dates");
				return(0);
			}
			ttyputerr("Bad TELLAID IO PLOT NOT option");
			return(1);
		}
		if (namesamen(pp, "focus", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state, curstate | PLOTFOCUS, VINTEGER);
			ttyputverbose("Plot output will focus on highlighted area");
			return(0);
		}
		if (namesamen(pp, "include-date", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state, curstate | PLOTDATES, VINTEGER);
			ttyputverbose("Plot output will include dates");
			return(0);
		}
		ttyputerr("Usage: tellaid io plot [not] (focus | include-dates)");
		return(1);
	}

#if IOCIF
	if (namesamen(pp, "cif", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&CIFINSQUARE) == 0)
				ttyputmsg("CIF wires will have rounded ends"); else
					ttyputmsg("CIF wires will have squared ends");
			if ((curstate&CIFOUTEXACT) == 0)
				ttyputmsg("CIF generation will include all facets"); else
					ttyputmsg("CIF generation will duplicate screen");
			if ((curstate&CIFOUTMERGE) == 0)
				ttyputmsg("CIF generation will list individual boxes"); else
					ttyputmsg("CIF generation will merge boxes");
			if ((curstate&CIFOUTADDDRC) == 0)
				ttyputmsg("CIF generation will ignore DRC cloak layer"); else
					ttyputmsg("CIF generation will include DRC cloak layer");
			if ((curstate&CIFOUTNOTCEN) == 0)
				ttyputmsg("CIF generation will put origins at center"); else
					ttyputmsg("CIF generation will put origins at lower corner");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "input", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate&CIFINSQUARE) == 0)
					ttyputmsg("CIF wires will have rounded ends"); else
						ttyputmsg("CIF wires will have squared ends");
				return(0);
			}
			l = strlen(pp = par[2]);
			switch (*pp)
			{
				case 'r':
					(void)setvalkey((INTBIG)io_aid, VAID, io_state,
						curstate & ~CIFINSQUARE, VINTEGER);
					ttyputverbose("CIF wires will have rounded ends");
					break;
				case 's':
					(void)setvalkey((INTBIG)io_aid, VAID, io_state,
						curstate | CIFINSQUARE, VINTEGER);
					ttyputverbose("CIF wires will have squared ends");
					break;
				default:
					ttyputerr("Bad TELLAID IO CIF INPUT option");
			}
			return(0);
		}
		if (namesamen(pp, "output", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate&CIFOUTEXACT) == 0)
					ttyputmsg("CIF generation will include all facets"); else
						ttyputmsg("CIF generation will duplicate screen");
				if ((curstate&CIFOUTMERGE) == 0)
					ttyputmsg("CIF generation will list individual boxes"); else
						ttyputmsg("CIF generation will merge boxes");
				if ((curstate&CIFOUTADDDRC) == 0)
					ttyputmsg("CIF generation will ignore DRC cloak layer"); else
						ttyputmsg("CIF generation will include DRC cloak layer");
				if ((curstate&CIFOUTNOTCEN) == 0)
					ttyputmsg("CIF generation will put origins at center"); else
						ttyputmsg("CIF generation will put origins at lower corner");
				if ((curstate&CIFOUTNOTOPCALL) == 0)
					ttyputmsg("CIF generation will instantiate top-level cell"); else
						ttyputmsg("CIF generation will not instantiate top-level cell");
				return(0);
			}

			l = strlen(pp = par[2]);
			if (namesamen(pp, "fully-instantiated", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~CIFOUTEXACT, VINTEGER);
				ttyputverbose("CIF generation will include all facets");
				return(0);
			}
			if (namesamen(pp, "exactly-as-displayed", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | CIFOUTEXACT, VINTEGER);
				ttyputverbose("CIF generation will duplicate screen");
				return(0);
			}
			if (namesamen(pp, "merge-boxes", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | CIFOUTMERGE, VINTEGER);
				ttyputverbose("CIF generation will merge boxes");
				return(0);
			}
			if (namesamen(pp, "individual-boxes", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~CIFOUTMERGE, VINTEGER);
				ttyputverbose("CIF generation will list individual boxes");
				return(0);
			}
			if (namesamen(pp, "include-cloak-layer", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | CIFOUTADDDRC, VINTEGER);
				ttyputverbose("CIF generation will include DRC cloak layer");
				return(0);
			}
			if (namesamen(pp, "ignore-cloak-layer", l) == 0 && l >= 2)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~CIFOUTADDDRC, VINTEGER);
				ttyputverbose("CIF generation will ignore DRC cloak layer");
				return(0);
			}
			if (namesamen(pp, "not-centered", l) == 0 && l >= 1)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | CIFOUTNOTCEN, VINTEGER);
				ttyputverbose("CIF generation will put origins at lower corner");
				return(0);
			}
			if (namesamen(pp, "centered", l) == 0 && l >= 1)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~CIFOUTNOTCEN, VINTEGER);
				ttyputverbose("CIF generation will put origins in center");
				return(0);
			}
			if (namesamen(pp, "instantiate-top", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~CIFOUTNOTOPCALL, VINTEGER);
				ttyputverbose("CIF generation will instantiate top-level cell");
				return(0);
			}
			if (namesamen(pp, "dont-instantiate-top", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | CIFOUTNOTOPCALL, VINTEGER);
				ttyputverbose("CIF generation will not instantiate top-level cell");
				return(0);
			}
		}
		ttyputerr("Usage: tellaid io cif input|output OPTION");
		return(1);
	}
#endif

#if IOGDS
	if (namesamen(pp, "gds", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&GDSOUTMERGE) == 0)
				ttyputmsg("GDS generation will list individual boxes"); else
					ttyputmsg("GDS generation will merge boxes");
			if ((curstate&GDSOUTADDDRC) == 0)
				ttyputmsg("GDS generation will ignore DRC cloak layer"); else
					ttyputmsg("GDS generation will include DRC cloak layer");
			if ((curstate&GDSINTEXT) == 0)
				ttyputmsg("GDS input ignores text"); else
					ttyputmsg("GDS input includes text");
			if ((curstate&GDSINEXPAND) == 0)
				ttyputmsg("GDS input does not expand instances"); else
					ttyputmsg("GDS input expands instances");
			if ((curstate&GDSINARRAYS) == 0)
				ttyputmsg("GDS input does not instantiate arrays"); else
					ttyputmsg("GDS input instantiates arrays");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "input", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate&GDSINTEXT) == 0)
					ttyputmsg("GDS input ignores text"); else
						ttyputmsg("GDS input includes text");
				if ((curstate&GDSINEXPAND) == 0)
					ttyputmsg("GDS input does not expand instances"); else
						ttyputmsg("GDS input expands instances");
				if ((curstate&GDSINARRAYS) == 0)
					ttyputmsg("GDS input does not instantiate arrays"); else
						ttyputmsg("GDS input instantiates arrays");
				return(0);
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "text", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSINTEXT, VINTEGER);
				ttyputverbose("GDS input includes text");
				return(0);
			}
			if (namesamen(pp, "no-text", l) == 0 && l >= 4)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSINTEXT, VINTEGER);
				ttyputverbose("GDS input ignores text");
				return(0);
			}
			if (namesamen(pp, "expand", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSINEXPAND, VINTEGER);
				ttyputverbose("GDS input expands instances");
				return(0);
			}
			if (namesamen(pp, "no-expand", l) == 0 && l >= 4)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSINEXPAND, VINTEGER);
				ttyputverbose("GDS input does not expand instances");
				return(0);
			}
			if (namesamen(pp, "arrays", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSINARRAYS, VINTEGER);
				ttyputverbose("GDS input instantiates arrays");
				return(0);
			}
			if (namesamen(pp, "no-arrays", l) == 0 && l >= 4)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSINARRAYS, VINTEGER);
				ttyputverbose("GDS input does not instantiate arrays");
				return(0);
			}
			if (namesamen(pp, "unknown-layers", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSINIGNOREUKN, VINTEGER);
				ttyputverbose("GDS input includes unknown layers");
				return(0);
			}
			if (namesamen(pp, "no-unknown-layers", l) == 0 && l >= 4)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSINIGNOREUKN, VINTEGER);
				ttyputverbose("GDS input ignores unknown layers");
				return(0);
			}
			ttyputerr("Unknown 'tellaid io gds input' option");
			return(0);
		}
		if (namesamen(pp, "output", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate&GDSOUTMERGE) == 0)
					ttyputmsg("GDS generation will list individual boxes"); else
						ttyputmsg("GDS generation will merge boxes");
				if ((curstate&GDSOUTADDDRC) == 0)
					ttyputmsg("GDS generation will ignore DRC cloak layer"); else
						ttyputmsg("GDS generation will include DRC cloak layer");
				return(0);
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "merge-boxes", l ) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSOUTMERGE, VINTEGER);
				ttyputverbose("GDS generation will merge boxes");
				return(0);
			}
			if (namesamen(pp, "individual-boxes", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSOUTMERGE, VINTEGER);
				ttyputverbose("GDS generation will list individual boxes");
				return(0);
			}
			if (namesamen(pp, "include-cloak-layer", l) == 0 && l >= 3)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | GDSOUTADDDRC, VINTEGER);
				ttyputverbose("GDS generation will include DRC cloak layer");
				return(0);
			}
			if (namesamen(pp, "ignore-cloak-layer", l) == 0 && l >= 2)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~GDSOUTADDDRC, VINTEGER);
				ttyputverbose("GDS generation will ignore DRC cloak layer");
				return(0);
			}
			if (namesamen(pp, "arc-resolution", l) == 0 && l >= 5)
			{
				getcontoursegmentparameters(&arcres, &arcsag);
				if (count > 3) arcres = atofr(par[3]) * 10 / WHOLE;
				ttyputverbose("GDS arc generation will create a line every %s degrees",
					frtoa(arcres*WHOLE/10));
				setcontoursegmentparameters(arcres, arcsag);
				return(0);
			}
			if (namesamen(pp, "arc-sag", l) == 0 && l >= 5)
			{
				getcontoursegmentparameters(&arcres, &arcsag);
				if (count > 3) arcsag = atola(par[3]);
				ttyputverbose("GDS arc generation will sag no more than %s", latoa(arcsag));
				setcontoursegmentparameters(arcres, arcsag);
				return(0);
			}
			ttyputerr("Unknown 'tellaid io gds output' option");
			return(0);
		}
		ttyputerr("Unknown 'tellaid io gds' option");
		return(1);
	}
#endif

#if IODXF
	if (namesamen(pp, "dxf", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&DXFALLLAYERS) == 0)
				ttyputmsg("DXF input will accept only layers listed by technology"); else
					ttyputmsg("DXF input will accept all layers");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "acceptable-layers", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate&DXFALLLAYERS) == 0)
					ttyputmsg("DXF input will accept only layers listed by technology"); else
						ttyputmsg("DXF input will accept all layers");
				return(0);
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "all", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate | DXFALLLAYERS, VINTEGER);
				ttyputverbose("DXF input will accept all layers");
				return(0);
			}
			if (namesamen(pp, "restrict", l) == 0)
			{
				(void)setvalkey((INTBIG)io_aid, VAID, io_state,
					curstate & ~DXFALLLAYERS, VINTEGER);
				ttyputverbose("DXF input will accept only layers listed by technology");
				return(0);
			}
		}
		if (namesamen(pp, "flatten-input", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate | DXFFLATTENINPUT, VINTEGER);
			ttyputverbose("DXF input will flatten input blocks");
			return(0);
		}
		if (namesamen(pp, "not-flatten-input", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~DXFFLATTENINPUT, VINTEGER);
			ttyputverbose("DXF input will retain input hierarchy");
			return(0);
		}
		ttyputerr("Bad tellaid io dxf OPTION");
		return(1);
	}
#endif

	if (namesamen(pp, "postscript", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&EPSPSCRIPT) == 0)
				ttyputmsg("PostScript output is in plain format"); else
					ttyputmsg("PostScript output is in encapsulated format");
			if ((curstate&PSCOLOR) == 0)
				ttyputmsg("PostScript output is gray-scale"); else
					ttyputmsg("PostScript output is in color");
			if ((curstate&PSROTATE) == 0)
				ttyputmsg("PostScript output is unrotated"); else
					ttyputmsg("PostScript output is rotated 90 degrees");
			if ((curstate&PSPLOTTER) == 0)
				ttyputmsg("PostScript output is printed"); else
					ttyputmsg("PostScript output is plotted");
			np = el_curlib->curnodeproto;
			if (np != NONODEPROTO)
			{
				var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING, io_postscriptfilename);
				if (var != NOVARIABLE)
					ttyputmsg("Facet %s synchronized to PostScript file %s",
						describenodeproto(np), (char *)var->addr);
			}
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "encapsulated", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate|EPSPSCRIPT, VINTEGER);
			ttyputverbose("PostScript output will be encapsulated format");
			return(0);
		}
		if (namesamen(pp, "plain", l) == 0 && l >= 2)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~EPSPSCRIPT, VINTEGER);
			ttyputverbose("PostScript output will be plain format");
			return(0);
		}
		if (namesamen(pp, "color", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate | PSCOLOR, VINTEGER);
			ttyputverbose("PostScript output will use color");
			return(0);
		}
		if (namesamen(pp, "gray-scale", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~PSCOLOR, VINTEGER);
			ttyputverbose("PostScript output will be gray-scale");
			return(0);
		}
		if (namesamen(pp, "rotate", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate | PSROTATE, VINTEGER);
			ttyputverbose("PostScript output will be rotated 90 degrees");
			return(0);
		}
		if (namesamen(pp, "no-rotate", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~PSROTATE, VINTEGER);
			ttyputverbose("PostScript output will appear unrotated");
			return(0);
		}
		if (namesamen(pp, "margin", l) == 0)
		{
			if (count == 3)
			{
				wid = atofr(par[2]);
				(void)setval((INTBIG)io_aid, VAID, "IO_postscript_margin", wid, VFRACT);
			} else
			{
				var = getval((INTBIG)io_aid, VAID, VFRACT, "IO_postscript_margin");
				if (var == NOVARIABLE) wid = muldiv(DEFAULTPSMARGIN, WHOLE, 75); else
					wid = var->addr;
			}
			ttyputverbose("PostScript output will use a %s-wide margin", frtoa(wid));
			return(0);
		}
		if (namesamen(pp, "plotter", l) == 0 && l >= 2)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate | PSPLOTTER, VINTEGER);
			if (count == 3)
			{
				wid = atofr(par[2]);
				(void)setval((INTBIG)io_aid, VAID, "IO_postscript_width", wid, VFRACT);
				ttyputverbose("PostScript output will assume a %s-wide plotter", frtoa(wid));
			} else
				ttyputverbose("PostScript output will assume a continuous-roll plotter");
			return(0);
		}
		if (namesamen(pp, "printer", l) == 0 && l >= 2)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~PSPLOTTER, VINTEGER);
			if (count == 4)
			{
				wid = atofr(par[2]);
				(void)setval((INTBIG)io_aid, VAID, "IO_postscript_width", wid, VFRACT);
				hei = atofr(par[3]);
				(void)setval((INTBIG)io_aid, VAID, "IO_postscript_height", hei, VFRACT);
				ttyputverbose("PostScript output will assume a %s x %s page", frtoa(wid), frtoa(hei));
			} else
				ttyputverbose("PostScript output will be assume a fixed-size sheet");
			return(0);
		}
		if (namesamen(pp, "unsynchronize", l) == 0)
		{
			np = el_curlib->curnodeproto;
			if (np == NONODEPROTO)
			{
				ttyputerr("Edit a facet before removing synchronization");
				return(1);
			}
			if (getvalkey((INTBIG)np, VNODEPROTO, VSTRING, io_postscriptfilename) != NOVARIABLE)
			{
				delvalkey((INTBIG)np, VNODEPROTO, io_postscriptfilename);
				ttyputverbose("Facet %s no longer synchronized to PostScript disk file",
					describenodeproto(np));
			}
			return(0);
		}
		if (namesamen(pp, "synchronize", l) == 0)
		{
			if (count <= 2)
			{
				ttyputerr("Usage: tellaid io postscript synchronize FILENAME");
				return(1);
			}
			np = el_curlib->curnodeproto;
			if (np == NONODEPROTO)
			{
				ttyputerr("Edit a facet to synchronize it to a PostScript file");
				return(1);
			}
			(void)setvalkey((INTBIG)np, VNODEPROTO, io_postscriptfilename,
				(INTBIG)par[1], VSTRING);
			ttyputverbose("Facet %s synchronized to PostScript file %s",
				describenodeproto(np), par[1]);
			return(0);
		}
		ttyputerr("Usage: tellaid io postscript OPTION");
		return(1);
	}

	if (namesamen(pp, "hpgl", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&HPGL2) == 0)
				ttyputmsg("HPGL output uses older (version 1) format"); else
					ttyputmsg("HPGL output uses newer (HPGL/2) format");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "1", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~HPGL2, VINTEGER);
			ttyputmsg("HPGL output will use older (version 1) format");
			return(0);
		}
		if (namesamen(pp, "2", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate|HPGL2, VINTEGER);
			ttyputverbose("HPGL output will use newer (HPGL/2) format");
			if (count >= 4 && namesamen(par[2], "scale", strlen(par[2])) == 0)
			{
				scale = myatoi(par[3]);
				(void)setval((INTBIG)io_aid, VAID, "IO_hpgl2_scale",
					scale, VINTEGER);
				ttyputverbose("HPGL/2 plots will scale at %ld %ss per pixel", scale, unitsname(el_units));
			}
			return(0);
		}
		ttyputerr("Usage: tellaid io hpgl [1|2]");
		return(1);
	}

#if IOEDIF
	if (namesamen(pp, "edif", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate&EDIFSCHEMATIC) == 0)
				ttyputmsg("EDIF output writes schematics"); else
					ttyputmsg("EDIF output writes netlists");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "schematic", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate | EDIFSCHEMATIC, VINTEGER);
			ttyputverbose("EDIF output will write schematics");
			return(0);
		}
		if (namesamen(pp, "netlist", l) == 0)
		{
			(void)setvalkey((INTBIG)io_aid, VAID, io_state,
				curstate & ~EDIFSCHEMATIC, VINTEGER);
			ttyputverbose("EDIF output will write netlists");
			return(0);
		}
		ttyputerr("Usage: tellaid io edif [schematic|netlist]");
		return(1);
	}
#endif

	if (namesamen(pp, "verbose", l) == 0)
	{
		if (count <= 1)
		{
			ttyputerr("Usage: tellaid io verbose OPTION");
			return(1);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "off", l) == 0 && l >= 2)
		{
			io_verbose = 0;
			ttyputverbose("I/O done silently");
			return(0);
		}
		if (namesamen(pp, "on", l) == 0 && l >= 2)
		{
			io_verbose = 1;
			ttyputverbose("I/O prints status");
			return(0);
		}
		if (namesamen(pp, "graphical", l) == 0 && l >= 1)
		{
			io_verbose = -1;
			ttyputverbose("I/O shows graphical progress");
			return(0);
		}
		ttyputerr("Unknown verbose option: %s", pp);
		return(1);
	}

	ttyputerr("Bad I/O option: %s", par[0]);
	return(1);
}

/*
 * make I/O requests of this aid:
 *
 * "read" TAKES: LIBRARY to read and string style
 * "write" TAKES: LIBRARY to write and string style
 * "verbose" TAKES: new verbose factor, RETURNS: old verbose factor
 */
INTBIG io_request(char *command, va_list ap)
{
	REGISTER LIBRARY *lib;
	REGISTER INTSML i, l, len, format;
	REGISTER INTBIG arg1, arg3;
	char *arg2;

	if (namesame(command, "verbose") == 0)
	{
		i = io_verbose;
		io_verbose = va_arg(ap, INTSML);
		return(i);
	}

	/* get the arguments */
	arg1 = va_arg(ap, INTBIG);
	arg2 = va_arg(ap, char*);

	/* find desired library and format */
	lib = (LIBRARY *)arg1;
	format = -1;
	len = strlen(arg2);
	for(i=0; io_formatlist[i].name != 0; i++)
		if (namesamen(arg2, io_formatlist[i].name, len) == 0 && len >= io_formatlist[i].required)
	{
		format = io_formatlist[i].bits;
		break;
	}
	if (format < 0)
	{
		ttyputerr("Unknown I/O format: %s", arg2);
		return(1);
	}

	if (namesame(command, "write") == 0)
	{
		/* announce the beginning of library output */
		for(i=0; i<el_maxaid; i++)
			if (el_aids[i].writelibrary != 0)
				(*el_aids[i].writelibrary)(lib, 0);

		switch (format)
		{
			case FBINARY:
				l = io_writebinlibrary(lib);
				break;

#if IOCIF
			case FCIF:
				l = io_writeciflibrary(lib);
				break;
#endif

#if IODXF
			case FDXF:
				l = io_writedxflibrary(lib);
				break;
#endif

#if IOEAGLE
			case FEAGLE:
				l = io_writeeaglelibrary(lib);
				break;
#endif

#if IOEDIF
			case FEDIF:
				l = io_writeediflibrary(lib);
				break;
#endif

#if IOGDS
			case FGDS:
				l = io_writegdslibrary(lib);
				break;
#endif

			case FHPGL:
				l = io_writehpgllibrary(lib);
				break;

#if IOL
			case FL:
				l = io_writellibrary(lib);
				break;
#endif

#if IOLEF
			case FLEF:
				l = io_writeleflibrary(lib);
				break;
#endif

#if IOPADS
			case FPADS:
				l = io_writepadslibrary(lib);
				break;
#endif

			case FPOSTSCRIPT:
			case FPRINTEDPOSTSCRIPT:
				l = io_writepostscriptlibrary(lib, (format == FPOSTSCRIPT ? 0 : 1));
				break;

#ifdef MACOS
			case FQUICKDRAW:
				l = io_writequickdrawlibrary(lib);
				break;
#endif

#if IOSKILL
			case FSKILL:
				l = io_writeskilllibrary(lib);
				break;
#endif

			case FTEXT:
				l = io_writetextlibrary(lib);
				break;

			default:
				for(i=0; io_formatlist[i].name != 0; i++)
					if (io_formatlist[i].bits == format)
				{
					ttyputerr("Cannot write %s files", io_formatlist[i].name);
					return(1);
				}
		}

		/* announce the ending of library output */
		if (l == 0)
		{
			/* lib->userbits |= READFROMDISK; */
			for(i=0; i<el_maxaid; i++)
				if (el_aids[i].writelibrary != 0)
					(*el_aids[i].writelibrary)(lib, 1);
		}
		return(l);
	}

	if (namesame(command, "read") == 0)
	{
		arg3 = va_arg(ap, INTBIG);
		switch (format)
		{
			case FBINARY:
				l = io_readbinlibrary(lib);
				break;

#if IOCIF
			case FCIF:
				l = io_readciflibrary(lib);
				break;
#endif

#if IODEF
			case FDEF:
				l = io_readdeflibrary(lib);
				break;
#endif

#if IODXF
			case FDXF:
				l = io_readdxflibrary(lib);
				break;
#endif

#if IOEDIF
			case FEDIF:
				l = io_readediflibrary(lib);
				break;
#endif

#if IOGDS
			case FGDS:
				l = io_readgdslibrary(lib, (INTSML)arg3);
				break;
#endif

#if IOLEF
			case FLEF:
				l = io_readleflibrary(lib);
				break;
#endif

#if IOSDF
			case FSDF:
				l = io_readsdflibrary(lib);
				break;
#endif

#if IOSUE
			case FSUE:
				l = io_readsuelibrary(lib);
				break;
#endif

			case FTEXT:
				l = io_readtextlibrary(lib);
				break;

#if VHDLAID
			case FVHDL:
				l = io_readvhdllibrary(lib);
				break;
#endif

			default:
				for(i=0; io_formatlist[i].name != 0; i++)
					if (io_formatlist[i].bits == format)
				{
					ttyputerr("Cannot read %s files", io_formatlist[i].name);
					return(1);
				}
		}

		/* announce the completion of library input */
		if (l == 0)
		{
			for(i=0; i<el_maxaid; i++)
				if (el_aids[i].readlibrary != 0)
					(*el_aids[i].readlibrary)(lib);
		}
		return(l);
	}
	return(-1);
}

/******************************* BLOATING *******************************/

/*
 * routine to indicate that CIF layer "layer" is to be bloated by "amount"
 */
void io_setoutputbloat(char *layer, INTBIG amount)
{
	REGISTER BLOAT *bl;

	/* special case when bloating the null layer */
	if (*layer == 0)
	{
		for(bl = io_curbloat; bl != NOBLOAT; bl = bl->nextbloat)
			ttyputmsg("Bloating layer %s by %ld %ss", bl->layer, bl->amount, unitsname(el_units));
		return;
	}

	/* first see if this layer is already being bloated */
	for(bl = io_curbloat; bl != NOBLOAT; bl = bl->nextbloat)
		if (namesame(bl->layer, layer) == 0)
	{
		if (bl->amount == amount)
		{
			ttyputmsg("Layer %s is already being bloated by %ld %ss",
				bl->layer, amount, unitsname(el_units));
			return;
		}
		ttyputmsg("Layer %s was being bloated by %ld %ss, now by %ld",
			bl->layer, bl->amount, unitsname(el_units), amount);
		bl->amount = amount;
		return;
	}

	bl = (BLOAT *)emalloc(sizeof (BLOAT), io_aid->cluster);
	if (bl == 0)
	{
		ttyputerr("No bloat memory");
		return;
	}
	(void)allocstring(&bl->layer, layer, io_aid->cluster);
	bl->amount = amount;
	bl->nextbloat = io_curbloat;
	io_curbloat = bl;
	ttyputmsg("Layer %s will be bloated by %ld %ss", layer, amount, unitsname(el_units));
}

/*
 * routine to tell the amount to bloat layer "lay"
 */
INTBIG io_getoutputbloat(char *layer)
{
	REGISTER BLOAT *bl;

	for(bl = io_curbloat; bl != NOBLOAT; bl = bl->nextbloat)
		if (namesame(bl->layer, layer) == 0) return(bl->amount);
	return(0);
}

/******************************* LIBRARY REPAIR *******************************/

/*
 * routine to complete formation of library "lib" that has just been read from
 * disk.  Some data is not read, but computed from other data.
 */
void io_fixnewlib(LIBRARY *lib, INTBIG statusitem)
{
	REGISTER NODEPROTO *np, *pnt;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER INTSML libunits;
	REGISTER INTBIG i, j, lam;
	INTBIG lx, ly;
	static POLYGON *poly = NOPOLYGON;
	static INTBIG edtec_option = 0;
	char line[50];
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *tech;
	REGISTER PORTPROTO *pp, *inpp;
	REGISTER PORTEXPINST *pe;
	extern AIDENTRY *net_aid;

	/* make sure units are correct */
	libunits = (INTSML)(((lib->userbits & LIBUNITS) >> LIBUNITSSH) << INTERNALUNITSSH);
	if ((libunits & INTERNALUNITS) != (el_units & INTERNALUNITS))
	{
		ttyputmsg("Converting library from %s units to %s units", unitsname(libunits),
			unitsname(el_units));
		changeinternalunits(lib, libunits, el_units);
	}

	/* adjust every facet in the library */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		np->firstinst = NONODEINST;
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		/* examine every nodeinst in the facet */
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			/* compute linked list of primitive nodes */
			pnt = ni->proto;
			if (pnt == NONODEPROTO) continue;
			if (pnt->firstinst != NONODEINST) pnt->firstinst->lastinst = ni;
			ni->nextinst = pnt->firstinst;
			ni->lastinst = NONODEINST;
			pnt->firstinst = ni;
		}
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			for(pe = pp->subnodeinst->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				if (pe->proto == pp->subportproto)
		{
			pp->subportexpinst = pe;  break;
		}
#ifdef MACOS
		/*
		 * The Macintosh uses a different date epoch than other machines, and this
		 * is handled in "dbtext.c".  However, the adjustment code is recent,
		 * and some old Macintosh libraries may have incorrect date information.
		 *
		 * The Macintosh epoch is 1904, but every other machine uses 1970.
		 * Therefore, the Macintosh numbers are 66 years larger.
		 *
		 * To check for bogus date information, see if the date is larger
		 * than the constant 0x9FE3EB1F.  This constant is 1989 in Mac time
		 * but is 2055 elsewhere (1989 is the earliest possible incarnation
		 * of Electric on the Mac).  If the date is that large, then either it
		 * is 2055 and Macs still exist (unlikely) or the date is bad and needs
		 * to have 66 years (0x7C254E10) subtracted from it.
		 */
		if (np->creationdate > 0x9FE3EB1F) np->creationdate -= 0x7C254E10;
		if (np->revisiondate > 0x9FE3EB1F) np->revisiondate -= 0x7C254E10;
#endif
	}

	/* now fill in the connection lists on the ports */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		inpp = pp;
		while (inpp->parent->primindex == 0) inpp = inpp->subportproto;
		pp->connects = inpp->connects;
	}

	/* create network information */
	if (statusitem > 0) DiaSetText(statusitem, "Building network data...");
	(void)askaid(net_aid, "total-re-number", (INTBIG)lib);

	/* check for incorrect layer counts on stored variables */
	io_fixtechlayers();

	/*
	 * see if this is version 4 or earlier...
	 * In version 4 and earlier, the basic unit was the centimicron and rotation
	 * was specified in degrees.  From version 5 and up, the basic unit is scaled
	 * by 20 to the half-millimicron and the rotation fields are scaled by 10 to
	 * tenth-degrees.  Also, must convert some variable names.
	 */
	var = getval((INTBIG)lib, VLIBRARY, VSTRING, "LIB_former_version");
	if (var == NOVARIABLE) return;
	i = atoi((char *)var->addr);
	if (i >= 5) return;

	/* setup for units conversion */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, io_aid->cluster);
	if (edtec_option == 0) edtec_option = makekey("EDTEC_option");

	/* must scale, first make sure the technologies agree with this library */
	if (lib != el_curlib)
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
			db_scaletechnology(tech, lib->lambda[tech->techindex], tech->deflambda);

	/* now scale */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center);
		if (var != NOVARIABLE)
		{
			((INTBIG *)var->addr)[0] *= 20;
			((INTBIG *)var->addr)[1] *= 20;
		}
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			ni->lowx *= 20;   ni->highx *= 20;
			ni->lowy *= 20;   ni->highy *= 20;
			ni->rotation *= 10;
			var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, edtec_option);
			if (var != NOVARIABLE)
			{
				if (var->addr == TECHLAMBDA)
				{
					var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, art_messagekey);
					if (var != NOVARIABLE)
					{
						lam = myatoi(&((char *)var->addr)[8]);
						(void)sprintf(line, "Lambda: %ld", lam*20);
						nextvarchangequiet();
						(void)setvalkey((INTBIG)ni, VNODEINST, art_messagekey,
							(INTBIG)line, VSTRING|VDISPLAY);
					}
				}
			}
			var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER|VISARRAY, el_trace);
			if (var != NOVARIABLE)
			{
				i = getlength(var);
				for(j=0; j<i; j++) ((INTBIG *)var->addr)[j] *= 20;
			}
			var = getvalkey((INTBIG)ni, VNODEINST, VINTEGER, art_degreeskey);
			if (var != NOVARIABLE) var->addr *= 10;
			boundobj(ni->geom, &ni->geom->lowx, &ni->geom->highx, &ni->geom->lowy, &ni->geom->highy);
		}
	}

	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			ai->width *= 20;
			ai->end[0].xpos *= 20;   ai->end[0].ypos *= 20;
			ai->end[1].xpos *= 20;   ai->end[1].ypos *= 20;
			(void)setshrinkvalue(ai, 0);
			var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius);
			if (var != NOVARIABLE) var->addr *= 20;
			for(i=0; i<2; i++)
			{
				/* make sure arcinst connects in portinst area */
				shapeportpoly(ai->end[i].nodeinst, ai->end[i].portarcinst->proto, poly, 0);
				if (isinside(ai->end[i].xpos, ai->end[i].ypos, poly) != 0) continue;
				portposition(ai->end[i].nodeinst, ai->end[i].portarcinst->proto, &lx, &ly);
				if (lx == ai->end[i].xpos && ly == ai->end[i].ypos) continue;

				/* try to make manhattan fix */
				if ((ai->end[0].xpos == ai->end[1].xpos) && isinside(ai->end[i].xpos, ly, poly) != 0)
					ai->end[i].ypos = ly; else
						if ((ai->end[0].ypos == ai->end[1].ypos) &&
							isinside(lx, ai->end[i].ypos, poly) != 0)
								ai->end[i].xpos = lx; else
				{
					ai->end[i].xpos = lx;   ai->end[i].ypos = ly;
				}
			}
			ai->length = computedistance(ai->end[0].xpos,ai->end[0].ypos,
				ai->end[1].xpos,ai->end[1].ypos);
			boundobj(ai->geom, &ai->geom->lowx, &ai->geom->highx, &ai->geom->lowy, &ai->geom->highy);
		}
		db_boundfacet(np, &np->lowx, &np->highx, &np->lowy, &np->highy);
		io_fixrtree(np->rtree);
	}

	/* now restore the technology lambda values */
	if (lib != el_curlib)
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
			db_scaletechnology(tech, el_curlib->lambda[tech->techindex], tech->deflambda);
}

struct
{
	char   *variable;
	char   *meaning;
	INTSML  uppertriangle;
	INTBIG  key;
	INTBIG  defaultint;
	char   *defaultstring;
	float   defaultfloat;
} io_techlayervariables[] =
{
	"IO_cif_layer_names",            "CIF Layer Names",                  0, 0,  0,     "", 0.0,
	"IO_dxf_layer_names",            "DXF Layer Names",                  0, 0,  0,     "", 0.0,
	"IO_gds_layer_numbers",          "GDS Layer Numbers",                0, 0, -1,     "", 0.0,
	"IO_skill_layer_names",          "SKILL Layer Names",                0, 0,  0,     "", 0.0,
	"SIM_spice_resistance",          "SPICE Layer Resistances",          0, 0,  0,     "", 0.0,
	"SIM_spice_capacitance",         "SPICE Layer Capacitances",         0, 0,  0,     "", 0.0,
	"DRC_min_unconnected_distances", "Unconnected Design Rule spacings", 1, 0, -WHOLE, "", 0.0,
	"DRC_min_connected_distances",   "Connected Design Rule spacings",   1, 0, -WHOLE, "", 0.0,
	0, 0, 0, 0, 0, "", 0.0
};

/*
 * this table rearranges the MOSIS CMOS Submicron layer tables from
 * their old 4-layer metal (34 layers) to 6-layer metal (40 layers)
 */
INTBIG tech_mocmossubarrange[] =
{
	 0,  1,  2,  3,  6,  7,  8,  9, 10, 11,
	12, 13, 14, 15, 16, 17, 18, 21, 22, 23,
	24, 25, 26, 27, 28, 31, 32, 33, 34, 35,
	36, 37, 38, 39
};

struct
{
	char *techname;
	INTBIG oldlayercount, newlayercount;
	INTBIG *arrangement;
} io_techlayerfixes[] =
{
	"mocmossub", 34, 40, tech_mocmossubarrange,
	0, 0, 0, 0
};

void io_fixtechlayers(void)
{
	REGISTER INTBIG i, j, k, l, l1, l2, oldpos, newpos, newl1, newl2, *newints;
	REGISTER float *newfloats;
	REGISTER char **newstrings;
	REGISTER TECHNOLOGY *tech;
	REGISTER VARIABLE *var;

	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		for(i=0; io_techlayervariables[i].variable != 0; i++)
		{
			if (io_techlayervariables[i].key == 0)
				io_techlayervariables[i].key = makekey(io_techlayervariables[i].variable);
			var = getvalkey((INTBIG)tech, VTECHNOLOGY, -1, io_techlayervariables[i].key);
			if (var == NOVARIABLE) continue;
			if (io_techlayervariables[i].uppertriangle != 0)
			{
				l = tech->layercount;
				if (getlength(var) == (l * l + l) / 2) continue;
			} else
			{
				if (getlength(var) == tech->layercount) continue;
			}

			/* layers are inconsistent: see if there are rules to fix it */
			for(j=0; io_techlayerfixes[j].techname != 0; j++)
			{
				if (namesame(tech->techname, io_techlayerfixes[j].techname) != 0) continue;
				if (io_techlayervariables[i].uppertriangle != 0)
				{
					l = io_techlayerfixes[j].oldlayercount;
					if ((l*l+l)/2 != getlength(var)) continue;
				} else
				{
					if (io_techlayerfixes[j].oldlayercount != getlength(var)) continue;
				}
				if (io_techlayerfixes[j].newlayercount != tech->layercount) continue;
				break;
			}
			if (io_techlayerfixes[j].techname != 0)
			{
				if (io_techlayervariables[i].uppertriangle != 0)
				{
					k = tech->layercount;
					l = (k * k + k)/2;
					newints = (INTBIG *)emalloc(l * SIZEOFINTBIG, tech->cluster);
					for(k=0; k<l; k++)
						newints[k] = io_techlayervariables[i].defaultint;
					for(l1=0; l1<io_techlayerfixes[j].oldlayercount; l1++)
					{
						for(l2=l1; l2<io_techlayerfixes[j].oldlayercount; l2++)
						{
							oldpos = (l1+1) * (l1/2) + (l1&1) * ((l1+1)/2);
							oldpos = l2 + io_techlayerfixes[j].oldlayercount * l1 - oldpos;
							newl1 = io_techlayerfixes[j].arrangement[l1];
							newl2 = io_techlayerfixes[j].arrangement[l2];
							newpos = (newl1+1) * (newl1/2) + (newl1&1) * ((newl1+1)/2);
							newpos = newl2 + tech->layercount * newl1 - newpos;
							newints[newpos] = ((INTBIG *)var->addr)[oldpos];
						}
					}
					(void)setvalkey((INTBIG)tech, VTECHNOLOGY, io_techlayervariables[i].key,
						(INTBIG)newints, (var->type&VTYPE)|VISARRAY|(l<<VLENGTHSH));
					efree((char *)newints);
				} else
				{
					/* able to fix the ordering */
					if ((var->type&VTYPE) == VSTRING)
					{
						newstrings = (char **)emalloc(tech->layercount * (sizeof (char *)),
							tech->cluster);
						for(k=0; k<tech->layercount; k++)
							newstrings[k] = io_techlayervariables[i].defaultstring;
						for(k=0; k<getlength(var); k++)
							newstrings[io_techlayerfixes[j].arrangement[k]] =
								((char **)var->addr)[k];
						(void)setvalkey((INTBIG)tech, VTECHNOLOGY, io_techlayervariables[i].key,
							(INTBIG)newstrings, (var->type&VTYPE)|VISARRAY|(tech->layercount<<VLENGTHSH));
						efree((char *)newstrings);
					} else if ((var->type&VTYPE) == VFLOAT)
					{
						newfloats = (float *)emalloc(tech->layercount * (sizeof (float)),
							tech->cluster);
						for(k=0; k<tech->layercount; k++)
							newfloats[k] = io_techlayervariables[i].defaultfloat;
						for(k=0; k<getlength(var); k++)
							newfloats[io_techlayerfixes[j].arrangement[k]] =
								((float *)var->addr)[k];
						(void)setvalkey((INTBIG)tech, VTECHNOLOGY, io_techlayervariables[i].key,
							(INTBIG)newfloats, (var->type&VTYPE)|VISARRAY|(tech->layercount<<VLENGTHSH));
						efree((char *)newfloats);
					} else
					{
						newints = (INTBIG *)emalloc(tech->layercount * SIZEOFINTBIG,
							tech->cluster);
						for(k=0; k<tech->layercount; k++)
							newints[k] = io_techlayervariables[i].defaultint;
						for(k=0; k<getlength(var); k++)
							newints[io_techlayerfixes[j].arrangement[k]] =
								((INTBIG *)var->addr)[k];
						(void)setvalkey((INTBIG)tech, VTECHNOLOGY, io_techlayervariables[i].key,
							(INTBIG)newints, (var->type&VTYPE)|VISARRAY|(tech->layercount<<VLENGTHSH));
						efree((char *)newints);
					}
				}
				continue;
			}

			/* unable to fix: issue a warning */
			ttyputmsg("Warning: %s in technology %s are inconsistent",
				io_techlayervariables[i].meaning, tech->techname);
		}
	}
}

void io_fixrtree(RTNODE *rtree)
{
	REGISTER INTSML i;
	REGISTER GEOM *geom;
	REGISTER RTNODE *subrt;

	if (rtree->total <= 0) return;
	if (rtree->flag != 0)
	{
		geom = (GEOM *)rtree->pointers[0];
		rtree->lowx = geom->lowx;   rtree->highx = geom->highx;
		rtree->lowy = geom->lowy;   rtree->highy = geom->highy;
		for(i=1; i<rtree->total; i++)
		{
			geom = (GEOM *)rtree->pointers[i];
			if (geom->lowx < rtree->lowx) rtree->lowx = geom->lowx;
			if (geom->highx > rtree->highx) rtree->highx = geom->highx;
			if (geom->lowy < rtree->lowy) rtree->lowy = geom->lowy;
			if (geom->highy > rtree->highy) rtree->highy = geom->highy;
		}
	} else
	{
		subrt = (RTNODE *)rtree->pointers[0];
		io_fixrtree(subrt);
		rtree->lowx = subrt->lowx;   rtree->highx = subrt->highx;
		rtree->lowy = subrt->lowy;   rtree->highy = subrt->highy;
		for(i=1; i<rtree->total; i++)
		{
			subrt = (RTNODE *)rtree->pointers[i];
			io_fixrtree(subrt);
			if (subrt->lowx < rtree->lowx) rtree->lowx = subrt->lowx;
			if (subrt->highx > rtree->highx) rtree->highx = subrt->highx;
			if (subrt->lowy < rtree->lowy) rtree->lowy = subrt->lowy;
			if (subrt->highy > rtree->highy) rtree->highy = subrt->highy;
		}
	}
}

/*
 * routine to convert port names that have changed (specifically those
 * in the Schematics technology).  Given the port name on a node proto,
 * returns the correct port (or NOPORTPROTO if not known).
 */
PORTPROTO *io_convertoldportname(char *portname, NODEPROTO *np)
{
	if (np->primindex == 0) return(NOPORTPROTO);
	if (np == sch_sourceprim || np == sch_meterprim)
	{
		if (namesame(portname, "top") == 0)
			return(np->firstportproto);
		if (namesame(portname, "bottom") == 0)
			return(np->firstportproto->nextportproto);
	}
	if (np == sch_twoportprim)
	{
		if (namesame(portname, "upperleft") == 0)
			return(np->firstportproto);
		if (namesame(portname, "lowerleft") == 0)
			return(np->firstportproto->nextportproto);
		if (namesame(portname, "upperright") == 0)
			return(np->firstportproto->nextportproto->nextportproto);
		if (namesame(portname, "lowerright") == 0)
			return(np->firstportproto->nextportproto->nextportproto->nextportproto);
	}
	return(NOPORTPROTO);
}

/******************** LAYER ORDERING FOR GRAPHIC COPY/PRINT ********************/

INTSML io_setuptechorder(TECHNOLOGY *tech)
{
	REGISTER INTSML i, j, k, l, m, *neworder, newamount;
	REGISTER INTBIG *layers;
	INTBIG order[LFNUMLAYERS];
	REGISTER VARIABLE *var;

	/* determine order of overlappable layers in current technology */
	io_maxlayers = 0;
	i = tech->layercount;
	var = getval((INTBIG)tech, VTECHNOLOGY, VINTEGER|VISARRAY,
		"TECH_layer_function");
	if (var != NOVARIABLE)
	{
		layers = (INTBIG *)var->addr;
		for(j=0; j<LFNUMLAYERS; j++)
			order[j] = layerfunctionheight(j);
		for(j=0; j<LFNUMLAYERS; j++)
		{
			for(k=0; k<LFNUMLAYERS; k++)
			{
				if (order[k] != j) continue;
				for(l=0; l<i; l++)
				{
					if ((layers[l]&LFTYPE) != k) continue;
					if (io_maxlayers >= io_mostlayers)
					{
						newamount = io_mostlayers * 2;
						if (newamount <= 0) newamount = 10;
						neworder = (INTSML *)emalloc(newamount * SIZEOFINTSML, io_aid->cluster);
						if (neworder == 0) return(io_maxlayers);
						for(m=0; m<io_maxlayers; m++)
							neworder[m] = io_overlaporder[m];
						if (io_mostlayers > 0) efree((char *)io_overlaporder);
						io_overlaporder = neworder;
						io_mostlayers = newamount;
					}
					io_overlaporder[io_maxlayers++] = l;
				}
				break;
			}
		}
	}
	return(io_maxlayers);
}

/*
 * Routine to return the layer in plotting position "i" (from 0 to the value returned
 * by "io_setuptechorder" - 1).
 */
INTSML io_nextplotlayer(INTBIG i)
{
	return(io_overlaporder[i]);
}

/******************** MATH HELPERS ********************/

/*
 * This routine is used by "ioedifo.c" and "routmaze.c".
 */
void io_compute_center(INTBIG xc, INTBIG yc, INTBIG x1, INTBIG y1,
	INTBIG x2, INTBIG y2, INTBIG *cx, INTBIG *cy)
{
	int r, dx, dy, a1, a2, a;
	double pie, theta, radius, Dx, Dy;

	/* reconstruct angles to p1 and p2 */
	Dx = x1 - xc;
	Dy = y1 - yc;
	radius = sqrt(Dx * Dx + Dy * Dy);
	r = rounddouble(radius);
	a1 = (int)io_calc_angle(r, (double)(x1 - xc), (double)(y1 - yc));
	a2 = (int)io_calc_angle(r, (double)(x2 - xc), (double)(y2 - yc));
	if (a1 < a2) a1 += 3600;
	a = (a1 + a2) >> 1;
	pie = acos(-1.0);
	theta = (double) a *pie / 1800.0;	/* in radians */
	Dx = radius * cos(theta);
	Dy = radius * sin(theta);
	dx = rounddouble(Dx);
	dy = rounddouble(Dy);
	*cx = xc + dx;
	*cy = yc + dy;
}

double io_calc_angle(double r, double dx, double dy)
{
	double ratio, a1, a2;

	ratio = 1800.0 / EPI;
	a1 = acos(dx/r) * ratio;
	a2 = asin(dy/r) * ratio;
	if (a2 < 0.0) return(3600.0 - a1);
	return(a1);
}
