/*
 * Electric(tm) VLSI Design System
 *
 * File: drc.c
 * Design-rule check aid
 * 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 "config.h"
#if DRCAID

#include "global.h"
#include "egraphics.h"
#include "drc.h"
#include "efunction.h"
#include "tech.h"
#include "edialogs.h"
#include "usr.h"

/* the DRC aid table */
static COMCOMP drccp = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("facet to be rechecked"), M_("check current facet")};
static KEYWORD drcnotopt[] =
{
	{"verbose",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"pointout",       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"reasonable",     0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP drcnotp = {drcnotopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Design Rule Checker negative action"), 0};
static COMCOMP drcfip = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("facet to ignored for short check DRC"), 0};
static COMCOMP drcfup = {NOKEYWORD, topoffacets, nextfacets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("facet to un-ignored for short check DRC"), 0};
static KEYWORD drcfopt[] =
{
	{"run",          0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"ignore",       1,{&drcfip,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"unignore",     1,{&drcfup,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP drcfp = {drcfopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Short Detecting Design Rule Checker action"), 0};
static KEYWORD drcnopt[] =
{
	{"run",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-dialog",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"no-dialog",         0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"verbose",           0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"reset-check-dates", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"just-1-error",      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"all-errors",        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP drcbatchp = {drcnopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Batch Design Rule Checker action"), 0};
static KEYWORD drcopt[] =
{
	{"check",                 1,{&drccp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"forget-ignored-errors", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not",                   1,{&drcnotp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"verbose",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"pointout",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"batch",                 1,{&drcbatchp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"shortcheck",            1,{&drcfp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"ecadcheck",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"reasonable",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP dr_drcp = {drcopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", M_("Design Rule Checker action"), M_("show defaults")};

/* dcheck modules */
#define	NODCHECK ((DCHECK *)-1)

#define	NODENEW    1					/* this nodeinst was created */
#define	ARCNEW     2					/* this arcinst was created */
#define	NODEMOD    3					/* this nodeinst was modified */
#define	ARCMOD     4					/* this arcinst was modified */
#define	NODEKILL   5					/* this nodeinst was deleted */
#define	ARCKILL    6					/* this arcinst was deleted */

typedef struct Idcheck
{
	INTBIG    entrytype;				/* type of object being checked */
	union    dr_entry
	{
		NODEINST *ni;
		ARCINST  *ai;
		void     *blind;
	} entryaddr;						/* object being checked */
	struct Idcheck *nextdcheck;			/* next in list */
} DCHECK;
static DCHECK   *dr_firstdcheck;
static DCHECK   *dr_dcheckfree;

/*
 * The meaning of "aid:drc.DRC_pointout" is as follows:
 *  0: not identifying errors
 *  1: errors being identified
 * >1: some errors have already been identified
 * -2: do not identify any more errors in this session
 */
static INTBIG    dr_pointoutkey;		/* key for "DRC_pointout" */
static INTBIG    dr_pointout;			/* cached value of "DRC_pointout" */
static INTBIG    dr_verbosekey;			/* key for "DRC_verbose" */
       INTBIG    dr_ignore_listkey;		/* key for "DRC_ignore_list" */
       INTBIG    dr_lastgooddrckey;		/* key for "DRC_last_good_drc" */
       INTBIG    dr_incrementalonkey;	/* key for "DRC_incrementalon" */
static INTBIG    dr_optionskey;			/* key for "DRC_options" */
       INTBIG    dr_options;			/* cached value of "DRC_options" */
static INTBIG    dr_pseudonet = 1;
static NODEINST *dr_tinynodeinst;
static GEOM     *dr_tinyobj;
       AIDENTRY *dr_aid;				/* the DRC aid object */

struct plist
{
	POLYGON **polygons;					/* polygons to be checked on node or arc */
	INTBIG polylistsize;				/* size of above list */
} dr_polylist, dr_subpolylist;

/* DRC Options */
DIALOGITEM dr_optionsdialogitems[] =
{
 /*  1 */ {0, {300,140,324,204}, BUTTON, N_("OK")},
 /*  2 */ {0, {300,40,324,104}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {284,410,300,459}, EDITTEXT, ""},
 /*  4 */ {0, {56,32,72,192}, CHECK, N_("Highlights errors")},
 /*  5 */ {0, {32,348,48,542}, POPUP, ""},
 /*  6 */ {0, {32,256,48,343}, MESSAGE, N_("For Layer:")},
 /*  7 */ {0, {104,276,275,552}, SCROLL, ""},
 /*  8 */ {0, {56,276,72,392}, MESSAGE, N_("Minimum Width:")},
 /*  9 */ {0, {284,276,300,406}, MESSAGE, N_("Minimum Spacing:")},
 /* 10 */ {0, {84,416,100,552}, RADIO, N_("Connected To:")},
 /* 11 */ {0, {84,276,100,411}, RADIO, N_("Unconnected To:")},
 /* 12 */ {0, {232,32,248,160}, BUTTON, N_("Edit Rules Deck")},
 /* 13 */ {0, {32,32,48,117}, CHECK, N_("On")},
 /* 14 */ {0, {0,248,324,249}, DIVIDELINE, ""},
 /* 15 */ {0, {56,400,72,455}, EDITTEXT, ""},
 /* 16 */ {0, {308,276,324,552}, BUTTON, N_("Reset to Defaults for Technology")},
 /* 17 */ {0, {8,348,24,542}, MESSAGE|INACTIVE, ""},
 /* 18 */ {0, {8,256,24,343}, MESSAGE, N_("Technology:")},
 /* 19 */ {0, {120,32,136,192}, CHECK, N_("Use dialog")},
 /* 20 */ {0, {8,8,24,169}, MESSAGE, N_("Incremental DRC:")},
 /* 21 */ {0, {84,4,85,248}, DIVIDELINE, ""},
 /* 22 */ {0, {96,8,112,169}, MESSAGE, N_("Hierarchical DRC:")},
 /* 23 */ {0, {196,4,197,248}, DIVIDELINE, ""},
 /* 24 */ {0, {208,8,224,169}, MESSAGE, N_("Dracula DRC Interface:")},
 /* 25 */ {0, {260,4,261,248}, DIVIDELINE, ""},
 /* 26 */ {0, {168,32,184,192}, BUTTON, N_("Clear valid DRC dates")},
 /* 27 */ {0, {144,32,160,192}, CHECK, N_("Just 1 error per facet")},
 /* 28 */ {0, {272,4,288,244}, CHECK, N_("Ignore center cuts in large contacts")}
};
DIALOG dr_optionsdialog = {{50,75,383,636}, N_("DRC Options"), 0, 28, dr_optionsdialogitems};

/* Hierarchical DRC */
DIALOGITEM dr_hierdialoglist[] =
{
 /*  1 */ {0, {8,8,32,72}, BUTTON, N_("Check")},
 /*  2 */ {0, {8,344,32,408}, BUTTON, N_("Done")},
 /*  3 */ {0, {38,8,132,408}, MESSAGE, ""},
 /*  4 */ {0, {8,214,32,326}, BUTTON, N_("Show Prev Error")},
 /*  5 */ {0, {8,94,32,206}, BUTTON, N_("Show Next Error")}
};
DIALOG dr_hierdialog = {{50,75,191,492}, N_("Hierarchical DRC"), 0, 5, dr_hierdialoglist};

/* DRC error */
DIALOGITEM dr_errdialogitems[] =
{
 /*  1 */ {0, {88,40,112,104}, BUTTON, N_("OK")},
 /*  2 */ {0, {128,232,152,360}, BUTTON, N_("Stop Checking")},
 /*  3 */ {0, {88,184,112,312}, BUTTON, N_("Ignore this error")},
 /*  4 */ {0, {128,96,152,224}, BUTTON, N_("Continue Silently")},
 /*  5 */ {0, {8,8,76,360}, MESSAGE, ""},
 /*  6 */ {0, {132,4,148,84}, MESSAGE|INACTIVE, N_("For now:")},
 /*  7 */ {0, {164,4,180,84}, MESSAGE|INACTIVE, N_("Permanently:")},
 /*  8 */ {0, {160,232,184,360}, BUTTON, N_("Stop Checking")},
 /*  9 */ {0, {160,96,184,224}, BUTTON, N_("Continue Silently")}
};
DIALOG dr_errdialog = {{50,75,243,444}, N_("DRC Violation"), 0, 9, dr_errdialogitems};

/* prototypes for local routines */
INTSML     dr_activeontransistor(GEOM*, INTSML, NETWORK*, INTBIG, INTBIG, INTBIG, INTBIG, GEOM*, INTSML, NETWORK*, INTBIG, INTBIG, INTBIG, INTBIG);
DCHECK    *dr_allocdcheck(void);
INTSML     dr_badbox(GEOM*, INTSML, TECHNOLOGY*, NODEPROTO*, POLYGON*, NETWORK*, INTSML);
void       dr_batchdlog(NODEPROTO *facet);
INTSML     dr_check(INTSML, NETWORK*, GEOM*, POLYGON*, INTSML, NETWORK*, GEOM*, POLYGON*, INTBIG);
INTSML     dr_checkarcinst(ARCINST*, INTSML);
INTSML     dr_checknodeinst(NODEINST*, INTSML);
NODEPROTO *dr_checkthisfacet(INTSML, char*);
INTSML     dr_croparcinst(ARCINST*, INTSML, INTBIG*, INTBIG*, INTBIG*, INTBIG*);
INTSML     dr_cropnodeinst(NODEINST*, INTSML, NETWORK*, GEOM*, INTBIG*, INTBIG*, INTBIG*, INTBIG*);
char      *dr_describeerror(INTSML, GEOM*, INTSML, GEOM*);
INTSML     dr_ensurepolylist(struct plist*, INTBIG);
char      *dr_errordlog(char *prompt);
void       dr_freedcheck(DCHECK*);
void       dr_loaddrcdialog(INTBIG *unconlist, INTBIG *conlist, INTBIG *minwidth);
void       dr_loaddrcdistance(INTBIG *unconlist, INTBIG *conlist);
INTSML     dr_objtouch(GEOM*, GEOM*);
void       dr_optionsdlog(void);
void       dr_queuecheck(GEOM*, INTSML);
void       dr_reporterrors(void);
void       dr_setignore(GEOM*, GEOM*);
void       dr_unignore(GEOM*);
void       dr_unqueuecheck(LIBRARY *lib);

/*
 * routine to allocate a new dcheck module from the pool (if any) or memory
 */
DCHECK *dr_allocdcheck(void)
{
	REGISTER DCHECK *d;

	if (dr_dcheckfree == NODCHECK)
	{
		d = (DCHECK *)emalloc(sizeof (DCHECK), dr_aid->cluster);
		if (d == 0) return(NODCHECK);
	} else
	{
		/* take module from free list */
		d = dr_dcheckfree;
		dr_dcheckfree = (DCHECK *)d->nextdcheck;
	}
	return(d);
}

/*
 * routine to return dcheck module "d" to the pool of free modules
 */
void dr_freedcheck(DCHECK *d)
{
	d->nextdcheck = dr_dcheckfree;
	dr_dcheckfree = d;
}

void dr_queuecheck(GEOM *geom, INTSML function)
{
	REGISTER DCHECK *d;

	d = dr_allocdcheck();
	if (d == NODCHECK)
	{
		ttyputnomemory();
		return;
	}
	d->entrytype = function;
	d->entryaddr.blind = geom->entryaddr.blind;
	d->nextdcheck = dr_firstdcheck;
	dr_firstdcheck = d;
}

/*
 * Routine to remove all check objects that are in library "lib"
 * (because the library has been deleted)
 */
void dr_unqueuecheck(LIBRARY *lib)
{
	REGISTER DCHECK *d, *nextd, *lastd;
	REGISTER LIBRARY *rlib;

	lastd = NODCHECK;
	for(d = dr_firstdcheck; d != NODCHECK; d = nextd)
	{
		nextd = d->nextdcheck;
		if (d->entrytype == OBJNODEINST) rlib = d->entryaddr.ni->parent->cell->lib; else
			rlib = d->entryaddr.ai->parent->cell->lib;
		if (rlib == lib)
		{
			if (lastd == NODCHECK) dr_firstdcheck = nextd; else
				lastd->nextdcheck = nextd;
			dr_freedcheck(d);
			continue;
		}
		lastd = d;
	}
}

/******************** CONTROL ********************/

void dr_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	REGISTER VARIABLE *var;

	/* only initialize during pass 1 */
	if (thisaid == NOAID || thisaid == 0) return;

	dr_aid = thisaid;

	/* initialize the design-rule checker lists */
	dr_polylist.polylistsize = dr_subpolylist.polylistsize = 0;
	dr_dcheckfree = NODCHECK;
	dr_firstdcheck = NODCHECK;

	dr_pointoutkey = makekey("DRC_pointout");
	dr_verbosekey = makekey("DRC_verbose");
	dr_ignore_listkey = makekey("DRC_ignore_list");
	dr_lastgooddrckey = makekey("DRC_last_good_drc");
	dr_incrementalonkey = makekey("DRC_incrementalon");
	dr_optionskey = makekey("DRC_options");
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_optionskey);
	if (var == NOVARIABLE) dr_options = 0; else
		dr_options = var->addr;
	nextvarchangequiet();
	(void)setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey, 0, VINTEGER|VDONTSAVE);
	dr_pointout = 0;
	DiaDeclareHook("drcopt", &dr_drcp, dr_optionsdlog);
}

void dr_done(void)
{
	REGISTER DCHECK *d;

	while (dr_firstdcheck != NODCHECK)
	{
		d = dr_firstdcheck;
		dr_firstdcheck = dr_firstdcheck->nextdcheck;
		dr_freedcheck(d);
	}
	while (dr_dcheckfree != NODCHECK)
	{
		d = dr_dcheckfree;
		dr_dcheckfree = dr_dcheckfree->nextdcheck;
		efree((char *)d);
	}
	drcb_term();
}

INTSML dr_set(INTSML count, char *par[])
{
	REGISTER INTSML negate;
	REGISTER INTBIG l, i, errs;
	REGISTER char *pp;
	REGISTER FILE *f;
	REGISTER NODEPROTO *np;
	REGISTER VARIABLE *var;
	REGISTER TECHNOLOGY *tech;
	char *newpar[3], *truename, *abortseq;
	float elapsed;
	static INTBIG filetypedrac = -1;

	if (count == 0)
	{
		var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_verbosekey);
		if (var == NOVARIABLE) i = 0; else i = var->addr;
		if (i == 0) ttyputmsg(_("Design rule checker is brief")); else
 			ttyputmsg(_("Design rule checker is verbose"));
		var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_pointoutkey);
		if (var == NOVARIABLE) i = 0; else i = var->addr;
		if (i == 0) ttyputmsg(_("Errors will not be highlighted")); else
			ttyputmsg(_("Errors will be highlighted"));
		return(0);
	}

	l = strlen(pp = par[0]);
	negate = 0;
	if (namesamen(pp, "not", l) == 0)
	{
		negate++;
		if (count <= 1)
		{
			count = ttygetparam(_("DRC negate option:"), &drcnotp, MAXPARS-1, &par[1]) + 1;
			if (count <= 1)
			{
				ttyputerr(_("Aborted"));
				return(1);
			}
		}
		l = strlen(pp = par[1]);
	}

	if (namesamen(pp, "forget-ignored-errors", l) == 0)
	{
		np = dr_checkthisfacet(count, par[1]);
		if (np == NONODEPROTO) return(1);

		/* erase all ignore lists on all objects */
		var = getvalkey((INTBIG)np, VNODEPROTO, VGEOM|VISARRAY, dr_ignore_listkey);
		if (var != NOVARIABLE)
			(void)delvalkey((INTBIG)np, VNODEPROTO, dr_ignore_listkey);

		ttyputmsg(_("Ignored violations turned back on."));
		return(0);
	}
	if (namesamen(pp, "check", l) == 0)
	{
		/* make sure network tool is on */
		if ((net_aid->aidstate&AIDON) == 0)
		{
			ttyputerr(_("Network tool must be running...turning it on"));
			aidturnon(net_aid, 0);
			ttyputerr(_("...now reissue the DRC command"));
			return(1);
		}

		np = dr_checkthisfacet(count, par[1]);
		if (np == NONODEPROTO) return(1);
		dr_examinenodeproto(np);
		ttyputmsg(_("Facet %s checked"), describenodeproto(np));
		return(0);
	}

	if (namesamen(pp, "ecadcheck", l) == 0)
	{
		np = getcurfacet();
		if (np == NONODEPROTO)
		{
			ttyputerr(_("Must be editing a facet to check with ECAD's Dracula"));
			return(1);
		}
		tech = np->tech;
		var = getval((INTBIG)tech, VTECHNOLOGY, VSTRING|VISARRAY, "DRC_ecad_deck");
		if (var == NOVARIABLE)
		{
			ttyputerr(_("Cannot find an ECAD deck in the %s technology"), tech->techname);
			return(1);
		}

		/* create the file */
		if (filetypedrac < 0)
			filetypedrac = setupfiletype("rul", "*.map", MACFSTAG('TEXT'), 0, "drac", _("Dracula"));
		f = xcreate("ecaddrc.RUL", filetypedrac, _("ECAD DRC Control File"), &truename);
		if (f == NULL)
		{
			if (truename != 0) ttyputerr(_("Cannot write %s"), truename);
			return(1);
		}

		/* write the deck */
		l = getlength(var);
		for(i=0; i<l; i++)
		{
			pp = ((char **)var->addr)[i];
			if (strncmp(pp, " PRIMARY =", 10) == 0)
			{
				xprintf(f, " PRIMARY = %s\n", np->cell->cellname);
				continue;
			}
			if (strncmp(pp, " INDISK =", 9) == 0)
			{
				xprintf(f, " INDISK = %s.cif\n", np->cell->cellname);
				continue;
			}
			xprintf(f, "%s\n", pp);
		}

		/* finished with control deck */
		xclose(f);
		ttyputverbose(_("Wrote 'ecaddrc.RUL'.  Now generating CIF for facet %s"), describenodeproto(np));

		/* tell I/O to write CIF */
		newpar[0] = "cif";
		newpar[1] = "output";
		newpar[2] = "include-cloak-layer";
		(void)tellaid(io_aid, 3, newpar);
		(void)askaid(io_aid, "write", (INTBIG)np->cell->lib, (INTBIG)"cif");
		newpar[0] = "cif";
		newpar[1] = "output";
		newpar[2] = "ignore-cloak-layer";
		(void)tellaid(io_aid, 3, newpar);
		return(0);
	}

	if (namesamen(pp, "batch", l) == 0)
	{
		if (count == 1)
		{
			ttyputusage("tellaid drc batch OPTIONS");
			return(0);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "run", l) == 0)
		{
			/* make sure network tool is on */
			if ((net_aid->aidstate&AIDON) == 0)
			{
				ttyputerr(_("Network tool must be running...turning it on"));
				aidturnon(net_aid, 0);
				ttyputerr(_("...now reissue the DRC command"));
				return(1);
			}

			/* make sure there is a facet to check */
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(0);
			}

			/* see if a dialog should be displayed */
			if ((dr_options&DRCHIERDIALOG) != 0)
			{
				dr_batchdlog(np);
			} else
			{
				(void)initinfstr();
				(void)addstringtoinfstr(_("Checking..."));
				abortseq = getinterruptkey();
				if (abortseq != 0)
					(void)formatinfstr(_(" (type %s to abort)"), abortseq);
				ttyputmsg(returninfstr());
				starttimer();
				errs = drcb_check(np);
				elapsed = endtimer();
				ttyputmsg(_("%ld errors found (took %g seconds)"), errs, elapsed);
			}
			return(0);
		}
		if (namesamen(pp, "use-dialog", l) == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options | DRCHIERDIALOG, VINTEGER);
			ttyputmsg(_("Hierarchical DRC will use a dialog interface"));
			return(0);
		}
		if (namesamen(pp, "no-dialog", l) == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options & ~DRCHIERDIALOG, VINTEGER);
			ttyputmsg(_("Hierarchical DRC will not use a dialog interface"));
			return(0);
		}
		if (namesamen(pp, "just-1-error", l) == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options | DRCFIRSTERROR, VINTEGER);
			ttyputmsg(_("Hierarchical DRC will stop after first error in a facet"));
			return(0);
		}
		if (namesamen(pp, "all-errors", l) == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options & ~DRCFIRSTERROR, VINTEGER);
			ttyputmsg(_("Hierarchical DRC will find all errors in a facet"));
			return(0);
		}
		if (namesamen(pp, "verbose", l) == 0)
			return(drcb_debug());
		if (namesamen(pp, "reset-check-dates", l) == 0)
		{
			drcb_reset_dates();
			ttyputmsg(_("All date information about valid DRC is reset"));
			return(0);
		}
		ttyputbadusage("tellaid drc batch");
		return(1);
	}

	if (namesamen(pp, "shortcheck", l) == 0)
	{
		if (count < 2)
		{
			ttyputusage("tellaid drc shortcheck (run|ignore|unignore)");
			return(1);
		}
		l = strlen(pp = par[1]);
		if (namesamen(pp, "run", l) == 0)
		{
			/* make sure network tool is on */
			if ((net_aid->aidstate&AIDON) == 0)
			{
				ttyputerr(_("Network tool must be running...turning it on"));
				aidturnon(net_aid, 0);
				ttyputerr(_("...now reissue the DRC command"));
				return(1);
			}
			return(dr_flatwrite(getcurfacet()));
		}
		if (namesamen(pp, "ignore", l) == 0)
		{
			if (count < 3)
			{
				ttyputusage("tellaid drc shortcheck ignore FACET");
				return(1);
			}
			return(dr_flatignore(par[2]));
		}
		if (namesamen(pp, "unignore", l) == 0)
		{
			if (count < 3)
			{
				ttyputusage("tellaid drc shortcheck unignore FACET");
				return(1);
			}
			return(dr_flatunignore(par[2]));
		}
		ttyputbadusage("tellaid drc shortcheck");
		return(1);
	}

	if (namesamen(pp, "verbose", l) == 0)
	{
		if (negate == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_verbosekey, 1, VINTEGER|VDONTSAVE);
			ttyputverbose(_("DRC verbose"));
		} else
		{
			if (getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_verbosekey) != NOVARIABLE)
				(void)delvalkey((INTBIG)dr_aid, VAID, dr_verbosekey);
			ttyputverbose(_("DRC brief"));
		}
		return(0);
	}

	if (namesamen(pp, "reasonable", l) == 0)
	{
		if (negate == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options | DRCREASONABLE, VINTEGER);
			ttyputverbose(_("DRC will consider only reasonable number of polygons"));
		} else
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey,
				dr_options & ~DRCREASONABLE, VINTEGER);
			ttyputverbose(_("DRC will consider all polygons"));
		}
		return(0);
	}

	if (namesamen(pp, "pointout", l) == 0)
	{
		if (negate == 0)
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey, 1, VINTEGER);
			ttyputverbose(_("DRC will identify errors"));
		} else
		{
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey, 0, VINTEGER);
			ttyputverbose(_("DRC will not identify errors"));
		}
		return(0);
	}
	ttyputbadusage("tellaid drc");
	return(1);
}

