/*
 * 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 "tecgen.h"
#include "tecmocmossub.h"
#include "usr.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", M_("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", M_("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", M_("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", M_("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", M_("DXF options"), 0};
#endif

#if IOGDS
static COMCOMP iogdsoarp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("maximum number of degrees per arc segment"), 0};
static COMCOMP iogdsoasp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("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", M_("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", M_("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", M_("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", M_("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", M_("negating control of plot output"), 0};
static KEYWORD ioplotopt[] =
{
	{"not",               1,{&ioplotnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"focus-highlighted", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"focus-window",      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", M_("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
};
COMCOMP io_ioverp = {ioveropt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("verbose option"), 0};

static COMCOMP iopostsynch = {NOKEYWORD, topoffile, nextfile, NOPARAMS, NOBACKUP,
	NOFILL|INPUTOPT, " \t", M_("PostScript synchronization file name"), 0};
static COMCOMP iopostplotwid = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("PostScript plotter width"), 0};
static COMCOMP iopostprintwid = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("PostScript printer width"), 0};
static COMCOMP iopostprinthei = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("PostScript printer height"), 0};
static COMCOMP iopostmargin = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("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}},
	{"stippled-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}},
	{"flat",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"hierarchical",     0,{NOKEY,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", M_("control of PostScript format"), 0};

static COMCOMP iohpgl2sp = {NOKEYWORD,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("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", M_("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", M_("HPGL version"), 0};

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

static KEYWORD iobinopt[] =
{
	{"no-backup",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"one-level-backup",    0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"many-level-backup",   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-check",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"check",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP iobinp = {iobinopt,NOTOPLIST,NONEXTLIST,NOPARAMS,NOBACKUP,
	INPUTOPT, " \t", M_("Binary options"), 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
	{"binary",         1,{&iobinp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"plot",           1,{&ioplotp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"verbose",        1,{&io_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", M_("Input/Output action"), 0};

struct
{
	char *name;
	INTSML required, bits;
} io_formatlist[] =
{
	{"binary",             1, FBINARY},
	{"nobackupbinary",     1, FBINARYNOBACKUP},
#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 IOECAD
	{"ecad",               2, FECAD},
#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 */
INTBIG       io_filetypeblib;		/* Binary library disk file descriptor */
INTBIG       io_filetypecif;		/* CIF disk file descriptor */
INTBIG       io_filetypedef;		/* DEF disk file descriptor */
INTBIG       io_filetypedxf;		/* DXF disk file descriptor */
INTBIG       io_filetypeeagle;		/* EAGLE disk netlist file descriptor */
INTBIG       io_filetypeecad;		/* ECAD disk netlist file descriptor */
INTBIG       io_filetypeedif;		/* EDIF disk file descriptor */
INTBIG       io_filetypegds;		/* GDS disk file descriptor */
INTBIG       io_filetypehpgl;		/* HPGL disk file descriptor */
INTBIG       io_filetypel;			/* L disk file descriptor */
INTBIG       io_filetypelef;		/* LEF disk file descriptor */
INTBIG       io_filetypepads;		/* PADS netlist disk file descriptor */
INTBIG       io_filetypeps;			/* PostScript disk file descriptor */
INTBIG       io_filetypesdf;		/* SDF disk file descriptor */
INTBIG       io_filetypeskill;		/* SKILL commands disk file descriptor */
INTBIG       io_filetypesue;		/* SUE disk file descriptor */
INTBIG       io_filetypetlib;		/* Text library disk file descriptor */
INTBIG       io_filetypevhdl;		/* VHDL disk file descriptor */

