/*
 * Electric(tm) VLSI Design System
 *
 * File: network.c
 * Network tool: module for maintenance of connectivity information
 * 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 "network.h"
#include "database.h"
#include "egraphics.h"
#include "sim.h"
#include "tecschem.h"
#include "efunction.h"
#include "edialogs.h"
#include "usr.h"
#include <math.h>
#include <ctype.h>

#define ELECTRICALNETS 1					/* comment out for temporal nets */

#define	NETPOWER   1						/* value when network is power */
#define	NETGROUND  2						/* value when network is ground */

/* variables */
AIDENTRY   *net_aid;						/* the Network aid object */
AIDENTRY   *net_current_source;				/* the tool currently making changes */
INTSML      net_debug;						/* nonzero for debugging */
INTSML      net_globalwork;					/* nonzero when doing major evaluation */
INTSML      net_connect_power_ground;		/* nonzero to connect separate rails */
INTBIG      net_connect_power_groundkey;	/* key for "NET_connect_PandG" */
INTSML      net_connect_common_name;		/* nonzero to connect nets with common names */
INTBIG      net_connect_common_namekey;		/* key for "NET_connect_common" */
INTBIG      net_ncc_optionskey;				/* key for "NET_ncc_options" */
INTBIG      net_lastgoodncckey;				/* key for "NET_last_good_ncc" */
INTBIG      net_auto_namekey;				/* key for "NET_auto_name" */
INTSML      net_auto_name;					/* nonzero to automatically name nets */
INTBIG      net_transistor_gate_count;		/* number of transistors connected at gate */
INTBIG      net_transistor_active_count;	/* number of transistors connected at active */
INTBIG      net_transistor_gate_area;		/* area of transistors connected at gate */
INTBIG      net_transistor_active_area;		/* area of transistors connected at active */
PNET       *net_pnetfree;
PCONN      *net_pconnfree;
PCOMP      *net_pcompfree;
INTSML      net_mostj = 0;
NETWORK   **net_primnetlist;

#define NUMBUSSTRINGBUFFERS 2
INTSML      net_busbufstringbufferpos = -1;
char      **net_busbufstringsarray[NUMBUSSTRINGBUFFERS];
INTSML      net_busbufstringcountarray[NUMBUSSTRINGBUFFERS];

#define NOAREAPERIM ((AREAPERIM *)-1)

typedef struct Iareaperim
{
	float              area;
	INTBIG             perimeter;
	INTSML             layer;
	TECHNOLOGY        *tech;
	struct Iareaperim *nextareaperim;
} AREAPERIM;

AREAPERIM  *net_firstareaperim;


#define NOBUSLIST ((BUSLIST *)-1)

typedef struct Ibuslist
{
	ARCINST         *ai;
	PORTPROTO       *pp;
	INTBIG           width;
	struct Ibuslist *nextbuslist;
} BUSLIST;

BUSLIST *net_buslistfree = NOBUSLIST;

/*********************** COMMAND PARSING ***********************/