void dr_examinenodeproto(NODEPROTO *np)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER VARIABLE *var;

	if (stopping(STOPREASONDRC)) return;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0) return;

	/* setup for identifying errors */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_pointoutkey);
	if (var != NOVARIABLE) dr_pointout = var->addr; else
		dr_pointout = 0;

#if 0
	/* now check every object */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (dr_checknodeinst(ni, 1) != 0) return;
		if (stopping(STOPREASONDRC)) return;
	}
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (dr_checkarcinst(ai, 1) != 0) return;
		if (stopping(STOPREASONDRC)) return;
	}
#else
	/* clear errors */
	initerrorlogging(_("DRC"));

	drcb_initincrementalcheck(np);
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		(void)drcb_checkincremental(ni->geom, 1);
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		(void)drcb_checkincremental(ai->geom, 1);
	dr_reporterrors();
#endif
}

void dr_slice(void)
{
#if 0
	REGISTER DCHECK *d, *nextd;
	REGISTER INTSML nomore, i;
	REGISTER VARIABLE *var;

	if (dr_firstdcheck == NODCHECK) return;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on"));
		aidturnon(net_aid, 0);
		return;
	}

	/* mark this activity */
	setactivity(_("DRC"));

	/* first clear the ignore information on any objects that changed */
	for(d = dr_firstdcheck; d != NODCHECK; d = d->nextdcheck)
	{
		if (d->entrytype == NODEMOD || d->entrytype == NODEKILL)
			dr_unignore(d->entryaddr.ni->geom);
		if (d->entrytype == ARCMOD || d->entrytype == ARCKILL)
			dr_unignore(d->entryaddr.ai->geom);
	}

	/* setup for identifying errors */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_pointoutkey);
	if (var != NOVARIABLE) dr_pointout = var->addr; else
		dr_pointout = 0;

	/* now check the objects */
	nomore = 0;
	for(d = dr_firstdcheck; d != NODCHECK; d = nextd)
	{
		nextd = d->nextdcheck;
		if (stopping(STOPREASONDRC) == 0 && nomore == 0)
		{
			if (d->entrytype == NODEMOD || d->entrytype == NODENEW)
			{
				if (dr_checknodeinst(d->entryaddr.ni, 0) != 0) nomore++;
			}
			if (d->entrytype == ARCMOD || d->entrytype == ARCNEW)
			{
				if (dr_checkarcinst(d->entryaddr.ai, 0) != 0) nomore++;
			}
		}
		dr_freedcheck(d);
	}
	dr_firstdcheck = NODCHECK;

	(void)stopping(STOPREASONDRC);

	/* restore highlighting features */
	i = abs(dr_pointout);
	if (i > 1) ttyputverbose(_("DRC done"));