/* 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_unifylambdavalues(LIBRARY *lib);
void   io_libraryoptiondlog(void);
void   io_getversion(LIBRARY *lib, INTBIG *major, INTBIG *minor, INTBIG *detail);
void   parseelectricversion(char *version, INTBIG *major, INTBIG *minor, INTBIG *detail);

INTBIG *io_getstatebits(void)
{
	REGISTER VARIABLE *var;
	REGISTER INTBIG i, len;
	static INTBIG mybits[NUMIOSTATEBITWORDS];

	var = getvalkey((INTBIG)io_aid, VAID, -1, io_state);
	if (var == NOVARIABLE)
	{
		for(i=1; i<NUMIOSTATEBITWORDS; i++) mybits[i] = 0;
		mybits[0] = DXFFLATTENINPUT|GDSINARRAYS;
		return(mybits);
	}
	if ((var->type&VISARRAY) != 0)
	{
		for(i=0; i<NUMIOSTATEBITWORDS; i++) mybits[i] = 0;
		len = getlength(var);
		for(i=0; i<len; i++) mybits[i] = ((INTBIG *)var->addr)[i];
		return(mybits);
	}
	for(i=1; i<NUMIOSTATEBITWORDS; i++) mybits[i] = 0;
	mybits[0] = var->addr;
	return(mybits);
}

void io_setstatebits(INTBIG *bits)
{
	(void)setvalkey((INTBIG)io_aid, VAID, io_state, (INTBIG)bits,
		VINTEGER|VISARRAY|(NUMIOSTATEBITWORDS<<VLENGTHSH));
}

void io_init(INTBIG  *argc, char *argv[], AIDENTRY *thisaid)
{
	INTBIG statebits[NUMIOSTATEBITWORDS], i;

	/* 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();
	for(i=1; i<NUMIOSTATEBITWORDS; i++) statebits[i] = 0;
	statebits[0] = DXFFLATTENINPUT|GDSINARRAYS;
	io_setstatebits(statebits);
	io_verbose = 0;

	DiaDeclareHook("libopt", &io_ioverp, io_libraryoptiondlog);

	/* create disk file descriptors */
	io_filetypeblib  = setupfiletype("elib", "*.elib",       MACFSTAG('Elec'), 1, "blib",  _("Binary library"));
	io_filetypecif   = setupfiletype("cif",  "*.cif",        MACFSTAG('TEXT'), 0, "cif",   _("CIF"));
	io_filetypedef   = setupfiletype("def",  "*.def",        MACFSTAG('TEXT'), 0, "def",   _("DEF"));
	io_filetypedxf   = setupfiletype("dxf",  "*.dxf",        MACFSTAG('TEXT'), 0, "dxf",   _("AutoCAD DXF"));
	io_filetypeeagle = setupfiletype("txt",  "*.txt",        MACFSTAG('TEXT'), 0, "eagle", _("Eagle netlist"));
	io_filetypeecad  = setupfiletype("txt",  "*.txt",        MACFSTAG('TEXT'), 0, "ecad",  _("ECAD netlist"));
	io_filetypeedif  = setupfiletype("edif", "*.edif;*.ed?", MACFSTAG('TEXT'), 0, "edif",  _("EDIF"));
	io_filetypegds   = setupfiletype("gds",  "*.gds",        MACFSTAG('GDS '), 1, "gds",   _("GDS II"));
	io_filetypehpgl  = setupfiletype("hpgl", "*.hpgl",       MACFSTAG('TEXT'), 0, "hpgl",  _("HPGL"));
	io_filetypel     = setupfiletype("l",    "*.l",          MACFSTAG('TEXT'), 0, "l",     _("L"));
	io_filetypelef   = setupfiletype("lef",  "*.lef",        MACFSTAG('TEXT'), 0, "lef",   _("LEF"));
	io_filetypepads  = setupfiletype("asc",  "*.asc",        MACFSTAG('TEXT'), 0, "pad",   _("PADS netlist"));
	io_filetypeps    = setupfiletype("ps",   "*.ps;*.eps",   MACFSTAG('TEXT'), 0, "ps",    _("PostScript"));
	io_filetypesdf   = setupfiletype("sdf",  "*.sdf",        MACFSTAG('TEXT'), 0, "sdf",   _("SDF"));
	io_filetypeskill = setupfiletype("il",   "*.il",         MACFSTAG('TEXT'), 0, "skill", _("SKILL commands"));
	io_filetypesue   = setupfiletype("sue",  "*.sue",        MACFSTAG('TEXT'), 0, "sue",   _("SUE"));
	io_filetypetlib  = setupfiletype("txt",  "*.txt",        MACFSTAG('TEXT'), 0, "tlib",  _("Text library"));
	io_filetypevhdl  = setupfiletype("vhdl", "*.vhdl;*.vhd", MACFSTAG('TEXT'), 0, "vhdl",  _("VHDL"));