static COMCOMP networkfacetp = {NOKEYWORD,topoffacets,nextfacets,NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Facet to re-number (default is current facet)"), 0};
static COMCOMP networknodehp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Net to highlight"), 0};
static COMCOMP networknodenp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Net whose connections should be listed"), 0};
static COMCOMP networknodelp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Net, ALL of whose ports should be listed"), 0};
static KEYWORD networkeqnopt[] =
{
	{"examine-hierarchy",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-port-sequence",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-port-names",                0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkeqnp = {networkeqnopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Network-compare negating option"), 0};
static KEYWORD networkeqopt[] =
{
	{"highlight-other",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"first-equate",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"second-equate",                 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"clear-equates",                 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"set-equates",                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"examine-hierarchy",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-port-sequence",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"use-port-names",                0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"not",                           1,{&networkeqnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkeqp = {networkeqopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Network comparing/equating option"), M_("do comparison")};
static KEYWORD networkpgopt[] =
{
	{"unify-all-networks",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"separate-unconnected-networks", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkpgp = {networkpgopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Network power and ground equating option"), M_("show current state")};
static KEYWORD networkcnopt[] =
{
	{"unify-always",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"unify-only-in-schematics", 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkcnp = {networkcnopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("How to handle networks with the same name"), M_("show current state")};
static KEYWORD networkanopt[] =
{
	{"on",                   0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"off",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkanp = {networkanopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	NOBACKUP, INPUTOPT, " \t", M_("Option to automatically name networks"), M_("show current state")};
static KEYWORD networkopt[] =
{
	{"highlight",                     1,{&networknodehp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"show-equivalent",               1,{&networknodehp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"name-connections",              1,{&networknodenp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"show-geometry",                 1,{&networknodenp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"list-hierarchical-ports",       1,{&networknodelp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"compare",                       1,{&networkeqp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"power-and-ground",              1,{&networkpgp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"like-named-nets",               1,{&networkcnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"re-number",                     1,{&networkfacetp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"auto-naming",                   1,{&networkanp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"total-re-number",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"rip-bus",                       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"debug-toggle",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP net_networkp = {networkopt, NOTOPLIST, NONEXTLIST, NOPARAMS, NOBACKUP,
	0, " \t", M_("Network maintenance"), 0};

/* Network Options */
DIALOGITEM net_optionsdialogitems[] =
{
 /*  1 */ {0, {64,324,88,388}, BUTTON, N_("OK")},
 /*  2 */ {0, {12,324,36,388}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {156,4,172,200}, POPUP, ""},
 /*  4 */ {0, {28,16,44,213}, CHECK, N_("Unify Power and Ground")},
 /*  5 */ {0, {52,16,68,213}, CHECK, N_("Unify all like-named nets")},
 /*  6 */ {0, {284,0,300,216}, CHECK, N_("Use port names in NCC")},
 /*  7 */ {0, {76,16,92,213}, CHECK, N_("Automatically name networks")},
 /*  8 */ {0, {184,4,200,216}, CHECK, N_("Verbose NCC")},
 /*  9 */ {0, {308,0,324,216}, CHECK, N_("Check port order in NCC")},
 /* 10 */ {0, {332,0,348,216}, CHECK, N_("Ignore power and ground in NCC")},
 /* 11 */ {0, {4,108,20,269}, MESSAGE, N_("Network numbering:")},
 /* 12 */ {0, {132,4,148,217}, MESSAGE, N_("For all facets:")},
 /* 13 */ {0, {148,228,276,396}, SCROLL, ""},
 /* 14 */ {0, {132,220,372,221}, DIVIDELINE, ""},
 /* 15 */ {0, {284,232,300,281}, RADIO, N_("Yes")},
 /* 16 */ {0, {284,284,300,329}, RADIO, N_("No")},
 /* 17 */ {0, {284,332,300,397}, RADIO, N_("Default")},
 /* 18 */ {0, {308,232,324,281}, RADIO, N_("Yes")},
 /* 19 */ {0, {308,284,324,329}, RADIO, N_("No")},
 /* 20 */ {0, {308,332,324,397}, RADIO, N_("Default")},
 /* 21 */ {0, {332,232,348,281}, RADIO, N_("Yes")},
 /* 22 */ {0, {332,284,348,329}, RADIO, N_("No")},
 /* 23 */ {0, {332,332,348,397}, RADIO, N_("Default")},
 /* 24 */ {0, {108,112,124,325}, MESSAGE, N_("Network consistency checking:")},
 /* 25 */ {0, {100,4,101,400}, DIVIDELINE, ""},
 /* 26 */ {0, {132,228,148,393}, MESSAGE, N_("Individual facet overrides:")},
 /* 27 */ {0, {228,28,244,177}, BUTTON, N_("Clear valid NCC dates")},
 /* 28 */ {0, {356,0,372,216}, CHECK, N_("Check transistor sizes in NCC")},
 /* 29 */ {0, {356,232,372,281}, RADIO, N_("Yes")},
 /* 30 */ {0, {356,284,372,329}, RADIO, N_("No")},
 /* 31 */ {0, {356,332,372,397}, RADIO, N_("Default")}
};
DIALOG net_optionsdialog = {{50,75,431,484}, N_("Network Options"), 0, 31, net_optionsdialogitems};

/* prototypes for local routines */
INTSML   net_addnametonet(char*, INTSML, NETWORK*, ARCINST*, PORTPROTO*, NODEPROTO*);
void     net_addstring(char*, INTBIG, INTSML*, INTSML*, char***);
char    *net_buildnetname(INTSML, char**);
char    *net_busnameofarc(ARCINST *ai);
void     net_checknetnamearity(char*, NODEPROTO*);
void     net_checkvalidconnection(NODEINST*, ARCINST*);
void     net_ensurebusses(NETWORK*, INTSML, char**);
char    *net_findnameofbus(ARCINST *ai, INTSML justbus);
void     net_findportsdown(NETWORK*, NODEPROTO*);
void     net_findportsup(NETWORK*, NODEPROTO*);
void     net_freenetwork(NETWORK*, NODEPROTO*);
void     net_highlightnet(NODEPROTO*, NETWORK*);
INTSML   net_isanumber(char *name);
void     net_joinnetworks(NODEINST *ni);
void     net_killnetwork(NETWORK*, NODEPROTO*);
INTSML   net_mergebuswires(NODEINST*);
INTSML   net_mergenet(NETWORK*, NETWORK*);
INTSML   net_nameallnets(NODEPROTO *np);
INTSML   net_nameallnodes(NODEPROTO *np, INTSML evenpins);
INTSML   net_namenet(char*, NETWORK*);
INTSML   net_nconnect(ARCINST*, NETWORK*, NETWORK**, NETWORK**);
NETWORK *net_newnetwork(NODEPROTO*);
void     net_optionsdlog(void);
NETWORK *net_parsenetwork(char*);
INTSML   net_pconnect(PORTPROTO*);
void     net_putarclinkonnet(NETWORK*, ARCINST*);
void     net_putarconnet(ARCINST*, NETWORK*);
void     net_putportonnet(PORTPROTO*, NETWORK*);
void     net_recursivelymarkabove(NODEPROTO*);
void     net_recursivelyredo(NODEPROTO*);
void     net_redoprim(void);
void     net_removebuslinks(NETWORK*);
void     net_renamenet(char*, char*, NODEPROTO*);
void     net_ripbus(void);
void     net_setnccoverrides(void);
void     net_setspecial(INTSML*, INTSML, NODEPROTO*);
void     net_startglobalwork(LIBRARY*);
INTSML   net_takearcfromnet(ARCINST*);
void     net_takearclinkfromnet(ARCINST*, NETWORK*);
INTSML   net_takeportfromnet(PORTPROTO*);
void     net_propgeometry(NODEPROTO *facet, XARRAY trans);
void     net_geometrypolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count);
void     net_ensuretempbusname(NETWORK *net);
BUSLIST *net_allocbuslist(void);
void     net_freebuslist(BUSLIST *bl);

/*********************** DATABASE INTERFACE ROUTINES ***********************/

void net_init(INTBIG *argc, char *argv[], AIDENTRY *thisaid)
{
	/* ignore pass 3 initialization */
	if (thisaid == 0) return;

	/* take aid pointer in pass 1 */
	if (thisaid != NOAID)
	{
		net_aid = thisaid;
		return;
	}

	/* initialize flattened network representation */
	net_pcompfree = NOPCOMP;
	net_pnetfree = NOPNET;
	net_pconnfree = NOPCONN;

	/* debugging off */
	net_debug = 0;

	net_lastgoodncckey = makekey("NET_last_good_ncc");

	/* set NCC options */
	net_ncc_optionskey = makekey("NET_ncc_options");
	nextvarchangequiet();
	(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, 0, VINTEGER|VDONTSAVE);

	/* do not automatically generate net names */
	net_auto_namekey = makekey("NET_auto_name");
	nextvarchangequiet();
	(void)setvalkey((INTBIG)net_aid, VAID, net_auto_namekey, 0, VINTEGER|VDONTSAVE);
	net_auto_name = 0;

	/* do not connect separate power and ground */
	net_connect_power_groundkey = makekey("NET_connect_PandG");
	nextvarchangequiet();
	(void)setvalkey((INTBIG)net_aid, VAID, net_connect_power_groundkey, 0, VINTEGER|VDONTSAVE);
	net_connect_power_ground = 0;

	/* do not connect common named nets in non-schematic facets */
	net_connect_common_namekey = makekey("NET_connect_common");
	nextvarchangequiet();
	(void)setvalkey((INTBIG)net_aid, VAID, net_connect_common_namekey, 0, VINTEGER|VDONTSAVE);
	net_connect_common_name = 0;

	/* node-number primitive ports (now and when technologies change) */
	registertechnologycache(net_redoprim);

	/* register options dialog */
	DiaDeclareHook("netopt", &net_networkp, net_optionsdlog);
}

void net_done(void)
{
	REGISTER INTBIG i, j, stringcount;
	REGISTER char **mystrings;
	REGISTER BUSLIST *bl;

	if (net_mostj != 0)
	{
		for(j=0; j<net_mostj; j++) efree((char *)net_primnetlist[j]);
		efree((char *)net_primnetlist);
		net_mostj = 0;
	}

	if (net_busbufstringbufferpos >= 0)
	{
		for(i=0; i<NUMBUSSTRINGBUFFERS; i++)
		{
			mystrings = net_busbufstringsarray[i];
			stringcount = net_busbufstringcountarray[i];
			for(j=0; j<stringcount; j++) efree((char *)mystrings[j]);
			if (stringcount > 0) efree((char *)mystrings);
		}
	}

	while (net_buslistfree != NOBUSLIST)
	{
		bl = net_buslistfree;
		net_buslistfree = net_buslistfree->nextbuslist;
		efree((char *)bl);
	}

	net_freediffmemory();
	net_freeflatmemory();
}

/*
 * routine to associate the primitives ports in each technology with unique
 * network objects according to connectivity within the node
 */
void net_redoprim(void)
{
	REGISTER INTSML j, maxj;
	REGISTER PORTPROTO *pp, *spt;
	REGISTER TECHNOLOGY *tech;
	REGISTER NODEPROTO *np;

	/* determine maximum number of ports on a primitive */
	maxj = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		j = 0;
		for(pp=np->firstportproto; pp!=NOPORTPROTO; pp=pp->nextportproto) j++;
		if (j > maxj) maxj = j;
	}

	/* create an array of network objects for the primitives */
	if (maxj > net_mostj)
	{
		if (net_mostj != 0)
		{
			for(j=0; j<net_mostj; j++) efree((char *)net_primnetlist[j]);
			efree((char *)net_primnetlist);
			net_mostj = 0;
		}
		net_primnetlist = (NETWORK **)emalloc(((sizeof (NETWORK *)) * maxj), net_aid->cluster);
		if (net_primnetlist == 0) return;
		for(j=0; j<maxj; j++)
		{
			net_primnetlist[j] = (NETWORK *)emalloc(sizeof (NETWORK), net_aid->cluster);
			if (net_primnetlist[j] == 0) return;
			net_primnetlist[j]->namecount = 0;
			net_primnetlist[j]->tempname = 0;
			net_primnetlist[j]->signals = 1;
			net_primnetlist[j]->nextnetwork = NONETWORK;   /* default values SRP 91-8-19 */
			net_primnetlist[j]->lastnetwork = NONETWORK;
			net_primnetlist[j]->networklist = (NETWORK **)NONETWORK;
			net_primnetlist[j]->parent = NONODEPROTO;
			net_primnetlist[j]->netname = NOSTRING; /* var examine does not check namecount */
			net_primnetlist[j]->arccount = 0;
			net_primnetlist[j]->refcount = 0;
			net_primnetlist[j]->portcount = 0;
			net_primnetlist[j]->buslinkcount = 0;
			net_primnetlist[j]->numvar = 0;
		}
		net_mostj = maxj;
	}

	/* assign unique networks to unconnected primitive ports */
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		j = 0;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			for(spt = np->firstportproto; spt != pp; spt = spt->nextportproto)
#ifdef ELECTRICALNETS
				if ((pp->userbits&PORTNET) == (spt->userbits&PORTNET))
#else
				if (((pp->parent->userbits&NFUNCTION) >> NFUNCTIONSH) != NPFLIPFLOP)
#endif
			{
				pp->network = spt->network;
				break;
			}
			if (spt == pp) pp->network = net_primnetlist[j++];
		}
	}
}

void net_examinenodeproto(NODEPROTO *np)
{
	net_reevaluatefacet(np);
}

INTSML net_set(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np, *wnp, *cnp;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib, *olib;
	REGISTER NETWORK *net, *onet;
	REGISTER INTSML l, total, showrequest, sorted;
	REGISTER INTBIG tr, fun, thishei, nexthei, options, lambda;
	REGISTER char *pt, *lname;
	REGISTER AREAPERIM *ap, *lastap, *nextap;
	REGISTER VARIABLE *var;
	float ratio;
	INTBIG x, y;
	REGISTER WINDOWPART *w;
	REGISTER NODEINST *ni, *uni;
	REGISTER PORTARCINST *pi;
	NODEPROTO *snp;
	static char *message[1] = {"total-re-number"};

	if (count == 0)
	{
		count = ttygetparam("NETWORK option:", &net_networkp, MAXPARS, par);
		if (count == 0)
		{
			ttyputerr(_("Aborted"));
			return(1);
		}
	}
	l = strlen(pt = par[0]);


	if (namesamen(pt, "rip-bus", l) == 0 && l >= 2)
	{
		net_ripbus();
		return(0);
	}

	if (namesamen(pt, "highlight", l) == 0 ||
		(namesamen(pt, "show-equivalent", l) == 0 && l > 5))
	{
		/* if this was associated, show that */
		if (net_equate(0) == 0) return(0);

		if (namesamen(pt, "show-equivalent", l) == 0) showrequest = 1; else
			showrequest = 0;
		if (count < 2)
		{
			net = net_gethighlightednet(0, (INTSML)(showrequest == 0 ? 1 : 0));
			if (net == NONETWORK) return(1);
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
			net = getnetwork(par[1], np);
			if (net == NONETWORK)
			{
				ttyputerr(_("No net called %s"), par[1]);
				return(1);
			}
		}

		ttyputverbose(_("Network '%s'"), describenetwork(net));
		np = net->parent;

		if (showrequest == 0)
		{
			/* show the arcs on this network */
			(void)askaid(us_aid, "clear");
			net_highlightnet(np, net);

			/* move down the hierarchy, showing nets where exported */
			snp = np;
			for(;;)
			{
				for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
					for(wnp = olib->firstnodeproto; wnp != NONODEPROTO; wnp = wnp->nextnodeproto)
						wnp->temp1 = 0;
				for(ni = snp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				{
					if (ni->proto->primindex != 0) continue;
					if (ni->proto->temp1 != 0) continue;
					ni->proto->temp1 = 1;
					cnp = contentsview(ni->proto);
					if (cnp == NONODEPROTO) cnp = ni->proto;
					uni = descentparent(cnp);
					if (uni == NONODEINST) continue;
					ni = uni;
					break;
				}
				if (ni == NONODEINST) break;

				/* see if the network connects to this node */
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->conarcinst->network == net) break;
				if (pi == NOPORTARCINST) break;

				snp = contentsview(ni->proto);
				if (snp == NONODEPROTO) snp = ni->proto;
				pp = equivalentport(ni->proto, pi->proto, snp);
				net = pp->network;
				net_highlightnet(snp, net);
			}
		}

		/* if there are associated VHDL or simulation windows, show in them */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if (w == el_curwindowpart) continue;
			switch (w->state&WINDOWTYPE)
			{
				case TEXTWINDOW:
				case POPTEXTWINDOW:
					/* must be editing this cell */
					if (w->curnodeproto == NONODEPROTO) break;
					if (w->curnodeproto->cell != np->cell) break;
					if (net->namecount > 0) pt = net->netname; else
						pt = describenetwork(net);
					if (w->curnodeproto->cellview == el_vhdlview ||
						w->curnodeproto->cellview == el_verilogview)
					{
						/* special intelligence for finding the net name in VHDL */
						(void)initinfstr();
						(void)addstringtoinfstr("entity ");
						(void)addstringtoinfstr(w->curnodeproto->cell->cellname);
						(void)addstringtoinfstr(" is port(");
						us_searchtext(w, returninfstr(), 1);
						if (net->portcount <= 0)
							us_searchtext(w, "signal ", 0);
						us_searchtext(w, pt, 0);
					} else
					{
						/* just look for the name, starting at the top */
						us_searchtext(w, pt, 1);
					}
					break;
#if SIMAID
				case WAVEFORMWINDOW:
					if (w->curnodeproto == NONODEPROTO) break;

					/* try converting this network to a HSPICE path */
					pt = sim_spice_signalname(net);
					tr = sim_window_findtrace(pt);
					if (tr != 0)
					{
						sim_window_sethighlighttrace(tr);
						break;
					}

					/* must be simulating this cell */
					if (w->curnodeproto->cell != np->cell) tr = 0; else
					{
						if (net->namecount > 0) pt = net->netname; else
							pt = describenetwork(net);
						tr = sim_window_findtrace(pt);
					}
					sim_window_sethighlighttrace(tr);
					break;
#endif
			}
		}

		/* quit now if not showing others in a multi-page schematic */
		if ((np->cellview->viewstate&MULTIPAGEVIEW) == 0) return(0);

		/* search for an export on this net */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (pp->network == net) break;
		if (pp == NOPORTPROTO) return(0);

		/* search all other windows for another page of this schematic */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if (w->curnodeproto == NONODEPROTO) continue;
			if ((w->curnodeproto->cellview->viewstate&MULTIPAGEVIEW) == 0) continue;
			if (w->curnodeproto->cell != np->cell) continue;
			if (w->curnodeproto == np) continue;

			/* find any equivalent ports in this facet */
			for(opp = w->curnodeproto->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
				if (namesame(opp->protoname, pp->protoname) == 0) break;
			if (opp == NOPORTPROTO) continue;

			/* show the arcs on this network */
			net_highlightnet(w->curnodeproto, opp->network);
		}
		return(0);
	}
	if (namesamen(pt, "show-geometry", l) == 0 && l > 5)
	{
		if (count < 2)
		{
			net = net_gethighlightednet(0, 1);
			if (net == NONETWORK) return(1);
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
			net = getnetwork(par[1], np);
			if (net == NONETWORK)
			{
				ttyputerr(_("No net called %s"), par[1]);
				return(1);
			}
		}
		np = net->parent;

		/* initialize polygon merging */
		mergeinit();

		/* mark the networks in this facet that are of interest */
		for(onet = np->firstnetwork; onet != NONETWORK; onet = onet->nextnetwork)
			onet->temp1 = 0;
		net->temp1 = 1;
		net_transistor_gate_count = net_transistor_active_count = 0;
		net_transistor_gate_area = net_transistor_active_area = 0;
		net_firstareaperim = NOAREAPERIM;

		/* examine circuit recursively */
		net_propgeometry(np, el_matid);

		/* get back the total geometry */
		mergedone(net_geometrypolygon);
		ttyputmsg(_("For network '%s' in facet %s:"), describenetwork(net),
			describenodeproto(np));

		/* sort the layers */
		sorted = 0;
		while (sorted == 0)
		{
			sorted = 1;
			lastap = NOAREAPERIM;
			for(ap = net_firstareaperim; ap != NOAREAPERIM; ap = ap->nextareaperim)
			{
				nextap = ap->nextareaperim;
				if (nextap == NOAREAPERIM) continue;
				fun = layerfunction(ap->tech, ap->layer);
				thishei = layerfunctionheight(fun);
				fun = layerfunction(nextap->tech, nextap->layer);
				nexthei = layerfunctionheight(fun);
				if (thishei < nexthei)
				{
					if (lastap == NOAREAPERIM) net_firstareaperim = nextap; else
						lastap->nextareaperim = nextap;
					ap->nextareaperim = nextap->nextareaperim;
					nextap->nextareaperim = ap;
					sorted = 0;
					break;
				}
				lastap = ap;
			}
		}
		lambda = lambdaoffacet(np);
		while (net_firstareaperim != NOAREAPERIM)
		{
			ap = net_firstareaperim;
			net_firstareaperim = ap->nextareaperim;
			lname = layername(ap->tech, ap->layer);
			if (ap->perimeter == 0)
			{
				ttyputmsg(_("Layer %s: area=%g  half-perimeter=%s"), lname,
					ap->area/(float)lambda/(float)lambda, latoa(ap->perimeter/2));
			} else
			{
				ratio = (ap->area / (float)lambda) / (float)(ap->perimeter/2);
				ttyputmsg(_("Layer %s: area=%g  half-perimeter=%s ratio=%g"), lname,
					ap->area/(float)lambda/(float)lambda,
					latoa(ap->perimeter/2), ratio);
			}
			efree((char *)ap);
		}
		if (net_transistor_gate_count > 0 || net_transistor_active_count > 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr(_("Connects to "));
			if (net_transistor_gate_count > 0)
				formatinfstr(_("%ld transistor %s (total area %s)"),
					net_transistor_gate_count, makeplural(_("gate"), net_transistor_gate_count),
						latoa(net_transistor_gate_area/lambda));
			if (net_transistor_active_count > 0)
			{
				if (net_transistor_gate_count > 0) (void)addstringtoinfstr(_(" and "));
				formatinfstr(_("%ld transistor %s (total area %s)"),
					net_transistor_active_count, makeplural(_("active"), net_transistor_active_count),
						latoa(net_transistor_active_area/lambda));
			}
			ttyputmsg("%s", returninfstr());
		}
		return(0);
	}

	if (namesamen(pt, "auto-naming", l) == 0)
	{
		if (count == 1)
		{
			ttyputmsg(_("Unnamed networks are %sautomatically named"),
				net_auto_name == 0 ? _("not ") : "");
			return(0);
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "on", l) == 0 && l >= 2)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_auto_namekey,
				1, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Unnamed networks will be automatically named"));
			return(0);
		}
		if (namesamen(pt, "off", l) == 0 && l >= 2)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_auto_namekey,
				0, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Unnamed networks will not be automatically named"));
			return(0);
		}
		ttyputbadusage("tellaid network auto-naming");
		return(1);
	}

	if (namesamen(pt, "name-connections", l) == 0)
	{
		if (count < 2)
		{
			net = net_gethighlightednet(0, 1);
			if (net == NONETWORK) return(1);
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
			net = getnetwork(par[1], np);
			if (net == NONETWORK)
			{
				ttyputerr(_("No net called %s"), par[1]);
				return(1);
			}
		}
		ttyputmsg(_("Network '%s'"), describenetwork(net));

		total = 0;
		for(ai = net->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			if (ai->network != net) continue;
			for(l=0; l<2; l++)
			{
				np = ai->end[l].nodeinst->proto;
				if (np->primindex != 0)
				{
					opp = np->firstportproto;
					for(pp = opp->nextportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					{
						if (pp->network != opp->network) break;
						opp = pp;
					}
					if (pp == NOPORTPROTO) continue;
				}
				if (total == 0) ttyputmsg(_("Network connects to:"));
				portposition(ai->end[l].nodeinst, ai->end[l].portarcinst->proto, &x, &y);
				ttyputmsg(_("  Node %s, port %s at (%s,%s)"), describenodeinst(ai->end[l].nodeinst),
					ai->end[l].portarcinst->proto->protoname, latoa(x), latoa(y));
				total++;
			}
		}
		if (total == 0) ttyputmsg(_("Network not connected"));
		return(0);
	}
	if (namesamen(pt, "power-and-ground", l) == 0)
	{
		if (count == 1)
		{
			ttyputmsg(_("Unconnected power and ground nets are %sequated"),
				net_connect_power_ground == 0 ? _("not ") : "");
			return(0);
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "unify-all-networks", l) == 0)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_power_groundkey,
				1, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Unconnected power and ground nets will be equated"));
			(void)tellaid(net_aid, 1, message);
			return(0);
		}
		if (namesamen(pt, "separate-unconnected-networks", l) == 0)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_power_groundkey,
				0, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Unconnected power and ground nets will not be equated"));
			(void)tellaid(net_aid, 1, message);
			return(0);
		}
		ttyputbadusage("tellaid network power-and-ground");
		return(1);
	}
	if (namesamen(pt, "like-named-nets", l) == 0)
	{
		if (count == 1)
		{
			ttyputmsg(_("Nets with the same name are equated %s"),
				net_connect_common_name == 0 ? _("only in schematics ") : _("always"));
			return(0);
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "unify-always", l) == 0)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_common_namekey,
				1, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Nets with the same name will always be equated"));
			(void)tellaid(net_aid, 1, message);
			return(0);
		}
		if (namesamen(pt, "unify-only-in-schematics", l) == 0)
		{
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_common_namekey,
				0, VINTEGER|VDONTSAVE);
			ttyputverbose(_("Nets with the same name will be equated only in schematics"));
			(void)tellaid(net_aid, 1, message);
			return(0);
		}
		ttyputbadusage("tellaid network like-named-nets");
		return(1);
	}
	if (namesamen(pt, "compare", l) == 0)
	{
		if (count == 1) return(net_compare());
		l = strlen(pt = par[1]);
		if (namesamen(pt, "highlight-other", l) == 0) return(net_equate(1));
		if (namesamen(pt, "clear-equates", l) == 0) return(net_clearequiv());
		if (namesamen(pt, "set-equates", l) == 0) return(net_setequiv());
		if (namesamen(pt, "first-equate", l) == 0) return(net_setfirst());
		if (namesamen(pt, "second-equate", l) == 0) return(net_setsecond());
		if (namesamen(pt, "not", l) == 0)
		{
			if (count <= 2)
			{
				ttyputusage("tellaid network compare not OPTION");
				return(1);
			}
			l = strlen(pt = par[2]);
			if (namesamen(pt, "examine-hierarchy", l) == 0)
			{
				var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options & ~NCCHIERARCHICAL,
					VINTEGER|VDONTSAVE);
				ttyputverbose(_("Compared circuits will not be hierarchically flattened"));
				return(0);
			}
			if (namesamen(pt, "use-port-sequence", l) == 0 && l >= 10)
			{
				var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options & ~NCCUNIFYPORTNAMES,
					VINTEGER|VDONTSAVE);
				ttyputverbose(_("Compared circuits will allow port reordering"));
				return(0);
			}
			if (namesamen(pt, "use-port-names", l) == 0 && l >= 10)
			{
				var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options & ~NCCUSEPORTNAMES,
					VINTEGER|VDONTSAVE);
				ttyputverbose(_("Compared circuits will not consider port names"));
				return(0);
			}
			ttyputbadusage("tellaid network compare not");
			return(1);
		}
		if (namesamen(pt, "examine-hierarchy", l) == 0)
		{
			var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options | NCCHIERARCHICAL,
				VINTEGER|VDONTSAVE);
			ttyputverbose(_("Compared circuits will be hierarchically flattened"));
			return(0);
		}
		if (namesamen(pt, "use-port-sequence", l) == 0 && l >= 10)
		{
			var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options | NCCUNIFYPORTNAMES,
				VINTEGER|VDONTSAVE);
			ttyputverbose(_("Compared circuits will not allow port reordering"));
			return(0);
		}
		if (namesamen(pt, "use-port-names", l) == 0 && l >= 10)
		{
			var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options | NCCUSEPORTNAMES,
				VINTEGER|VDONTSAVE);
			ttyputverbose(_("Compared circuits will consider port names"));
			return(0);
		}
		ttyputbadusage("tellaid network compare");
		return(1);
	}
	if (namesamen(pt, "list-hierarchical-ports", l) == 0)
	{
		/* get the currently highlighted network */
		if (count < 2)
		{
			net = net_gethighlightednet(1, 1);
			if (net == NONETWORK) return(1);
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
			net = getnetwork(par[1], np);
			if (net == NONETWORK)
			{
				ttyputerr(_("No net called %s"), par[1]);
				return(1);
			}
		}

		/* find all exports on network "net" */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					pp->temp1 = 0;
		net_findportsup(net, net->parent);
		(void)ttyputmsg(_("Going down the hierarchy from facet %s"), describenodeproto(net->parent));
		net_findportsdown(net, net->parent);
		return(0);
	}
	if (namesamen(pt, "debug-toggle", l) == 0)
	{
		if (net_debug == 0)
		{
			net_debug = 1;
			ttyputmsg(_("Network debugging on"));
		} else
		{
			net_debug = 0;
			ttyputmsg(_("Network debugging off"));
		}
		return(0);
	}
	if (namesamen(pt, "re-number", l) == 0 && l >= 2)
	{
		if (count >= 2)
		{
			np = getnodeproto(par[1]);
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No facet named %s"), par[1]);
				return(1);
			}
			if (np->primindex != 0)
			{
				ttyputerr(_("Can only renumber facets, not primitives"));
				return(1);
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return(1);
			}
		}

		net_reevaluatefacet(np);
		ttyputverbose(_("Facet %s re-numbered"), describenodeproto(np));
		return(0);
	}
	if (namesamen(pt, "total-re-number", l) == 0)
	{
		net_startglobalwork(el_curlib);
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->userbits |= REDOFACETNET;
		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if ((np->userbits&REDOFACETNET) != 0) net_recursivelyredo(np);
		ttyputverbose(_("Entire library re-numbered"));
		return(0);
	}

	ttyputbadusage("tellaid network");
	return(1);
}

/*
 * make request of the network tool:
 * "total-re-number" TAKES: the LIBRARY to be renumbered
 * "re-number" TAKES: the FACET to be renumbered
 * "rename" TAKES: old STRING name, new STRING name, FACET
 * "name-nodes" TAKES: FACET with nodes; RETURNS: number of nodes named
 * "name-all-nodes" TAKES: FACET with nodes; RETURNS: number of nodes named
 */
INTBIG net_request(char *command, va_list ap)
{
	REGISTER LIBRARY *lib, *olib;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG arg1, arg2, arg3;
	REGISTER INTSML retval;

	if (namesame(command, "total-re-number") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		lib = (LIBRARY *)arg1;
		net_startglobalwork(lib);
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->userbits |= REDOFACETNET;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if ((np->userbits&REDOFACETNET) != 0) net_recursivelyredo(np);
		return(0);
	}
	if (namesame(command, "re-number") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		np = (NODEPROTO *)arg1;
		net_reevaluatefacet(np);
		return(0);
	}
	if (namesame(command, "name-nodes") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		np = (NODEPROTO *)arg1;
		retval = net_nameallnodes(np, 0);
		return(retval);
	}
	if (namesame(command, "name-all-nodes") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		np = (NODEPROTO *)arg1;
		retval = net_nameallnodes(np, 1);
		return(retval);
	}
	if (namesame(command, "name-nets") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		np = (NODEPROTO *)arg1;
		retval = net_nameallnets(np);
		return(retval);
	}
	if (namesame(command, "rename") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);
		arg2 = va_arg(ap, INTBIG);
		arg3 = va_arg(ap, INTBIG);

		net_renamenet((char *)arg1, (char *)arg2, (NODEPROTO *)arg3);
		return(0);
	}
	return(-1);
}

/*********************** BROADCAST ROUTINES ***********************/

void net_startbatch(AIDENTRY *source, INTSML undoredo)
{
	net_globalwork = 0;
	net_current_source = source;
}

void net_endbatch(void)
{
	REGISTER NODEPROTO *np;
	REGISTER LIBRARY *lib;

	if (net_globalwork == 0) return;

	if (net_debug != 0) ttyputmsg(_("Network: doing entire facet rechecks"));

	/* look for all facets that need to be renumbered */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		if ((lib->userbits&REDOFACETLIB) == 0) continue;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if ((np->userbits&REDOFACETNET) != 0) net_recursivelyredo(np);
	}
}

/*
 * routine to redo the connectivity within facet "np" (and recursively,
 * all facets below that in need of renumbering).
 */
void net_recursivelyredo(NODEPROTO *np)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *cnp;

	/* first see if any lower facets need to be renumbered */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		cnp = contentsview(ni->proto);
		if (cnp != NONODEPROTO)
		{
			if (cnp->primindex == 0 && (cnp->userbits&REDOFACETNET) != 0)
				net_recursivelyredo(cnp);
		}
		if (cnp != ni->proto)
		{
			if (ni->proto->primindex == 0 && (ni->proto->userbits&REDOFACETNET) != 0)
				net_recursivelyredo(ni->proto);
		}
	}
	net_reevaluatefacet(np);

	/* mark this facet rechecked */
	np->userbits &= ~REDOFACETNET;
}

/*
 * routine to redo the connectivity within facet "np".  The "network" fields
 * of the arcs and ports are set to be consistent.  This data and this routine
 * are used by other tools such as the design-rule checker and the simulator
 */