#else
	REGISTER DCHECK *d, *nextd;
	REGISTER NODEPROTO *np;
	REGISTER INTSML didcheck;
	REGISTER VARIABLE *var;

	/* see if the tool should be off */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_incrementalonkey);
	if (var != NOVARIABLE && var->addr == 0)
	{
		aidturnoff(dr_aid, 0);
		return;
	}

	if (dr_firstdcheck == NODCHECK) return;

	/* make sure network tool is on */
	if ((net_aid->aidstate&AIDON) == 0)
	{
		ttyputerr(_("Network tool must be running...turning it on"));
		aidturnon(net_aid, 0);
		return;
	}

	/* mark this activity */
	setactivity(_("DRC"));

	/* first clear the ignore information on any objects that changed */
	for(d = dr_firstdcheck; d != NODCHECK; d = d->nextdcheck)
	{
		if (d->entrytype == NODEMOD || d->entrytype == NODEKILL)
			dr_unignore(d->entryaddr.ni->geom);
		if (d->entrytype == ARCMOD || d->entrytype == ARCKILL)
			dr_unignore(d->entryaddr.ai->geom);
	}

	/* clear errors */
	initerrorlogging(_("DRC"));

	np = NONODEPROTO;
	didcheck = 0;
	for(d = dr_firstdcheck; d != NODCHECK; d = nextd)
	{
		nextd = d->nextdcheck;
		if (stopping(STOPREASONDRC) == 0)
		{
			if (d->entrytype == NODEMOD || d->entrytype == NODENEW)
			{
				NODEINST *ni;

				ni = d->entryaddr.ni;
				if ((ni->userbits&DEADN) == 0)
				{
					if (ni->parent != np)
					{
						np = ni->parent;
						drcb_initincrementalcheck(np);
						didcheck = 1;
					}
					(void)drcb_checkincremental(ni->geom, 0);
				}
			}
			if (d->entrytype == ARCMOD || d->entrytype == ARCNEW)
			{
				ARCINST *ai;

				ai = d->entryaddr.ai;
				if ((ai->userbits&DEADA) == 0)
				{
					if (ai->parent != np)
					{
						np = ai->parent;
						drcb_initincrementalcheck(np);
						didcheck = 1;
					}
					(void)drcb_checkincremental(ai->geom, 0);
				}
			}
		}
		dr_freedcheck(d);
	}
	dr_firstdcheck = NODCHECK;
	if (didcheck != 0) dr_reporterrors();
#endif
}

void dr_reporterrors(void)
{
	REGISTER INTBIG i, errs, len;
	REGISTER char *pt, *answer;
	REGISTER VARIABLE *var;
	GEOM *geom1, *geom2;

	/* setup for identifying errors */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_pointoutkey);
	if (var != NOVARIABLE) dr_pointout = var->addr; else
		dr_pointout = 0;

	termerrorlogging();

	errs = numerrors();
	for(i=0; i<errs; i++)
	{
		if (dr_pointout <= 0)
		{
			pt = reportnexterror(0, 0, 0);
			ttyputerr("%s", pt);
		} else
		{
			/* save highlight features if this is the first error */
			pt = reportnexterror(1, &geom1, &geom2);
			dr_pointout++;
			answer = dr_errordlog(pt);
			if (answer == 0) continue;
			len = strlen(answer);
			if (namesamen(answer, "quit-checking", len) == 0) break;
			if (namesamen(answer, "continue-silently", len) == 0)
			{
				dr_pointout = -2;
				continue;
			}
			if (namesamen(answer, "highlight-off", len) == 0)
			{
				dr_pointout = 0;
				setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey, 0, VINTEGER|VDONTSAVE);
				continue;
			}
			if (namesamen(answer, "drc-off", len) == 0)
			{
				aidturnoff(dr_aid, 0);
				continue;
			}
			if (namesamen(answer, "ignore-error", len) == 0)
			{
				/* mark objects "geom1" and "geom2" as ok */
				dr_setignore(geom1, geom2);
				dr_setignore(geom2, geom1);
				ttyputverbose(_("Ignored"));
				continue;
			}
		}
	}

	/* restore highlighting features */
	i = abs(dr_pointout);
	if (i > 1) ttyputverbose(_("DRC done"));
}

/******************** DATABASE CHANGES ********************/

void dr_modifynodeinst(NODEINST *ni,INTBIG oldlx,INTBIG oldly,INTBIG oldhx,INTBIG oldhy,
	INTSML oldrot,INTSML oldtran)
{
	dr_queuecheck(ni->geom, NODEMOD);
}