#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 */
	curstate = io_getstatebits();

	/* check for bloating specifications */
	if (namesamen(pp, "bloat-output", l) == 0)
	{
		if (count <= 1)
		{
			io_setoutputbloat("", 0);
			return(0);
		}
		if (count < 3)
		{
			ttyputusage("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[0]&PLOTFOCUS) != 0)
			{
				if ((curstate[0]&PLOTFOCUSDPY) != 0)
					ttyputmsg(_("Plot output will focus on displayed window")); else
						ttyputmsg(_("Plot output will focus on highlighted area"));
			} else ttyputmsg(_("Plot output will display entire facet"));
			if ((curstate[0]&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)
			{
				ttyputusage("tellaid io plot not OPTION");
				return(1);
			}
			l = strlen(pp = par[2]);
			if (namesamen(pp, "focus", l) == 0)
			{
				curstate[0] &= ~(PLOTFOCUS|PLOTFOCUSDPY);
				io_setstatebits(curstate);
				ttyputverbose(_("Plot output will display entire facet"));
				return(0);
			}
			if (namesamen(pp, "include-date", l) == 0)
			{
				curstate[0] &= ~PLOTDATES;
				io_setstatebits(curstate);
				ttyputverbose(_("Plot output will not include dates"));
				return(0);
			}
			ttyputbadusage("tellaid io plot not");
			return(1);
		}
		if (namesamen(pp, "focus-highlighted", l) == 0 && l >= 7)
		{
			curstate[0] = (curstate[0] & ~PLOTFOCUSDPY) | PLOTFOCUS;
			io_setstatebits(curstate);
			ttyputverbose(_("Plot output will focus on highlighted area"));
			return(0);
		}
		if (namesamen(pp, "focus-window", l) == 0 && l >= 7)
		{
			curstate[0] |= (PLOTFOCUSDPY|PLOTFOCUS);
			io_setstatebits(curstate);
			ttyputverbose(_("Plot output will focus on highlighted area"));
			return(0);
		}
		if (namesamen(pp, "include-date", l) == 0)
		{
			curstate[0] |= PLOTDATES;
			io_setstatebits(curstate);
			ttyputverbose(_("Plot output will include dates"));
			return(0);
		}
		ttyputusage("tellaid io plot [not] (focus | include-dates)");
		return(1);
	}

	/* check for binary specifications */
	if (namesamen(pp, "binary", l) == 0)
	{
		l = strlen(pp = par[1]);
		if (namesamen(pp, "no-backup", l) == 0 && l >= 4)
		{
			curstate[0] = (curstate[0] & ~BINOUTBACKUP) | BINOUTNOBACK;
			io_setstatebits(curstate);
			ttyputverbose(_("Binary output will not keep backups"));
			return(0);
		}
		if (namesamen(pp, "one-level-backup", l) == 0)
		{
			curstate[0] = (curstate[0] & ~BINOUTBACKUP) | BINOUTONEBACK;
			io_setstatebits(curstate);
			ttyputverbose(_("Binary output will keep one backup"));
			return(0);
		}
		if (namesamen(pp, "many-level-backup", l) == 0)
		{
			curstate[0] = (curstate[0] & ~BINOUTBACKUP) | BINOUTFULLBACK;
			io_setstatebits(curstate);
			ttyputverbose(_("Binary output will keep full backups"));
			return(0);
		}

		if (namesamen(pp, "no-check", l) == 0 && l >= 4)
		{
			curstate[0] &= ~CHECKATWRITE;
			io_setstatebits(curstate);
			ttyputverbose(_("Binary output will not check the database"));
			return(0);
		}
		if (namesamen(pp, "check", l) == 0)
		{
			curstate[0] |= CHECKATWRITE;
			io_setstatebits(curstate);
			ttyputverbose(_("Binary output will check the database"));
			return(0);
		}
		ttyputusage("tellaid io binary (no-backup | one-level-backup | many-level-backup | no-check | check)");
		return(1);
	}

#if IOCIF
	if (namesamen(pp, "cif", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&CIFINSQUARE) == 0)
				ttyputmsg(_("CIF wires will have rounded ends")); else
					ttyputmsg(_("CIF wires will have squared ends"));
			if ((curstate[0]&CIFOUTEXACT) == 0)
				ttyputmsg(_("CIF generation will include all facets")); else
					ttyputmsg(_("CIF generation will duplicate screen"));
			if ((curstate[0]&CIFOUTMERGE) == 0)
				ttyputmsg(_("CIF generation will list individual boxes")); else
					ttyputmsg(_("CIF generation will merge boxes"));
			if ((curstate[0]&CIFOUTADDDRC) == 0)
				ttyputmsg(_("CIF generation will ignore DRC cloak layer")); else
					ttyputmsg(_("CIF generation will include DRC cloak layer"));
			if ((curstate[0]&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[0]&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':
					curstate[0] &= ~CIFINSQUARE;
					io_setstatebits(curstate);
					ttyputverbose(_("CIF wires will have rounded ends"));
					break;
				case 's':
					curstate[0] |= CIFINSQUARE;
					io_setstatebits(curstate);
					ttyputverbose(_("CIF wires will have squared ends"));
					break;
				default:
					ttyputbadusage("tellaid io cif input");
			}
			return(0);
		}
		if (namesamen(pp, "output", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate[0]&CIFOUTEXACT) == 0)
					ttyputmsg(_("CIF generation will include all facets")); else
						ttyputmsg(_("CIF generation will duplicate screen"));
				if ((curstate[0]&CIFOUTMERGE) == 0)
					ttyputmsg(_("CIF generation will list individual boxes")); else
						ttyputmsg(_("CIF generation will merge boxes"));
				if ((curstate[0]&CIFOUTADDDRC) == 0)
					ttyputmsg(_("CIF generation will ignore DRC cloak layer")); else
						ttyputmsg(_("CIF generation will include DRC cloak layer"));
				if ((curstate[0]&CIFOUTNOTCEN) == 0)
					ttyputmsg(_("CIF generation will put origins at center")); else
						ttyputmsg(_("CIF generation will put origins at lower corner"));
				if ((curstate[0]&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)
			{
				curstate[0] &= ~CIFOUTEXACT;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will include all facets"));
				return(0);
			}
			if (namesamen(pp, "exactly-as-displayed", l) == 0)
			{
				curstate[0] |= CIFOUTEXACT;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will duplicate screen"));
				return(0);
			}
			if (namesamen(pp, "merge-boxes", l) == 0)
			{
				curstate[0] |= CIFOUTMERGE;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will merge boxes"));
				return(0);
			}
			if (namesamen(pp, "individual-boxes", l) == 0 && l >= 3)
			{
				curstate[0] &= ~CIFOUTMERGE;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will list individual boxes"));
				return(0);
			}
			if (namesamen(pp, "include-cloak-layer", l) == 0 && l >= 3)
			{
				curstate[0] |= CIFOUTADDDRC;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will include DRC cloak layer"));
				return(0);
			}
			if (namesamen(pp, "ignore-cloak-layer", l) == 0 && l >= 2)
			{
				curstate[0] &= ~CIFOUTADDDRC;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will ignore DRC cloak layer"));
				return(0);
			}
			if (namesamen(pp, "not-centered", l) == 0 && l >= 1)
			{
				curstate[0] |= CIFOUTNOTCEN;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will put origins at lower corner"));
				return(0);
			}
			if (namesamen(pp, "centered", l) == 0 && l >= 1)
			{
				curstate[0] &= ~CIFOUTNOTCEN;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will put origins in center"));
				return(0);
			}
			if (namesamen(pp, "instantiate-top", l) == 0 && l >= 3)
			{
				curstate[0] &= ~CIFOUTNOTOPCALL;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will instantiate top-level cell"));
				return(0);
			}
			if (namesamen(pp, "dont-instantiate-top", l) == 0 && l >= 3)
			{
				curstate[0] |= CIFOUTNOTOPCALL;
				io_setstatebits(curstate);
				ttyputverbose(_("CIF generation will not instantiate top-level cell"));
				return(0);
			}
		}
		ttyputusage("tellaid io cif input|output OPTION");
		return(1);
	}
#endif

#if IOGDS
	if (namesamen(pp, "gds", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&GDSOUTMERGE) == 0)
				ttyputmsg(_("GDS generation will list individual boxes")); else
					ttyputmsg(_("GDS generation will merge boxes"));
			if ((curstate[0]&GDSOUTADDDRC) == 0)
				ttyputmsg(_("GDS generation will ignore DRC cloak layer")); else
					ttyputmsg(_("GDS generation will include DRC cloak layer"));
			if ((curstate[0]&GDSINTEXT) == 0)
				ttyputmsg(_("GDS input ignores text")); else
					ttyputmsg(_("GDS input includes text"));
			if ((curstate[0]&GDSINEXPAND) == 0)
				ttyputmsg(_("GDS input does not expand instances")); else
					ttyputmsg(_("GDS input expands instances"));
			if ((curstate[0]&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[0]&GDSINTEXT) == 0)
					ttyputmsg(_("GDS input ignores text")); else
						ttyputmsg(_("GDS input includes text"));
				if ((curstate[0]&GDSINEXPAND) == 0)
					ttyputmsg(_("GDS input does not expand instances")); else
						ttyputmsg(_("GDS input expands instances"));
				if ((curstate[0]&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)
			{
				curstate[0] |= GDSINTEXT;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input includes text"));
				return(0);
			}
			if (namesamen(pp, "no-text", l) == 0 && l >= 4)
			{
				curstate[0] &= ~GDSINTEXT;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input ignores text"));
				return(0);
			}
			if (namesamen(pp, "expand", l) == 0)
			{
				curstate[0] |= GDSINEXPAND;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input expands instances"));
				return(0);
			}
			if (namesamen(pp, "no-expand", l) == 0 && l >= 4)
			{
				curstate[0] &= ~GDSINEXPAND;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input does not expand instances"));
				return(0);
			}
			if (namesamen(pp, "arrays", l) == 0)
			{
				curstate[0] |= GDSINARRAYS;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input instantiates arrays"));
				return(0);
			}
			if (namesamen(pp, "no-arrays", l) == 0 && l >= 4)
			{
				curstate[0] &= ~GDSINARRAYS;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input does not instantiate arrays"));
				return(0);
			}
			if (namesamen(pp, "unknown-layers", l) == 0)
			{
				curstate[0] |= GDSINIGNOREUKN;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input includes unknown layers"));
				return(0);
			}
			if (namesamen(pp, "no-unknown-layers", l) == 0 && l >= 4)
			{
				curstate[0] &= ~GDSINIGNOREUKN;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS input ignores unknown layers"));
				return(0);
			}
			ttyputbadusage("tellaid io gds input");
			return(0);
		}
		if (namesamen(pp, "output", l) == 0)
		{
			if (count <= 2)
			{
				if ((curstate[0]&GDSOUTMERGE) == 0)
					ttyputmsg(_("GDS generation will list individual boxes")); else
						ttyputmsg(_("GDS generation will merge boxes"));
				if ((curstate[0]&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)
			{
				curstate[0] |= GDSOUTMERGE;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS generation will merge boxes"));
				return(0);
			}
			if (namesamen(pp, "individual-boxes", l) == 0 && l >= 3)
			{
				curstate[0] &= ~GDSOUTMERGE;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS generation will list individual boxes"));
				return(0);
			}
			if (namesamen(pp, "include-cloak-layer", l) == 0 && l >= 3)
			{
				curstate[0] |= GDSOUTADDDRC;
				io_setstatebits(curstate);
				ttyputverbose(_("GDS generation will include DRC cloak layer"));
				return(0);
			}
			if (namesamen(pp, "ignore-cloak-layer", l) == 0 && l >= 2)
			{
				curstate[0] &= ~GDSOUTADDDRC;
				io_setstatebits(curstate);
				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);
			}
			ttyputbadusage("tellaid io gds output");
			return(0);
		}
		ttyputbadusage("tellaid io gds");
		return(1);
	}
#endif

#if IODXF
	if (namesamen(pp, "dxf", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&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[0]&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)
			{
				curstate[0] |= DXFALLLAYERS;
				io_setstatebits(curstate);
				ttyputverbose(_("DXF input will accept all layers"));
				return(0);
			}
			if (namesamen(pp, "restrict", l) == 0)
			{
				curstate[0] &= ~DXFALLLAYERS;
				io_setstatebits(curstate);
				ttyputverbose(_("DXF input will accept only layers listed by technology"));
				return(0);
			}
		}
		if (namesamen(pp, "flatten-input", l) == 0)
		{
			curstate[0] |= DXFFLATTENINPUT;
			io_setstatebits(curstate);
			ttyputverbose(_("DXF input will flatten input blocks"));
			return(0);
		}
		if (namesamen(pp, "not-flatten-input", l) == 0)
		{
			curstate[0] &= ~DXFFLATTENINPUT;
			io_setstatebits(curstate);
			ttyputverbose(_("DXF input will retain input hierarchy"));
			return(0);
		}
		ttyputbadusage("tellaid io dxf");
		return(1);
	}