void net_reevaluatefacet(NODEPROTO *np)
{
	REGISTER ARCINST *ai;
	REGISTER BUSLIST *buslist, *bl, *lastbl, *nextbl;
	REGISTER PORTPROTO *pp;
	REGISTER VARIABLE *var;
	REGISTER NETWORK *net, *nextnet;
	REGISTER NODEPROTO *subnp;
	REGISTER INTBIG sorted, width;
	NETWORK *power, *ground;
	NODEINST *ni;

	if (net_debug != 0)
		ttyputmsg(_("Network: rechecking facet %s"), describenodeproto(np));

	/* first delete all network objects in this facet */
	for(net = np->firstnetwork; net != NONETWORK; net = nextnet)
	{
		nextnet = net->nextnetwork;
		net_freenetwork(net, np);
	}
	np->firstnetwork = NONETWORK;

	/* unmark all network data in the facet */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->network = NONETWORK;
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		pp->network = NONETWORK;

	/* now re-construct the electrical connections */
	power = ground = NONETWORK;

	/* make a list of all busses */
	buslist = NOBUSLIST;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->proto != sch_busarc) continue;
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
		if (var == NOVARIABLE) continue;
		width = net_buswidth((char *)var->addr);
		bl = net_allocbuslist();
		if (bl == NOBUSLIST) break;
		bl->ai = ai;
		bl->pp = NOPORTPROTO;
		bl->width = width;
		bl->nextbuslist = buslist;
		buslist = bl;
	}
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		width = net_buswidth(pp->protoname);
		if (width <= 1) continue;
		bl = net_allocbuslist();
		if (bl == NOBUSLIST) break;
		bl->ai = NOARCINST;
		bl->pp = pp;
		bl->width = width;
		bl->nextbuslist = buslist;
		buslist = bl;
	}

	/* sort by bus width */
	sorted = 0;
	while (sorted == 0)
	{
		sorted = 1;
		lastbl = NOBUSLIST;
		for(bl = buslist; bl != NOBUSLIST; bl = bl->nextbuslist)
		{
			nextbl = bl->nextbuslist;
			if (nextbl == NOBUSLIST) continue;
			if (bl->width < nextbl->width)
			{
				if (lastbl == NOBUSLIST) buslist = nextbl; else
					lastbl->nextbuslist = nextbl;
				bl->nextbuslist = nextbl->nextbuslist;
				nextbl->nextbuslist = bl;
				bl = nextbl;
				sorted = 0;
			}
			lastbl = bl;
		}
	}

	/* now renumber arcs, starting with the narrowest */
	for(bl = buslist; bl != NOBUSLIST; bl = nextbl)
	{
		nextbl = bl->nextbuslist;
		if (bl->ai != NOARCINST)
		{
			ai = bl->ai;
			if (ai->network != NONETWORK) continue;
			net = net_newnetwork(np);
			(void)net_nconnect(ai, net, &power, &ground);
		} else
		{
			pp = bl->pp;
			if (pp->network == NONETWORK) (void)net_pconnect(pp);
		}
		net_freebuslist(bl);
	}

	/* renumber all other bus arcs */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network != NONETWORK) continue;
		if (ai->proto != sch_busarc) continue;
		net = net_newnetwork(np);
		(void)net_nconnect(ai, net, &power, &ground);
	}

	/* finally renumber non-bus arcs */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network != NONETWORK) continue;
		if (ai->proto == sch_busarc) continue;
		net = net_newnetwork(np);
		(void)net_nconnect(ai, net, &power, &ground);
	}

	/* evaluate "wire_con" nodes which join nets */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto != sch_wireconprim) continue;
		net_joinnetworks(ni);
	}

	/* update connectivity values on unconnected ports */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		if (pp->network == NONETWORK) (void)net_pconnect(pp);

	/* finally merge all individual signals connected in busses by subfacets...bgw */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (subnp == NONODEPROTO) continue;
		if (subnp->primindex != 0) continue;
		if (net_mergebuswires(ni) != 0) net_recursivelymarkabove(subnp);
	}
}

void net_modifyportproto(PORTPROTO *pp, NODEINST *oldsubni, PORTPROTO *oldsubpp)
{
	if (net_debug != 0) ttyputmsg(_("Network: port %s modified"), pp->protoname);

	/* stop now if the entire facet will be renumbered */
	if (net_globalwork != 0 && (pp->parent->userbits&REDOFACETNET) != 0) return;

	if (net_pconnect(pp) != 0)
	{
		if (net_debug != 0) ttyputmsg(_("Network: must recheck instances of %s"),
			describenodeproto(pp->parent));
		net_recursivelymarkabove(pp->parent);
	}
}

void net_newobject(INTBIG addr, INTBIG type)
{
	NETWORK *power, *ground;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER NODEPROTO *np;

	if ((type&VTYPE) == VARCINST)
	{
		ai = (ARCINST *)addr;
		ai->network = NONETWORK;

		/* stop now if the entire facet will be renumbered */
		if (net_globalwork != 0 && (ai->parent->userbits&REDOFACETNET) != 0) return;

		/* ignore nonelectrical arcs */
		if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) return;

		if (net_debug != 0)
			ttyputmsg(_("Network: arc %s created"), describearcinst(ai));

		/* remove any former network information */
		(void)net_takearcfromnet(ai);

		/* create a new network and propagate it */
		power = ground = NONETWORK;
		if (net_nconnect(ai, net_newnetwork(ai->parent), &power, &ground) != 0)
		{
			if (net_debug != 0)
				ttyputmsg(_("Network: must recheck instances of %s"), describenodeproto(ai->parent));
			net_recursivelymarkabove(ai->parent);
		}
	} else if ((type&VTYPE) == VPORTPROTO)
	{
		pp = (PORTPROTO *)addr;
		pp->network = NONETWORK;

		/* stop now if the entire facet will be renumbered */
		if (net_globalwork != 0 && (pp->parent->userbits&REDOFACETNET) != 0) return;

		if (net_debug != 0) ttyputmsg(_("Network: port %s created"), pp->protoname);

		if (net_pconnect(pp) != 0)
		{
			if (net_debug != 0)
				ttyputmsg(_("Network: must recheck instances of %s"), describenodeproto(pp->parent));
			net_recursivelymarkabove(pp->parent);
		}
	} else if ((type&VTYPE) == VNODEPROTO)
	{
		np = (NODEPROTO *)addr;
		if (net_debug != 0) ttyputmsg(_("Network: facet %s created"), describenodeproto(np));

		/* queue this facet for renumbering */
		net_startglobalwork(np->cell->lib);

		/* mark this facet to be renumbered */
		np->userbits |= REDOFACETNET;
	}
}

void net_killobject(INTBIG addr, INTBIG type)
{
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *ai;
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER PORTEXPINST *pe;
	REGISTER INTSML i;
	NETWORK *power, *ground;
	REGISTER NETWORK *net, *nextnet;

	if ((type&VTYPE) == VARCINST)
	{
		ai = (ARCINST *)addr;

		if (net_debug != 0)
			ttyputmsg(_("Network: arc %s killed"), describearcinst(ai));

		/* stop now if the entire facet will be renumbered */
		if (net_globalwork != 0 && (ai->parent->userbits&REDOFACETNET) != 0) return;

		if (ai->proto == sch_busarc)
		{
			/* for busses, must redo entire facet */
			net_recursivelymarkabove(ai->parent);
			return;
		}

		/* if this arc was on a multiply-named network, reevaluate the facet */
		if (ai->network != NONETWORK && ai->network->namecount > 1)
		{
			np = ai->parent;
			net_startglobalwork(np->cell->lib);
			np->userbits |= REDOFACETNET;
			return;
		}

		/* remove network pointers to this arc */
		(void)net_takearcfromnet(ai);

		/* renumber both sides of now unconnected network */
		power = ground = NONETWORK;
		for(i=0; i<2; i++)
		{
			for(pi = ai->end[i].nodeinst->firstportarcinst; pi != NOPORTARCINST;
				pi = pi->nextportarcinst)
			{
				if ((pi->conarcinst->userbits&DEADA) != 0) continue;

				/* remove any former network information */
				(void)net_takearcfromnet(pi->conarcinst);

				/* create a new network and propagate it */
				if (net_nconnect(pi->conarcinst, net_newnetwork(ai->parent), &power, &ground) != 0)
				{
					if (net_debug != 0)
						ttyputmsg(_("Network: must recheck instances of %s"), describenodeproto(ai->parent));
					net_recursivelymarkabove(ai->parent);
					return;
				}
			}
			for(pe = ai->end[i].nodeinst->firstportexpinst; pe != NOPORTEXPINST;
				pe = pe->nextportexpinst)
			{
				if (net_pconnect(pe->exportproto) != 0)
				{
					if (net_debug != 0)
						ttyputmsg(_("Network: must recheck instances of %s"), describenodeproto(ai->parent));
					net_recursivelymarkabove(ai->parent);
					return;
				}
			}
		}
	} else if ((type&VTYPE) == VPORTPROTO)
	{
		pp = (PORTPROTO *)addr;
		if (net_debug != 0) ttyputmsg(_("Network: port %s killed"), pp->protoname);

		/* stop now if the entire facet will be renumbered */
		if (net_globalwork != 0 && (pp->parent->userbits&REDOFACETNET) != 0) return;

		/* remove network pointers to this port */
		(void)net_takeportfromnet(pp);

		/* renumber all arcs connected to the node that this port used */
		power = ground = NONETWORK;
		for(pi = pp->subnodeinst->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if ((pi->conarcinst->userbits&DEADA) != 0) continue;

			/* remove any former network information */
			(void)net_takearcfromnet(pi->conarcinst);

			/* create a new network and propagate it */
			if (net_nconnect(pi->conarcinst, net_newnetwork(pp->parent), &power, &ground) != 0)
			{
				if (net_debug != 0)
					ttyputmsg(_("Network: must recheck instances of %s"), describenodeproto(pp->parent));
				net_recursivelymarkabove(pp->parent);
				return;
			}
		}
	} else if ((type&VTYPE) == VNODEPROTO)
	{
		np = (NODEPROTO *)addr;

		/* delete all network objects in this facet */
		for(net = np->firstnetwork; net != NONETWORK; net = nextnet)
		{
			nextnet = net->nextnetwork;
			net_freenetwork(net, np);
		}
		np->firstnetwork = NONETWORK;
	}
}

void net_newvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG newtype)
{
	REGISTER VARIABLE *var;
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	NETWORK *power, *ground;
	char **strings, *netname;
	REGISTER INTSML count;

	if ((newtype&VCREF) != 0) return;

	/* handle changes to aid:network.NET_connect_PandG */
	if (key == net_connect_power_groundkey && (type&VTYPE) == VAID && (AIDENTRY *)addr == net_aid)
	{
		var = getvalkey(addr, type, VINTEGER, key);
		if (var != NOVARIABLE) net_connect_power_ground = (INTSML)var->addr;
		return;
	}

	/* handle changes to aid:network.NET_connect_common */
	if (key == net_connect_common_namekey && (type&VTYPE) == VAID && (AIDENTRY *)addr == net_aid)
	{
		var = getvalkey(addr, type, VINTEGER, key);
		if (var != NOVARIABLE) net_connect_common_name = (INTSML)var->addr;
		return;
	}

	/* handle changes to aid:network.NET_auto_name */
	if (key == net_auto_namekey && (type&VTYPE) == VAID && (AIDENTRY *)addr == net_aid)
	{
		var = getvalkey(addr, type, VINTEGER, key);
		if (var != NOVARIABLE) net_auto_name = (INTSML)var->addr;
		return;
	}

	/* handle changes to an ARCINSTs "ARC_name" variable */
	if (key != el_arc_name) return;
	if ((type&(VTYPE|VISARRAY)) != VARCINST) return;
	var = getvalkey(addr, type, VSTRING, key);
	if (var == NOVARIABLE || *((char *)var->addr) == 0) return;

	if (net_debug != 0)
		ttyputmsg(_("Network: arc name %s created"), (char *)var->addr);

	/* stop now if the entire facet will be renumbered */
	ai = (ARCINST *)addr;
	if (net_globalwork != 0 && (ai->parent->userbits&REDOFACETNET) != 0) return;

	/* check name for validity */
	count = net_evalbusname((INTSML)((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH),
		(char *)var->addr, &strings, ai, ai->parent, 1);
	if (count < 0)
	{
		ttyputerr(_("Warning (facet %s): invalid network name: %s"), describenodeproto(ai->parent),
			(char *)var->addr);
		return;
	}

	/* for busses, must redo entire facet */
	if (ai->proto == sch_busarc)
	{
		net_recursivelymarkabove(ai->parent);
		return;
	}

	/* if merging common net names, check this one */
	net = NONETWORK;
	if (net_connect_common_name != 0 || ai->parent->cellview == el_schematicview ||
		ai->proto->tech == sch_tech)
	{
		/* reconstruct network name and see if it exists in this facet */
		(void)allocstring(&netname, net_buildnetname(count, strings), el_tempcluster);
		net = getnetwork(netname, ai->parent);
		efree(netname);
	}
	if (net == NONETWORK) net = net_newnetwork(ai->parent); else
	{
		if (net_current_source == us_aid)
			ttyputmsg(_("Network %s extended to this arc"), describenetwork(net));
	}

	/* propagate the network through this facet */
	power = ground = NONETWORK;
	if (net_nconnect(ai, net, &power, &ground) != 0)
	{
		if (net_debug != 0) ttyputmsg(_("Network: must recheck instances of %s"),
			describenodeproto(ai->parent));
		net_recursivelymarkabove(ai->parent);
	}
}

void net_killvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG oldaddr, INTBIG oldtype,
	INTBIG olddescript)
{
	REGISTER ARCINST *ai;
	NETWORK *power, *ground;

	/* only interested in the string variable "ARC_name" on ARCINSTs */
	if (key != el_arc_name) return;
	if ((type&(VTYPE|VISARRAY)) != VARCINST) return;
	if ((oldtype&(VTYPE|VISARRAY|VCREF)) != VSTRING) return;
	if (*((char *)oldaddr) == 0) return;

	if (net_debug != 0)
		ttyputmsg(_("Network: arc name %s deleted"), (char *)oldaddr);

	/* get the arc being unnamed */
	ai = (ARCINST *)addr;
	if (ai->network == NONETWORK) return;

	/* stop now if the entire facet will be renumbered */
	if (net_globalwork != 0 && (ai->parent->userbits&REDOFACETNET) != 0) return;

	/* no network functions on nonelectrical arcs */
	if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) return;

	/* if this network is at all complex, must renumber facet */
	if (ai->network->namecount != 1 || ai->network->arccount != 1 ||
		ai->network->portcount != 0 || ai->network->buslinkcount != 0)
	{
		net_recursivelymarkabove(ai->parent);
		return;
	}

	/* remove any former network information */
	(void)net_takearcfromnet(ai);

	/* create a new network and propagate it */
	power = ground = NONETWORK;
	if (net_nconnect(ai, net_newnetwork(ai->parent), &power, &ground) != 0)
	{
		if (net_debug != 0) ttyputmsg(_("Network: must recheck instances of %s"),
			describenodeproto(ai->parent));
		net_recursivelymarkabove(ai->parent);
	}
}

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

	var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_connect_power_groundkey);
	if (var != NOVARIABLE) net_connect_power_ground = (INTSML)var->addr;

	var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_connect_common_namekey);
	if (var != NOVARIABLE) net_connect_common_name = (INTSML)var->addr;

	var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_auto_namekey);
	if (var != NOVARIABLE) net_auto_name = (INTSML)var->addr;
}

/*********************** RECURSIVE NETWORK TRACING ***********************/

/*
 * routine to trace all electrical paths starting at the given arcinst "ai"
 * and set the network to "newnetwork".  The pointers "power" and "ground"
 * are the current power and ground networks in the facet.  Returns nonzero
 * if the facet should be rechecked (because ports were modified).  Traces
 * contents ports in case of iconic connections.
 */