void dr_modifyarcinst(ARCINST *ai,INTBIG oldxA, INTBIG oldyA, INTBIG oldxB, INTBIG oldyB,
	INTBIG oldwid, INTBIG oldlen)
{
	dr_queuecheck(ai->geom, ARCMOD);
}

void dr_newobject(INTBIG addr, INTBIG type)
{
	if ((type&VTYPE) == VNODEINST)
		dr_queuecheck(((NODEINST *)addr)->geom, NODENEW); else
			if ((type&VTYPE) == VARCINST)
				dr_queuecheck(((ARCINST *)addr)->geom, ARCNEW);
}

void dr_killobject(INTBIG addr, INTBIG type)
{
	if ((type&VTYPE) == VNODEINST)
		dr_queuecheck(((NODEINST *)addr)->geom, NODEKILL); else
			if ((type&VTYPE) == VARCINST)
				dr_queuecheck(((ARCINST *)addr)->geom, ARCKILL);
}

void dr_eraselibrary(LIBRARY *lib)
{
	dr_unqueuecheck(lib);
}

void dr_newvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG newtype)
{
	REGISTER VARIABLE *var;

	if (key == dr_optionskey)
	{
		var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_optionskey);
		if (var != NOVARIABLE) dr_options = var->addr;
	}
}

void dr_readlibrary(LIBRARY *lib)
{
	REGISTER VARIABLE *var;

	/* recache DRC variables */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_optionskey);
	if (var != NOVARIABLE) dr_options = var->addr;
}

/******************** SUPPORT ROUTINES ********************/

/*
 * routine to check the design rules about nodeinst "ni".  If "partial"
 * is nonzero, only check those other objects whose geom pointers are
 * greater than this one (i.e. only check any pair of objects once).
 * The routine returns nonzero if checking is to be terminated.
 */
INTSML dr_checknodeinst(NODEINST *ni, INTSML partial)
{
	REGISTER INTBIG tot, j, ret;
	REGISTER NODEPROTO *np;
	XARRAY trans;
	REGISTER NETWORK *net;

	np = ni->proto;
	if (np->primindex == 0) return(0);
	makerot(ni, trans);

	/* get number of polygons on this node */
	tot = nodeEpolys(ni, 0);

	/* make sure there is space for the polygons */
	if (dr_ensurepolylist(&dr_polylist, tot) != 0) return(1);

	/* get all of the polygons on this node */
	for(j=0; j<tot; j++) shapeEnodepoly(ni, j, dr_polylist.polygons[j]);

	/* examine the polygons on this node */
	for(j=0; j<tot; j++)
	{
		if (dr_polylist.polygons[j]->layer < 0) continue;
		if (dr_polylist.polygons[j]->portproto == NOPORTPROTO) net = NONETWORK; else
			net = dr_network(ni, dr_polylist.polygons[j]->portproto);
		xformpoly(dr_polylist.polygons[j], trans);
		ret = dr_badbox(ni->geom, dr_polylist.polygons[j]->layer, np->tech,
			ni->parent, dr_polylist.polygons[j], net, partial);
		if (ret < 0) return(1);
		if (ret > 0) break;
	}
	return(0);
}

/*
 * routine to check the design rules about arcinst "ai".  If "partial"
 * is nonzero, only check those other objects whose geom pointers are
 * greater than this one (i.e. only check any pair of objects once).
 * The routine returns nonzero if checking is to be terminated.
 */
INTSML dr_checkarcinst(ARCINST *ai, INTSML partial)
{
	REGISTER INTBIG tot, j, ret;

	/* see how many polygons there are */
	tot = arcpolys(ai);

	/* make sure there is space for the polygons */
	if (dr_ensurepolylist(&dr_polylist, tot) != 0) return(1);

	/* get all of the polygons on this arc */
	for(j=0; j<tot; j++) shapearcpoly(ai, j, dr_polylist.polygons[j]);

	/* examine the polygons on this arc */
	for(j=0; j<tot; j++)
	{
		if (dr_polylist.polygons[j]->layer < 0) continue;
		ret = dr_badbox(ai->geom, dr_polylist.polygons[j]->layer,
			ai->proto->tech, ai->parent, dr_polylist.polygons[j], ai->network, partial);
		if (ret < 0) return(1);
		if (ret > 0) break;
	}
	return(0);
}

/*
 * routine to make a design-rule check on a piece of silicon which is
 * described in polygon "obj".  This silicon is on layer "nlayer" in
 * technology "tech", is part of the object at geometry module "geom", is on
 * electrical layer "nindex" and is in facet "facet".  If "partial" is nonzero
 * then only objects whose geometry modules are greater than this one will
 * be checked (to prevent any pair of objects from being checked twice).
 * A positive return indicates that an error has been detected and reported.
 * A negative return indicates that checking should be terminated.
 */
INTSML dr_badbox(GEOM *geom, INTSML nlayer, TECHNOLOGY *tech, NODEPROTO *facet,
	POLYGON *obj, NETWORK *nnet, INTSML partial)
{
	REGISTER GEOM *pi;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER ARCINST *ai;
	REGISTER NETWORK *net;
	REGISTER INTBIG bound, search, dist;
	REGISTER INTBIG j, tot, touch;
	REGISTER INTSML ret;
	static POLYGON *poly = NOPOLYGON;
	INTBIG lx, hx, ly, hy;
	XARRAY trans;
	INTSML con;

	/* see how far around the box it is necessary to search */
	bound = maxdrcsurround(tech, facet->cell->lib, nlayer);
	if (bound < 0) return(0);

	/* get bounds */
	getbbox(obj, &lx, &hx, &ly, &hy);

	/* make sure there is a polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, dr_aid->cluster);

	/* search in the area surrounding the box */
	search = initsearch(lx-bound,hx+bound, ly-bound,hy+bound, facet);
	for(;;)
	{
		if ((pi = nextobject(search)) == NOGEOM) break;
		if (pi == geom) continue;
		if (partial != 0 && pi < geom) continue;

		/* see if the objects directly touch */
		touch = dr_objtouch(pi, geom);

		if (pi->entrytype == OBJNODEINST)
		{
			ni = pi->entryaddr.ni;   np = ni->proto;

			/* no hierarchical DRC: ignore nodes that are not primitive */
			if (np->primindex == 0) continue;

			/* don't check between technologies */
			if (np->tech != tech) continue;

			/* prepare to examine every layer in this nodeinst */
			makerot(ni, trans);
			tot = nodeEpolys(ni, 0);

			/* make sure there is space for the polygons */
			if (dr_ensurepolylist(&dr_subpolylist, tot) != 0) return(1);

			/* get the shape of each nodeinst layer */
			for(j=0; j<tot; j++)
				shapeEnodepoly(ni, j, dr_subpolylist.polygons[j]);

			for(j=0; j<tot; j++)
			{
				if (dr_subpolylist.polygons[j]->layer < 0) continue;

				/* find the global electrical net for this layer */
				net = NONETWORK;
				if (dr_subpolylist.polygons[j]->portproto != NOPORTPROTO)
					net = dr_network(ni, dr_subpolylist.polygons[j]->portproto);

				/* see whether the two objects are electrically connected */
				if (net != NONETWORK && net == nnet) con = 1; else con = 0;

				/* if they connect electrically and adjoin, don't check */
				if (con != 0 && touch != 0) continue;

				/* see how close they can get */
				dist = drcmindistance(tech, facet->cell->lib, nlayer, dr_subpolylist.polygons[j]->layer, con);
				if (dist < 0) continue;

				xformpoly(dr_subpolylist.polygons[j], trans);

				/* check the distance */
				ret = dr_check(nlayer, nnet, geom, obj, dr_subpolylist.polygons[j]->layer, net, pi,
					dr_subpolylist.polygons[j], dist);
				if (ret != 0) { termsearch(search);   return(ret); }
			}
		} else
		{
			ai = pi->entryaddr.ai;

			/* don't check between technologies */
			if (ai->proto->tech != tech) continue;

			/* see whether the two objects are electrically connected */
			net = ai->network;
			if (nnet != NONETWORK && net == nnet) con = 1; else con = 0;

			/* if they connect electrically and adjoin, don't check */
			if (con != 0 && touch != 0) continue;

			/* prepare to examine every layer in this arcinst */
			tot = arcpolys(ai);

			/* make sure there is space for the polygons */
			if (dr_ensurepolylist(&dr_subpolylist, tot) != 0) return(1);

			/* get the shape of each arcinst layer */
			for(j=0; j<tot; j++) shapearcpoly(ai, j, dr_subpolylist.polygons[j]);

			for(j=0; j<tot; j++)
			{
				if (dr_subpolylist.polygons[j]->layer < 0) continue;

				/* see how close they can get */
				dist = drcmindistance(tech, facet->cell->lib, nlayer, dr_subpolylist.polygons[j]->layer, con);
				if (dist < 0) continue;

				/* check the distance */
				ret = dr_check(nlayer, nnet, geom, obj, dr_subpolylist.polygons[j]->layer, net, pi,
					dr_subpolylist.polygons[j], dist);
				if (ret != 0) { termsearch(search);   return(ret); }
			}
		}
	}
	return(0);
}

/*
 * routine to see if the polygon "poly1" in "pos1" on layer "layer1" with electrical
 * network "net1" comes within "dist" from the polygon "poly2" in "pos2" on layer
 * "layer2" with electrical network "net2".
 * A positive return indicates that an error has been detected and reported.
 * A negative return indicates that checking should be terminated.
 */