#endif

	if (namesamen(pp, "postscript", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&EPSPSCRIPT) == 0)
				ttyputmsg(_("PostScript output is in plain format")); else
					ttyputmsg(_("PostScript output is in encapsulated format"));
			if ((curstate[0]&PSCOLOR) == 0)
				ttyputmsg(_("PostScript output is gray-scale")); else
					ttyputmsg(_("PostScript output is in color"));
			if ((curstate[0]&PSROTATE) == 0)
				ttyputmsg(_("PostScript output is unrotated")); else
					ttyputmsg(_("PostScript output is rotated 90 degrees"));
			if ((curstate[0]&PSPLOTTER) == 0)
				ttyputmsg(_("PostScript output is printed")); else
					ttyputmsg(_("PostScript output is plotted"));
			if ((curstate[0]&PSHIERARCHICAL) == 0)
				ttyputmsg(_("PostScript output is flat")); else
					ttyputmsg(_("PostScript output is hierarchical"));
			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, "flat", l) == 0)
		{
			curstate[0] &= ~PSHIERARCHICAL;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be flat"));
			return(0);
		}
		if (namesamen(pp, "hierarchical", l) == 0 && l >= 2)
		{
			curstate[0] |= PSHIERARCHICAL;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be hierarchical"));
			return(0);
		}
		if (namesamen(pp, "encapsulated", l) == 0)
		{
			curstate[0] |= EPSPSCRIPT;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be encapsulated format"));
			return(0);
		}
		if (namesamen(pp, "plain", l) == 0 && l >= 2)
		{
			curstate[0] &= ~EPSPSCRIPT;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be plain format"));
			return(0);
		}
		if (namesamen(pp, "color", l) == 0)
		{
			curstate[0] = (curstate[0] & ~PSCOLORSTIP) | PSCOLOR;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will use color"));
			return(0);
		}
		if (namesamen(pp, "stippled-color", l) == 0 && l >= 2)
		{
			curstate[0] |= PSCOLOR | PSCOLORSTIP;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will use color"));
			return(0);
		}
		if (namesamen(pp, "gray-scale", l) == 0)
		{
			curstate[0] &= ~PSCOLOR;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be gray-scale"));
			return(0);
		}
		if (namesamen(pp, "rotate", l) == 0)
		{
			curstate[0] |= PSROTATE;
			io_setstatebits(curstate);
			ttyputverbose(_("PostScript output will be rotated 90 degrees"));
			return(0);
		}
		if (namesamen(pp, "no-rotate", l) == 0)
		{
			curstate[0] &= ~PSROTATE;
			io_setstatebits(curstate);
			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)
		{
			curstate[0] |= PSPLOTTER;
			io_setstatebits(curstate);
			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)
		{
			curstate[0] &= ~PSPLOTTER;
			io_setstatebits(curstate);
			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 && l >= 2)
		{
			if (count <= 2)
			{
				ttyputusage("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);
		}
		ttyputusage("tellaid io postscript OPTION");
		return(1);
	}

	if (namesamen(pp, "hpgl", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&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)
		{
			curstate[0] &= ~HPGL2;
			io_setstatebits(curstate);
			ttyputmsg(_("HPGL output will use older (version 1) format"));
			return(0);
		}
		if (namesamen(pp, "2", l) == 0)
		{
			curstate[0] |= HPGL2;
			io_setstatebits(curstate);
			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);
		}
		ttyputusage("tellaid io hpgl [1|2]");
		return(1);
	}