INTSML net_nconnect(ARCINST *ai, NETWORK *newnetwork, NETWORK **power, NETWORK **ground)
{
	REGISTER ARCINST *oai;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi, *spo;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *cpp, *copp;
	REGISTER NODEPROTO *cnp;
	REGISTER VARIABLE *var;
	REGISTER char *pt, *base1, *base2;
	INTSML special;
	REGISTER INTSML i, recheck, ret, tempname;
	REGISTER INTBIG fun, width, buswidth, len1, len2;

	/* ignore arcs with no signals on them */
	if (((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH) == APNONELEC) return(0);

	/* see if this arc is already on the network */
	if (ai->network == newnetwork) return(0);

	/* ignore if two busses have different width */
	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
	if (ai->proto == sch_busarc && newnetwork->signals > 1 &&
		((ai->network != NONETWORK && ai->network->signals > 1) || var != NOVARIABLE))
	{
		/* bus network */
		if (var != NOVARIABLE) width = net_buswidth((char *)var->addr); else
			width = ai->network->signals;
		if (width != newnetwork->signals)
		{
			/* different width networks meet: see if it is sensible */
			base1 = describenetwork(newnetwork);
			if (var != NOVARIABLE) base2 = (char *)var->addr; else
				base2 = describenetwork(ai->network);
			for(len1 = 0; base1[len1] != 0; len1++) if (base1[len1] == '[') break;
			for(len2 = 0; base2[len2] != 0; len2++) if (base2[len2] == '[') break;
			if (len1 != len2 || namesamen(base1, base2, len1) != 0)
			{
				ttyputmsg(_("Warning (facet %s): networks '%s' and '%s' connected with different lengths"),
					describenodeproto(ai->parent), base1, base2);
			}
			return(1);
		}
	}

	/* presume that recheck of facet is not necessary */
	recheck = 0;

	/* free previous network information */
	(void)net_takearcfromnet(ai);

	/* add this arc to the network */
	net_putarconnet(ai, newnetwork);

	/* if this arc has a name, establish it */
	if (var != NOVARIABLE && *((char *)var->addr) != 0)
	{
		/* add this arc name to the network */
		if (net_debug != 0)
			ttyputmsg(_("Adding name %s to network %s"), (char *)var->addr, describenetwork(newnetwork));
		if ((var->type&VDISPLAY) == 0) tempname = 1; else tempname = 0;
		ret = net_addnametonet((char *)var->addr, tempname, newnetwork, ai, NOPORTPROTO,
			ai->parent);
		if (ret > 0) recheck++;
		if (ret >= 0) net_putarclinkonnet(newnetwork, ai);
	} else
	{
#if 1
		/* see if this arc touches bus-width ports on nodes */
		buswidth = 1;
		if (ai->end[0].nodeinst->proto->primindex == 0)
		{
			for (pt = ai->end[0].portarcinst->proto->protoname; *pt != 0; pt++)
				if (*pt == '[') break;
			if (*pt != 0)
			{
				buswidth = net_buswidth(ai->end[0].portarcinst->proto->protoname);
			}
		}
		if (ai->end[1].nodeinst->proto->primindex == 0)
		{
			for (pt = ai->end[1].portarcinst->proto->protoname; *pt != 0; pt++)
				if (*pt == '[') break;
			if (*pt != 0)
			{
				width = net_buswidth(ai->end[1].portarcinst->proto->protoname);
				if (buswidth == 1) buswidth = width; else
					if (buswidth != width) buswidth = 1;
			}
		}
		if (buswidth > 1 && newnetwork->signals == 1)
		{
			/* unnamed bus: generate individual unnamed signals */
			newnetwork->networklist = (NETWORK **)emalloc(((sizeof (NETWORK *)) * buswidth),
				newnetwork->parent->cell->cluster);
			if (newnetwork->networklist != 0)
			{
				newnetwork->signals = (INTSML)buswidth;
				net_ensuretempbusname(newnetwork);
				for(i=0; i<buswidth; i++)
				{
					newnetwork->networklist[i] = net_newnetwork(newnetwork->parent);
					newnetwork->networklist[i]->buslinkcount++;
				}
			}
			recheck++;
		}
#endif
	}

	/* initialize the special information about this network */
	special = 0;

	/* recursively set all arcs and nodes touching this */
	for(i=0; i<2; i++)
	{
		/* establish the contents relationships */
		ni = ai->end[i].nodeinst;
		if (ni == NONODEINST) continue;
		cnp = contentsview(ni->proto);
		if (cnp == NONODEPROTO) cnp = ni->proto;
		cpp = equivalentport(ni->proto, ai->end[i].portarcinst->proto, cnp);
		if (cpp == NOPORTPROTO)
		{
			cpp = ai->end[i].portarcinst->proto;
			if (cpp == NOPORTPROTO) continue;
		}

		/* if this network hit a "wire connection", must reevaluate the facet */
		if (ni->proto == sch_wireconprim) recheck++;

#ifdef ELECTRICALNETS
		/* if this arc end connects to an isolated port, ignore it */
		if ((cpp->userbits&PORTISOLATED) != 0) continue;

		/* do not follow nonbus wires onto a bus pin */
		if (cnp == sch_buspinprim && ai->proto != sch_busarc) continue;

		/* if this arc end is negated, ignore its node propagation */
		if ((ai->userbits&ISNEGATED) != 0)
		{
			if ((ai->userbits&REVERSEEND) == 0)
			{
				if (i == 0) continue;
			} else
			{
				if (i == 1) continue;
			}
		}
#endif

		/* check out the node on the arc */
		fun = (cnp->userbits&NFUNCTION)>>NFUNCTIONSH;
		if (fun == NPCONPOWER) net_setspecial(&special, NETPOWER, ai->parent); else
			if (fun == NPCONGROUND) net_setspecial(&special, NETGROUND, ai->parent);

		if (net_connect_power_ground != 0)
		{
			/* see if subport on the node is power or ground */
			if (portispower(cpp) != 0) net_setspecial(&special, NETPOWER, ai->parent); else
				if (portisground(cpp) != 0) net_setspecial(&special, NETGROUND, ai->parent);
		}

		/* look at all other arcs connected to the node on this end of the arc */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			/* select an arcinst that has not been examined */
			oai = pi->conarcinst;
			if (oai == ai) continue;
			if (oai->network == newnetwork) continue;

			/* establish the contents connection for this portarcinst */
			copp = equivalentport(ni->proto, pi->proto, cnp);
			if (copp == NOPORTPROTO) copp = pi->proto;

			/* see if the two ports connect electrically */
			if (cpp->network != copp->network) continue;

			/* do not follow nonbus wires from a bus pin */
			if (cnp == sch_buspinprim && oai->proto != sch_busarc)
				continue;

#ifdef ELECTRICALNETS
			/* if this arc end is negated, ignore its node propagation */
			if ((oai->userbits&ISNEGATED) != 0)
			{
				if ((oai->userbits&REVERSEEND) == 0)
				{
					if (oai->end[0].portarcinst == pi) continue;
				} else
				{
					if (oai->end[1].portarcinst == pi) continue;
				}
			}
#endif

			/* recurse on the nodes of this arcinst */
			if (net_nconnect(oai, newnetwork, power, ground) != 0) recheck++;
		}

		/* set the port information for any exports */
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			if ((cpp = equivalentport(ni->proto, pe->proto, cnp)) == NOPORTPROTO)
				cpp = pe->proto;

			/* find the connectivity index for this export */
			for(spo = ni->firstportarcinst; spo != NOPORTARCINST; spo = spo->nextportarcinst)
			{
				if ((copp = equivalentport(ni->proto, spo->proto, cnp)) == NOPORTPROTO)
					copp = spo->proto;

				if (cpp->network != copp->network) continue;
				if (spo->conarcinst->network != newnetwork) continue;

				/* add this port name to the network */
				if (net_addnametonet(pe->exportproto->protoname, 0, newnetwork,
					spo->conarcinst, NOPORTPROTO, ni->parent) > 0) recheck++;

				/* network extends to export, set it */
				(void)net_takeportfromnet(pe->exportproto);
				net_putportonnet(pe->exportproto, newnetwork);

				if (net_connect_power_ground != 0)
				{
					/* check for power or ground ports */
					if (portispower(pe->exportproto) != 0)
					{
						net_setspecial(&special, NETPOWER, ai->parent);
					} else if (portisground(pe->exportproto) != 0)
					{
						net_setspecial(&special, NETGROUND, ai->parent);
					}
				}
				recheck++;
				break;
			}
		}

		/* combine power and ground nets */
		if (special == NETPOWER)
		{
			if (*power != NONETWORK)
			{
				if (net_mergenet(*power, newnetwork) != 0) recheck++;
			}
			*power = newnetwork;
		}
		if (special == NETGROUND)
		{
			if (*ground != NONETWORK)
			{
				if (net_mergenet(*ground, newnetwork) != 0) recheck++;
			}
			*ground = newnetwork;
		}
	}
	if (ai->proto != sch_busarc) for (i = 0; i < 2; i++)
	{
		/* check that connection between wire and busses is valid */
		ni = ai->end[i].nodeinst;
		if (ni == NONODEINST) continue;
		if (ni->proto == sch_buspinprim)
		{
			/* check to ensure that the bus has this network on it */
			net_checkvalidconnection(ni, ai);
		}
	}
	return(recheck);
}

void net_setspecial(INTSML *special, INTSML newvalue, NODEPROTO *facet)
{
	if (*special == 0)
	{
		*special = newvalue;
		return;
	}
	if (*special == newvalue) return;

	ttyputerr(_("Warning (facet %s): power and ground are connected"),
		describenodeproto(facet));
}

/*
 * routine to determine the network number of port "pp".  Returns nonzero
 * if the network information has changed.
 */
INTSML net_pconnect(PORTPROTO *pp)
{
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER NETWORK *oldnet, *net;
	NETWORK *power, *ground;
	REGISTER INTSML ret;
	REGISTER INTBIG width;

	/* throw away any existing network information */
	oldnet = pp->network;
	if (oldnet != NONETWORK) (void)net_takeportfromnet(pp);

#ifdef ELECTRICALNETS
	/* if port comes from an isolated subport, give it new number */
	if ((pp->subportproto->userbits&PORTISOLATED) != 0)
	{
		net = net_newnetwork(pp->parent);
		if (net_debug != 0)
			ttyputmsg(_("Network: creating new network for isolated subport %s"), pp->protoname);
		(void)net_addnametonet(pp->protoname, 0, net, NOARCINST, pp, pp->parent);
		net_putportonnet(pp, net);
		return(1);
	}
#endif

	/* next see if that port connects to an arc */
	width = net_buswidth(pp->protoname);
	for(pi = pp->subnodeinst->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->proto->network == pp->subportproto->network)
	{
		/* do not follow bus pins onto non-bus arcs */
		if (pp->subnodeinst->proto == sch_buspinprim && pi->conarcinst->proto != sch_busarc)
			continue;

		net = pi->conarcinst->network;
		if (width > 1) net = NONETWORK;
		if (net == NONETWORK)
		{
			net = net_newnetwork(pp->parent);
			if (net_debug != 0)
				ttyputmsg(_("Network: creating new network for %s (on an arc with no net)"),
					pp->protoname);
		} else if (net_debug != 0)
			ttyputmsg(_("Network: adding port %s to net %s"), pp->protoname, describenetwork(net));
		(void)net_addnametonet(pp->protoname, 0, net, pi->conarcinst, pp, pp->parent);
		net_putportonnet(pp, net);
		if (pp->network != oldnet) ret = 1; else ret = 0;;
		power = ground = NONETWORK;
		if (net_nconnect(pi->conarcinst, net, &power, &ground) != 0) ret = 1;
		return(ret);
	}

	/* finally see if that port connects to another export */
	for(pe = pp->subnodeinst->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
	{
		if (pe->exportproto == pp) continue;
		if (pe->proto->network != pp->subportproto->network) continue;
		net = pe->exportproto->network;
		if (net == NONETWORK)
		{
			net = net_newnetwork(pp->parent);
			if (net_debug != 0)
				ttyputmsg(_("Network: creating new network for %s (on a net with an unnetted port)"),
					pp->protoname);
		} else if (net_debug != 0)
			ttyputmsg(_("Network: adding port %s to export %s"),
				pp->protoname, pe->exportproto->protoname);
		net_putportonnet(pp, net);
		(void)net_addnametonet(pp->protoname, 0, net, NOARCINST, pp, pp->parent);
		return(pp->network != oldnet);
	}

	/* give up and assign a new net number */
	net = net_newnetwork(pp->parent);
	if (net_debug != 0)
		ttyputmsg(_("Network: creating new network for %s (gave up on all other tests)"),
			pp->protoname);
	(void)net_addnametonet(pp->protoname, 0, net, NOARCINST, pp, pp->parent);
	net_putportonnet(pp, net);
	return(1);
}

/*
 * routine to add the name "name" to network "newnetwork", assuming that it
 * is connected to arc "ai" (which may be NOARCINST if it is not connected)
 * or port "pp" (which may be NOPORTPROTO if it is not an export) in facet
 * "facet".  If this addition causes a merging of networks, the routine
 * returns a positive value.  If the network name is invalid, the routine
 * returns a negative value.
 */
INTSML net_addnametonet(char *name, INTSML tempname, NETWORK *newnetwork, ARCINST *ai,
	PORTPROTO *pp, NODEPROTO *facet)
{
	REGISTER NETWORK *net;
	INTSML len, wid, busexists;
	REGISTER INTSML count, recheck, i;
	REGISTER INTBIG bits;
	char **strings, *netname;

	/* special case if this is a temporary name */
	if (tempname != 0)
	{
		/* if the net already has a real name, stop now */
		if (newnetwork->namecount > 0) return(-1);

		/* mark the network as having a temporary name */
		newnetwork->tempname = 1;
	} else
	{
		/* this is a real name: if the net has a temporary one, kill it */
		if (newnetwork->tempname != 0 && newnetwork->namecount == 1)
		{
			efree(newnetwork->netname);
			newnetwork->namecount = 0;
			newnetwork->tempname = 0;
		}
	}

	/* see if the network already has this name */
	netname = newnetwork->netname;
	for(i=0; i < newnetwork->namecount; i++)
	{
		if (namesame(name, netname) == 0) return(0);
		len = strlen(netname) + 1;
		netname += len;
	}

	/* see if the net can be a bus */
	bits = APUNKNOWN;
	if (pp != NOPORTPROTO)
	{
		/* determine whether this port can be a bus */
		for(i=0; pp->connects[i] != NOARCPROTO; i++)
		{
			bits = (pp->connects[i]->userbits&AFUNCTION) >> AFUNCTIONSH;
			if (bits == APBUS) break;
		}
	} else if (ai != NOARCINST)
	{
		/* determine whether this arc can be a bus */
		bits = (ai->proto->userbits&AFUNCTION) >> AFUNCTIONSH;
	}

	/* check net name for validity */
	recheck = 0;
	count = net_evalbusname(bits, name, &strings, ai, facet, 1);
	if (count < 0)
	{
		ttyputerr(_("Warning (facet %s): network name '%s' is invalid"), describenodeproto(facet), name);
		return(-1);
	}

	/* check width for consistency */
	busexists = 0;
	if (newnetwork->namecount > 0)
	{
		wid = net_buswidth(newnetwork->netname);
		if (count != wid)
		{
			ttyputerr(_("Warning (facet %s): networks %s and %s cannot connect (different width)"),
				describenodeproto(facet), name, newnetwork->netname);
			return(-1);
		}
	}
	if (count > 1 && count == newnetwork->signals)
	{
		/* add new names of signals in the bus to those already on the network */
		for (i=0; i<count; i++)
		{
			net_checknetnamearity(strings[i], facet);
			(void)allocstring(&netname, strings[i], el_tempcluster);
			net = NONETWORK;
			if (net_connect_common_name != 0 || facet->cellview == el_schematicview ||
				(ai != NOARCINST && ai->proto->tech == sch_tech) ||
					(pp != NOPORTPROTO && pp->parent->primindex != 0 && pp->parent->tech == sch_tech))
						net = getnetwork(netname, facet);
			(void)net_namenet(netname, newnetwork->networklist[i]);
			if (net != NONETWORK && net != newnetwork->networklist[i])
				if (net_mergenet(net, newnetwork->networklist[i]) != 0) recheck++;
		}
		busexists++;
	}

	/* see if this net name is in use */
	(void)allocstring(&netname, net_buildnetname(count, strings), el_tempcluster);
	net = NONETWORK;
	if (net_connect_common_name != 0 || facet->cellview == el_schematicview ||
		(ai != NOARCINST && ai->proto->tech == sch_tech) ||
			(pp != NOPORTPROTO && pp->parent->primindex != 0 && pp->parent->tech == sch_tech))
				net = getnetwork(netname, facet);

	/* for busses, name the network and install appropriate bus links */
	if (count > 1)
	{
		if (net_namenet(netname, newnetwork) == 0)
			if (busexists == 0)
				net_ensurebusses(newnetwork, count, strings);
	} else
	{
		net_checknetnamearity(strings[0], facet);
		(void)net_namenet(strings[0], newnetwork);
	}

	/* merge the new network with an old one of the same name, if it exists...bgw */
	if (net != NONETWORK && net != newnetwork)
		if (net_mergenet(net, newnetwork) != 0) recheck++;

	efree(netname);
	return(recheck);
}

/*
 * recursive routine to re-do connectivity of entire library starting
 * at highest level facet.  Current point for re-doing connectivity is "np".
 * This routine is called when the node of a port has changed and all
 * instances of the facet need to be renumbered.  The "REDOFACETNET" flag on
 * facets prevents them from being checked multiple times.
 */
void net_recursivelymarkabove(NODEPROTO *np)
{
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *icnp;

	net_startglobalwork(np->cell->lib);

	/* skip this if already done */
	if ((np->userbits&REDOFACETNET) != 0) return;

	/* mark this facet to be renumbered */
	np->userbits |= REDOFACETNET;

	/* now do all facets higher than this, allowing for iconic connections */
	for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
	{
		if ((ni->parent->userbits&REDOFACETNET) == 0)
			net_recursivelymarkabove(ni->parent);

		/* mark above icon also */
		if ((icnp = iconview(ni->parent)) == NONODEPROTO) return;
		if ((ni->parent->userbits&REDOFACETNET) == 0)
			net_recursivelymarkabove(icnp);
	}
}

/*********************** NETLIST MAINTENANCE ROUTINES ***********************/

/*
 * routine to add network "net" to arc "ai"
 */
void net_putarconnet(ARCINST *ai, NETWORK *net)
{
	if (net == NONETWORK) return;
	ai->network = net;
	net->refcount++;
}

/*
 * routine to remove the network link from arc "ai".  Returns nonzero if
 * the network has been deleted
 */
INTSML net_takearcfromnet(ARCINST *ai)
{
	REGISTER NETWORK *net;

	net = ai->network;
	if (net == NONETWORK) return(0);
	ai->network = NONETWORK;
	net_takearclinkfromnet(ai, net);
	net->refcount--;

	/* delete the network if all counts are zero */
	if (net->portcount <= 0 && net->refcount <= 0 && net->arccount <= 0 && net->buslinkcount <= 0)
	{
		net_killnetwork(net, ai->parent);
		return(1);
	}
	return(0);
}

/*
 * routine to add network "net" to port "pp"
 */
void net_putportonnet(PORTPROTO *pp, NETWORK *net)
{
	if (net == NONETWORK) return;
	pp->network = net;
	net->portcount++;
}

/*
 * routine to remove the network link from port "pp".  Returns nonzero
 * if the network has been deleted
 */
INTSML net_takeportfromnet(PORTPROTO *pp)
{
	REGISTER NETWORK *net;

	net = pp->network;
	if (net == NONETWORK) return(0);
	pp->network = NONETWORK;
	net->portcount--;

	/* delete the network if all counts are zero */
	if (net->portcount <= 0 && net->refcount <= 0 && net->arccount <= 0 && net->buslinkcount <= 0)
	{
		net_killnetwork(net, pp->parent);
		return(1);
	}
	return(0);
}

/*
 * routine to add arc "ai" to the list of named arcs on network "net".
 */
void net_putarclinkonnet(NETWORK *net, ARCINST *ai)
{
	REGISTER ARCINST **arclist;
	REGISTER INTSML i;

	if (net == NONETWORK) return;

	/* if there are no arcs on the net, add this */
	if (net->arccount == 0)
	{
		net->arcaddr = (INTBIG)ai;
		net->arccount = 1;
	} else
	{
		/* check that the arc isn't already on the list */
		if (net->arccount == 1)
		{
			if ((ARCINST *)net->arcaddr == ai) return;
		} else
		{
			for(i=0; i<net->arccount; i++)
				if (((ARCINST **)net->arcaddr)[i] == ai) return;
		}

		/* make space for the array of arc pointers */
		arclist = (ARCINST **)emalloc(((sizeof (ARCINST *)) * (net->arccount + 1)),
			ai->parent->cell->cluster);
		if (arclist == 0) return;

		/* load the array */
		if (net->arccount == 1) arclist[0] = (ARCINST *)net->arcaddr; else
		{
			for(i=0; i<net->arccount; i++)
				arclist[i] = ((ARCINST **)net->arcaddr)[i];
			efree((char *)net->arcaddr);
		}

		/* add this arc and place the array on the net */
		arclist[net->arccount] = ai;
		net->arcaddr = (INTBIG)arclist;
		net->arccount++;
	}
}

/*
 * routine to remove mention of arc "ai" on network "net"
 */
void net_takearclinkfromnet(ARCINST *ai, NETWORK *net)
{
	REGISTER INTSML i, j;

	if (net == NONETWORK) return;

	/* if there is one arc, check that */
	if (net->arccount == 1)
	{
		if ((ARCINST *)net->arcaddr == ai) net->arccount--;
	} else
	{
		for(i=0; i<net->arccount; i++)
			if (((ARCINST **)net->arcaddr)[i] == ai)
		{
			for(j=i+1; j < net->arccount; j++)
				((ARCINST **)net->arcaddr)[j-1] = ((ARCINST **)net->arcaddr)[j];
			net->arccount--;
			if (net->arccount == 1)
			{
				ai = ((ARCINST **)net->arcaddr)[0];
				efree((char *)net->arcaddr);
				net->arcaddr = (INTBIG)ai;
			}
			break;
		}
	}
}

/*
 * routine to ensure that there are networks with the names of each
 * individual bus member described by the "count" signals in "strings".
 * They are all part of the network "net"
 */
void net_ensurebusses(NETWORK *net, INTSML count, char **strings)
{
	REGISTER INTSML i;
	REGISTER NETWORK *singlenet;
	char *subnetname;

#if 1
	if (net->signals == count)
	{
		/* already have signals in place: name them */
		for(i=0; i<count; i++)
		{
			net_checknetnamearity(strings[i], net->parent);
			singlenet = getnetwork(strings[i], net->parent);
			if (singlenet == NONETWORK)
			{
				singlenet = net->networklist[i];
				if (singlenet->tempname != 0 && singlenet->namecount > 0)
				{
					efree(singlenet->netname);
					singlenet->namecount = 0;
					singlenet->tempname = 0;
				}
				(void)net_namenet(strings[i], net->networklist[i]);
			}
		}
		return;
	}
#endif

	net->signals = count;
	net_ensuretempbusname(net);
	net->networklist = (NETWORK **)emalloc(((sizeof (NETWORK *)) * count),
		net->parent->cell->cluster);
	if (net->networklist == 0) return;

	/* generate network names for each array member */
	for(i=0; i<count; i++)
	{
		net_checknetnamearity(strings[i], net->parent);
		(void)allocstring(&subnetname, strings[i], el_tempcluster);
		net->networklist[i] = getnetwork(subnetname, net->parent);
		efree(subnetname);
		if (net->networklist[i] == NONETWORK)
		{
			net->networklist[i] = net_newnetwork(net->parent);
			(void)net_namenet(strings[i], net->networklist[i]);
		}
		net->networklist[i]->buslinkcount++;
	}
}

/*
 * routine to give network "net" the name "name".  Returns nonzero if the network
 * already has the name.
 */
INTSML net_namenet(char *name, NETWORK *net)
{
	REGISTER char *newname, *pt;
	REGISTER INTSML len, totallen, i;

	if (net->namecount == 0)
	{
		/* network is unnamed: name it */
		net->namecount++;
		(void)allocstring(&net->netname, name, net->parent->cell->cluster);
		return(0);
	}

	/* see if the network already has this name */
	pt = net->netname;
	totallen = 0;
	for(i=0; i<net->namecount; i++)
	{
		if (namesame(name, pt) == 0) return(1);
		len = strlen(pt) + 1;
		totallen += len;
		pt += len;
	}

	/* add the new name to the network name */
	newname = (char *)emalloc((totallen + strlen(name) + 1), net->parent->cell->cluster);
	if (newname == 0) return(0);

	for(i=0; i<totallen; i++) newname[i] = net->netname[i];
	(void)strcpy(&newname[totallen], name);
	efree(net->netname);
	net->netname = newname;
	net->namecount++;
	return(0);
}

/*
 * routine to get a new network object from facet "facet" and return the
 * address.  Returns NONETWORK on error.
 */
NETWORK *net_newnetwork(NODEPROTO *facet)
{
	REGISTER NETWORK *net, *onet;
	char line[40];
	static INTBIG tempnetname = 1;

	if (facet->cell->freenetwork != NONETWORK)
	{
		/* take from list of unused networks objects in the cell */
		net = facet->cell->freenetwork;
		facet->cell->freenetwork = net->nextnetwork;
	} else
	{
		/* allocate a new network object */
		net = (NETWORK *)emalloc(sizeof(NETWORK), facet->cell->cluster);
		if (net == 0) return(NONETWORK);
	}
	if (net_auto_name != 0)
	{
		for(;;)
		{
			sprintf(line, "NET%ld", tempnetname++);
			for(onet = facet->firstnetwork; onet != NONETWORK; onet = onet->nextnetwork)
			{
				if (onet->namecount == 0) continue;
				if (namesame(line, onet->netname) == 0) break;
			}
			if (onet == NONETWORK) break;
		}
		allocstring(&net->netname, line, net_aid->cluster);
		net->namecount = 1;
		net->tempname = 1;
	} else
	{
		net->netname = NOSTRING;
		net->namecount = 0;
		net->tempname = 0;
	}
	net->arccount = net->refcount = net->portcount = net->buslinkcount = 0;
	net->parent = facet;
	net->signals = 1;
	net->nextnetwork = NONETWORK;
	net->lastnetwork = NONETWORK;
	net->firstvar = NOVARIABLE;
	net->numvar = 0;

	/* link the network into the facet */
	net->nextnetwork = facet->firstnetwork;
	net->lastnetwork = NONETWORK;
	if (facet->firstnetwork != NONETWORK) facet->firstnetwork->lastnetwork = net;
	facet->firstnetwork = net;
	return(net);
}

/*
 * routine to return network "net" to facet "facet"
 */
void net_killnetwork(NETWORK *net, NODEPROTO *facet)
{
	if (net->lastnetwork == NONETWORK) facet->firstnetwork = net->nextnetwork; else
		net->lastnetwork->nextnetwork = net->nextnetwork;
	if (net->nextnetwork != NONETWORK)
		net->nextnetwork->lastnetwork = net->lastnetwork;

	/* routine to remove bus links */
	net_removebuslinks(net);

	/* delete the network */
	net_freenetwork(net, facet);
}

/*
 * routine to remove bus links to network "net"
 */
void net_removebuslinks(NETWORK *net)
{
	REGISTER NETWORK *subnet;
	REGISTER INTSML i;

	if (net->signals <= 1) return;

	for(i=0; i<net->signals; i++)
	{
		subnet = net->networklist[i];
		if (subnet == NONETWORK) continue;
		subnet->buslinkcount--;
		if (subnet->portcount <= 0 && subnet->refcount <= 0 &&
			subnet->arccount <= 0 && subnet->buslinkcount <= 0)
				net_killnetwork(subnet, subnet->parent);
	}
	efree((char *)net->networklist);
	net->signals = 1;
}

void net_freenetwork(NETWORK *net, NODEPROTO *facet)
{
	if (net->namecount != 0) efree(net->netname);
	net->namecount = 0;
	net->netname = NOSTRING;
	if (net->signals > 1) efree((char *)net->networklist);
	net->signals = 1;
	if (net->numvar != 0) db_freevars(&net->firstvar, &net->numvar);
	if (net->arccount > 1) efree((char *)net->arcaddr);

	/* insert in linked list of free networks in the cell */
	net->nextnetwork = facet->cell->freenetwork;
	facet->cell->freenetwork = net;
}

/*
 * routine to replace all occurrences of "oldnet" with "newnet".  Returns
 * nonzero if ports were changed.  NOTE: merged busses must have same width
 * and this routine cannot handle that error condition properly.  Shouldn't
 * ever happen.
 */
INTSML net_mergenet(NETWORK *oldnet, NETWORK *newnet)
{
	REGISTER char *pt;
	REGISTER INTSML i, portschanged, ret;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER NETWORK *net;

	/* merging is easy if the nets are already the same */
	if (oldnet == newnet) return(0);

	/* cannot merge busses of dissimilar width (system error) */
	if (oldnet->signals != newnet->signals)
	{
		ttyputerr(_("Warning (facet %s): cannot connect net %s (%d wide) and net %s (%d wide)"),
			describenodeproto(newnet->parent), describenetwork(oldnet), oldnet->signals,
				describenetwork(newnet), newnet->signals);
		return(1);
	}

#if 1
	/* if one of the nets has a temporary name, drop it */
	if (newnet->tempname != 0 && newnet->namecount > 0 && oldnet->namecount > 0)
	{
		/* new network has temporary name, old has real name */
		efree(newnet->netname);
		newnet->namecount = 0;
		newnet->tempname = 0;
	}
	if (oldnet->tempname == 0 || newnet->namecount == 0)
	{
		/* add the names of old network on the new one */
		pt = oldnet->netname;
		for(i=0; i<oldnet->namecount; i++)
		{
			(void)net_namenet(pt, newnet);
			pt += strlen(pt) + 1;
		}
	}
#endif

	/* if there are bus links on the old network, switch it to the new one */
	if (oldnet->buslinkcount != 0)
	{
		for(net = oldnet->parent->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			if (net->signals <= 1) continue;
			for(i=0; i<net->signals; i++)
			{
				if (net->networklist[i] != oldnet) continue;
				net->networklist[i] = newnet;
				newnet->buslinkcount++;
				oldnet->buslinkcount--;

				/* delete the network if all counts are zero */
				if (oldnet->portcount <= 0 && oldnet->refcount <= 0 &&
					oldnet->arccount <= 0 && oldnet->buslinkcount <= 0)
				{
					net_killnetwork(oldnet, oldnet->parent);
					return(0);
				}
			}
			if (oldnet->buslinkcount == 0) break;
		}
	}

	/* place arc links on new network */
	if (oldnet->arccount == 1)
		net_putarclinkonnet(newnet, (ARCINST *)oldnet->arcaddr); else
			for(i=0; i<oldnet->arccount; i++)
				net_putarclinkonnet(newnet, ((ARCINST **)oldnet->arcaddr)[i]);

	/* replace arc references to the old network with the new one */
	for(ai = oldnet->parent->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network == oldnet)
		{
			ret = net_takearcfromnet(ai);
			net_putarconnet(ai, newnet);
			if (ret != 0) return(0);
		}
	}

	/* replace port references to the old network with the new one */
	portschanged = 0;
	for(pp = oldnet->parent->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->network != oldnet) continue;
		ret = net_takeportfromnet(pp);
		net_putportonnet(pp, newnet);
		if (ret != 0) return(1);
		portschanged++;
	}
	return(portschanged);
}