INTSML dr_check(INTSML layer1, NETWORK *net1, GEOM *pos1, POLYGON *poly1, INTSML layer2,
	NETWORK *net2, GEOM *pos2, POLYGON *poly2, INTBIG dist)
{
	REGISTER TECHNOLOGY *tech1, *tech2;
	REGISTER INTSML len, isbox1, isbox2;
	REGISTER INTBIG pd;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np;
	REGISTER char *ret, *answer;
	INTBIG lx1, hx1, ly1, hy1, lx2, hx2, ly2, hy2;

	/* turn off flag that the nodeinst may be undersized */
	dr_tinynodeinst = NONODEINST;

	isbox1 = isbox(poly1, &lx1, &hx1, &ly1, &hy1);
	if (isbox1 == 0) getbbox(poly1, &lx1, &hx1, &ly1, &hy1);
	isbox2 = isbox(poly2, &lx2, &hx2, &ly2, &hy2);
	if (isbox2 == 0) getbbox(poly2, &lx2, &hx2, &ly2, &hy2);

	/* special code if both polygons are manhattan */
	if (isbox1 != 0 && isbox2 != 0)
	{
		/* crop out parts of any arc that is covered by an adjoining node */
		if (pos1->entrytype == OBJARCINST)
		{
			if (dr_croparcinst(pos1->entryaddr.ai, layer1, &lx1, &hx1, &ly1, &hy1)) return(0);
		}
		if (pos2->entrytype == OBJARCINST)
		{
			if (dr_croparcinst(pos2->entryaddr.ai, layer2, &lx2, &hx2, &ly2, &hy2)) return(0);
		}

		/* crop out parts of a box covered by a similar layer on the other node */
		if (pos1->entrytype == OBJNODEINST)
		{
			if (dr_cropnodeinst(pos1->entryaddr.ni, layer2, net2, pos2, &lx2,&hx2, &ly2, &hy2))
				return(0);
		}
		if (pos2->entrytype == OBJNODEINST)
		{
			if (dr_cropnodeinst(pos2->entryaddr.ni, layer1, net1, pos1, &lx1,&hx1, &ly1, &hy1))
				return(0);
		}

		/* now compare the box extents */
		if (hx1+dist <= lx2 || lx1-dist >= hx2 || hy1+dist <= ly2 || ly1-dist >= hy2)
			return(0);
	} else
	{
		switch (poly1->style)
		{
			case FILLEDRECT:
			case CLOSEDRECT:
				maketruerect(poly1);
				break;
			case FILLED:
			case CLOSED:
			case CROSSED:
			case OPENED:
			case OPENEDT1:
			case OPENEDT2:
			case OPENEDT3:
			case VECTORS:
				break;
			default:
				return(0);
		}

		switch (poly2->style)
		{
			case FILLEDRECT:
			case CLOSEDRECT:
				maketruerect(poly2);
				break;
			case FILLED:
			case CLOSED:
			case CROSSED:
			case OPENED:
			case OPENEDT1:
			case OPENEDT2:
			case OPENEDT3:
			case VECTORS:
				break;
			default:
				return(0);
		}

		/* make sure polygons don't intersect */
		if (polyintersect(poly1, poly2) == 0)
		{
			/* find distance between polygons */
			pd = polyseparation(poly1, poly2);
			if (pd >= dist) return(0);
		}
	}

	/*
	 * special case: ignore errors between two active layers connected
	 * to either side of a field-effect transistor that is inside of
	 * the error area.
	 */
	if (isbox1 != 0 && isbox2 != 0 &&
		dr_activeontransistor(pos1, layer1, net1, lx1,hx1,ly1,hy1,
			pos2, layer2, net2, lx2,hx2,ly2,hy2) != 0) return(0);

	/* check/describe the error */
	ret = dr_describeerror(layer1, pos1, layer2, pos2);
	if (ret == 0) return(0);
	(void)initinfstr();
	(void)addstringtoinfstr(ret);

	/* get technology for each box */
	if (pos1->entrytype == OBJARCINST)
		tech1 = pos1->entryaddr.ai->proto->tech; else
			tech1 = pos1->entryaddr.ni->proto->tech;
	if (pos2->entrytype == OBJARCINST)
		tech2 = pos2->entryaddr.ai->proto->tech; else
			tech2 = pos2->entryaddr.ni->proto->tech;

	/* add in verbosity if requested */
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_verbosekey);
	if (var != NOVARIABLE && var->addr != 0)
	{
		if (net1 == NONETWORK || net2 == NONETWORK || net1 == net2)
			(void)addstringtoinfstr(_(".  Connected ")); else
				(void)addstringtoinfstr(_(".  Unconnected "));
		(void)formatinfstr(_("bad boxes are (%s-%s, %s-%s) on layer %s of  AND (%s-%s, %s-%s) on layer %s of %s"),
			latoa(lx1), latoa(hx1), latoa(ly1), latoa(hy1), layername(tech1,layer1), geomname(pos1),
			latoa(lx2), latoa(hx2), latoa(ly2), latoa(hy2), layername(tech2,layer2), geomname(pos2));
	}

	/* if a node was too small to crop an arc, it may explain the error */
	if (dr_tinynodeinst != NONODEINST)
	{
		/* see if the node/arc that failed was involved in the error */
		if ((dr_tinynodeinst->geom==pos1 || dr_tinynodeinst->geom==pos2) &&
			(dr_tinyobj == pos1 || dr_tinyobj == pos2))
		{
			(void)formatinfstr(_(".  %s is too small for the %s"),
				describenodeinst(dr_tinynodeinst), geomname(dr_tinyobj));
		}
	}

	if (dr_pointout <= 0)
	{
		ttyputerr("%s", returninfstr());
	} else
	{
		/* save highlight features if this is the first error */
		(void)askaid(us_aid, "clear");
		np = geomparent(pos1);
		(void)askaid(us_aid, "show-area", lx1,hx1,ly1,hy1,np);
		(void)askaid(us_aid, "show-area", lx2,hx2,ly2,hy2,np);
		flushscreen();
		dr_pointout++;
		answer = dr_errordlog(returninfstr());
		if (answer == 0) return(1);
		len = strlen(answer);
		if (namesamen(answer, "quit-checking", len) == 0) return(-1);
		if (namesamen(answer, "continue-silently", len) == 0)
		{
			dr_pointout = -2;
			return(1);
		}
		if (namesamen(answer, "highlight-off", len) == 0)
		{
			dr_pointout = 0;
			setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey, 0, VINTEGER|VDONTSAVE);
			return(1);
		}
		if (namesamen(answer, "drc-off", len) == 0)
		{
			aidturnoff(dr_aid, 0);
			return(1);
		}
		if (namesamen(answer, "ignore-error", len) == 0)
		{
			/* mark objects "pos1" and "pos2" as ok */
			dr_setignore(pos1, pos2);
			dr_setignore(pos2, pos1);
			ttyputverbose(_("Ignored"));
			return(1);
		}
	}
	return(1);
}

/*
 * routine to see if the two boxes are active elements, connected to opposite
 * sides of a field-effect transistor that resides inside of the box area.
 * Returns nonzero if so.
 */
INTSML dr_activeontransistor(GEOM *pos1, INTSML layer1, NETWORK *net1, INTBIG lx1,
	INTBIG hx1, INTBIG ly1, INTBIG hy1, GEOM *pos2, INTSML layer2, NETWORK *net2,
	INTBIG lx2, INTBIG hx2, INTBIG ly2, INTBIG hy2)
{
	REGISTER INTBIG fun, sea;
	REGISTER INTSML on1, on2;
	REGISTER GEOM *g;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER TECHNOLOGY *tech1, *tech2;

	/* networks must be different */
	if (net1 == net2) return(0);

	/* get technology for each box */
	if (pos1->entrytype == OBJARCINST)
		tech1 = pos1->entryaddr.ai->proto->tech; else
			tech1 = pos1->entryaddr.ni->proto->tech;
	if (pos2->entrytype == OBJARCINST)
		tech2 = pos2->entryaddr.ai->proto->tech; else
			tech2 = pos2->entryaddr.ni->proto->tech;

	/* layers must be active */
	fun = layerfunction(tech1, layer1) & LFTYPE;
	if (fun != APDIFF && fun != APDIFFP && fun != APDIFFN && fun != APDIFFS && fun != APDIFFW)
		return(0);
	fun = layerfunction(tech2, layer2) & LFTYPE;
	if (fun != APDIFF && fun != APDIFFP && fun != APDIFFN && fun != APDIFFS && fun != APDIFFW)
		return(0);

	/* both layers are active: see if there is a field-effect transistor on them */
	sea = initsearch(mini(lx1,lx2), maxi(hx1,hx2), mini(ly1,ly2), maxi(hy1,hy2), geomparent(pos1));
	if (sea < 0) return(0);
	for(;;)
	{
		g = nextobject(sea);
		if (g == NOGEOM) break;
		if (!isfet(g)) continue;

		/* got a transistor */
		ni = g->entryaddr.ni;
		on1 = on2 = 0;
		for(pi=ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if (pi->conarcinst->network == net1) on1++;
			if (pi->conarcinst->network == net2) on2++;
		}
		if (on1 == 0 || on2 == 0) continue;

		/* transistor found that connects to both actives */
		termsearch(sea);
		return(1);
	}
	return(0);
}

/*
 * routine to add DRC ignore information to object "pos1" so that it will
 * ignore errors with object "pos2"
 */
void dr_setignore(GEOM *pos1, GEOM *pos2)
{
	REGISTER GEOM **ignorelist, *p1, *p2;
	GEOM *dummylist[2];
	REGISTER VARIABLE *var;
	REGISTER INTBIG i, len;
	REGISTER NODEPROTO *np;

	np = geomparent(pos1);
	var = getvalkey((INTBIG)np, VNODEPROTO, VGEOM|VISARRAY, dr_ignore_listkey);

	/* if the list is empty: create it with one entry */
	if (var == NOVARIABLE)
	{
		dummylist[0] = pos1;   dummylist[1] = pos2;
		(void)setvalkey((INTBIG)np, VNODEPROTO, dr_ignore_listkey, (INTBIG)dummylist,
			VGEOM|VISARRAY|(2<<VLENGTHSH)|VDONTSAVE);
		return;
	}

	/* search list to see if this entry is on it */
	len = getlength(var);
	for(i=0; i<len; i += 2)
	{
		p1 = ((GEOM **)var->addr)[i];
		p2 = ((GEOM **)var->addr)[i+1];
		if (pos1 == p1 && pos2 == p2) return;
		if (pos1 == p2 && pos2 == p1) return;
	}

	/* entry not in list: add it */
	ignorelist = (GEOM **)emalloc(((len+2) * (sizeof (GEOM *))), el_tempcluster);
	if (ignorelist == 0)
	{
		ttyputnomemory();
		return;
	}

	/* update the list */
	for(i=0; i<len; i++) ignorelist[i] = ((GEOM **)var->addr)[i];
	ignorelist[len] = pos1;   ignorelist[len+1] = pos2;
	(void)setvalkey((INTBIG)np, VNODEPROTO, dr_ignore_listkey, (INTBIG)ignorelist,
		(INTBIG)(VGEOM|VISARRAY|((len+2)<<VLENGTHSH)|VDONTSAVE));
	efree((char *)ignorelist);
}