#if IOEDIF
	if (namesamen(pp, "edif", l) == 0)
	{
		if (count <= 1)
		{
			if ((curstate[0]&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)
		{
			curstate[0] |= EDIFSCHEMATIC;
			io_setstatebits(curstate);
			ttyputverbose(_("EDIF output will write schematics"));
			return(0);
		}
		if (namesamen(pp, "netlist", l) == 0)
		{
			curstate[0] &= ~EDIFSCHEMATIC;
			io_setstatebits(curstate);
			ttyputverbose(_("EDIF output will write netlists"));
			return(0);
		}
		ttyputusage("tellaid io edif [schematic|netlist]");
		return(1);
	}
#endif

	if (namesamen(pp, "verbose", l) == 0)
	{
		if (count <= 1)
		{
			ttyputusage("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);
		}
		ttyputbadusage("tellaid io verbose");
		return(1);
	}

	ttyputbadusage("tellaid io");
	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 = (INTSML)va_arg(ap, INTBIG);
		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, 0);
				break;
			case FBINARYNOBACKUP:
				l = io_writebinlibrary(lib, 1);
				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 IOECAD
			case FECAD:
				l = io_writeecadlibrary(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);
				/* make sure that the lambda values are consistent */
				io_unifylambdavalues(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);
				/* make sure that the lambda values are consistent */
				io_unifylambdavalues(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)
	{
		ttyputnomemory();
		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, *onp;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *olib;
	REGISTER INTBIG libunits, i, j, lam;
	INTBIG lx, ly, major, minor, detail;
	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;

	/* make sure units are correct */
	libunits = ((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);
	}

	/* check validity of instances */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			if (olib == np->cell->lib) break;
		if (olib == NOLIBRARY)
		{
			ttyputerr(_("ERROR: Facet %s: has invalid cell structure"), describenodeproto(np));
			continue;
		}
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto->primindex != 0) continue;
			olib = ni->proto->cell->lib;
			for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
				if (olib == ni->proto->cell->lib) break;
			if (olib == NOLIBRARY)
			{
				ttyputerr(_("ERROR: Facet %s: has instance from invalid library"), describenodeproto(np));
				ni->proto = gen_univpinprim;
				continue;
			}
			for(onp = olib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
				if (onp == ni->proto) break;
			if (onp == NONODEPROTO)
			{
				ttyputerr(_("ERROR: Facet %s: has instance with invalid prototype"), describenodeproto(np));
				ni->proto = gen_univpinprim;
				continue;
			}
		}
	}

	/* 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 node instances */
			pnt = ni->proto;
			if (pnt == NONODEPROTO)
			{
				ttyputerr(_("Facet %s: has a node without a prototype"), describenodeproto(np));
				ni->proto = gen_univpinprim;
			}
			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)
	{
		extern ARCPROTO **gen_upconn;
		inpp = pp;
		while (inpp != NOPORTPROTO && inpp->parent->primindex == 0) inpp = inpp->subportproto;
		if (inpp != NOPORTPROTO)
			pp->connects = inpp->connects;
		if (pp->connects == 0)
		{
			pp->connects = gen_upconn;
			pp->subnodeinst = np->firstnodeinst;
			if (pp->subnodeinst == NONODEINST) pp->subnodeinst = dummynode();
			pp->subportproto = pp->subnodeinst->proto->firstportproto;
		}
	}

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

	/* set the facet's technology */
	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		np->tech = whattech(np);

	/* 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.
	 */
	io_getversion(lib, &major, &minor, &detail);

	/* for versions 6.02k or earlier, convert mocmossub via4-via6 */
	if (major < 6 ||
		(major == 6 && minor < 2) ||
		(major == 6 && minor == 2 && detail < 12))
	{
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (ni->proto->primindex == 0) continue;
				if (ni->proto->tech != mocmossub_tech) continue;
				if (namesame(ni->proto->primname, "Metal-3-Metal-4-Con") == 0 ||
					namesame(ni->proto->primname, "Metal-4-Metal-5-Con") == 0 ||
					namesame(ni->proto->primname, "Metal-5-Metal-6-Con") == 0)
				{
					lam = lib->lambda[mocmossub_tech->techindex];
					modifynodeinst(ni, -lam, -lam, lam, lam, 0, 0);
				}
			}
		}
	}

	/* the rest of the changes are just for version 4 or earlier */
	if (major >= 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)
			changetechnologylambda(tech, lib->lambda[tech->techindex]);

	/* 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)
			changetechnologylambda(tech, el_curlib->lambda[tech->techindex]);
}

/*
 * Routine to find the version of Electric that generated library "lib" and
 * parse it into three fields: the major version number, minor version, and
 * a detail version number.
 */