/*
 * routine to initialize global evaluation
 */
void net_startglobalwork(LIBRARY *lib)
{
	REGISTER NODEPROTO *np;
	REGISTER LIBRARY *olib;

	/* if this is not the first request for global evaluation, quit */
	if (net_globalwork == 0)
	{
		/* clear all library flags */
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			olib->userbits &= ~REDOFACETLIB;
		net_globalwork++;
	}

	/* if this library hasn't been started, do so */
	if ((lib->userbits&REDOFACETLIB) == 0)
	{
		lib->userbits |= REDOFACETLIB;

		/* clear all flags in this library */
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->userbits &= ~REDOFACETNET;
	}
}

/*
 * rename network "oldname" to "newname" in facet "np"
 */
void net_renamenet(char *oldname, char *newname, NODEPROTO *np)
{
	REGISTER char *pt, *name;
	REGISTER INTSML funct, i, k, count, found;
	REGISTER INTBIG descript;
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	REGISTER VARIABLE *var;
	char **strings;

	/* check for duplicate name */
	if (namesame(oldname, newname) == 0)
	{
		ttyputmsg(_("Network name has not changed in facet %s"), describenodeproto(np));
		return;
	}

	/* validate the names */
	for(pt = oldname; *pt != 0; pt++) if (*pt == '[') break;
	if (*pt == '[')
	{
		ttyputerr(_("Must rename unqualified networks (without the '[') in facet %s"),
			describenodeproto(np));
		return;
	}
	for(pt = newname; *pt != 0; pt++) if (*pt == '[') break;
	if (*pt == '[')
	{
		ttyputerr(_("New name must be unqualified (without the '[') in facet %s"),
			describenodeproto(np));
		return;
	}

	/* make sure new name is not in use */
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		if (net->signals > 1) funct = APBUS; else funct = APUNKNOWN;
		name = net->netname;
		for(k=0; k<net->namecount; k++)
		{
			count = net_evalbusname(funct, name, &strings, NOARCINST, np, 1);
			if (count < 0) continue;
			for(i=0; i<count; i++) if (namesame(newname, strings[i]) == 0)
			{
				ttyputerr(_("Network name %s already exists in facet %s"), newname,
					describenodeproto(np));
				return;
			}
			name += strlen(name) + 1;
		}
	}

	/* substitute in all arcs */
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		/* see if the arc has a name */
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
		if (var == NOVARIABLE) continue;

		/* parse the network name */
		count = net_evalbusname((INTSML)((ai->proto->userbits&AFUNCTION)>>AFUNCTIONSH),
			(char *)var->addr, &strings, ai, np, 1);
		if (count < 0) continue;

		/* see if it mentions the old network name */
		found = 0;
		for(i=0; i<count; i++)
		{
			if (namesame(strings[i], oldname) != 0) continue;
			(void)reallocstring(&strings[i], newname, net_aid->cluster);
			found++;
		}
		if (found == 0) continue;

		/* rename the arc */
		descript = var->textdescript;
		startobjectchange((INTBIG)ai, VARCINST);
		var = setvalkey((INTBIG)ai, VARCINST, el_arc_name,
			(INTBIG)net_buildnetname(count, strings), VSTRING|VDISPLAY);
		if (var == NOVARIABLE) continue;
		modifydescript((INTBIG)ai, VARCINST, var, descript);
		endobjectchange((INTBIG)ai, VARCINST);
	}
	ttyputmsg(_("Network %s renamed to %s"), oldname, newname);
}

/*********************** NETLIST GEOMETRY ROUTINES ***********************/

/*
 * Helper routine to gather the geometry in facet "facet" and below where the network's
 * "temp1" is nonzero.  Appropriate polygons are merged.  "trans" is the transformation
 * to this point in the hierarchy.
 */
void net_propgeometry(NODEPROTO *facet, XARRAY trans)
{
	static POLYGON *poly = NOPOLYGON;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	XARRAY rot, trn, rottrn, subrot;
	REGISTER NETWORK *net;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG fun, nfun, ignorelayer, height, highest, polys, diffs, transarea;
	REGISTER INTBIG total, i, found;
	INTBIG length, width;
	char *extra;

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(4, db_cluster);

	/* include all arcs on desired networks */
	for(ai = facet->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network->temp1 == 0) continue;
		total = arcpolys(ai);
		for(i=0; i<total; i++)
		{
			shapearcpoly(ai, i, poly);
			xformpoly(poly, trans);
			mergestorepolygon(poly->layer, ai->proto->tech, poly);
		}
	}
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* see if any selected networks touch this node */
		if (ni->proto->primindex == 0)
		{
			for(net = ni->proto->firstnetwork; net != NONETWORK; net = net->nextnetwork)
				net->temp1 = 0;
		} else
		{
			for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				pp->network->temp1 = 0;
		}
		found = 0;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if (pi->conarcinst->network->temp1 == 0) continue;
			pi->proto->network->temp1 = 1;
			found = 1;
		}
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			if (pe->exportproto->network->temp1 == 0) continue;
			pe->proto->network->temp1 = 1;
			found = 1;
		}
		if (found == 0) continue;

		if (ni->proto->primindex == 0)
		{
			/* facet instance: recurse */
			makerot(ni, rot);
			maketrans(ni, trn);
			transmult(trn, rot, rottrn);
			transmult(rottrn, trans, subrot);
			net_propgeometry(ni->proto, subrot);
		} else
		{
			/* primitive: include layers that touch desired networks */
			makerot(ni, rot);
			transmult(trans, rot, subrot);
			ignorelayer = -1;
			nfun = nodefunction(ni, &extra);
			if (nfun == NPCONTACT)
			{
				/* find highest layer and ignore it */
				total = nodeEpolys(ni, 0);
				highest = -1;
				for(i=0; i<total; i++)
				{
					shapeEnodepoly(ni, i, poly);
					fun = layerfunction(ni->proto->tech, poly->layer);
					if ((fun&LFPSEUDO) != 0) continue;
					height = layerfunctionheight(fun);
					if (height > highest)
					{
						highest = height;
						ignorelayer = poly->layer;
					}
				}
			}
			polys = diffs = 0;
			total = nodeEpolys(ni, 0);
			for(i=0; i<total; i++)
			{
				shapeEnodepoly(ni, i, poly);
				if (poly->layer == ignorelayer) continue;
				fun = layerfunction(ni->proto->tech, poly->layer);
				if (poly->portproto == NOPORTPROTO) continue;
				if (poly->portproto->network->temp1 == 0) continue;
				if ((fun&LFPSEUDO) != 0) continue;
				if (layerispoly(fun) != 0) polys++;
				if ((fun&LFTYPE) == LFDIFF) diffs++;
				xformpoly(poly, subrot);
				mergestorepolygon(poly->layer, ni->proto->tech, poly);
			}
			if (nfun == NPTRANMOS || nfun == NPTRADMOS || nfun == NPTRAPMOS)
			{
				transistorsize(ni, &length, &width);
				transarea = length * width;
				if (polys > 0)
				{
					net_transistor_gate_count++;
					net_transistor_gate_area += transarea;
				}
				if (diffs > 0)
				{
					net_transistor_active_count++;
					net_transistor_active_area += transarea;
				}
			}
		}
	}
}

/*
 * Helper routine that is given merged geometry from the "network geometry" command.
 */