/*
 * routine to delete DRC ignore information on object "pos"
 */
void dr_unignore(GEOM *pos)
{
	REGISTER GEOM **ignorelist, *p1, *p2;
	REGISTER VARIABLE *var;
	REGISTER INTBIG i, len, pt;
	REGISTER NODEPROTO *np;

	np = geomparent(pos);
	var = getvalkey((INTBIG)np, VNODEPROTO, VGEOM|VISARRAY, dr_ignore_listkey);

	/* if the list is empty there is nothing to do */
	if (var == NOVARIABLE) return;

	/* see if this entry is mentioned in the list */
	len = getlength(var);
	for(i=0; i<len; i++) if (((GEOM **)var->addr)[i] == pos) break;
	if (i >= len) return;

	/* create a new list without the entry */
	ignorelist = (GEOM **)emalloc((len * (sizeof (GEOM *))), el_tempcluster);
	if (ignorelist == 0)
	{
		ttyputnomemory();
		return;
	}

	/* search list and remove entries that mention this module */
	pt = 0;
	for(i=0; i<len; i += 2)
	{
		p1 = ((GEOM **)var->addr)[i];
		p2 = ((GEOM **)var->addr)[i+1];
		if (p1 == pos || p2 == pos) continue;
		ignorelist[pt++] = p1;   ignorelist[pt++] = p2;
	}

	/* set the list back in place */
	if (pt > 0) (void)setvalkey((INTBIG)np, VNODEPROTO, dr_ignore_listkey,
		(INTBIG)ignorelist, (INTBIG)(VGEOM|VISARRAY|(pt<<VLENGTHSH)|VDONTSAVE)); else
			(void)delvalkey((INTBIG)np, VNODEPROTO, dr_ignore_listkey);
	efree((char *)ignorelist);
}

/*
 * routine to crop the box on layer "nlayer", electrical index "nindex"
 * and bounds (lx-hx, ly-hy) against the nodeinst "ni".  Only those layers
 * in the nodeinst that are the same layer and the same electrical network
 * are checked.  The routine returns nonzero if the bounds are reduced
 * to nothing.
 */
INTSML dr_cropnodeinst(NODEINST *ni, INTSML nlayer, NETWORK *nnet, GEOM *npos, INTBIG *lx,
	INTBIG *hx, INTBIG *ly, INTBIG *hy)
{
	XARRAY trans;
	INTBIG xl, xh, yl, yh, bx, ux, by, uy;
	REGISTER INTBIG tot, j, isconnected, temp;
	REGISTER NETWORK *net;
	static POLYGON *poly = NOPOLYGON;

	/* make sure there is a polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, dr_aid->cluster);

	tot = nodeEpolys(ni, 0);
	isconnected = 0;
	for(j=0; j<tot; j++)
	{
		shapeEnodepoly(ni, j, poly);
		if (poly->layer != nlayer) continue;
		if (nnet != NONETWORK)
		{
			if (poly->portproto == NOPORTPROTO) continue;
			net = dr_network(ni, poly->portproto);
			if (net != NONETWORK && net != nnet) continue;
		}
		isconnected++;
		break;
	}
	if (isconnected == 0) return(0);

	tot = nodepolys(ni, 0);
	makerot(ni, trans);
	for(j=0; j<tot; j++)
	{
		/* get the description of the nodeinst layer */
		shapenodepoly(ni, j, poly);
		if (poly->layer != nlayer) continue;

		/* warning: does not handle arbitrary polygons, only boxes */
		if (isbox(poly, &xl, &xh, &yl, &yh) == 0) continue;
		xform(xl,yl, &bx,&by, trans);
		xform(xh,yh, &ux,&uy, trans);
		if (bx > ux) { temp = bx; bx = ux; ux = temp; }
		if (by > uy) { temp = by; by = uy; uy = temp; }
		temp = cropbox(lx,hx,ly,hy, bx,ux,by,uy);
		if (temp > 0) return(1);
		if (temp < 0) { dr_tinynodeinst = ni;   dr_tinyobj = npos; }
	}
	return(0);
}

/*
 * routine to crop away any part of layer "lay" of arcinst "ai" that coincides
 * with a similar layer on a connecting nodeinst.  The bounds of the arcinst
 * are in the reference parameters (lx-hx, ly-hy).  The routine returns zero
 * normally, 1 if the arcinst is cropped into oblivion.
 */
INTSML dr_croparcinst(ARCINST *ai, INTSML lay, INTBIG *lx, INTBIG *hx, INTBIG *ly, INTBIG *hy)
{
	INTBIG bx, by, ux, uy, xl, xh, yl, yh;
	XARRAY trans;
	REGISTER INTBIG temp, i, j, tot;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	static POLYGON *poly = NOPOLYGON;

	/* make sure there is a polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, dr_aid->cluster);

	for(i=0; i<2; i++)
	{
		/* find the primitive nodeinst at the true end of the portinst */
		ni = ai->end[i].nodeinst;   np = ni->proto;
		pp = ai->end[i].portarcinst->proto;
		while (np->primindex == 0)
		{
			ni = pp->subnodeinst;   np = ni->proto;
			pp = pp->subportproto;
		}

		makerot(ni, trans);
		tot = nodepolys(ni, 0);
		for(j=0; j<tot; j++)
		{
			shapenodepoly(ni, j, poly);
			if (poly->layer != lay) continue;

			/* warning: does not handle arbitrary polygons, only boxes */
			if (isbox(poly, &xl, &xh, &yl, &yh) == 0) continue;
			xform(xl,yl, &bx,&by, trans);
			xform(xh,yh, &ux,&uy, trans);
			if (bx > ux) { temp = bx; bx = ux; ux = temp; }
			if (by > uy) { temp = by; by = uy; uy = temp; }

			temp = cropbox(lx,hx,ly,hy, bx,ux,by,uy);
			if (temp > 0) return(1);
			if (temp < 0) { dr_tinynodeinst = ni;   dr_tinyobj = ai->geom; }
		}
	}
	return(0);
}

char *dr_describeerror(INTSML layer1, GEOM *pos1, INTSML layer2, GEOM *pos2)
{
	static char line[200];
	REGISTER TECHNOLOGY *tech1, *tech2;
	REGISTER INTBIG len, i, dontsaylayer;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np;
	REGISTER GEOM *p1, *p2;

	/* first see if this is to be ignored */
	np = geomparent(pos1);
	var = getvalkey((INTBIG)np, VNODEPROTO, VGEOM|VISARRAY, dr_ignore_listkey);
	if (var != NOVARIABLE)
	{
		len = getlength(var);
		for(i=0; i<len; i += 2)
		{
			p1 = ((GEOM **)var->addr)[i];
			p2 = ((GEOM **)var->addr)[i+1];
			if (p1 == pos1 && p2 == pos2) return(0);
			if (p1 == pos2 && p2 == pos1) return(0);
		}
	}

	/* now determine technologies of these objects */
	if (pos1->entrytype == OBJARCINST)
		tech1 = pos1->entryaddr.ai->proto->tech; else
			tech1 = pos1->entryaddr.ni->proto->tech;
	if (pos2->entrytype == OBJARCINST)
		tech2 = pos2->entryaddr.ai->proto->tech; else
			tech2 = pos2->entryaddr.ni->proto->tech;

	/* layers too close: error */
	if (pos1->entrytype == OBJNODEINST) (void)strcpy(line, _("Node ")); else
		(void)strcpy(line, _("Arc "));
	(void)strcat(line, geomname(pos1));
	if (layer1 == layer2)
	{
		(void)strcat(line, _(" and "));
		if (pos2->entrytype == OBJNODEINST) (void)strcat(line, _("node ")); else
			(void)strcat(line, _("arc "));
		(void)strcat(line, geomname(pos2));
		(void)strcat(line, _(" too close"));
		if (pos1->entrytype != OBJARCINST && pos2->entrytype != OBJARCINST) dontsaylayer = 0; else
			dontsaylayer = 1;
		if (dontsaylayer != 0)
		{
			if (pos1->entrytype == OBJARCINST && arcpolys(pos1->entryaddr.ai) != 1)
				dontsaylayer = 0;
			if (pos2->entrytype == OBJARCINST && arcpolys(pos2->entryaddr.ai) != 1)
				dontsaylayer = 0;
		}
		if (dontsaylayer == 0)
		{
			(void)strcat(line, _(" on layer "));
			(void)strcat(line, layername(tech1, layer1));
		}
	} else
	{
		if (pos1->entrytype != OBJARCINST || arcpolys(pos1->entryaddr.ai) != 1)
		{
			(void)strcat(line, _(", layer "));
			(void)strcat(line, layername(tech1, layer1));
		}
		(void)strcat(line, _(" too close to "));
		if (pos2->entrytype == OBJNODEINST) (void)strcat(line, _("node ")); else
			(void)strcat(line, _("arc "));
		(void)strcat(line, geomname(pos2));
		if (pos2->entrytype != OBJARCINST || arcpolys(pos2->entryaddr.ai) != 1)
		{
			(void)strcat(line, _(", layer "));
			(void)strcat(line, layername(tech2, layer2));
		}
	}
	return(line);
}

/*
 * routine to tell whether the objects at geometry modules "po1" and "po2"
 * touch directly (that is, an arcinst connected to a nodeinst).  The routine
 * returns nonzero if they touch
 */
INTSML dr_objtouch(GEOM *po1, GEOM *po2)
{
	REGISTER GEOM *temp;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER INTSML i;

	if (po1->entrytype == OBJNODEINST)
	{
		if (po2->entrytype == OBJNODEINST) return(0);
		temp = po1;   po1 = po2;   po2 = temp;
	}
	if (po2->entrytype == OBJARCINST) return(0);

	/* see if the arcinst at "po1" touches the nodeinst at "po2" */
	ni = po2->entryaddr.ni;
	ai = po1->entryaddr.ai;
	for(i=0; i<2; i++) if (ai->end[i].nodeinst == ni) return(1);
	return(0);
}