void io_getversion(LIBRARY *lib, INTBIG *major, INTBIG *minor, INTBIG *detail)
{
	REGISTER VARIABLE *var;

	var = getval((INTBIG)lib, VLIBRARY, VSTRING, "LIB_former_version");
	if (var == NOVARIABLE)
	{
		*major = *minor = *detail = 0;
		return;
	}
	parseelectricversion((char *)var->addr, major, minor, detail);
}

/*
 * Routine to parse the version of Electric in "version" into three fields:
 * the major version number, minor version, and a detail version number.
 * The detail version number can be a letter.  If it is omitted, it is
 * assumed to be 999.  If it is a number, it is beyond 1000.  For example:
 *    "6.02d"     major=6, minor=2, detail=4
 *    "6.02"      major=6, minor=2, detail=999
 *    "6.02.1"    major=6, minor=2, detail=1001
 */
void parseelectricversion(char *version, INTBIG *major, INTBIG *minor, INTBIG *detail)
{
	REGISTER char *pt;

	/* parse the version fields */
	pt = version;
	*major = atoi(pt);
	while (isdigit(*pt) != 0) pt++;
	if (*pt++ != '.') return;
	*minor = atoi(pt);
	while (isdigit(*pt) != 0) pt++;
	if (*pt == 0) { *detail = 999;   return; }
	if (*pt == '.')
	{
		*detail = atoi(&pt[1]) + 1000;
	} else
	{
		if (islower(*pt) != 0) *detail = *pt - 'a' + 1; else
			if (isupper(*pt) != 0) *detail = *pt - 'A' + 1;
	}
#if 0
	ttyputmsg("Version '%s' is numbered %ld.%ld.%ld", version, *major, *minor, *detail);
#endif
}

/* Technology: Lambda Adjustment */
DIALOGITEM io_techadjlamdialogitems[] =
{
 /*  1 */ {0, {248,220,272,300}, BUTTON, N_("Done")},
 /*  2 */ {0, {220,264,236,512}, BUTTON, N_("Use New Size for all Technologies")},
 /*  3 */ {0, {220,8,236,256}, BUTTON, N_("Use Current Size for all Technologies")},
 /*  4 */ {0, {28,300,128,512}, SCROLL, ""},
 /*  5 */ {0, {148,8,164,164}, MESSAGE, N_("Current lambda size:")},
 /*  6 */ {0, {148,264,164,416}, MESSAGE, N_("New lambda size:")},
 /*  7 */ {0, {148,164,164,256}, MESSAGE, ""},
 /*  8 */ {0, {148,416,164,512}, MESSAGE, ""},
 /*  9 */ {0, {196,264,212,512}, BUTTON, N_("<< Use New Size in Current Library")},
 /* 10 */ {0, {196,8,212,256}, BUTTON, N_("Use Current Size in New Library >>")},
 /* 11 */ {0, {80,16,96,292}, MESSAGE, N_("and choose from the actions below.")},
 /* 12 */ {0, {168,8,184,256}, MESSAGE, N_("Found in library:")},
 /* 13 */ {0, {168,264,184,512}, MESSAGE, N_("Found in library:")},
 /* 14 */ {0, {8,8,24,508}, MESSAGE, N_("This new library uses different lambda values than existing libraries.")},
 /* 15 */ {0, {28,8,44,292}, MESSAGE, N_("You should unify the lambda values.")},
 /* 16 */ {0, {60,8,76,292}, MESSAGE, N_("Click on each technology in this list")},
 /* 17 */ {0, {136,8,137,512}, DIVIDELINE, ""},
 /* 18 */ {0, {112,8,128,284}, MESSAGE, N_("Use 'Check and Repair Libraries' when done.")}
};
DIALOG io_techadjlamdialog = {{75,75,356,596}, N_("Lambda Value Adjustment"), 0, 18, io_techadjlamdialogitems};