void net_geometrypolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count)
{
	REGISTER INTBIG per, seglen, i,  lastx, lasty, thisx, thisy;
	float area, side1, side2;
	REGISTER AREAPERIM *ap;

	/* compute the perimeter */
	per = 0;
	for(i=0; i<count; i++)
	{
		if (i == 0)
		{
			lastx = x[count-1];   lasty = y[count-1];
		} else
		{
			lastx = x[i-1];   lasty = y[i-1];
		}
		seglen = computedistance(lastx, lasty, x[i], y[i]);
		per += seglen;
	}

	/* compute the area */
	area = 0.0;
	lastx = x[0];
	lasty = y[0];
	for (i=1; i<count; i++)
	{
		thisx = x[i];
		thisy = y[i];

		/* triangulate around the polygon */
		side1 = (float)(thisx - lastx);
		side2 = (float)(lasty + thisy);
		area += (side1 * side2) / 2.0f;
		lastx = thisx;
		lasty = thisy;
	}
	side1 = (float)(x[0] - lastx);
	side2 = (float)(y[0] + lasty);
	area += (side1 * side2) / 2.0f;
	area = (float)fabs(area);

	/* find an AREAPERIM with this information */
	for(ap = net_firstareaperim; ap != NOAREAPERIM; ap = ap->nextareaperim)
		if (layer == ap->layer && tech == ap->tech) break;
	if (ap == NOAREAPERIM)
	{
		ap = (AREAPERIM *)emalloc(sizeof (AREAPERIM), net_aid->cluster);
		if (ap == 0) return;
		ap->nextareaperim = net_firstareaperim;
		net_firstareaperim = ap;
		ap->area = 0.0;
		ap->perimeter = 0;
		ap->tech = tech;
		ap->layer = layer;
	}

	/* accumulate area and perimeter */
	ap->area += area;
	ap->perimeter += per;
}

/*********************** NETLIST SEARCH ROUTINES ***********************/

/*
 * helper routine for "tellaid network list-hierarchical-ports" to print all
 * ports connected to net "net" in facet "facet", and recurse up the hierarchy
 */
void net_findportsup(NETWORK *net, NODEPROTO *facet)
{
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *pp;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTARCINST *pi;

	if (stopping(STOPREASONPORT)) return;

	/* look at every node in the facet */
	for(pp = facet->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->network != net) continue;
		if (pp->temp1 != 0) continue;
		pp->temp1 = 1;
		(void)ttyputmsg(_("  Export %s in facet %s"), pp->protoname,
			describenodeproto(facet));

		/* ascend to higher facet and continue */
		for(ni = facet->firstinst; ni != NONODEINST; ni = ni->nextinst)
		{
			/* see if there is an arc connected to this port */
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (pi->proto->network == pp->network)
			{
				net_findportsup(pi->conarcinst->network, ni->parent);
				break;
			}
			if (pi != NOPORTARCINST) continue;

			/* try further exporting of ports */
			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				if (pe->proto->network == pp->network)
			{
				net_findportsup(pe->exportproto->network, ni->parent);
				break;
			}
		}
	}
}

/*
 * helper routine for "tellaid network list-hierarchical-ports" to print all
 * ports connected to net "net" in facet "facet", and recurse down the hierarchy
 */
void net_findportsdown(NETWORK *net, NODEPROTO *facet)
{
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER NODEPROTO *cnp;
	REGISTER PORTPROTO *cpp;

	if (stopping(STOPREASONPORT)) return;

	/* look at every node in the facet */
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* only want complex nodes */
		if (ni->proto->primindex != 0) continue;

		/* look at all wires connected to the node */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			/* ignore arc if not connected to net */
			if (pi->conarcinst->network != net) continue;

			if ((cnp = contentsview(pi->proto->parent)) == NONODEPROTO)
				cnp = pi->proto->parent;
			if ((cpp = equivalentport(pi->proto->parent, pi->proto, cnp)) == NOPORTPROTO)
				cpp = pi->proto;

			if (cpp->temp1 != 0) continue;
			cpp->temp1 = 1;
			(void)ttyputmsg(_("  Export %s in facet %s"), cpp->protoname, describenodeproto(cnp));

			/* descend to lower contents facet and continue */
			net_findportsdown(cpp->network, cnp);
		}
	}
}

/*
 * routine to obtain the currently selected network.  If a facet is selected
 * and has no local ports, the returned network may be inside of the facet.
 * This will happen only if "lookinside" is nonzero.  Returns NONETWORK if
 * no network can be found.
 */
NETWORK *net_gethighlightednet(INTSML lookinside, INTSML disperror)
{
	REGISTER GEOM *geom;
	char selected[50];
	REGISTER char *netname;
	REGISTER INTBIG len, cursimtrace, line, fromchar, tochar;
	REGISTER NODEINST *ni;
	REGISTER PORTPROTO *por, *lastpp, *pp;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER EDITOR *e;

	/* no network if no window */
	if (el_curwindowpart == NOWINDOWPART) return(NONETWORK);

#if SIMAID
	/* if current window is simulation, invade structures and find highlighted net */
	if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
	{
		cursimtrace = sim_window_gethighlighttrace();
		if (cursimtrace == 0) return(NONETWORK);
		netname = sim_window_gettracename(cursimtrace);
		return(net_parsenetwork(netname));
	}
#endif

	/* if current window is text, invade structures and find highlighted net name */
	if ((el_curwindowpart->state&WINDOWTYPE) == TEXTWINDOW ||
		(el_curwindowpart->state&WINDOWTYPE) == POPTEXTWINDOW)
	{
		/* only understand selection of net name in Point-and-Click editor */
		e = el_curwindowpart->editor;
		if ((e->state&EDITORTYPE) != PACEDITOR) return(NONETWORK);

		/* copy and count the number of selected characters */
		len = 0;
		selected[0] = 0;
		for(line = e->curline; line <= e->endline; line++)
		{
			if (line == e->curline) fromchar = e->curchar; else fromchar = 0;
			if (line == e->endline) tochar = e->endchar; else
				tochar = strlen(e->textarray[line])+1;
			len += tochar - fromchar;
			if (len >= 50) return(NONETWORK);
			(void)strncat(selected, &e->textarray[line][fromchar], tochar - fromchar);
		}
		if (selected[0] == 0) return(NONETWORK);

		/* turn this string into a network name */
		return(net_parsenetwork(selected));
	}

	geom = (GEOM *)askaid(us_aid, "get-object");
	if (geom == NOGEOM)
	{
		if (disperror != 0) ttyputerr(_("Find a single object first"));
		return(NONETWORK);
	}
	if (geom->entrytype == OBJARCINST) return(geom->entryaddr.ai->network);

	ni = geom->entryaddr.ni;
	por = (PORTPROTO *)askaid(us_aid, "get-port");

	/* if no port is specified, must figure out which one */
	if (por == NOPORTPROTO)
	{
		/* if all ports are connected, use any one */
		lastpp = ni->proto->firstportproto;
		for(pp = lastpp; pp != NOPORTPROTO; pp = pp->nextportproto)
			if (pp->network != lastpp->network) break;
		if (pp == NOPORTPROTO) por = lastpp;
	}
	if (por == NOPORTPROTO)
	{
		if (disperror != 0) ttyputerr(_("Must select a port on the node"));
		return(NONETWORK);
	}

	/* look for arcs on this network */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->proto->network == por->network)
			return(pi->conarcinst->network);

	/* look for exports on this network */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		if (pe->proto->network == por->network)
			return(pe->exportproto->network);

	/* if allowed to look inside, use that network */
	if (lookinside != 0 && ni->proto->primindex == 0) return(por->network);

	if (disperror != 0) ttyputerr(_("This port is not connected to anything"));
	return(NONETWORK);
}

/*
 * routine to convert the network name "name" to a valid network.
 */
NETWORK *net_parsenetwork(char *name)
{
	REGISTER NETWORK *net, *guessnet;
	REGISTER WINDOWPART *w;
	REGISTER NODEPROTO *np;
	REGISTER char *pt;
	REGISTER INTBIG i, k, l, len, c1, c2;

	while (*name != 0 && *name <= ' ') name++;
	if (*name == 0) return(NONETWORK);

	/* handle network names encoded as "NETxxxxx" */
	if (name[0] == 'N' && name[1] == 'E' && name[2] == 'T')
	{
		guessnet = (NETWORK *)myatoi(&name[3]);

		/* validate against all possible networks */
		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		{
			if ((w->state&WINDOWTYPE) == WAVEFORMWINDOW || (w->state&WINDOWTYPE) == DISPWINDOW ||
				(w->state&WINDOWTYPE) == DISP3DWINDOW)
			{
				np = w->curnodeproto;
				if (np == NONODEPROTO) continue;

				/* does this facet have the network? */
				for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
					if (net == guessnet) return(net);
			}
		}
	}

	/* if there are dots in the name, check for HSPICE network specification */
	for(pt = name; *pt != 0; pt++) if (*pt == '.') break;
	if (*pt == '.')
	{
		net = sim_spice_networkfromname(name);
		if (net != NONETWORK) return(net);
	}

	/* see if it matches a network name in the current facet */
	np = getcurfacet();
	if (np != NONODEPROTO)
	{
		if ((np->cellview->viewstate&TEXTVIEW) != 0)
		{
			np = layoutview(np);
			if (np == NONODEPROTO) return(NONETWORK);
		}
		net = getnetwork(name, np);
		if (net != NONETWORK) return(net);

		/* check for prefix "v(" and "l(" HSPICE prefixes (see also "simwindow.c:sim_window_findtrace()")*/
		if ((name[0] == 'l' || name[0] == 'v') && name[1] == '(')
		{
			net = getnetwork(&name[2], np);
			if (net != NONETWORK) return(net);
		}

		/* if there are underscores, see if they match original names */
		for(pt = name; *pt != 0; pt++) if (*pt == '_') break;
		if (*pt != 0)
		{
			len = strlen(name);
			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			{
				pt = net->netname;
				for(k=0; k<net->namecount; k++)
				{
					l = strlen(pt);
					if (l == len)
					{
						for(i=0; i<len; i++)
						{
							c1 = name[i];   if (isupper(c1)) c1 = tolower(c1);
							c2 = pt[i];     if (isupper(c2)) c2 = tolower(c2);
							if (c1 == c2) continue;
							if (c1 == '_' && !isalnum(c2)) continue;
							break;
						}
						if (i >= len) return(net);
					}
					pt += strlen(pt) + 1;
				}
			}
		}
	}

	/* not found */
	return(NONETWORK);
}

/*
 * routine to highlight the arcs on network "net" in facet "np".
 */
void net_highlightnet(NODEPROTO *np, NETWORK *net)
{
	REGISTER ARCINST *ai;
	REGISTER INTSML i, j, first;

	(void)initinfstr();
	first = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network == NONETWORK) continue;
		if (ai->network == net)
		{
			if (first != 0) (void)addtoinfstr('\n');
			first++;
			(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
				describenodeproto(ai->parent), (INTBIG)ai->geom);
			continue;
		}

		/* show busses containing this single network */
		if (net->signals <= 1 && ai->network->signals > 1)
			for (i=0; i<ai->network->signals; i++)
				if (ai->network->networklist[i] == net)
		{
			if (first != 0) (void)addtoinfstr('\n');
			first++;
			(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
				describenodeproto(ai->parent), (INTBIG)ai->geom);
			break;
		}

		/* show all individual wires contained in this bus...bgw */
		if (net->signals > 1 && ai->network->signals == 1)
			for (i=0; i<net->signals; i++)
				if (net->networklist[i] == ai->network)
		{
			if (first != 0) (void)addtoinfstr('\n');
			first++;
			(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
				describenodeproto(ai->parent), (INTBIG)ai->geom);
			break;
		}

		/* show busses with any wires on the desired bus */
		if (net->signals > 1 && ai->network->signals > 1)
		{
			for (i=0; i<net->signals; i++)
			{
				for (j=0; j<ai->network->signals; j++)
					if (ai->network->networklist[j] == net->networklist[i]) break;
				if (j < ai->network->signals) break;
			}
			if (i < net->signals)
			{
				if (first != 0) (void)addtoinfstr('\n');
				first++;
				(void)formatinfstr("FACET=%s FROM=0%lo;-1;0",
					describenodeproto(ai->parent), (INTBIG)ai->geom);
			}
		}
	}
	(void)askaid(us_aid, "show-multiple", (INTBIG)returninfstr());
}

/*********************** BUS NAME ROUTINES ***********************/

/*
 * routine to parse the bus name "name" found on an arc that has function
 * "funct" in facet "facet".  Returns the number of signals described by the
 * name (returns -1 on error).  The pointer at "strings" is filled with an
 * array of individual bus names.
 */
INTSML net_evalbusname(INTBIG funct, char *name, char ***strings,
	ARCINST *thisai, NODEPROTO *facet, INTSML showerrors)
{
	REGISTER VARIABLE *var;
	REGISTER char *key, *cindex, *endindex, *errorstring, *busname, *pt;
	char *ptin, *savekey, numbuf[20], ***mystrings;
	INTSML count, *stringcount;
	REGISTER INTSML ch1, ch2, ch3, perfect;
	REGISTER INTBIG indexval, endindexval, origfunct, i;

	/* initialize */
	if (net_busbufstringbufferpos < 0)
	{
		net_busbufstringbufferpos = 0;
		for(i=0; i<NUMBUSSTRINGBUFFERS; i++) net_busbufstringcountarray[i] = 0;
	}
	mystrings = &net_busbufstringsarray[net_busbufstringbufferpos];
	stringcount = &net_busbufstringcountarray[net_busbufstringbufferpos];
	net_busbufstringbufferpos++;
	if (net_busbufstringbufferpos >= NUMBUSSTRINGBUFFERS)
		net_busbufstringbufferpos = 0;

	count = 0;
	ptin = name;
	perfect = 0;
	savekey = 0;
	for(;;)
	{
		key = getkeyword(&ptin, "[],");
		if (key == NOSTRING) break;
		ch1 = tonextchar(&ptin);
		if (ch1 == ']')
		{
			if (showerrors != 0)
				ttyputmsg(_("Facet %s, network '%s': unmatched ']' in name"),
					describenodeproto(facet), name);
			break;
		}
		if (ch1 == ',' || ch1 == 0)
		{
			/* add unindexed network name "key" to list */
			if (*key == 0)
			{
				if (showerrors != 0)
					ttyputmsg(_("Facet %s, network '%s': empty network name"),
						describenodeproto(facet), name);
				break;
			}
			net_addstring(key, -1, &count, stringcount, mystrings);
			if (ch1 == 0) { perfect = 1;   break; }
			continue;
		}

		/* '[' encountered: process array entries */
		if (savekey != 0) efree(savekey);
		if (*key != 0) (void)allocstring(&savekey, key, el_tempcluster); else
		{
			/* no name before the '[', look for an assumed bus name */
			busname = net_busnameofarc(thisai);
			if (showerrors != 0)
				ttyputmsg(_("Facet %s, network '%s': empty bus name, assuming '%s'"),
					describenodeproto(facet), name, busname);
			(void)allocstring(&savekey, busname, el_tempcluster);

			/* if the name begins with '[' and that name is on this arc, fix it */
			if (*name == '[' && thisai != NOARCINST)
			{
				var = getvalkey((INTBIG)thisai, VARCINST, VSTRING, el_arc_name);
				if (var != NOVARIABLE)
				{
					pt = (char *)var->addr;
					if (*pt == '[')
					{
						(void)initinfstr();
						(void)addstringtoinfstr(busname);
						(void)addstringtoinfstr(pt);
						setvalkey((INTBIG)thisai, VARCINST, el_arc_name,
							(INTBIG)returninfstr(), var->type);
					}
				}
			}
		}

		/* loop through the indexed entries */
		for(;;)
		{
			cindex = getkeyword(&ptin, ",:]");
			ch2 = tonextchar(&ptin);
			if (cindex == NOSTRING) break;
			if (*cindex == 0)
			{
				if (showerrors != 0)
					ttyputmsg(_("Facet %s, network '%s': empty network index"),
						describenodeproto(facet), name);
				break;
			}
			if (net_isanumber(cindex) != 0)
			{
				indexval = myatoi(cindex);
				if (indexval < 0)
				{
					if (showerrors != 0)
						ttyputmsg(_("Facet %s, network '%s': array indices cannot be negative"),
							describenodeproto(facet), name);
					break;
				}
			} else indexval = -1;
			if (ch2 == ']' || ch2 == ',')
			{
				/* add entry "indexval" in the array with name "key" */
				(void)initinfstr();
				(void)addstringtoinfstr(savekey);
				(void)addtoinfstr('[');
				(void)addstringtoinfstr(cindex);
				(void)addtoinfstr(']');
				net_addstring(returninfstr(), indexval, &count, stringcount, mystrings);
				if (ch2 == ']') break;
				continue;
			}

			/* ':' found, handle range of values */
			if (indexval < 0)
			{
				if (showerrors != 0)
					ttyputerr(_("Warning (facet %s): network '%s' has nonnumeric start of index range"),
						describenodeproto(facet), name);
				break;
			}
			endindex = getkeyword(&ptin, ",]");
			ch3 = tonextchar(&ptin);
			if (endindex == NOSTRING) break;
			if (*endindex == 0)
			{
				if (showerrors != 0)
					ttyputmsg(_("Warning (facet %s): network '%s' has missing end of index range"),
						describenodeproto(facet), name);
				break;
			}
			if (net_isanumber(endindex) == 0)
			{
				if (showerrors != 0)
					ttyputmsg(_("Warning (facet %s): network '%s' has nonnumeric end of index range"),
						describenodeproto(facet), name);
				break;
			}
			endindexval = myatoi(endindex);
			if (endindexval < 0)
			{
				if (showerrors != 0)
					ttyputmsg(_("Warning (facet %s): network '%s' has negative end of index range"),
						describenodeproto(facet), name);
				break;
			}
			if (endindexval == indexval)
			{
				if (showerrors != 0)
					ttyputmsg(_("Warning (facet %s): network '%s' has equal start and end indices"),
						describenodeproto(facet), name);
				break;
			}

			/* add an array from "indexval" to "endindexval" */
			if (indexval < endindexval)
			{
				for(i=indexval; i<=endindexval; i++)
				{
					(void)initinfstr();
					(void)addstringtoinfstr(savekey);
					sprintf(numbuf, "[%ld]", i);
					(void)addstringtoinfstr(numbuf);
					net_addstring(returninfstr(), i, &count, stringcount, mystrings);
				}
			} else
			{
				for(i=indexval; i>=endindexval; i--)
				{
					(void)initinfstr();
					(void)addstringtoinfstr(savekey);
					sprintf(numbuf, "[%ld]", i);
					(void)addstringtoinfstr(numbuf);
					net_addstring(returninfstr(), i, &count, stringcount, mystrings);
				}
			}
			if (ch3 == ']') break;
		}

		/* see what follows the ']' */
		key = getkeyword(&ptin, ",");
		if (key == NOSTRING) break;
		ch1 = tonextchar(&ptin);
		if (*key != 0)
		{
			if (showerrors != 0)
				ttyputmsg(_("Facet %s, network '%s': missing comma between names"),
					describenodeproto(facet), name);
			break;
		}
		if (ch1 == 0) { perfect = 1;   break; }
	}
	if (savekey != 0) efree(savekey);

	/* if there are errors and no strings were extracted, treat as wire */
	origfunct = funct;
	if (perfect == 0 && count == 0) funct = APUNKNOWN;

	/* see if multiple signals were found on single-signal arc */
	if (count != 1 && funct != APBUS)
	{
		errorstring = (char *)emalloc((strlen(name)+1), el_tempcluster);
		(void)strcpy(errorstring, name);
		for(ptin = errorstring; *ptin != 0; ptin++)
			if (*ptin == ',' || *ptin == ':' || *ptin == '[' || *ptin == ']')
				*ptin = 'X';
		if (showerrors != 0)
		{
			if (origfunct != APBUS)
				ttyputerr(_("Warning (facet %s): network '%s' cannot name a single wire, using '%s'"),
					describenodeproto(facet), name, errorstring); else
						ttyputerr(_("Warning (facet %s): network name '%s' is unintelligible, using '%s'"),
							describenodeproto(facet), name, errorstring);
		}
		count = 0;
		net_addstring(errorstring, -1, &count, stringcount, mystrings);
		*strings = *mystrings;
		efree(errorstring);
		return(count);
	}

	/* if there are errors, build what is available */
	if (showerrors != 0)
	{
		if (perfect == 0)
			ttyputerr(_("Warning (facet %s): network name '%s' is unintelligible, using '%s'"),
				describenodeproto(facet), name, net_buildnetname(count, *mystrings));
	}

	*strings = *mystrings;
	return(count);
}