/*
 * routine to tell the network associated with nodeinst "ni", portproto "ppt"
 */
NETWORK *dr_network(NODEINST *ni, PORTPROTO *ppt)
{
	REGISTER NETWORK *net;

	net = getnetonport(ni, ppt);
	if (net != NONETWORK) return(net);
	return((NETWORK *)(dr_pseudonet++));
}

/*
 * routine to determine the node prototype specified in "name" (given that
 * there were "count" parameters to the command and that "name" should be in
 * the second).  Returns NONODEPROTO if there is an error.
 */
NODEPROTO *dr_checkthisfacet(INTSML count, char *name)
{
	REGISTER NODEPROTO *np;

	if (count < 2)
	{
		np = getcurfacet();
		if (np == NONODEPROTO) ttyputerr(_("No current facet"));
		return(np);
	}
	np = getnodeproto(name);
	if (np == NONODEPROTO)
	{
		ttyputerr(_("No facet named %s"), name);
		return(NONODEPROTO);
	}
	if (np->primindex != 0)
	{
		ttyputerr(_("Can only check facets, not primitives"));
		return(NONODEPROTO);
	}
	if (np->cell->lib != el_curlib)
	{
		ttyputerr(_("Can only check facets in the current library"));
		return(NONODEPROTO);
	}
	return(np);
}

/*
 * routine to accumulate a list of polygons at least "tot" long in the
 * polygon structure "list".  The routine returns nonzero if there is no
 * more memory.
 */
INTSML dr_ensurepolylist(struct plist *list, INTBIG tot)
{
	REGISTER POLYGON **newpolylist;
	REGISTER INTBIG j;

	/* make sure the list is the right size */
	if (list->polylistsize < tot)
	{
		newpolylist = (POLYGON **)emalloc((tot * (sizeof (POLYGON *))), dr_aid->cluster);
		if (newpolylist == 0)
		{
			ttyputnomemory();
			return(1);
		}
		for(j=0; j<tot; j++) newpolylist[j] = NOPOLYGON;
		if (list->polylistsize != 0)
		{
			for(j=0; j<list->polylistsize; j++)
				newpolylist[j] = list->polygons[j];
			efree((char *)list->polygons);
		}
		list->polygons = newpolylist;
		list->polylistsize = tot;
	}

	/* make sure there is a polygon for each entry in the list */
	for(j=0; j<tot; j++)
	{
		if (list->polygons[j] != NOPOLYGON) continue;
		list->polygons[j] = allocpolygon(4, dr_aid->cluster);
	}
	return(0);
}

/****************************** DIALOGS ******************************/

/*
 * special case for the "hierarchical drc" dialog
 * Check                = 1 (button)
 * Done                 = 2 (button)
 * Error Message        = 3 (stat text)
 * Show Next Error      = 5 (button)
 * Show Prev Error      = 4 (button)
 */
void dr_batchdlog(NODEPROTO *facet)
{
	INTBIG itemHit, errs;
	INTSML oldplease;
	char *msg, lne[50];
	float elapsed;

	/* display the hierarchical DRC dialog box */
	if (DiaInitDialog(&dr_hierdialog) != 0) return;
	errs = numerrors();
	if (errs == 0)
	{
		DiaDimItem(4);
		DiaDimItem(5);
	} else
	{
		DiaUnDimItem(4);
		DiaUnDimItem(5);
	}

	/* loop until done */
	oldplease = el_pleasestop;
	for(;;)
	{
		el_pleasestop = oldplease;
		itemHit = DiaNextHit();
		if (itemHit == 2) break;
		if (itemHit == 1)
		{
			/* "check" button */
			DiaSetText(3, _("Checking..."));
			starttimer();
			errs = drcb_check(facet);
			elapsed = endtimer();
			(void)sprintf(lne, _("%ld errors found (took %g seconds)"), errs, elapsed);
			DiaSetText(3, lne);
			DiaUnDimItem(4);
			DiaUnDimItem(5);
			continue;
		}
		if (itemHit == 5)
		{
			/* "show next error" button */
			msg = reportnexterror(1, 0, 0);
			if (msg == 0) msg = "";
			DiaSetText(3, msg);
			continue;
		}
		if (itemHit == 4)
		{
			/* "show prev error" button */
			msg = reportpreverror();
			if (msg == 0) msg = "";
			DiaSetText(3, msg);
			continue;
		}
	}

	DiaDoneDialog();
}

/*
 * special case for the "DRC Options" dialog
 * Distance                = 3 (edit text)
 * DRC highlights errors   = 4 (check)
 * From layer              = 5 (popup)
 * To layer                = 7 (scroll)
 * Connected rules         = 10 (radio)
 * Unconnected rules       = 11 (radio)
 * Edit Dracula deck       = 12 (button)
 * Incremental DRC on      = 13 (check)
 * Min width               = 15 (edit text)
 * Factor Reset            = 16 (button)
 * Technology name         = 17 (stat text)
 * Hierarchical dialog     = 19 (check)
 * Clear valid DRC dates   = 26 (button)
 * Just 1 error per facet  = 27 (check)
 * Reasonable cuts         = 28 (check)
 */