/*
 * Routine to examine new library "lib" and make sure that the lambda values in it are
 * the same as existing libraries/technologies.  Automatically adjusts values if possible,
 * and prompts the user to help out if necessary.
 *
 * Use new unit always     = 2 (button)
 * Use current unit always = 3 (button)
 * List of technologies    = 4 (scroll)
 * Current lambda value    = 7 (message)
 * New lambda value        = 8 (message)
 * Use new unit            = 9 (button)
 * Use current unit        = 10 (button)
 * Current library         = 12 (message)
 * New library             = 13 (message)
 */
void io_unifylambdavalues(LIBRARY *lib)
{
	REGISTER TECHNOLOGY *tech, *otech, **techarray;
	REGISTER LIBRARY *olib;
	REGISTER NODEPROTO *np;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER INTBIG itemHit, i, count, recompute, newunit, curunit, *newlamarray;
	REGISTER char *pt;
	float lambdainmicrons;
	char line[50];

	/* see if this library has incompatible lambda values */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (tech == art_tech) continue;
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			if (lib->lambda[tech->techindex] != olib->lambda[tech->techindex])
				break;
		}
		if (olib != NOLIBRARY) break;
	}
	if (tech == NOTECHNOLOGY) return;

	/* this library has different values: check usage in this and other libraries */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		olib->temp1 = (INTBIG)emalloc(el_maxtech * (sizeof (NODEPROTO *)), el_tempcluster);
		if (olib->temp1 == 0) return;
		for(i=0; i<el_maxtech; i++) ((NODEPROTO **)olib->temp1)[i] = NONODEPROTO;
	}
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (ni->proto->primindex == 0) continue;
				tech = ni->proto->tech;
				((NODEPROTO **)olib->temp1)[tech->techindex] = np;
			}
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				tech = ai->proto->tech;
				((NODEPROTO **)olib->temp1)[tech->techindex] = np;
			}
		}
	}

	/* see if there are inconsistencies that cannot be automatically resolved */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		if (tech == art_tech) continue;
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			if (((NODEPROTO **)olib->temp1)[tech->techindex] == NONODEPROTO) continue;
			if (((NODEPROTO **)lib->temp1)[tech->techindex] == NONODEPROTO) continue;
			if (lib->lambda[tech->techindex] != olib->lambda[tech->techindex])
				break;
		}
		if (olib != NOLIBRARY) break;
	}
	if (tech != NOTECHNOLOGY)
	{
		/* get the user to resolve the inconsistencies */
		DiaInitDialog(&io_techadjlamdialog);
		DiaInitTextDialog(4, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1,
			SCSELMOUSE|SCREPORT);
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		{
			if (tech == art_tech) continue;
			for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			{
				if (((NODEPROTO **)olib->temp1)[tech->techindex] == NONODEPROTO) continue;
				if (((NODEPROTO **)lib->temp1)[tech->techindex] == NONODEPROTO) continue;
				if (lib->lambda[tech->techindex] != olib->lambda[tech->techindex])
					break;
			}
			if (olib == NOLIBRARY) continue;
			DiaStuffLine(4, tech->techname);
		}
		DiaSelectLine(4, 0);
		recompute = 1;
		tech = NOTECHNOLOGY;
		for(;;)
		{
			if (recompute != 0)
			{
				recompute = 0;

				/* figure out which technology is selected */
				i = DiaGetCurLine(4);
				pt = DiaGetScrollLine(4, i);
				for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
					if (namesame(pt, tech->techname) == 0) break;
				if (tech == NOTECHNOLOGY) continue;

				/* figure out which units are in force */
				curunit = newunit = lib->lambda[tech->techindex];
				for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
				{
					if ((olib->userbits&HIDDENLIBRARY) != 0) continue;
					if (((NODEPROTO **)olib->temp1)[tech->techindex] == NONODEPROTO) continue;
					if (olib->lambda[tech->techindex] != lib->lambda[tech->techindex]) break;
				}
				if (olib != NOLIBRARY) curunit = olib->lambda[tech->techindex];

				/* see if it has already been overridden */
				if (((INTBIG *)lib->temp1)[tech->techindex] == 0) newunit = curunit;

				/* set dialog values and offer choices */
				lambdainmicrons = scaletodispunit(curunit, DISPUNITMIC);
				sprintf(line, "%gu", lambdainmicrons);
				DiaSetText(7, line);
				lambdainmicrons = scaletodispunit(newunit, DISPUNITMIC);
				sprintf(line, "%gu", lambdainmicrons);
				DiaSetText(8, line);
				if (newunit == curunit)
				{
					DiaDimItem(9);
					DiaDimItem(10);
					DiaSetText(12, "");
					DiaSetText(13, "");
				} else
				{
					DiaUnDimItem(9);
					DiaUnDimItem(10);
					(void)initinfstr();
					(void)formatinfstr(_("Found in library %s"), olib->libname);
					DiaSetText(12, returninfstr());
					(void)initinfstr();
					(void)formatinfstr(_("Found in library %s"), lib->libname);
					DiaSetText(13, returninfstr());
				}
			}
			itemHit = DiaNextHit();
			if (itemHit == OK) break;
			if (itemHit == 4) recompute = 1;
			if (itemHit == 9)
			{
				/* use new unit */
				if (tech != NOTECHNOLOGY) ((NODEPROTO **)olib->temp1)[tech->techindex] = 0;
				recompute = 1;
				continue;
			}
			if (itemHit == 2)
			{
				/* use new unit always */
				for(otech = el_technologies; otech != NOTECHNOLOGY; otech = otech->nexttechnology)
					((NODEPROTO **)olib->temp1)[otech->techindex] = 0;
				recompute = 1;
				DiaDimItem(3);
				continue;
			}
			if (itemHit == 3)
			{
				/* use current unit always */
				for(otech = el_technologies; otech != NOTECHNOLOGY; otech = otech->nexttechnology)
					((NODEPROTO **)lib->temp1)[otech->techindex];
				recompute = 1;
				DiaDimItem(2);
				continue;
			}
			if (itemHit == 10)
			{
				/* use current unit */
				if (tech != NOTECHNOLOGY) ((NODEPROTO **)lib->temp1)[tech->techindex] = 0;
				recompute = 1;
				continue;
			}
		}
		DiaDoneDialog();
	}

	/* adjust lambda values in old libraries to match this one */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
	{
		if (olib == lib) continue;

		/* see how many technologies are affected */
		count = 0;
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		{
			if (((NODEPROTO **)lib->temp1)[tech->techindex] == NONODEPROTO) continue;
			if (lib->lambda[tech->techindex] == olib->lambda[tech->techindex]) continue;
			count++;
		}
		if (count == 0) continue;

		/* adjust this library */
		techarray = (TECHNOLOGY **)emalloc(count * (sizeof (TECHNOLOGY *)), el_tempcluster);
		if (techarray == 0) return;
		newlamarray = (INTBIG *)emalloc(count * SIZEOFINTBIG, el_tempcluster);
		if (newlamarray == 0) return;
		count = 0;
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		{
			if (((NODEPROTO **)lib->temp1)[tech->techindex] == NONODEPROTO) continue;
			if (lib->lambda[tech->techindex] == olib->lambda[tech->techindex]) continue;
			techarray[count] = tech;
			newlamarray[count] = lib->lambda[tech->techindex];
			count++;
		}
		changelambda(count, techarray, newlamarray, olib, 1);
		efree((char *)techarray);
		efree((char *)newlamarray);
	}

	/* change lambda values in this library to match old ones */
	count = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
	{
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			if (((NODEPROTO **)olib->temp1)[tech->techindex] == NONODEPROTO) continue;
			if (lib->lambda[tech->techindex] != olib->lambda[tech->techindex])
				break;
		}
		if (olib == NOLIBRARY) continue;
		count++;
	}
	if (count != 0)
	{
		/* adjust new library */
		techarray = (TECHNOLOGY **)emalloc(count * (sizeof (TECHNOLOGY *)), el_tempcluster);
		if (techarray == 0) return;
		newlamarray = (INTBIG *)emalloc(count * SIZEOFINTBIG, el_tempcluster);
		if (newlamarray == 0) return;
		count = 0;
		for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		{
			for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			{
				if (((NODEPROTO **)olib->temp1)[tech->techindex] == NONODEPROTO) continue;
				if (lib->lambda[tech->techindex] != olib->lambda[tech->techindex])
					break;
			}
			if (olib == NOLIBRARY) continue;
			techarray[count] = tech;
			newlamarray[count] = olib->lambda[tech->techindex];
			count++;
		}
		changelambda(count, techarray, newlamarray, lib, 1);
		efree((char *)techarray);
		efree((char *)newlamarray);
	}

	/* free memory used here */
	for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		efree((char *)olib->temp1);
}