/*
 * Routine to follow arc "ai" and find a bus with a unique name.  This is the name to
 * use for the arc (which has been named with an empty bus name and just an index).
 */
char *net_busnameofarc(ARCINST *ai)
{
	REGISTER ARCINST *oai;
	REGISTER char *ch;

	if (ai != NOARCINST)
	{
		for(oai = ai->parent->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
			oai->temp1 = 0;
		ch = net_findnameofbus(ai, 1);
		if (ch != 0) return(ch);
		ch = net_findnameofbus(ai, 0);
		if (ch != 0) return(ch);
	}
	return("XXX");
}

/*
 * Routine to determine the name of the bus on arc "ai".  The assumption is that
 * the bus has been partially named (i.e. "[0:2]") and that some other bus has
 * a more full name.  Only searches bus arcs if "justbus" is nonzero.
 */
char *net_findnameofbus(ARCINST *ai, INTSML justbus)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *oai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER INTBIG i, fun;
	REGISTER char *ch;

	if (ai->proto == sch_busarc)
	{
		if (ai->network != NONETWORK)
		{
			if (ai->network->namecount > 0)
			{
				(void)initinfstr();
				for(ch = ai->network->netname; *ch != 0;  ch++)
				{
					if (*ch == '[') break;
					(void)addtoinfstr(*ch);
				}
				return(returninfstr());
			}
		}
	}
	ai->temp1 = 1;
	for(i=0; i<2; i++)
	{
		ni = ai->end[i].nodeinst;
		fun = (ni->proto->userbits & NFUNCTION) >> NFUNCTIONSH;
		if (fun != NPPIN && fun != NPCONTACT && fun != NPNODE && fun != NPCONNECT) continue;

		/* follow arcs out of this node */
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			if ((pi->proto->userbits&PORTISOLATED) != 0) continue;
			oai = pi->conarcinst;
			if (oai->temp1 != 0) continue;
			if (justbus != 0 && oai->proto != sch_busarc) continue;

			ch = net_findnameofbus(oai, justbus);
			if (ch != 0) return(ch);
		}

		/* look at exports for array names */
		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		{
			if (net_buswidth(pe->exportproto->protoname) > 1)
			{
				(void)initinfstr();
				for(ch = pe->exportproto->protoname; *ch != 0;  ch++)
				{
					if (*ch == '[') break;
					(void)addtoinfstr(*ch);
				}
				return(returninfstr());
			}
		}
	}
	return(0);
}

void net_addstring(char *key, INTBIG cindex, INTSML *count, INTSML *stringcount,
	char ***mystrings)
{
	REGISTER char **newstrings;
	REGISTER INTSML i;

	if (*count >= *stringcount)
	{
		newstrings = (char **)emalloc(((sizeof (char *)) * (*count + 1)), net_aid->cluster);
		if (newstrings == 0) return;
		for(i=0; i < *stringcount; i++)
			newstrings[i] = (*mystrings)[i];
		for(i = *stringcount; i < *count + 1; i++)
			(void)allocstring(&newstrings[i], "", net_aid->cluster);
		if (*stringcount != 0)
			efree((char *)*mystrings);
		*stringcount = *count + 1;
		*mystrings = newstrings;
	}
	(void)reallocstring(&(*mystrings)[*count], key, net_aid->cluster);
	(*count)++;
}

/*
 * routine to build a bus name given the "count" signals on the bus described
 * by the array of signal names in "strings"
 */
char *net_buildnetname(INTSML count, char **strings)
{
	REGISTER INTBIG i, j, k, l, len, thisindex, startindex;
	REGISTER char *pt;

	(void)initinfstr();
	for(i=0; i<count; i++)
	{
		/* parse up to a '[' */
		for(pt = strings[i]; *pt != 0; pt++) if (*pt == '[') break;
		if (*pt != '[')
		{
			/* nonbus name: just add it */
			(void)addstringtoinfstr(strings[i]);
		} else
		{
			/* get size of prefix, add it to output name */
			len = pt - strings[i] + 1;
			for(j=0; j<len; j++) (void)addtoinfstr(strings[i][j]);

			for(j = i; j < count; j++)
			{
				if (namesamen(strings[i], strings[j], len) != 0) break;

				/* include this index's name */
				if (j != i) (void)addtoinfstr(',');
				for(l=0; strings[j][len+l] != 0 && strings[j][len+l] != ']'; l++)
					(void)addtoinfstr(strings[j][len+l]);

				/* include all names in this bus */
				if (net_isanumber(&strings[j][len]) != 0)
				{
					startindex = myatoi(&strings[j][len]);

					/* see if there is an ascending sequence */
					for(k = j+1; k < count; k++)
					{
						if (namesamen(strings[i], strings[k], len) != 0) break;
						if (net_isanumber(&strings[k][len]) == 0) break;
						thisindex = myatoi(&strings[k][len]);
						if (thisindex - startindex != j - k) break;
					}
					if (k > j+2)
					{
						(void)addtoinfstr(':');
						for(l=0; strings[k-1][len+l] != 0 && strings[k-1][len+l] != ']'; l++)
							(void)addtoinfstr(strings[k-1][len+l]);
						j = k - 1;
						continue;
					}

					/* see if there is a descending sequence */
					for(k = j+1; k < count; k++)
					{
						if (namesamen(strings[i], strings[k], len) != 0) break;
						if (net_isanumber(&strings[k][len]) == 0) break;
						thisindex = myatoi(&strings[k][len]);
						if (startindex - thisindex != j - k) break;
					}
					if (k > j+2)
					{
						(void)addtoinfstr(':');
						for(l=0; strings[k-1][len+l] != 0 && strings[k-1][len+l] != ']'; l++)
							(void)addtoinfstr(strings[k-1][len+l]);
						j = k - 1;
						continue;
					}
				}
			}
			(void)addtoinfstr(']');
			i = j - 1;
		}
		if (i < count-1) (void)addtoinfstr(',');
	}
	return(returninfstr());
}

/*
 * Helper routine to determine whether the string "name" is a number (but it may
 * end with network index characters ":", "]", or ",".
 */
INTSML net_isanumber(char *name)
{
	REGISTER char *pt, save;
	INTSML ret;

	for(pt = name; *pt != 0; pt++)
		if (*pt == ':' || *pt == ']' || *pt == ',') break;
	if (*pt == 0) return(isanumber(name));
	save = *pt;
	*pt = 0;
	ret = isanumber(name);
	*pt = save;
	return(ret);
}

/*
 * routine to check the arity of network named "key" and
 * print a warning if the arity is wrong
 */
void net_checknetnamearity(char *key, NODEPROTO *np)
{
	REGISTER INTBIG len, k;
	REGISTER char *name, *pt;
	REGISTER NETWORK *net;

	for(pt = key; *pt != 0; pt++)
		if (*pt == '[') break;
	if (*pt == 0)
	{
		/* name is not indexed: make sure there are no networks that do index */
		len = strlen(key);
		for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
		{
			name = net->netname;
			for(k=0; k<net->namecount; k++)
			{
				if (namesamen(name, key, len) == 0 && name[len] == '[')
				{
					ttyputerr(_("Warning (facet %s): bus network '%s' used ambiguously"),
						describenodeproto(np), key);
					return;
				}
				name += strlen(name) + 1;
			}
		}
		return;
	}

	/* name is indexed: make sure there are no networks without one */
	*pt = 0;
	len = strlen(key);
	*pt = '[';
	for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
	{
		name = net->netname;
		for(k=0; k<net->namecount; k++)
		{
			if (name[len] == 0 && namesamen(name, key, len) == 0)
			{
				ttyputerr(_("Warning (facet %s): bus network '%s' used ambiguously"),
					describenodeproto(np), key);
				return;
			}
			name += strlen(name) + 1;
		}
	}
}

/*
 * routine to ensure that single-wire arc "ai" properly connects to the bus
 * running through bus-pin "ni".  Prints a warning message if error found.
 */
void net_checkvalidconnection(NODEINST *ni, ARCINST *ai)
{
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *oai;
	REGISTER INTBIG j, found;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;

	/* find a bus arc on this node */
	if (ai->network == NONETWORK) return;
	found = 0;
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		oai = pi->conarcinst;
		if (oai->proto != sch_busarc) continue;
		if (oai->network == NONETWORK) continue;
		if (oai->network->signals <= 1) continue;

		/* make sure the bus has this network on it */
		for(j=0; j<oai->network->signals; j++)
			if (oai->network->networklist[j] == ai->network) return;
		found++;
	}

	/* now check exported bus pins */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
	{
		pp = pe->exportproto;
		if (pp->network == NONETWORK) continue;
		if (pp->network->signals <= 1) continue;

		/* make sure the bus has this network on it */
		for(j=0; j<pp->network->signals; j++)
			if (pp->network->networklist[j] == ai->network) return;
		found++;
	}

	if (found > 0)
	{
		ttyputmsg(_("Warning (facet %s): network '%s' not a part of connected busses"),
			describenodeproto(ai->parent), describenetwork(ai->network));
		if (ai->network->namecount == 0)
			ttyputmsg(_("   (Set a network name on the '%s' arc)"), describearcinst(ai));
	}
}

/*
 * routine to get the network attached to "ni" at its port "pp"
 */
NETWORK *getnetonport(NODEINST *ni, PORTPROTO *pp)
{
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;

	/* see if the port is on an arc */
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->proto->network == pp->network)
			return(pi->conarcinst->network);

	/* see if the port is an export */
	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
		if (pe->proto->network == pp->network)
			return(pe->exportproto->network);

	/* sorry, this port is not on a network */
	return(NONETWORK);
}

/*
 * Routine to rip the currently selected bus arc out into individual wires.
 */
void net_ripbus(void)
{
	REGISTER ARCINST *ai, *aiw;
	REGISTER NODEINST *niw, *nib, *niblast;
	REGISTER NETWORK *net;
	REGISTER VARIABLE *var;
	REGISTER INTSML i, count, lowend;
	char **strings;
	REGISTER INTBIG lowx, highx, sepx, lowy, highy, sepy, stublen, lowxbus, lowybus, lambda;
	INTBIG sxw, syw, sxb, syb;

	ai = (ARCINST *)us_getobject(OBJARCINST, 0);
	if (ai == NOARCINST) return;
	if (ai->proto != sch_busarc)
	{
		ttyputerr(_("Must select a bus arc to rip it into individual signals"));
		return;
	}
	net = ai->network;
	if (net == NONETWORK)
	{
		ttyputerr(_("Bus has no network information"));
		return;
	}
	if (net->namecount == 0)
	{
		ttyputerr(_("Bus has no name"));
		return;
	}
	if (net->signals <= 1)
	{
		ttyputerr(_("Bus must have multiple signals"));
		return;
	}

	/* determine which bus name to use */
	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
	if (var != NOVARIABLE && *((char *)var->addr) != 0)
		count = net_evalbusname(APBUS, (char *)var->addr, &strings, ai, ai->parent, 1); else
			count = net_evalbusname(APBUS, net->netname, &strings, ai, ai->parent, 1);

	/* determine length of stub wires */
	lambda = lambdaofarc(ai);
	stublen = ai->length / 3;
	stublen = (stublen + lambda/2) / lambda * lambda;

	/* determine location of individual signals */
	if (ai->end[0].xpos == ai->end[1].xpos)
	{
		lowx = ai->end[0].xpos;
		if (lowx < (ai->parent->lowx + ai->parent->highx) / 2) lowx += stublen; else
			lowx -= stublen;
		sepx = 0;

		if (ai->end[0].ypos < ai->end[1].ypos) lowend = 0; else lowend = 1;
		lowy = (ai->end[lowend].ypos + lambda - 1) / lambda * lambda;
		highy = ai->end[1-lowend].ypos / lambda * lambda;
		if (highy-lowy >= (net->signals-1) * lambda)
		{
			/* signals fit on grid */
			sepy = ((highy-lowy) / ((net->signals-1) * lambda)) * lambda;
			lowy = ((highy - lowy) - (sepy * (net->signals-1))) / 2 + lowy;
			lowy = (lowy + lambda/2) / lambda * lambda;
		} else
		{
			/* signals don't fit: just make them even */
			lowy = ai->end[lowend].ypos;
			highy = ai->end[1-lowend].ypos;
			sepy = (highy-lowy) / (net->signals-1);
		}
		lowxbus = ai->end[0].xpos;   lowybus = lowy;
	} else if (ai->end[0].ypos == ai->end[1].ypos)
	{
		lowy = ai->end[0].ypos;
		if (lowy < (ai->parent->lowy + ai->parent->highy) / 2) lowy += stublen; else
			lowy -= stublen;
		sepy = 0;

		if (ai->end[0].xpos < ai->end[1].xpos) lowend = 0; else lowend = 1;
		lowx = (ai->end[lowend].xpos + lambda - 1) / lambda * lambda;
		highx = ai->end[1-lowend].xpos / lambda * lambda;
		if (highx-lowx >= (net->signals-1) * lambda)
		{
			/* signals fit on grid */
			sepx = ((highx-lowx) / ((net->signals-1) * lambda)) * lambda;
			lowx = ((highx - lowx) - (sepx * (net->signals-1))) / 2 + lowx;
			lowx = (lowx + lambda/2) / lambda * lambda;
		} else
		{
			/* signals don't fit: just make them even */
			lowx = ai->end[lowend].xpos;
			highx = ai->end[1-lowend].xpos;
			sepx = (highx-lowx) / (net->signals-1);
		}
		lowxbus = lowx;   lowybus = ai->end[0].ypos;
	} else
	{
		ttyputerr(_("Bus must be horizontal or vertical to be ripped out"));
		return;
	}

	/* turn off highlighting */
	us_clearhighlightcount();

	defaultnodesize(sch_wirepinprim, &sxw, &syw);
	defaultnodesize(sch_buspinprim, &sxb, &syb);
	for(i=0; i<count; i++)
	{
		/* make the wire pin */
		niw = newnodeinst(sch_wirepinprim, lowx-sxw/2, lowx+sxw/2, lowy-syw/2, lowy+syw/2, 0, 0,
			ai->parent);
		if (niw == NONODEINST) break;
		endobjectchange((INTBIG)niw, VNODEINST);

		/* make the bus pin */
		nib = newnodeinst(sch_buspinprim, lowxbus-sxb/2, lowxbus+sxb/2, lowybus-syb/2, lowybus+syb/2,
			0, 0, ai->parent);
		if (nib == NONODEINST) break;
		endobjectchange((INTBIG)nib, VNODEINST);

		/* wire them */
		aiw = newarcinst(sch_wirearc, defaultarcwidth(sch_wirearc), ai->userbits,
			niw, sch_wirepinprim->firstportproto, lowx, lowy, nib,
				sch_buspinprim->firstportproto, lowxbus, lowybus, ai->parent);
		if (aiw == NOARCINST) break;
		setvalkey((INTBIG)aiw, VARCINST, el_arc_name,
			(INTBIG)strings[i], VSTRING|VDISPLAY);
		endobjectchange((INTBIG)aiw, VARCINST);

		/* wire to the bus pin */
		if (i == 0)
		{
			aiw = newarcinst(sch_busarc, defaultarcwidth(sch_busarc), ai->userbits,
				ai->end[lowend].nodeinst, ai->end[lowend].portarcinst->proto,
					ai->end[lowend].xpos, ai->end[lowend].ypos,
						nib, sch_buspinprim->firstportproto, lowxbus, lowybus, ai->parent);
		} else
		{
			aiw = newarcinst(sch_busarc, defaultarcwidth(sch_busarc), ai->userbits,
				niblast, sch_buspinprim->firstportproto, lowxbus-sepx, lowybus-sepy,
					nib, sch_buspinprim->firstportproto, lowxbus, lowybus, ai->parent);
		}
		if (aiw == NOARCINST) break;
		endobjectchange((INTBIG)aiw, VARCINST);

		/* advance to the next segment */
		niblast = nib;
		lowx += sepx;      lowy += sepy;
		lowxbus += sepx;   lowybus += sepy;
	}

	/* wire up the last segment */
	aiw = newarcinst(sch_busarc, defaultarcwidth(sch_busarc), ai->userbits,
		ai->end[1-lowend].nodeinst, ai->end[1-lowend].portarcinst->proto,
			ai->end[1-lowend].xpos, ai->end[1-lowend].ypos,
				niblast, sch_buspinprim->firstportproto, lowxbus-sepx, lowybus-sepy, ai->parent);
	if (aiw == NOARCINST) return;
	if (var != NOVARIABLE && *((char *)var->addr) != 0)
		setvalkey((INTBIG)aiw, VARCINST, el_arc_name, var->addr, VSTRING|VDISPLAY);
	endobjectchange((INTBIG)aiw, VARCINST);

	/* remove original arc */
	startobjectchange((INTBIG)ai, VARCINST);
	if (killarcinst(ai))
		ttyputerr(_("Error deleting original arc"));
}