void dr_optionsdlog(void)
{
	REGISTER INTBIG itemHit, conchanged, unconchanged, minwidchanged, options,
		higherrors, i, l, *conlist, *unconlist, numentries, dist, newhigherrors,
		layer1, layer2, temp, dindex, *minwidth, *list;
	REGISTER char **layernames, *pt, *qual;
	INTSML dummy, clearvaliddrcdates;
	char header[200], *dummyfile[1];
	REGISTER VARIABLE *var;
	REGISTER WINDOWPART *w;
	REGISTER EDITOR *ed;
	void tech_initmaxdrcsurround(void);

	if (DiaInitDialog(&dr_optionsdialog) != 0) return;
	DiaInitTextDialog(7, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, 0,
		SCSELMOUSE|SCREPORT);

	/* cache the minimum-width rules for this technology */
	minwidth = (INTBIG *)emalloc(el_curtech->layercount * SIZEOFINTBIG, el_tempcluster);
	if (minwidth == 0) return;
	var = getval((INTBIG)el_curtech, VTECHNOLOGY, VFRACT|VISARRAY,
		"DRC_min_width");
	if (var == NOVARIABLE)
	{
		for(i=0; i<el_curtech->layercount; i++) minwidth[i] = XX;
	} else
	{
		for(i=0; i<el_curtech->layercount; i++) minwidth[i] = ((INTBIG *)var->addr)[i];
	}

	/* cache the spacing rules for this technology */
	numentries = (el_curtech->layercount * el_curtech->layercount -
		el_curtech->layercount) / 2 + el_curtech->layercount;
	unconlist = (INTBIG *)emalloc(numentries * SIZEOFINTBIG, el_tempcluster);
	if (unconlist == 0) return;
	var = getval((INTBIG)el_curtech, VTECHNOLOGY, VFRACT|VISARRAY,
		"DRC_min_unconnected_distances");
	if (var == NOVARIABLE)
	{
		for(i=0; i<numentries; i++) unconlist[i] = XX;
	} else
	{
		for(i=0; i<numentries; i++) unconlist[i] = ((INTBIG *)var->addr)[i];
	}
	conlist = (INTBIG *)emalloc(numentries * SIZEOFINTBIG, el_tempcluster);
	if (conlist == 0) return;
	var = getval((INTBIG)el_curtech, VTECHNOLOGY, VFRACT|VISARRAY,
		"DRC_min_connected_distances");
	if (var == NOVARIABLE)
	{
		for(i=0; i<numentries; i++) conlist[i] = XX;
	} else
	{
		for(i=0; i<numentries; i++) conlist[i] = ((INTBIG *)var->addr)[i];
	}

	/* make the layer popup */
	layernames = (char **)emalloc(el_curtech->layercount * (sizeof (char *)), el_tempcluster);
	for(i=0; i<el_curtech->layercount; i++)
		layernames[i] = layername(el_curtech, (INTSML)i);
	DiaSetPopup(5, el_curtech->layercount, layernames);
	efree((char *)layernames);

	DiaSetText(17, el_curtech->techname);
	DiaSetControl(11, 1);
	var = getvalkey((INTBIG)dr_aid, VAID, VINTEGER, dr_pointoutkey);
	if (var == NOVARIABLE) higherrors = 0; else higherrors = var->addr;
	if (higherrors != 0) DiaSetControl(4, 1);

	if ((dr_options&DRCHIERDIALOG) != 0) DiaSetControl(19, 1);
	if ((dr_options&DRCFIRSTERROR) != 0) DiaSetControl(27, 1);
	if ((dr_options&DRCREASONABLE) != 0) DiaSetControl(28, 1);

	if ((dr_aid->aidstate & AIDON) != 0) DiaSetControl(13, 1);
	dr_loaddrcdialog(unconlist, conlist, minwidth);
	dr_loaddrcdistance(unconlist, conlist);
	conchanged = unconchanged = minwidchanged = 0;
	clearvaliddrcdates = 0;

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL || itemHit == 12) break;
		if (itemHit == 4 || itemHit == 13 || itemHit == 19 || itemHit == 27 ||
			itemHit == 28)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
		if (itemHit == 26)
		{
			clearvaliddrcdates = 1;
			DiaDimItem(26);
			continue;
		}
		if (itemHit == 3)
		{
			/* typed into distance field */
			i = DiaGetCurLine(7);
			if (i < 0) continue;
			pt = DiaGetText(3);
			while (*pt == ' ' || *pt == '\t') pt++;
			if (*pt == 0 || namesame(pt, _("none")) == 0) dist = XX; else
				dist = atofr(pt);
			layer1 = DiaGetPopupEntry(5);
			layer2 = i;
			if (layer1 > layer2) { temp = layer1; layer1 = layer2;  layer2 = temp; }
			dindex = (layer1+1) * (layer1/2) + (layer1&1) * ((layer1+1)/2);
			dindex = layer2 + el_curtech->layercount * layer1 - dindex;
			if (DiaGetControl(11) != 0)
			{
				if (unconlist[dindex] != dist) unconchanged++;
				unconlist[dindex] = dist;
			} else
			{
				if (conlist[dindex] != dist) conchanged++;
				conlist[dindex] = dist;
			}

			(void)initinfstr();
			(void)addstringtoinfstr(layername(el_curtech, (INTSML)i));
			if (dist == XX) (void)addstringtoinfstr(_(" (NONE)")); else
				(void)formatinfstr(" (%s)", frtoa(dist));
			DiaSetScrollLine(7, i, returninfstr());
			continue;
		}
		if (itemHit == 15)
		{
			/* typed into min-width field */
			pt = DiaGetText(15);
			while (*pt == ' ' || *pt == '\t') pt++;
			if (*pt == 0 || namesame(pt, _("none")) == 0) dist = XX; else
				dist = atofr(pt);
			layer1 = DiaGetPopupEntry(5);
			if (minwidth[layer1] != dist) minwidchanged++;
			minwidth[layer1] = dist;
			continue;
		}
		if (itemHit == 5)
		{
			/* changed layer popup */
			dr_loaddrcdialog(unconlist, conlist, minwidth);
			dr_loaddrcdistance(unconlist, conlist);
			continue;
		}
		if (itemHit == 7)
		{
			/* clicked on new entry in scroll list of "to" layers */
			dr_loaddrcdistance(unconlist, conlist);
			continue;
		}
		if (itemHit == 10 || itemHit == 11)
		{
			DiaSetControl(10, 0);
			DiaSetControl(11, 0);
			DiaSetControl(itemHit, 1);
			dr_loaddrcdialog(unconlist, conlist, minwidth);
			dr_loaddrcdistance(unconlist, conlist);
			continue;
		}
		if (itemHit == 16)
		{
			/* factory reset of variables */
			for(i=0; el_curtech->variables[i].name != 0; i++)
			{
				if (namesame(el_curtech->variables[i].name, "DRC_min_width") == 0)
				{
					list = (INTBIG *)el_curtech->variables[i].value;
					for(l=0; l<el_curtech->layercount; l++) minwidth[l] = list[l];
					continue;
				}
				if (namesame(el_curtech->variables[i].name, "DRC_min_unconnected_distances") == 0)
				{
					list = (INTBIG *)el_curtech->variables[i].value;
					for(l=0; l<numentries; l++) unconlist[l] = list[l];
					continue;
				}
				if (namesame(el_curtech->variables[i].name, "DRC_min_connected_distances") == 0)
				{
					list = (INTBIG *)el_curtech->variables[i].value;
					for(l=0; l<numentries; l++) conlist[l] = list[l];
					continue;
				}
			}
			unconchanged++;
			conchanged++;
			minwidchanged++;
			dr_loaddrcdialog(unconlist, conlist, minwidth);
			dr_loaddrcdistance(unconlist, conlist);
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		if (DiaGetControl(4) != 0) newhigherrors = 1; else
			newhigherrors = 0;
		if (newhigherrors != higherrors)
			(void)setvalkey((INTBIG)dr_aid, VAID, dr_pointoutkey,
				newhigherrors, VINTEGER);
		options = 0;
		if (DiaGetControl(19) != 0) options |= DRCHIERDIALOG;
		if (DiaGetControl(27) != 0) options |= DRCFIRSTERROR;
		if (DiaGetControl(28) != 0) options |= DRCREASONABLE;
		if (options != dr_options)
	 		(void)setvalkey((INTBIG)dr_aid, VAID, dr_optionskey, options, VINTEGER);
		if (unconchanged != 0)
		{
			setval((INTBIG)el_curtech, VTECHNOLOGY, "DRC_min_unconnected_distances",
				(INTBIG)unconlist, VFRACT|VISARRAY|(numentries<<VLENGTHSH));
		}
		if (conchanged != 0)
		{
			setval((INTBIG)el_curtech, VTECHNOLOGY, "DRC_min_connected_distances",
				(INTBIG)conlist, VFRACT|VISARRAY|(numentries<<VLENGTHSH));
		}
		if (minwidchanged != 0)
		{
			setval((INTBIG)el_curtech, VTECHNOLOGY, "DRC_min_width",
				(INTBIG)minwidth, VFRACT|VISARRAY|(el_curtech->layercount<<VLENGTHSH));
		}

		/* recache the database tables for the design rules */
		tech_initmaxdrcsurround();

		/* change state of DRC */
		if (DiaGetControl(13) != 0)
		{
			if ((dr_aid->aidstate & AIDON) == 0)
			{
				aidturnon(dr_aid, 0);
				setvalkey((INTBIG)dr_aid, VAID, dr_incrementalonkey, 1, VINTEGER);
			}
		} else
		{
			if ((dr_aid->aidstate & AIDON) != 0)
			{
				aidturnoff(dr_aid, 0);
				setvalkey((INTBIG)dr_aid, VAID, dr_incrementalonkey, 0, VINTEGER);
			}
		}

		/* clear valid DRC dates if requested */
		if (clearvaliddrcdates != 0) drcb_reset_dates();
	}
	DiaDoneDialog();
	efree((char *)unconlist);
	efree((char *)conlist);
	efree((char *)minwidth);

	if (itemHit == 12)
	{
		/* now edit the dracula */
		qual = "DRC_ecad_deck";
		sprintf(header, _("ECAD deck for technology %s"), el_curtech->techname);

		var = getval((INTBIG)el_curtech, VTECHNOLOGY, -1, qual);
		if (var == NOVARIABLE)
		{
			dummyfile[0] = "";
			var = setval((INTBIG)el_curtech, VTECHNOLOGY, qual, (INTBIG)dummyfile,
				VSTRING|VISARRAY|(1<<VLENGTHSH));
			if (var == NOVARIABLE)
			{
				ttyputerr(_("Cannot create DRC_ecad_deck on the technology"));
				return;
			}
		} else
			var->type &= ~VDONTSAVE;

		/* get a new window, put an editor in it */
		w = us_wantnewwindow(0);
		if (w == NOWINDOWPART) return;
		if (us_makeeditor(w, header, &dummy, &dummy) == NOWINDOWPART) return;
		ed = w->editor;
		ed->editobjqual = qual;
		ed->editobjaddr = (char *)el_curtech;
		ed->editobjtype = VTECHNOLOGY;
		ed->editobjvar = var;
		us_suspendgraphics(w);

		l = getlength(var);
		for(i=0; i<l; i++)
		{
			(void)initinfstr();
			(void)addstringtoinfstr(describevariable(var, (INTSML)i, -1));
			us_addline(w, i, returninfstr());
		}
		us_resumegraphics(w);
		w->changehandler = us_varchanges;
	}
}

void dr_loaddrcdialog(INTBIG *unconlist, INTBIG *conlist, INTBIG *minwidth)
{
	REGISTER INTBIG i, j, layer1, layer2, temp, dindex;
	REGISTER INTBIG dist, *list;

	j = DiaGetPopupEntry(5);
	if (minwidth[j] == XX) DiaSetText(15, _("NONE")); else
		DiaSetText(15, frtoa(minwidth[j]));
	DiaLoadTextDialog(7, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, 0);
	if (DiaGetControl(11) != 0) list = unconlist; else
		list = conlist;
	for(i=0; i<el_curtech->layercount; i++)
	{
		layer1 = j;
		layer2 = i;
		if (layer1 > layer2) { temp = layer1; layer1 = layer2;  layer2 = temp; }
		dindex = (layer1+1) * (layer1/2) + (layer1&1) * ((layer1+1)/2);
		dindex = layer2 + el_curtech->layercount * layer1 - dindex;
		dist = list[dindex];
		(void)initinfstr();
		(void)addstringtoinfstr(layername(el_curtech, (INTSML)i));
		if (dist == XX) (void)addstringtoinfstr(_(" (NONE)")); else
			(void)formatinfstr(" (%s)", frtoa(dist));
		DiaStuffLine(7, returninfstr());
	}
	DiaSelectLine(7, 0);
}

void dr_loaddrcdistance(INTBIG *unconlist, INTBIG *conlist)
{
	REGISTER INTBIG layer1, layer2, temp, dindex;
	REGISTER INTBIG dist, *list;

	if (DiaGetControl(11) != 0) list = unconlist; else
		list = conlist;
	layer1 = DiaGetPopupEntry(5);
	layer2 = DiaGetCurLine(7);

	if (layer1 > layer2) { temp = layer1; layer1 = layer2;  layer2 = temp; }
	dindex = (layer1+1) * (layer1/2) + (layer1&1) * ((layer1+1)/2);
	dindex = layer2 + el_curtech->layercount * layer1 - dindex;
	dist = list[dindex];
	if (dist == XX) DiaSetText(3, _("NONE")); else
		DiaSetText(3, frtoa(dist));
}

/*
 * special case for "DRC error" options
 * 5 = Error message                  (stat text)
 * 1 = OK                             (button)
 * 2 = Quit-checking, for now         (button)
 * 3 = Ignore-error                   (button)
 * 4 = Continue-silently, for now     (button)
 * 8 = Quit-checking, permanently     (button)
 * 9 = Continue-silently, permanently (button)
 */
char *dr_errordlog(char *prompt)
{
	INTBIG itemHit;
	INTSML oldplease;
	char *ret;

	/* display the DRC error dialog box */
	if (DiaInitDialog(&dr_errdialog) != 0) return(0);

	/* load the message */
	DiaSetText(5, prompt);

	/* loop until done */
	oldplease = el_pleasestop;
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == 1 || itemHit == 2 || itemHit == 3 || itemHit == 4 ||
			itemHit == 8 || itemHit == 9) break;
	}
	el_pleasestop = oldplease;

	ret = 0;
	switch (itemHit)
	{
		case 2:  ret = "quit-checking";       break;
		case 3:  ret = "ignore-error";        break;
		case 4:  ret = "continue-silently";   break;
		case 8:  ret = "drc-off";             break;
		case 9:  ret = "highlight-off";       break;
		default: ret = "acknowledge";         break;
	}
	DiaDoneDialog();
	return(ret);
}

#endif  /* DRCAID - at top */