/****************************** LIBRARY OPTIONS DIALOG ******************************/

/* Library: Options */
DIALOGITEM io_liboptdialogitems[] =
{
 /*  1 */ {0, {124,148,148,228}, BUTTON, N_("OK")},
 /*  2 */ {0, {124,16,148,96}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {8,8,24,236}, RADIO, N_("No backup of library files")},
 /*  4 */ {0, {32,8,48,236}, RADIO, N_("Backup of last library file")},
 /*  5 */ {0, {56,8,72,236}, RADIO, N_("Backup history of library files")},
 /*  6 */ {0, {92,8,108,236}, CHECK, N_("Check database after write")}
};
DIALOG io_liboptdialog = {{75,75,232,320}, N_("Library Options"), 0, 6, io_liboptdialogitems};

/*
 * special case for the "library options" dialog
 * no backup          = 3 (radio)
 * backup one level   = 4 (radio)
 * backup history     = 5 (radio)
 * check after write  = 6 (check)
 */
void io_libraryoptiondlog(void)
{
	INTBIG itemHit, i, *origstate, curstate[NUMIOSTATEBITWORDS];

	/* display the library paths dialog box */
	if (DiaInitDialog(&io_liboptdialog) != 0) return;

	/* get current state of I/O aid */
	origstate = io_getstatebits();
	for(i=0; i<NUMIOSTATEBITWORDS; i++) curstate[i] = origstate[i];
	switch (curstate[0]&BINOUTBACKUP)
	{
		case BINOUTNOBACK:   DiaSetControl(3, 1);   break;
		case BINOUTONEBACK:  DiaSetControl(4, 1);   break;
		case BINOUTFULLBACK: DiaSetControl(5, 1);   break;
	}
	if ((curstate[0]&CHECKATWRITE) != 0) DiaSetControl(6, 1);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == 3 || itemHit == 4 || itemHit == 5)
		{
			DiaSetControl(3, 0);
			DiaSetControl(4, 0);
			DiaSetControl(5, 0);
			DiaSetControl(itemHit, 1);
			continue;
		}
		if (itemHit == 6)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		curstate[0] &= ~(BINOUTBACKUP|CHECKATWRITE);
		if (DiaGetControl(4) != 0) curstate[0] |= BINOUTONEBACK; else
			if (DiaGetControl(5) != 0) curstate[0] |= BINOUTFULLBACK;
		if (DiaGetControl(6) != 0) curstate[0] |= CHECKATWRITE;
		for(i=0; i<NUMIOSTATEBITWORDS; i++)
			if (curstate[i] != origstate[i]) break;
		if (i < NUMIOSTATEBITWORDS)
			io_setstatebits(curstate);
	}
	DiaDoneDialog();
}

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