/*
 * Routine to name all nodes in facet "np" that do not already have node names.
 * Returns the number of nodes that were named.
 */
INTSML net_nameallnodes(NODEPROTO *np, INTSML evenpins)
{
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;
	REGISTER char *name;
	char newname[50], *dummy;
	INTSML named, fun;
	INTBIG cindex, highpseudo;

	/* do not name nodes in "icon" facets */
	if (np->cellview == el_iconview || np->cellview == el_simsnapview) return(0);

	named = 0;
	highpseudo = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (evenpins == 0 && ni->firstportexpinst == NOPORTEXPINST)
		{
			fun = nodefunction(ni, &dummy);
			if (fun == NPPIN || fun == NPART) continue;
		}

		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
		if (var != NOVARIABLE)
		{
			/* check for existing pseudo-names */
			name = (char *)var->addr;
			if (namesamen(name, "node", 4) != 0) continue;
			cindex = atoi(&name[4]);
			if (cindex > highpseudo) highpseudo = cindex;
		} else named++;
	}
	if (named == 0) return(named);
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (evenpins == 0 && ni->firstportexpinst == NOPORTEXPINST)
		{
			fun = nodefunction(ni, &dummy);
			if (fun == NPPIN || fun == NPART) continue;
		}

		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
		if (var != NOVARIABLE) continue;
		highpseudo++;
		(void)sprintf(newname, "node%ld", highpseudo);
		var = setvalkey((INTBIG)ni, VNODEINST, el_node_name, (INTBIG)newname, VSTRING);
	}
	return(named);
}

INTSML net_nameallnets(NODEPROTO *np)
{
	REGISTER ARCINST *ai;
	REGISTER NETWORK *net;
	REGISTER VARIABLE *var;
	REGISTER char *name;
	char newname[50];
	INTBIG cindex, highpseudo;
	INTSML named;

	/* do not name arcs in "icon" facets */
	if (np->cellview == el_iconview || np->cellview == el_simsnapview) return(0);

	/* find highest numbered net in the facet */
	highpseudo = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
		if (var == NOVARIABLE) continue;

		/* check for existing pseudo-names */
		name = (char *)var->addr;
		if (namesamen(name, "net", 3) != 0) continue;
		cindex = atoi(&name[3]);
		if (cindex > highpseudo) highpseudo = cindex;
	}

	/* find nets with no name and name them */
	named = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		net = ai->network;
		if (net == NONETWORK || net->namecount > 0) continue;

		highpseudo++;
		(void)sprintf(newname, "net%ld", highpseudo);
		var = setvalkey((INTBIG)ai, VARCINST, el_arc_name, (INTBIG)newname, VSTRING);
		named++;
	}
	return(named);
}

/*
 * Routine to ensure that network "net" (a bus) has a proper bus name if it is named temporarily.
 */
void net_ensuretempbusname(NETWORK *net)
{
	INTSML count;
	char index[30];

	if (net->signals <= 1) return;
	if (net->tempname == 0) return;
	if (net->namecount != 1) return;
	count = net_buswidth(net->netname);
	if (count != 1) return;
	(void)initinfstr();
	(void)addstringtoinfstr(net->netname);
	sprintf(index, "[1:%d]", net->signals);
	(void)addstringtoinfstr(index);
	efree(net->netname);
	net->namecount = 0;
	net_namenet(returninfstr(), net);
}

/*
 * Routine to evaluate the implied bus width of the string 'name'
 */
INTSML net_buswidth(char *name)
{
	REGISTER INTSML count;
	char **strings;
	
	count = net_evalbusname(APBUS, name, &strings, NOARCINST, NONODEPROTO, 0);
	return(count);
}

/*
 * Routine to implement the "wire_con" primitive which joins two arcs and attaches
 * busses of unequal length (by repeating signals on the shorter bus until fully
 * attached to the larger bus).
 */
void net_joinnetworks(NODEINST *ni)
{
	REGISTER ARCINST *ai, *smallai, *largeai;
	REGISTER INTBIG smallnum, largenum, num, wirecount, i;
	REGISTER PORTARCINST *pi;
	REGISTER NETWORK *net, *smallnet, *largenet;

	/* find the narrow and wide busses on this connector */
	smallai = largeai = NOARCINST;
	smallnum = largenum = 0;
	wirecount = 0;
	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
	{
		ai = pi->conarcinst;
		wirecount++;
		net = ai->network;
		if (net == NONETWORK) continue;
		num = net->signals;
		if (smallai == NOARCINST || num <= smallnum)
		{
			smallai = ai;
			smallnum = num;
		}
		if (largeai == NOARCINST || num > largenum)
		{
			largeai = ai;
			largenum = num;
		}
	}
	if (wirecount < 2) return;
	if (wirecount > 2)
	{
		ttyputmsg(_("Facet %s, connector %s can only merge two arcs (has %ld)"),
			describenodeproto(ni->parent), describenodeinst(ni), wirecount);
		return;
	}
	if (smallai == largeai) return;

	for(i=0; i<largenum; i++)
	{
		if (smallnum == 1) smallnet = smallai->network; else
		{
			smallnet = smallai->network->networklist[i % smallnum];
		}
		if (largenum == 1) largenet = largeai->network; else
		{
			largenet = largeai->network->networklist[i];
		}
		if (smallnet == largenet) continue;
		(void)net_mergenet(smallnet, largenet);
	}

	/* also merge the busses if they are the same length */
	if (smallnum == largenum && smallnum > 1)
		(void)net_mergenet(smallai->network, largeai->network);
}

/*
 * Routine to locate individual signals contained within busses in
 * node instance ni which are connected inside of the contentsview
 * of ni->proto, and then to merge the two individual networks.
 */
INTSML net_mergebuswires(NODEINST *ni)
{
	NODEPROTO *np, *cnp;
	PORTPROTO *pp, *opp, *cpp, *ocpp;
	NETWORK *net, *onet, *cnet, *ocnet, *cmnet, *ocmnet, *mnet, *omnet, *thenet;
	INTSML j, k, recheck;
	REGISTER char *busname;

	/* primitives do not connect individual signals in busses */
	if (ni->proto->primindex != 0) return(0);

	/* establish connections to contents nodeproto */
	np = ni->proto;
	if ((cnp = contentsview(np)) == NONODEPROTO) cnp = np;

	/* presume no checking above is required */
	recheck = 0;

	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		/* see if this port can connect to a bus */
		for(j=0; pp->connects[j] != NOARCPROTO; j++)
			if (pp->connects[j] == sch_busarc) break;
		if (pp->connects[j] == NOARCPROTO) continue;

		/* make sure this port has a network attached to it */
		if ((net = getnetonport(ni, pp)) == NONETWORK) continue;
		if ((cpp = equivalentport(np, pp, cnp)) == NOPORTPROTO) continue;

		/* get the network inside the instance at this port */
		cnet = cpp->network;

		/* nothing to do if the network is a single wire */
		if (cnet->signals <= 1) continue;

		/* now find another network attached to this node instance */
		for (opp = pp->nextportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
		{
			/* make sure this port also has a network attached to it */
			if ((onet = getnetonport(ni, opp)) == NONETWORK) continue;

			/* skip if there is no matching port in the contents view */
			if ((ocpp = equivalentport(np, opp, cnp)) == NOPORTPROTO) continue;

			/* get the network inside the instance at this port */
			ocnet = ocpp->network;

			/* nothing to do if they both are the same net */
			if (cnet == ocnet) continue;

			/* nothing to do if the network is a single wire */
			if (ocnet->signals <= 1) continue;

			/* establish connectivity of signals inside exported bus ports */
			for(j=0; j<cnet->signals; j++)
			{
				cmnet = cnet->networklist[j];

				for (k=0; k<ocnet->signals; k++)
				{
					ocmnet = ocnet->networklist[k];
					if (cmnet == ocmnet)
					{
						/* if busses are wide enough, merge connecting signals */
						if (j < net->signals && k < onet->signals)
						{
							if (net->signals == 1) mnet = net; else
								mnet = net->networklist[j];
							if (onet->signals == 1) omnet = onet; else
								omnet = onet->networklist[k];
							net_mergenet(mnet, omnet);
						} else
						{
							if (net->namecount == 0) busname = describenetwork(onet); else
								busname = describenetwork(net);
							if (j >= net->signals) thenet = cnet; else thenet = ocnet;
							ttyputmsg(_("Warning (facet %s): bus %s cannot connect to port %s of node %s (different width)"),
								describenodeproto(ni->parent), busname, describenetwork(thenet),
									describenodeinst(ni));
							break;
						}
					}
				}
				if (k < ocnet->signals) break;
			}
		}
	}
	return(recheck);
}

/*
 * Routine to allocate a "buslist" object that helps to sort busses by width.
 */
BUSLIST *net_allocbuslist(void)
{
	REGISTER BUSLIST *bl;

	if (net_buslistfree == NOBUSLIST)
	{
		bl = (BUSLIST *)emalloc(sizeof(BUSLIST), net_aid->cluster);
		if (bl == 0) return(NOBUSLIST);
	} else
	{
		bl = net_buslistfree;
		net_buslistfree = net_buslistfree->nextbuslist;
	}
	return(bl);
}

/*
 * Routine to free buslist object "bl" to the pool of unused objects.
 */
void net_freebuslist(BUSLIST *bl)
{
	bl->nextbuslist = net_buslistfree;
	net_buslistfree = bl;
}

/****************************** DIALOG ******************************/

#define NUMOVERRIDES   4

struct
{
	int yes, no, def;
	int overridebit, yesbit;
} net_nccoverridebuttons[NUMOVERRIDES] =
{
	15, 16, 17, NCCUSEPORTNAMESOVER, NCCUSEPORTNAMES,
	18, 19, 20, NCCUNIFYPORTNAMESOVER, NCCUNIFYPORTNAMES,
	21, 22, 23, NCCIGNOREPWRGNDOVER, NCCIGNOREPWRGND,
	29, 30, 31, NCCCHECKSIZEOVER, NCCCHECKSIZE
};

/*
 * special case for the "Network Options" dialog
 * Hierarchical NCC options  = 3 (popup)
 * Unify Power and Ground    = 4 (check)
 * Unify all like-named nets = 5 (check)
 * Use port names in NCC     = 6 (check)
 * Automatically name nets   = 7 (check)
 * Verbose NCC               = 8 (check)
 * Unify port order in NCC   = 9 (check)
 * Ignore power/ground in NCC= 10 (check)
 * List of facets            = 13 (scroll)
 * Yes to "port names"       = 15 (radio)
 * No to "port names"        = 16 (radio)
 * Default to "port names"   = 17 (radio)
 * Yes to "port order"       = 18 (radio)
 * No to "port order"        = 19 (radio)
 * Default to "port order"   = 20 (radio)
 * Yes to "ignore p&g"       = 21 (radio)
 * No to "ignore p&g"        = 22 (radio)
 * Default to "ignore p&g"   = 23 (radio)
 * Clear valid NCC dates     = 27 (button)
 * Check comp. size in NCC   = 28 (check)
 * Yes to "check size"       = 29 (radio)
 * No to "check size"        = 30 (radio)
 * Default to "check size"   = 31 (radio)
 */
void net_optionsdlog(void)
{
	INTBIG itemHit;
	REGISTER VARIABLE *var;
	REGISTER INTBIG i, options, oldoptions, which, clearvalidnccdates;
	REGISTER LIBRARY *lib;
	char *choices[3], *pt;
	REGISTER NODEPROTO *np;
	static char *hierchoices[3] = {N_("Check current facets only"),
		N_("Flatten hierarchy"), N_("Recursively check subfacets")};

	if (DiaInitDialog(&net_optionsdialog) != 0) return;
	for(i=0; i<3; i++) choices[i] = _(hierchoices[i]);
	DiaSetPopup(3, 3, choices);
	DiaInitTextDialog(13, topoffacets, nextfacets, DiaNullDlogDone, 0,
		SCSELMOUSE|SCSELKEY|SCREPORT);
	(void)us_setscrolltocurrentfacet(13, 0, 0, 0);
	if (net_connect_power_ground != 0) DiaSetControl(4, 1);
	if (net_connect_common_name != 0) DiaSetControl(5, 1);
	if (net_auto_name != 0) DiaSetControl(7, 1);

	var = getvalkey((INTBIG)net_aid, VAID, VINTEGER, net_ncc_optionskey);
	if (var == NOVARIABLE) options = 0; else options = var->addr;
	oldoptions = options;
	if ((options&NCCHIERARCHICAL) != 0)
	{
		DiaSetPopupEntry(3, 1);
	} else
	{
		if ((options&NCCRECURSE) != 0) DiaSetPopupEntry(3, 2);
	}
	if ((options&NCCUSEPORTNAMES) != 0) DiaSetControl(6, 1);
	if ((options&NCCVERBOSE) != 0) DiaSetControl(8, 1);
	if ((options&NCCUNIFYPORTNAMES) != 0) DiaSetControl(9, 1);
	if ((options&NCCIGNOREPWRGND) != 0) DiaSetControl(10, 1);
	if ((options&NCCCHECKSIZE) != 0) DiaSetControl(28, 1);

	/* cache individual facet overrides */
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, net_ncc_optionskey);
		if (var == NOVARIABLE) np->temp1 = 0; else np->temp1 = var->addr;
	}

	net_setnccoverrides();
	clearvalidnccdates = 0;

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit();
		if (itemHit == OK || itemHit == CANCEL) break;
		if ((itemHit >= 4 && itemHit <= 10) || itemHit == 28)
		{
			DiaSetControl(itemHit, 1 - DiaGetControl(itemHit));
			continue;
		}
		for(i=0; i<NUMOVERRIDES; i++)
		{
			if (itemHit == net_nccoverridebuttons[i].yes ||
				itemHit == net_nccoverridebuttons[i].no ||
				itemHit == net_nccoverridebuttons[i].def)
			{
				DiaSetControl(net_nccoverridebuttons[i].yes, 0);
				DiaSetControl(net_nccoverridebuttons[i].no, 0);
				DiaSetControl(net_nccoverridebuttons[i].def, 0);
				DiaSetControl(itemHit, 1);

				which = DiaGetCurLine(13);
				if (which < 0) break;
				pt = DiaGetScrollLine(13, which);
				if (*pt == 0) break;
				np = getnodeproto(pt);
				if (np == NONODEPROTO) break;
				np->temp1 &= ~(net_nccoverridebuttons[i].overridebit |
					net_nccoverridebuttons[i].yesbit);
				if (itemHit != net_nccoverridebuttons[i].def)
					np->temp1 |= net_nccoverridebuttons[i].overridebit;
				if (itemHit == net_nccoverridebuttons[i].yes)
					np->temp1 |= net_nccoverridebuttons[i].yesbit;
				break;
			}
		}
		if (itemHit == 13)
		{
			net_setnccoverrides();
			continue;
		}
		if (itemHit == 27)
		{
			/* clear valid NCC dates */
			clearvalidnccdates = 1;
			DiaDimItem(27);
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		options = 0;
		i = DiaGetPopupEntry(3);
		switch (i)
		{
			case 0:		/* current level only */
				break;
			case 1:		/* flatten hierarchy */
				options |= NCCHIERARCHICAL;
				break;
			case 2:		/* recursively check single levels */
				options |= NCCRECURSE;
				break;
		}
		if (DiaGetControl(6) != 0) options |= NCCUSEPORTNAMES;
		if (DiaGetControl(8) != 0) options |= NCCVERBOSE;
		if (DiaGetControl(9) != 0) options |= NCCUNIFYPORTNAMES;
		if (DiaGetControl(10) != 0) options |= NCCIGNOREPWRGND;
		if (DiaGetControl(28) != 0) options |= NCCCHECKSIZE;
		if (options != oldoptions)
			(void)setvalkey((INTBIG)net_aid, VAID, net_ncc_optionskey, options, VINTEGER);

		if (DiaGetControl(4) != 0) i = 1; else i = 0;
		if (i != net_connect_power_ground)
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_power_groundkey, i, VINTEGER);

		if (DiaGetControl(5) != 0) i = 1; else i = 0;
		if (i != net_connect_common_name)
			(void)setvalkey((INTBIG)net_aid, VAID, net_connect_common_namekey, i, VINTEGER);

		if (DiaGetControl(7) != 0) i = 1; else i = 0;
		if (i != net_auto_name)
			(void)setvalkey((INTBIG)net_aid, VAID, net_auto_namekey, i, VINTEGER);

		/* make changes to facet overrides */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) oldoptions = 0; else oldoptions = var->addr;
			if (oldoptions == np->temp1) continue;
			if (np->temp1 == 0) delvalkey((INTBIG)np, VNODEPROTO, net_ncc_optionskey); else
				setvalkey((INTBIG)np, VNODEPROTO, net_ncc_optionskey, np->temp1, VINTEGER);
		}

		/* clear valid NCC dates if requested */
		if (clearvalidnccdates != 0)
		{
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				{
					var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, net_lastgoodncckey);
					if (var == NOVARIABLE) continue;
					delvalkey((INTBIG)np, VNODEPROTO, net_lastgoodncckey);
				}
			}
		}
	}
	DiaDoneDialog();
}

void net_setnccoverrides(void)
{
	REGISTER INTBIG which, i, override;
	REGISTER char *pt;
	REGISTER NODEPROTO *np;

	which = DiaGetCurLine(13);
	if (which < 0) return;
	pt = DiaGetScrollLine(13, which);
	if (*pt == 0) return;
	np = getnodeproto(pt);
	if (np == NONODEPROTO) return;

	override = np->temp1;
	for(i=0; i<NUMOVERRIDES; i++)
	{
		DiaSetControl(net_nccoverridebuttons[i].yes, 0);
		DiaSetControl(net_nccoverridebuttons[i].no, 0);
		DiaSetControl(net_nccoverridebuttons[i].def, 0);
		if ((override&net_nccoverridebuttons[i].overridebit) == 0)
		{
			DiaSetControl(net_nccoverridebuttons[i].def, 1);
		} else
		{
			if ((override&net_nccoverridebuttons[i].yesbit) != 0)
			{
				DiaSetControl(net_nccoverridebuttons[i].yes, 1);
			} else
			{
				DiaSetControl(net_nccoverridebuttons[i].no, 1);
			}
		}
	}
}
