/* -*- tab-width: 4 -*-
 *
 * 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 "usrdiacom.h"
#include <math.h>
#include <ctype.h>

/* variables */
       TOOL       *net_tool;					/* the Network tool object */
static TOOL       *net_current_source;			/* the tool currently making changes */
static BOOLEAN     net_debug;					/* true for debugging */
static BOOLEAN     net_globalwork;				/* nonzero when doing major evaluation */
static INTBIG      net_optionskey;				/* key for "NET_options" */
       INTBIG      net_ncc_optionskey;			/* key for "NET_ncc_options" */
       INTBIG      net_ncc_comptolerancekey;	/* key for "NET_ncc_component_tolerance" */
       INTBIG      net_ncc_comptoleranceamtkey;	/* key for "NET_ncc_component_tolerance_amt" */
       INTBIG      net_ncc_matchkey;			/* key for "NET_ncc_match" */
       INTBIG      net_node_abbrev_key;			/* key for "NET_node_abbreviations" */
       INTBIG      net_node_abbrevlen_key;		/* key for "NET_node_abbreviation_length" */
       INTBIG      net_ncc_processors_key;		/* key for "NET_ncc_num_processors" */
       INTBIG      net_options;					/* cached value for "NET_options" */
       PNET       *net_pnetfree;
       PCOMP      *net_pcompfree;
static INTBIG      net_mostj = 0;
static NETWORK   **net_primnetlist;
static void       *net_merge;					/* for polygon merging */
static BOOLEAN     net_checkresistorstate;		/* true to check for changes to resistor topology */

/* obsolete variables */
static INTBIG      net_connect_power_groundkey;	/* key for "NET_connect_PandG" */
static INTBIG      net_connect_common_namekey;	/* key for "NET_connect_common" */
static INTBIG      net_found_obsolete_variables = 0;	/* nonzero if library had above variables */

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

       TRANSISTORINFO   net_transistor_p_gate;		/* info on P transistors connected at gate */
       TRANSISTORINFO   net_transistor_n_gate;		/* info on N transistors connected at gate */
       TRANSISTORINFO   net_transistor_p_active;	/* info on P transistors connected at active */
       TRANSISTORINFO   net_transistor_n_active;	/* info on N transistors connected at active */

/* working memory for "net_samenetworkname()" */
static INTBIG     net_namecompstringtotal = 0;
static char     **net_namecompstrings;

/* working memory for "net_nconnect()" */
static char      *net_arrayedarcname = 0;

/* working memory for "net_addnodename()" */
static INTBIG     net_nodenamelisttotal = 0;
static INTBIG     net_nodenamelistcount;
static char     **net_nodenamelist;

/* working memory for "net_addnettolist()" */
static INTBIG    net_highnetscount;
static INTBIG    net_highnetstotal = 0;
static NETWORK **net_highnets;

static AREAPERIM *net_firstareaperim;

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

static BUSLIST *net_buslists;
static INTBIG   net_buslistcount;
static INTBIG   net_buslisttotal = 0;


#define NOARCCHECK ((ARCCHECK *)-1)

typedef struct Iarccheck
{
	ARCINST *ai;
	struct Iarccheck *nextarccheck;
} ARCCHECK;

ARCCHECK *net_arccheckfree = NOARCCHECK;
ARCCHECK *net_firstarccheck;

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

static COMCOMP networkfacetp = {NOKEYWORD,topoffacets,nextfacets,NOPARAMS,
	INPUTOPT, " \t", M_("Facet to re-number (default is current facet)"), 0};
static COMCOMP networknodehp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	INPUTOPT, " \t", M_("Net to highlight"), 0};
static COMCOMP networknodenp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	INPUTOPT, " \t", M_("Net whose connections should be listed"), 0};
static COMCOMP networknodelp = {NOKEYWORD, topofnets, nextnets, NOPARAMS,
	INPUTOPT, " \t", M_("Net, ALL of whose ports should be listed"), 0};
static KEYWORD networkeqnopt[] =
{
	{"check-export-names",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"check-node-sizes",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"flatten-hierarchy",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"recurse",                       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkeqnp = {networkeqnopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	INPUTOPT, " \t", M_("Network-compare negating option"), 0};
static KEYWORD networkeqopt[] =
{
	{"flatten-hierarchy",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"recurse",                       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"highlight-other",               0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"check-export-names",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"check-node-sizes",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"pre-analysis",                  0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
#ifdef FORCESUNTOOLS
	{"analyze-facet",                 0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
#endif
	{"not",                           1,{&networkeqnp,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP net_networkeqp = {networkeqopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	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}},
	{"identify",                      0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkpgp = {networkpgopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	INPUTOPT, " \t", M_("Network power and ground equating option"), 0};
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,
	INPUTOPT, " \t", M_("How to handle networks with the same name"), 0};
static KEYWORD networkunopt[] =
{
	{"ascend-numbering",              0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"descend-numbering",             0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"0-base",                        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"1-base",                        0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
static COMCOMP networkunp = {networkunopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	INPUTOPT, " \t", M_("Option to handle unnamed busses"), 0};
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}},
	{"name-facet-objects",            0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"name-library-objects",          0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"show-geometry",                 1,{&networknodenp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"list-hierarchical-ports",       1,{&networknodelp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"list-ports-below",              1,{&networknodelp,NOKEY,NOKEY,NOKEY,NOKEY}},
	{"compare",                       1,{&net_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}},
	{"default-busses",                1,{&networkunp,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}},
	{"extract",                       0,{NOKEY,NOKEY,NOKEY,NOKEY,NOKEY}},
	TERMKEY
};
COMCOMP net_networkp = {networkopt, NOTOPLIST, NONEXTLIST, NOPARAMS,
	0, " \t", M_("Network maintenance"), 0};

/* prototypes for local routines */
static INTBIG           net_addnametonet(char*, INTBIG, NETWORK*, ARCINST*, PORTPROTO*, NODEPROTO*);
static void             net_addnettolist(NETWORK *net);
static BOOLEAN          net_addnodename(char *name);
static void             net_addstring(char*, INTBIG*, INTBIG*, char***);
static void             net_addtobuslist(ARCINST *ai, PORTPROTO *pp, INTBIG width);
static void             net_addtotransistorinfo(TRANSISTORINFO *ti, INTBIG length, INTBIG width);
static ARCCHECK        *net_allocarccheck(void);
static int              net_buslistwidthascending(const void *e1, const void *e2);
static char            *net_busnameofarc(ARCINST *ai);
static INTBIG           net_buswidthofarc(ARCINST *ai);
static void             net_checknetnamearity(char*, NODEPROTO*);
static void             net_checkvalidconnection(NODEINST*, ARCINST*);
static void             net_cleartransistorinfo(TRANSISTORINFO *ti);
static BOOLEAN          net_donconnect(ARCINST *ai, NETWORK *newnetwork, NODEPROTO *np);
static void             net_ensurebusses(NETWORK*, INTBIG, char**, INTBIG);
static void             net_ensuretempbusname(NETWORK *net);
static char            *net_findnameinbus(char *wantname, char *busname);
static char            *net_findnameofbus(ARCINST *ai, BOOLEAN justbus);
static void             net_findportsdown(NETWORK*, NODEPROTO*);
static void             net_findportsup(NETWORK*, NODEPROTO*);
static void             net_freearccheck(ARCCHECK *ac);
static void             net_geometrypolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG count);
static void             net_highlightnet(void*, NODEPROTO*, NETWORK*);
static void             net_highlightsubnet(void *infstr, NODEPROTO *topnp, NODEINST *ni, XARRAY trans, NETWORK *net);
static void             net_insertstring(char *key, INTBIG index, INTBIG *count, INTBIG *stringcount, char ***mystrings);
static BOOLEAN          net_isanumber(char *name);
static void             net_joinnetworks(NODEINST *ni);
static void             net_killnetwork(NETWORK*, NODEPROTO*);
static BOOLEAN          net_mergebuswires(NODEINST*);
static BOOLEAN          net_mergenet(NETWORK*, NETWORK*);
static INTBIG           net_nameallnets(NODEPROTO *np);
static INTBIG           net_nameallnodes(NODEPROTO *np, BOOLEAN evenpins);
static BOOLEAN          net_namenet(char*, NETWORK*);
static NODEPROTO       *net_nccdlgselectedfacet(void *dia);
static void             net_nccoptionsdlog(void);
static BOOLEAN          net_nconnect(ARCINST*, NETWORK*, NODEPROTO*);
static NETWORK         *net_newnetwork(NODEPROTO*);
static char            *net_nextfacets(void);
static void             net_optionsdlog(void);
static NETWORK         *net_parsenetwork(char*);
static BOOLEAN          net_pconnect(PORTPROTO*);
static void             net_propgeometry(NODEPROTO *facet, XARRAY trans, BOOLEAN recurse);
static void             net_putarclinkonnet(NETWORK*, ARCINST*);
static void             net_putarconnet(ARCINST*, NETWORK*);
static void             net_putportonnet(PORTPROTO*, NETWORK*);
static void             net_recursivelymarkabove(NODEPROTO*);
static BOOLEAN          net_recursivelyredo(NODEPROTO*);
static void             net_reevaluatefacet(NODEPROTO*);
static void             net_removebuslinks(NETWORK*);
static void             net_renamenet(char*, char*, NODEPROTO*);
static void             net_ripbus(void);
static void             net_setglobalnet(INTBIG *special, INTBIG newindex, char *netname, NETWORK *net,
							INTBIG characteristics, NODEPROTO *facet);
static void             net_setnccoverrides(void *dia);
static void             net_showgeometry(NETWORK *net);
static void             net_startglobalwork(LIBRARY*);
static BOOLEAN          net_takearcfromnet(ARCINST*);
static void             net_takearclinkfromnet(ARCINST*, NETWORK*);
static BOOLEAN          net_takeportfromnet(PORTPROTO*);
static void             net_tellschematics(void);
static BOOLEAN          net_topoffacets(char **c);
static void             net_totalrenumber(LIBRARY*);

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

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

	/* take tool pointer in pass 1 */
	if (thistool != NOTOOL)
	{
		net_tool = thistool;
		return;
	}

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

	/* debugging off */
	net_debug = FALSE;

	/* set network options */
	net_optionskey = makekey("NET_options");
	nextchangequiet();
	net_options = NETDEFBUSBASEDESC;
	(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey, net_options, VINTEGER|VDONTSAVE);
	net_node_abbrev_key = makekey("NET_node_abbreviations");
	net_node_abbrevlen_key = makekey("NET_node_abbreviation_length");
	net_checkresistorstate = FALSE;

	/* get obsolete variable names */
	net_connect_power_groundkey = makekey("NET_connect_PandG");
	net_connect_common_namekey = makekey("NET_connect_common");

	/* set NCC options */
	net_ncc_optionskey = makekey("NET_ncc_options");
	nextchangequiet();
	(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey, NCCNOMERGEPARALLEL, VINTEGER|VDONTSAVE);
	net_ncc_comptolerancekey = makekey("NET_ncc_component_tolerance");
	net_ncc_comptoleranceamtkey = makekey("NET_ncc_component_tolerance_amt");
	net_ncc_matchkey = makekey("NET_ncc_match");
#ifdef FORCESUNTOOLS
	{
		INTBIG numproc;
		net_ncc_processors_key = makekey("NET_ncc_num_processors");
		if (graphicshas(CANDOTHREADS)) numproc = enumprocessors(); else
			numproc = 1;
		nextchangequiet();
		(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_processors_key, numproc, VINTEGER|VDONTSAVE);
	}
#endif

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

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

void net_done(void)
{
#ifdef DEBUGMEMORY
	REGISTER INTBIG i, j, stringcount;
	REGISTER char **mystrings;
	REGISTER ARCCHECK *ac;

	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_arccheckfree != NOARCCHECK)
	{
		ac = net_arccheckfree;
		net_arccheckfree = ac->nextarccheck;
		efree((char *)ac);
	}

	if (net_buslisttotal > 0) efree((char *)net_buslists);
	if (net_namecompstringtotal > 0) efree((char *)net_namecompstrings);
	if (net_arrayedarcname != 0) efree(net_arrayedarcname);
	if (net_highnetstotal > 0) efree((char *)net_highnets);
	if (net_nodenamelisttotal > 0)
	{
		for(i=0; i<net_nodenamelisttotal; i++)
			if (net_nodenamelist[i] != 0) efree(net_nodenamelist[i]);
		efree((char *)net_nodenamelist);
	}
	net_freediffmemory();
	net_freeflatmemory();
#endif
}

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

	/* count the number of network objects that are needed */
	maxj = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			for(spt = np->firstportproto; spt != pp; spt = spt->nextportproto)
				if ((pp->userbits&PORTNET) == (spt->userbits&PORTNET))
			{
				pp->network = spt->network;
				break;
			}
			if (spt == pp) maxj++;
		}
	}

	/* 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_tool->cluster);
		if (net_primnetlist == 0) return;
		for(j=0; j<maxj; j++)
		{
			net_primnetlist[j] = (NETWORK *)emalloc(sizeof (NETWORK), net_tool->cluster);
			if (net_primnetlist[j] == 0) return;
			net_primnetlist[j]->namecount = 0;
			net_primnetlist[j]->tempname = 0;
			net_primnetlist[j]->globalnet = -1;
			net_primnetlist[j]->buswidth = 1;
			net_primnetlist[j]->nextnetwork = NONETWORK;
			net_primnetlist[j]->prevnetwork = 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 */
	j = 0;
	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			for(spt = np->firstportproto; spt != pp; spt = spt->nextportproto)
				if ((pp->userbits&PORTNET) == (spt->userbits&PORTNET))
			{
				pp->network = spt->network;
				break;
			}
			if (spt == pp) pp->network = net_primnetlist[j++];
		}
	}
}

void net_slice(void)
{
	REGISTER VARIABLE *var;

	if (net_found_obsolete_variables != 0)
	{
		net_found_obsolete_variables = 0;
		var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_connect_power_groundkey);
		if (var != NOVARIABLE)
			(void)delvalkey((INTBIG)net_tool, VTOOL, net_connect_power_groundkey);
		var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_connect_common_namekey);
		if (var != NOVARIABLE)
			(void)delvalkey((INTBIG)net_tool, VTOOL, net_connect_common_namekey);
	}
	if (net_checkresistorstate)
	{
		net_checkresistorstate = FALSE;
		net_tellschematics();
	}
}

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

void net_set(INTBIG count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp, *opp;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib;
	REGISTER NETWORK *net, **netlist;
	REGISTER INTBIG i, l, total, fun, tr, options;
	BOOLEAN showrequest;
	REGISTER char *pt;
	REGISTER VARIABLE *var;
	NETWORK *mynet[2];
	INTBIG x, y;
	REGISTER WINDOWPART *w;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	static char *message[1] = {"total-re-number"};
	REGISTER void *infstr;

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


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

	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;

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

		/* first highlight the arcs on this network */
		if (!showrequest)
		{
			infstr = initinfstr();
			for(i=0; netlist[i] != NONETWORK; i++)
			{
				net = netlist[i];
				ttyputverbose(M_("Network '%s'"), describenetwork(net));
				np = net->parent;

				/* show the arcs on this network */
				net_highlightnet(infstr, np, net);
#if 0		/* no need: lower-level nets are shown as lines */
				/* 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;

						/* ignore recursive references (showing icon in contents) */
						if (ni->proto->cell == snp->cell) continue;
						cnp = contentsview(ni->proto);
						if (cnp == NONODEPROTO) cnp = ni->proto;
						uni = descentparent(cnp, &index);
						if (uni == NONODEINST) continue;
						ni = uni;
						break;
					}
					if (ni == NONODEINST) break;
/* !!! bus issues */
					/* 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(infstr, snp, net);
				}
#endif
				/* handle multi-page schematic */
				if ((np->cellview->viewstate&MULTIPAGEVIEW) != 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)
					{
						/* 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 */
							opp = getportproto(w->curnodeproto, pp->protoname);
							if (opp == NOPORTPROTO) continue;

							/* show the arcs on this network */
							net_highlightnet(infstr, w->curnodeproto, opp->network);
						}
					}
				}
			}
			(void)asktool(us_tool, "show-multiple", (INTBIG)returninfstr(infstr));
		}

		/* if there are associated VHDL or simulation windows, show in them */
		for(i=0; netlist[i] != NONETWORK; i++)
		{
			net = netlist[i];
			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 */
							infstr = initinfstr();
							addstringtoinfstr(infstr, "entity ");
							addstringtoinfstr(infstr, w->curnodeproto->cell->cellname);
							addstringtoinfstr(infstr, " is port(");
							us_searchtext(w, returninfstr(infstr), 0, 1);
							if (net->portcount <= 0)
								us_searchtext(w, "signal ", 0, 0);
							us_searchtext(w, pt, 0, 0);
						} else
						{
							/* just look for the name, starting at the top */
							us_searchtext(w, pt, 0, 1);
						}
						break;
#if SIMTOOL
					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_cleartracehighlight();
							sim_window_addhighlighttrace(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);
						}
						if (tr != 0)
						{
							sim_window_cleartracehighlight();
							sim_window_addhighlighttrace(tr);
						}
						break;
#endif
				}
			}
		}
		return;
	}

	if (namesamen(pt, "show-geometry", l) == 0 && l > 5)
	{
		if (count < 2)
		{
			netlist = net_gethighlightednets(FALSE);
			if (netlist[0] == NONETWORK) return;
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return;
			}
			mynet[0] = getnetwork(par[1], np);
			if (mynet[0] == NONETWORK)
			{
				ttyputerr(M_("No net called %s"), par[1]);
				return;
			}
			mynet[1] = NONETWORK;
			netlist = mynet;
		}
		for(i=0; netlist[i] != NONETWORK; i++)
		{
			net = netlist[i];
			net_showgeometry(net);
		}
		return;
	}

	if (namesamen(pt, "extract", l) == 0)
	{
		np = getcurfacet();
		if (np == NONODEPROTO)
		{
			ttyputerr(_("No current facet to extract"));
			return;
		}
		net_conv_to_internal(np);
		return;
	}

	if (namesamen(pt, "default-busses", l) == 0 && l > 3)
	{
		if (count == 1)
		{
			ttyputusage("telltool network default-busses OPTION");
			return;
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "ascend-numbering", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options & ~NETDEFBUSBASEDESC, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Default busses will be numbered in ascending order"));
			return;
		}
		if (namesamen(pt, "descend-numbering", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options|NETDEFBUSBASEDESC, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Default busses will be numbered in descending order"));
			return;
		}
		if (namesamen(pt, "0-base", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options & ~NETDEFBUSBASE1, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Default busses will be numbered starting at 0"));
			return;
		}
		if (namesamen(pt, "1-base", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options|NETDEFBUSBASE1, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Default busses will be numbered starting at 1"));
			return;
		}
		ttyputbadusage("telltool network default-busses");
		return;
	}

	if (namesamen(pt, "name-facet-objects", l) == 0 && l >= 6)
	{
		np = getcurfacet();
		if (np == NONODEPROTO)
		{
			ttyputerr(M_("No current facet"));
			return;
		}

		(void)net_nameallnodes(np, TRUE);
		(void)net_nameallnets(np);
		return;
	}

	if (namesamen(pt, "name-library-objects", l) == 0 && l >= 6)
	{
		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			(void)net_nameallnodes(np, TRUE);
			(void)net_nameallnets(np);
		}
		return;
	}

	if (namesamen(pt, "name-connections", l) == 0 && l >= 6)
	{
		if (count < 2)
		{
			netlist = net_gethighlightednets(FALSE);
			if (netlist[0] == NONETWORK)
			{
				ttyputerr(_("Cannot determine network from highlighting"));
				return;
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(M_("No current facet"));
				return;
			}
			mynet[0] = getnetwork(par[1], np);
			if (mynet[0] == NONETWORK)
			{
				ttyputerr(M_("No net called %s"), par[1]);
				return;
			}
			mynet[1] = NONETWORK;
			netlist = mynet;
		}
		for(i=0; netlist[i] != NONETWORK; i++)
		{
			net = netlist[i];
			ttyputmsg(_("Network '%s':"), describenetwork(net));

			total = 0;
			for(ni = net->parent->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				np = ni->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;
				}

				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
					if (pi->conarcinst->network == net) break;
				if (pi == NOPORTARCINST) continue;

				if (total == 0) ttyputmsg(_("  Connects to:"));
				portposition(ni, pi->proto, &x, &y);
				ttyputmsg(_("    Node %s, port %s at (%s,%s)"), describenodeinst(ni),
					pi->proto->protoname, latoa(x, 0), latoa(y, 0));
				total++;
			}
			if (total == 0) ttyputmsg(_("  Not connected"));
		}
		return;
	}

	if (namesamen(pt, "power-and-ground", l) == 0)
	{
		if (count == 1)
		{
			ttyputusage("telltool network power-and-ground OPTION");
			return;
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "unify-all-networks", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options|NETCONPWRGND, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Unconnected power and ground nets will be equated"));
			telltool(net_tool, 1, message);
			return;
		}
		if (namesamen(pt, "separate-unconnected-networks", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options & ~NETCONPWRGND, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Unconnected power and ground nets will not be equated"));
			telltool(net_tool, 1, message);
			return;
		}
		if (namesamen(pt, "identify", l) == 0)
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return;
			}
			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
				net->temp1 = 0;
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
			{
				if (portispower(pp) || portisground(pp))
					pp->network->temp1 = 1;
			}
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (ni->proto->primindex == 0) continue;
				fun = nodefunction(ni);
				if (fun != NPCONPOWER && fun != NPCONGROUND) continue;
				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				{
					ai = pi->conarcinst;
					ai->network->temp1 = 1;
				}
			}
			infstr = initinfstr();
			total = 0;
			for(net = np->firstnetwork; net != NONETWORK; net = net->nextnetwork)
			{
				if (net->temp1 == 0) continue;
				net_highlightnet(infstr, np, net);
				total++;
			}
			(void)asktool(us_tool, "show-multiple", (INTBIG)returninfstr(infstr));
			if (total == 0)
				ttyputmsg(_("This facet has no Power or Ground networks"));
			return;
		}
		
		ttyputbadusage("telltool network power-and-ground");
		return;
	}

	if (namesamen(pt, "like-named-nets", l) == 0)
	{
		if (count == 1)
		{
			ttyputusage("telltool network like-named-nets OPTION");
			return;
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "unify-always", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options|NETCONCOMMONNAME, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Nets with the same name will always be equated"));
			telltool(net_tool, 1, message);
			return;
		}
		if (namesamen(pt, "unify-only-in-schematics", l) == 0)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey,
				net_options & ~NETCONCOMMONNAME, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Nets with the same name will be equated only in schematics"));
			telltool(net_tool, 1, message);
			return;
		}
		ttyputbadusage("telltool network like-named-nets");
		return;
	}

	if (namesamen(pt, "compare", l) == 0)
	{
		if (count == 1)
		{
			net_compare(FALSE, TRUE, NONODEPROTO, NONODEPROTO);
			return;
		}
		l = strlen(pt = par[1]);
		if (namesamen(pt, "highlight-other", l) == 0)
		{
			net_equate(1);
			return;
		}
		if (namesamen(pt, "pre-analysis", l) == 0)
		{
			net_compare(TRUE, TRUE, NONODEPROTO, NONODEPROTO);
			return;
		}
#ifdef FORCESUNTOOLS
		if (namesamen(pt, "analyze-facet", l) == 0)
		{
			(void)net_analyzefacet();
			return;
		}
#endif
		if (namesamen(pt, "not", l) == 0)
		{
			if (count <= 2)
			{
				ttyputusage("telltool network compare not OPTION");
				return;
			}
			l = strlen(pt = par[2]);
			if (namesamen(pt, "check-export-names", l) == 0 && l >= 7)
			{
				var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
					options & ~NCCCHECKEXPORTNAMES, VINTEGER|VDONTSAVE);
				ttyputverbose(M_("Circuit comparison will not check export names"));
				return;
			}
			if (namesamen(pt, "check-node-sizes", l) == 0 && l >= 7)
			{
				var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
					options & ~NCCCHECKSIZE, VINTEGER|VDONTSAVE);
				ttyputverbose(M_("Circuit comparison will not check node sizes"));
				return;
			}
			if (namesamen(pt, "flatten-hierarchy", l) == 0)
			{
				var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
					options & ~NCCHIERARCHICAL, VINTEGER|VDONTSAVE);
				ttyputverbose(M_("Circuits will be compared without flattening hierarchy"));
				return;
			}
			if (namesamen(pt, "recurse", l) == 0)
			{
				var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) options = 0; else options = var->addr;
				(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
					options & ~NCCRECURSE, VINTEGER|VDONTSAVE);
				ttyputverbose(M_("Circuits will be compared without recursing through hierarchy"));
				return;
			}
			ttyputbadusage("telltool network compare not");
			return;
		}
		if (namesamen(pt, "check-export-names", l) == 0 && l >= 7)
		{
			var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
				options | NCCCHECKEXPORTNAMES, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Circuit comparison will check export names"));
			return;
		}
		if (namesamen(pt, "check-node-sizes", l) == 0 && l >= 7)
		{
			var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
				options | NCCCHECKSIZE, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Circuit comparison will check node sizes"));
			return;
		}
		if (namesamen(pt, "flatten-hierarchy", l) == 0)
		{
			var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
				options | NCCHIERARCHICAL, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Circuits will be compared with hierarchy flattened"));
			return;
		}
		if (namesamen(pt, "recurse", l) == 0)
		{
			var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
			if (var == NOVARIABLE) options = 0; else options = var->addr;
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey,
				options | NCCRECURSE, VINTEGER|VDONTSAVE);
			ttyputverbose(M_("Circuits will be compared recursing through hierarchy"));
			return;
		}
		ttyputbadusage("telltool network compare");
		return;
	}

	if (namesamen(pt, "list-ports-below", l) == 0 && l >= 6)
	{
		/* get the currently highlighted network */
		if (count < 2)
		{
			netlist = net_gethighlightednets(FALSE);
			if (netlist[0] == NONETWORK) return;
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return;
			}
			mynet[0] = getnetwork(par[1], np);
			if (mynet[0] == NONETWORK)
			{
				ttyputerr(M_("No net called %s"), par[1]);
				return;
			}
			mynet[1] = NONETWORK;
			netlist = mynet;
		}

		for(i=0; netlist[i] != NONETWORK; i++)
		{
			net = netlist[i];
			ttyputmsg(_("Network '%s':"), describenetwork(net));

			/* 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_findportsdown(net, net->parent);
		}
		return;
	}

	if (namesamen(pt, "list-hierarchical-ports", l) == 0 && l >= 6)
	{
		/* get the currently highlighted network */
		if (count < 2)
		{
			netlist = net_gethighlightednets(FALSE);
			if (netlist[0] == NONETWORK) return;
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return;
			}
			mynet[0] = getnetwork(par[1], np);
			if (mynet[0] == NONETWORK)
			{
				ttyputerr(M_("No net called %s"), par[1]);
				return;
			}
			mynet[1] = NONETWORK;
			netlist = mynet;
		}

		for(i=0; netlist[i] != NONETWORK; i++)
		{
			net = netlist[i];
			ttyputmsg(_("Network '%s':"), describenetwork(net));

			/* 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;
	}

	if (namesamen(pt, "debug-toggle", l) == 0 && l > 3)
	{
		if (!net_debug)
		{
			net_debug = TRUE;
			ttyputmsg(M_("Network debugging on"));
		} else
		{
			net_debug = FALSE;
			ttyputmsg(M_("Network debugging off"));
		}
		return;
	}

	if (namesamen(pt, "re-number", l) == 0 && l >= 2)
	{
		if (count >= 2)
		{
			np = getnodeproto(par[1]);
			if (np == NONODEPROTO)
			{
				ttyputerr(M_("No facet named %s"), par[1]);
				return;
			}
			if (np->primindex != 0)
			{
				ttyputerr(M_("Can only renumber facets, not primitives"));
				return;
			}
		} else
		{
			np = getcurfacet();
			if (np == NONODEPROTO)
			{
				ttyputerr(_("No current facet"));
				return;
			}
		}

		net_reevaluatefacet(np);
		ttyputverbose(M_("Facet %s re-numbered"), describenodeproto(np));
		return;
	}

	if (namesamen(pt, "total-re-number", l) == 0)
	{
		net_totalrenumber(NOLIBRARY);
		ttyputverbose(M_("All libraries re-numbered"));
		return;
	}

	ttyputbadusage("telltool network");
}

/*
 * make request of the network tool:
 * "total-re-number" renumbers all libraries
 * "library-re-number" TAKES: library to renumbers
 * "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 NODEPROTO *np;
	REGISTER LIBRARY *lib;
	REGISTER INTBIG arg1, arg2, arg3;
	REGISTER INTBIG retval;

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

		lib = (LIBRARY *)arg1;
		net_totalrenumber(lib);
		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, FALSE);
		return(retval);
	}
	if (namesame(command, "name-all-nodes") == 0)
	{
		/* get the arguments */
		arg1 = va_arg(ap, INTBIG);

		np = (NODEPROTO *)arg1;
		retval = net_nameallnodes(np, TRUE);
		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(TOOL *source, BOOLEAN undoredo)
{
	/* code cannot be called by multiple procesors: uses globals */
	NOT_REENTRANT;

	net_globalwork = FALSE;
	net_current_source = source;
}

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

	if (!net_globalwork) return;

	if (net_debug) ttyputmsg(M_("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) continue;
			if (net_recursivelyredo(np))
			{
				ttyputmsg(_("Networks information may be incorrect...should redo network numbering"));
				break;
			}
		}
		if (np != NONODEPROTO) break;
	}
	net_globalwork = FALSE;
}

/*
 * Routine to renumber all networks.
 */
void net_totalrenumber(LIBRARY *lib)
{
	REGISTER LIBRARY *olib;
	REGISTER NODEPROTO *np;

	if (lib == NOLIBRARY)
	{
		/* renumber every library */
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			olib->userbits |= REDOFACETLIB;
			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->userbits |= REDOFACETNET;
		}
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
		{
			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			{
				if ((np->userbits&REDOFACETNET) == 0) continue;
				if (net_recursivelyredo(np)) return;
			}
		}
	} else
	{
		/* renumber just this library */
		lib->userbits |= REDOFACETLIB;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			np->userbits |= REDOFACETNET;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if ((np->userbits&REDOFACETNET) == 0) continue;
			if (net_recursivelyredo(np)) return;
		}
	}
}

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

	if (stopping(STOPREASONNETWORK) != 0)
	{
		return(TRUE);
	}

	/* first see if any lower facets need to be renumbered */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* ignore recursive references (showing icon in contents) */
		subnp = ni->proto;
		if (subnp->cell == np->cell) continue;

		cnp = contentsview(subnp);
		if (cnp != NONODEPROTO)
		{
			if (cnp->primindex == 0 && (cnp->userbits&REDOFACETNET) != 0)
			{
				if (net_recursivelyredo(cnp)) return(TRUE);
			}
		}
		if (cnp != subnp)
		{
			if (subnp->primindex == 0 && (subnp->userbits&REDOFACETNET) != 0)
			{
				if (net_recursivelyredo(subnp)) return(TRUE);
			}
		}
	}
	net_reevaluatefacet(np);

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

/*
 * 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 *bl;
	REGISTER PORTPROTO *pp;
	REGISTER VARIABLE *var;
	REGISTER NETWORK *net, *nextnet;
	REGISTER NODEPROTO *subnp;
	REGISTER PORTARCINST *pi;
	REGISTER INTBIG i, width;
	NODEINST *ni;

	if (net_debug)
		ttyputmsg(M_("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;
	for(i=0; i<np->globalnetcount; i++)
		np->globalnetworks[i] = 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;

	/* make a list of all named busses */
	net_buslistcount = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->proto != sch_busarc) continue;
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
		if (var == NOVARIABLE) continue;
		width = net_buswidth((char *)var->addr);
		net_addtobuslist(ai, NOPORTPROTO, width);
	}
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		width = net_buswidth(pp->protoname);
		if (width <= 1) continue;
		net_addtobuslist(NOARCINST, pp, width);
	}

	/* handle busses first */
	if (net_buslistcount > 0)
	{
		/* sort by bus width */
		esort(net_buslists, net_buslistcount, sizeof (BUSLIST), net_buslistwidthascending);

		/* renumber arcs, starting with the narrowest */
		for(i=0; i<net_buslistcount; i++)
		{
			bl = &net_buslists[i];
			if (bl->ai != NOARCINST)
			{
				ai = bl->ai;
				if (ai->network != NONETWORK) continue;
				net = net_newnetwork(np);
				(void)net_nconnect(ai, net, np);
			} else
			{
				pp = bl->pp;
				if (pp->network == NONETWORK) (void)net_pconnect(pp);
			}
		}
	}

	/* renumber all other bus arcs (on unnamed networks) */
	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, np);
	}

	/* 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, np);
	}

	/* 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 */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (subnp == NONODEPROTO) continue;
		if (subnp->primindex != 0) continue;

		/* ignore recursive references (showing icon in contents) */
		if (subnp->cell == np->cell) continue;
		if (net_mergebuswires(ni)) net_recursivelymarkabove(subnp);
	}

	/* do checks where arcs touch busses */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto == sch_buspinprim)
		{
			/* see if there is a wire arc on it */
			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
				if (pi->conarcinst->proto != sch_busarc) break;
			if (pi != NOPORTARCINST)
				net_checkvalidconnection(ni, pi->conarcinst);
		}
	}
}

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

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

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

void net_newobject(INTBIG addr, INTBIG type)
{
	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 && (ai->parent->userbits&REDOFACETNET) != 0) return;

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

		if (net_debug)
			ttyputmsg(M_("Network: arc %s created"), describearcinst(ai));

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

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

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

		if (net_debug) ttyputmsg(M_("Network: port %s created"), pp->protoname);

		if (net_pconnect(pp))
		{
			if (net_debug)
				ttyputmsg(M_("Network: must recheck instances of %s"),
					describenodeproto(pp->parent));
			net_recursivelymarkabove(pp->parent);
		}
	} else if ((type&VTYPE) == VNODEPROTO)
	{
		np = (NODEPROTO *)addr;
		if (net_debug) ttyputmsg(M_("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 INTBIG i;
	REGISTER NETWORK *net, *onet, *nextnet;

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

		if (net_debug)
			ttyputmsg(M_("Network: arc %s killed"), describearcinst(ai));

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

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

		/* if arc connects to an export, renumber all above this facet */
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			onet = pp->network;
			if (onet == net) break;
			if (onet->buswidth <= 1) continue;
			for(i=0; i<onet->buswidth; i++)
				if (onet->networklist[i] == net) break;
			if (i < onet->buswidth) break;
		}
		if (pp != NOPORTPROTO)
		{
			/* deleted arc connects to an export: must renumber above */
			net_recursivelymarkabove(np);
			return;
		}

		/* if this arc was on a multiply-named network, reevaluate the facet */
		if (net != NONETWORK && net->namecount > 1)
		{
			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 */
		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(np), np))
				{
					if (net_debug)
						ttyputmsg(M_("Network: must recheck instances of %s"), describenodeproto(ai->parent));
					net_recursivelymarkabove(np);
					return;
				}
			}
			for(pe = ai->end[i].nodeinst->firstportexpinst; pe != NOPORTEXPINST;
				pe = pe->nextportexpinst)
			{
				if (net_pconnect(pe->exportproto))
				{
					if (net_debug)
						ttyputmsg(M_("Network: must recheck instances of %s"),
							describenodeproto(np));
					net_recursivelymarkabove(np);
					return;
				}
			}
		}
	} else if ((type&VTYPE) == VPORTPROTO)
	{
		pp = (PORTPROTO *)addr;
		np = pp->parent;
		if (net_debug) ttyputmsg(M_("Network: port %s killed"), pp->protoname);

		/* stop now if the entire facet will be renumbered */
		if (net_globalwork && (np->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 */
		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(np), np))
			{
				if (net_debug)
					ttyputmsg(M_("Network: must recheck instances of %s"),
						describenodeproto(np));
				net_recursivelymarkabove(np);
				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;
	char **strings, *netname;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG count;
	REGISTER PORTPROTO *pp, *subpp;

	if ((newtype&VCREF) != 0)
	{
		if ((type&VTYPE) == VPORTPROTO)
		{
			netname = changedvariablename(type, key, newtype);
			if (namesame(netname, "protoname") == 0)
			{
				if (net_debug)
					ttyputmsg(M_("Network: export name %s created"), netname);

				/* stop now if the entire facet will be renumbered */
				pp = (PORTPROTO *)addr;
				subpp = pp;
				ni = subpp->subnodeinst;
				while (ni->proto->primindex == 0)
				{
					ni = subpp->subnodeinst;
					subpp = subpp->subportproto;
				}
				if (ni->proto != sch_buspinprim) return;
				if (net_globalwork && (pp->parent->userbits&REDOFACETNET) != 0) return;

				/* check name for validity */
				count = net_evalbusname(APBUS, pp->protoname, &strings, NOARCINST, pp->parent, 1);
				if (count < 0)
				{
					ttyputerr(_("Warning (facet %s): invalid network name: %s"), describenodeproto(pp->parent),
						pp->protoname);
					return;
				}

				/* for busses, must redo entire facet */
				if (pp->network->buswidth != count)
				{
					net_recursivelymarkabove(pp->parent);
					return;
				}

			}
		}
		return;
	}

	/* handle changes to variables on the network object */
	if ((type&VTYPE) == VTOOL && (TOOL *)addr == net_tool)
	{
		if (key == net_optionskey)
		{
			var = getvalkey(addr, type, VINTEGER, key);
			if (var != NOVARIABLE)
			{
				net_options = var->addr;
				net_checkresistorstate = TRUE;
			}
			return;
		}
		return;
	}

	/* handle changes to node variables */
	if ((type&VTYPE) == VNODEINST)
	{
		/* handle changes to node name variable (look for arrays) */
		if (key == el_node_name_key)
		{
			net_setnodewidth((NODEINST *)addr);
		}
		return;
	}

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

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

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

	/* check name for validity */
	count = net_evalbusname((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(np), (char *)var->addr);
		return;
	}

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

	/* if merging common net names, check this one */
	net = NONETWORK;
	if ((net_options&NETCONCOMMONNAME) != 0 || np->cellview == el_schematicview ||
		ai->proto->tech == sch_tech)
	{
		/* see if network exists in this facet */
		net = getnetwork((char *)var->addr, np);
	}
	if (net == NONETWORK) net = net_newnetwork(np); else
	{
		if (net_current_source == us_tool)
			ttyputmsg(_("Network %s extended to this arc"), describenetwork(net));
	}

	/* propagate the network through this facet */
	if (net_nconnect(ai, net, np))
	{
		if (net_debug) ttyputmsg(M_("Network: must recheck instances of %s"),
			describenodeproto(np));
		net_recursivelymarkabove(np);
	}
}

void net_killvariable(INTBIG addr, INTBIG type, INTBIG key, INTBIG oldaddr, INTBIG oldtype,
	UINTBIG *olddescript)
{
	REGISTER ARCINST *ai;
	REGISTER NODEPROTO *np;

	/* handle changes to node name variable (look for arrays) */
	if ((type&VTYPE) == VNODEINST)
	{
		if (key == el_node_name_key)
		{
			net_setnodewidth((NODEINST *)addr);
		}
		return;
	}

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

	if (net_debug)
		ttyputmsg(M_("Network: arc name %s deleted"), (char *)oldaddr);

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

	/* stop now if the entire facet will be renumbered */
	if (net_globalwork && (np->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(np);
		return;
	}

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

	/* create a new network and propagate it */
	if (net_nconnect(ai, net_newnetwork(np), np))
	{
		if (net_debug) ttyputmsg(M_("Network: must recheck instances of %s"),
			describenodeproto(np));
		net_recursivelymarkabove(np);
	}
}

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

	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_optionskey);
	if (var != NOVARIABLE)
	{
		net_options = var->addr;
		net_checkresistorstate = TRUE;
	}

	/* handle obsolete flags */
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_connect_power_groundkey);
	if (var != NOVARIABLE)
	{
		if (var->addr != 0) net_options |= NETCONPWRGND; else
			net_options &= ~NETCONPWRGND;
		net_found_obsolete_variables = 1;
	}
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_connect_common_namekey);
	if (var != NOVARIABLE)
	{
		if (var->addr != 0) net_options |= NETCONCOMMONNAME; else
			net_options &= ~NETCONCOMMONNAME;
		net_found_obsolete_variables = 1;
	}
}

void net_eraselibrary(LIBRARY *lib)
{
}

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

/*
 * Routine to allocate an "arc check" object for network tracing.
 * Returns NOARCCHECK on error.
 */
ARCCHECK *net_allocarccheck(void)
{
	REGISTER ARCCHECK *ac;

	if (net_arccheckfree != NOARCCHECK)
	{
		ac = net_arccheckfree;
		net_arccheckfree = ac->nextarccheck;
	} else
	{
		ac = (ARCCHECK *)emalloc(sizeof (ARCCHECK), net_tool->cluster);
		if (ac == 0) return(NOARCCHECK);
	}
	return(ac);
}

/*
 * Routine to free arc check object "ac".
 */
void net_freearccheck(ARCCHECK *ac)
{
	ac->nextarccheck = net_arccheckfree;
	net_arccheckfree = ac;
}

/*
 * 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 true
 * if the facet should be rechecked (because ports were modified).  Traces
 * contents ports in case of iconic connections.
 */
BOOLEAN net_nconnect(ARCINST *ai, NETWORK *newnetwork, NODEPROTO *np)
{
	REGISTER ARCCHECK *ac;
	REGISTER ARCINST *oai;
	BOOLEAN result, thisresult;

	/* mark all arcs as un-checked */
	for(oai = np->firstarcinst; oai != NOARCINST; oai = oai->nextarcinst)
		oai->temp2 = 0;

	/* create an arc-check object for this request */
	ac = net_allocarccheck();
	if (ac == NOARCCHECK) return(FALSE);
	ac->ai = ai;
	ai->temp2 = 1;

	/* put this on the list */
	ac->nextarccheck = NOARCCHECK;
	net_firstarccheck = ac;

	/* process everything on the list */
	result = FALSE;
	while (net_firstarccheck != NOARCCHECK)
	{
		/* remove the top of the list */
		ac = net_firstarccheck;
		net_firstarccheck = ac->nextarccheck;

		/* process it */
		thisresult = net_donconnect(ac->ai, newnetwork, np);
		if (thisresult) result = TRUE;

		/* free this list object */
		net_freearccheck(ac);
	}
	return(result);
}

BOOLEAN net_donconnect(ARCINST *ai, NETWORK *newnetwork, NODEPROTO *np)
{
	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, *arcname;
	static char arctag[50];
	INTBIG special;
	REGISTER INTBIG ret, i, j, tempname, fun, width, buswidth, len1, len2, base;
	REGISTER BOOLEAN recheck;
	REGISTER ARCCHECK *ac;

	if (net_debug)
		ttyputmsg(M_("Setting network %ld onto arc %s"), newnetwork, describearcinst(ai));

	if (ai->network == newnetwork) return(FALSE);

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

	/* ignore if two busses have different width */
	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
	if (var == NOVARIABLE) arcname = 0; else
		arcname = (char *)var->addr;
	if (ai->proto == sch_busarc && newnetwork->buswidth > 1 &&
		((ai->network != NONETWORK && ai->network->buswidth > 1) || var != NOVARIABLE))
	{
		/* bus network */
		if (var == NOVARIABLE) width = ai->network->buswidth; else
		{
			width = net_buswidth(arcname);
			if (width == 1 && newnetwork->buswidth > 1)
			{
				/* name indicates one signal, see if it simply omitted any array specification */
				for(pt = arcname; *pt != 0; pt++)
					if (*pt == '[') break;
				if (*pt == 0)
				{
					/* use the implied width */
					width = newnetwork->buswidth;
				}
			}
		}	
		if (width != newnetwork->buswidth)
		{
			/* 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(np), base1, base2);
			}
			return(TRUE);
		}
	}

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

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

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

	/* determine the width of bus arcs by looking at adjoining facet exports */
	buswidth = net_buswidthofarc(ai);

	/* if this arc has a name, establish it */
	if (arcname != 0 && *arcname != 0)
	{
		/* add this arc name to the network */
		if (net_debug)
			ttyputmsg(M_("Adding name %s to network %s"), arcname, describenetwork(newnetwork));
		if ((var->type&VDISPLAY) == 0) tempname = 1; else tempname = 0;
		if (buswidth > 1)
		{
			/* be sure there is an array specification, create if not */
			for(pt = arcname; *pt != 0; pt++) if (*pt == '[' || *pt == ',') break;
			if (*pt == 0)
			{
				if (net_arrayedarcname != 0) efree(net_arrayedarcname);
				if ((net_options&NETDEFBUSBASE1) == 0) base = 0; else base = 1;
				if ((net_options&NETDEFBUSBASEDESC) == 0)
				{
					(void)sprintf(arctag, "[%ld:%ld]", base, buswidth-1+base);
				} else
				{
					(void)sprintf(arctag, "[%ld:%ld]", buswidth-1+base, base);
				}
				net_arrayedarcname = (char *)emalloc(strlen(arcname)+strlen(arctag)+2,
					net_tool->cluster);
				if (net_arrayedarcname == 0) return(FALSE);
				strcpy(net_arrayedarcname, arcname);
				strcat(net_arrayedarcname, arctag);
				arcname = net_arrayedarcname;
			}
		}
		ret = net_addnametonet(arcname, tempname, newnetwork, ai, NOPORTPROTO, np);
		if (ret > 0) recheck = TRUE;
		if (ret >= 0) net_putarclinkonnet(newnetwork, ai);
	} else
	{
		if (buswidth > 1 && newnetwork->buswidth == 1)
		{
			/* unnamed bus: generate individual unnamed signals */
			newnetwork->networklist = (NETWORK **)emalloc(((sizeof (NETWORK *)) * buswidth),
				np->cell->cluster);
			if (newnetwork->networklist != 0)
			{
				newnetwork->buswidth = (INTSML)buswidth;
				net_ensuretempbusname(newnetwork);
				for(i=0; i<buswidth; i++)
				{
					newnetwork->networklist[i] = net_newnetwork(np);
					newnetwork->networklist[i]->buslinkcount++;
				}
			}
			recheck = TRUE;
		}
	}

	if (buswidth > 1 && newnetwork->buswidth > 1 && buswidth != newnetwork->buswidth)
	{
		ttyputmsg(_("Warning: facet %s has %d-wide %s connected to node with %ld-wide port"),
			describenodeproto(np), newnetwork->buswidth, describearcinst(ai), buswidth);
	}

	/* initialize the special information about this network */
	special = newnetwork->globalnet;

	/* 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 = TRUE;

		/* 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;
			}
		}

		/* check out the node on the arc */
		if (cnp == sch_globalprim)
		{
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, sch_globalnamekey);
			if (var != NOVARIABLE)
			{
				net_setglobalnet(&special, -1, (char *)var->addr, newnetwork,
					((ni->userbits&NTECHBITS) >> NTECHBITSSH) << STATEBITSSH, np);
				recheck = TRUE;
			}
		} else
		{
			fun = (cnp->userbits&NFUNCTION)>>NFUNCTIONSH;
			if (fun == NPCONPOWER)
			{
				net_setglobalnet(&special, GLOBALNETPOWER, 0, newnetwork, PWRPORT, np);
				recheck = TRUE;
			} else if (fun == NPCONGROUND)
			{
				net_setglobalnet(&special, GLOBALNETGROUND, 0, newnetwork, GNDPORT, np);
				recheck = TRUE;
			}
		}

		if ((net_options&NETCONPWRGND) != 0)
		{
			/* see if subport on the node is power or ground */
			if (portispower(cpp))
			{
				net_setglobalnet(&special, GLOBALNETPOWER, 0, newnetwork, PWRPORT, np);
				recheck = TRUE;
			} else if (portisground(cpp))
			{
				net_setglobalnet(&special, GLOBALNETGROUND, 0, newnetwork, GNDPORT, np);
				recheck = TRUE;
			}
		}

		/* 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->temp2 != 0) continue;
			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;
			if (copp->network == NONETWORK) continue;

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

			/* see if the two ports connect electrically */
			if (cpp->network != copp->network)
			{
				/* check for single signals ending up as part of another bus */
				if (newnetwork->buswidth == 1 && copp->network->buswidth > 1)
				{
					for(j=0; j<copp->network->buswidth; j++)
						if (cpp->network == copp->network->networklist[j]) break;
					if (j < copp->network->buswidth)
						recheck = TRUE;
				}
				continue;
			}

			/* 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;
				}
			}

			/* recurse on the nodes of this arcinst */
			if (net_debug)
				ttyputmsg(M_("Propagating network %ld to arc %s"), newnetwork,
					describearcinst(oai));

			/* create an arc-check object for this request */
			ac = net_allocarccheck();
			if (ac == NOARCCHECK) return(FALSE);
			ac->ai = oai;
			oai->temp2 = 1;

			/* put this on the list */
			ac->nextarccheck = net_firstarccheck;
			net_firstarccheck = ac;
		}

		/* 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, np) > 0)
						recheck = TRUE;

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

				if ((net_options&NETCONPWRGND) != 0)
				{
					/* check for power or ground ports */
					if (portispower(pe->exportproto))
					{
						net_setglobalnet(&special, GLOBALNETPOWER, 0, newnetwork, PWRPORT, np);
						recheck = TRUE;
					} else if (portisground(pe->exportproto))
					{
						net_setglobalnet(&special, GLOBALNETGROUND, 0, newnetwork, GNDPORT, np);
						recheck = TRUE;
					}
				}
				recheck = TRUE;
				break;
			}
		}
	}
	return(recheck);
}

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

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

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

	/* 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(np);
			if (net_debug)
				ttyputmsg(M_("Network: creating new network for %s (on an arc with no net)"),
					pp->protoname);
		} else if (net_debug)
			ttyputmsg(M_("Network: adding port %s to net %s"), pp->protoname,
				describenetwork(net));
		(void)net_addnametonet(pp->protoname, 0, net, pi->conarcinst, pp, np);
		net_putportonnet(pp, net);
		if (pp->network != oldnet) ret = TRUE; else ret = FALSE;
		if (net_nconnect(pi->conarcinst, net, np)) ret = TRUE;
		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) continue;
		net_putportonnet(pp, net);
		(void)net_addnametonet(pp->protoname, 0, net, NOARCINST, pp, pp->parent);
		if (pp->network != oldnet) return(TRUE);
		return(FALSE);
	}

	/* give up and assign a new net number */
	net = net_newnetwork(np);
	if (net_debug)
		ttyputmsg(M_("Network: creating new network for %s (gave up on all other tests)"),
			pp->protoname);
	(void)net_addnametonet(pp->protoname, 0, net, NOARCINST, pp, np);
	net_putportonnet(pp, net);
#if 1
	if (np->cellview == el_schematicview || (np->cellview->viewstate&MULTIPAGEVIEW) != 0)
	{
		if (portispower(pp))
		{
			special = -1;
			net_setglobalnet(&special, GLOBALNETPOWER, 0, net, PWRPORT, np);
		} else if (portisground(pp))
		{
			special = -1;
			net_setglobalnet(&special, GLOBALNETGROUND, 0, net, GNDPORT, np);
		}
	}
#endif
	return(TRUE);
}

/*
 * Routine to update the array information on node "ni" (its name has changed).
 */
void net_setnodewidth(NODEINST *ni)
{
	REGISTER INTBIG newarraysize;
	REGISTER VARIABLE *var;

	/* see if this node can be arrayed */
	if (!isschematicview(ni->parent)) newarraysize = 0; else
	{
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
		if (var == NOVARIABLE)
		{
			newarraysize = 0;
		} else
		{
			newarraysize = net_buswidth((char *)var->addr);
		}
	}
	if (newarraysize != ni->arraysize)
	{
		ni->arraysize = newarraysize;
	}
}

/*
 * 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.
 */
INTBIG net_addnametonet(char *name, INTBIG tempname, NETWORK *newnetwork, ARCINST *ai,
	PORTPROTO *pp, NODEPROTO *facet)
{
	REGISTER NETWORK *net, *subnet;
	INTBIG len, wid, busexists;
	REGISTER INTBIG count, i, bits, dontname;
	REGISTER BOOLEAN recheck;
	REGISTER char *pt, *opt;
	char **strings, *netname;

	/* special case if this is a temporary name */
	if (tempname != 0)
	{
		/* if the net already has a real name, check it out */
		if (newnetwork->namecount > 0)
		{
			/* if the existing network name isn't temporary, ignore this temp one */
			if (newnetwork->tempname == 0) return(-1);

			/* if the new name is arrayed and the old isn't, use the new */
			for(pt = name; *pt != 0; pt++) if (*pt == '[') break;
			for(opt = newnetwork->netname; *opt != 0; opt++) if (*opt == '[') break;
			if (*opt == '[' || *pt == 0) return(-1);
			efree(newnetwork->netname);
			newnetwork->namecount = 0;
		}

		/* 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 = FALSE;
	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->buswidth)
	{
		/* 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_options&NETCONCOMMONNAME) != 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);
			subnet = newnetwork->networklist[i];

			/* special case if this is a temporary name */
			dontname = 0;
			if (tempname != 0)
			{
				/* this is a temp name: ignore if already a real one */
				if (subnet->namecount > 0) dontname = 1; else
					subnet->tempname = 1;
			} else
			{
				/* this is a real name: if the net has a temporary one, kill it */
				if (subnet->tempname != 0 && subnet->namecount == 1)
				{
					efree(subnet->netname);
					subnet->namecount = 0;
					subnet->tempname = 0;
				}
			}

			if (dontname == 0) (void)net_namenet(netname, subnet);
			if (net != NONETWORK && net != subnet)
			{
				if (net_mergenet(net, subnet)) recheck = TRUE;
			}
			efree(netname);
		}
		busexists++;
	}

	/* see if this net name is in use */
	net = NONETWORK;
	if ((net_options&NETCONCOMMONNAME) != 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(name, facet);

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

	/* reset "tempname" field since "net_namenet" wipes it out */
	if (tempname != 0) newnetwork->tempname = 1;

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

/*
 * 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, *inp;

	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)
	{
		/* ignore recursive references (showing icon in contents) */
		if (ni->proto->cell == ni->parent->cell) continue;

		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);
	}

	/* if this facet has an icon, search for instances of that */
	inp = iconview(np);
	if (inp == NONODEPROTO) return;

	/* now do all facets higher than the icon */
	for(ni = inp->firstinst; ni != NONODEINST; ni = ni->nextinst)
	{
		/* ignore recursive references (showing icon in contents) */
		if (ni->proto->cell == ni->parent->cell) continue;

		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 true if
 * the network has been deleted
 */
BOOLEAN net_takearcfromnet(ARCINST *ai)
{
	REGISTER NETWORK *net;

	net = ai->network;
	if (net == NONETWORK) return(FALSE);
	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(TRUE);
	}
	return(FALSE);
}

/*
 * 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 true
 * if the network has been deleted
 */
BOOLEAN net_takeportfromnet(PORTPROTO *pp)
{
	REGISTER NETWORK *net;

	net = pp->network;
	if (net == NONETWORK) return(FALSE);
	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(TRUE);
	}
	return(FALSE);
}

/*
 * 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 INTBIG 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 INTBIG 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, INTBIG count, char **strings, INTBIG tempname)
{
	REGISTER INTBIG i, dontname;
	REGISTER NETWORK *singlenet;
	char *subnetname;

	if (net->buswidth == 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];

				/* special case if this is a temporary name */
				dontname = 0;
				if (tempname != 0)
				{
					/* this is a temp name: ignore if already a real one */
					if (singlenet->namecount > 0) dontname = 1; else
						singlenet->tempname = 1;
				} else
				{
					/* this is a real name: if the net has a temporary one, kill it */
					if (singlenet->tempname != 0 && singlenet->namecount > 0)
					{
						efree(singlenet->netname);
						singlenet->namecount = 0;
						singlenet->tempname = 0;
					}
				}
				if (dontname == 0)
					(void)net_namenet(strings[i], net->networklist[i]);
			}
		}
		return;
	}

	net->buswidth = (INTSML)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++;
		net->networklist[i]->tempname = (INTSML)tempname;
	}
}

/*
 * routine to give network "net" the name "name".  Returns true if the network
 * already has the name.
 */
BOOLEAN net_namenet(char *name, NETWORK *net)
{
	REGISTER char *newname, *pt, *opt;
	REGISTER INTBIG len, totallen, i, j, insertloc, match;

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

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

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

	/* insert the new name alphabetically */
	pt = net->netname;
	j = 0;
	for(i=0; i<insertloc; i++)
	{
		while (*pt != 0) newname[j++] = *pt++;
		newname[j++] = *pt++;
	}
	opt = name;
	while (*opt != 0) newname[j++] = *opt++;
	newname[j++] = 0;
	for(i=insertloc; i<net->namecount; i++)
	{
		while (*pt != 0) newname[j++] = *pt++;
		newname[j++] = *pt++;
	}

	efree(net->netname);
	net->netname = newname;
	net->namecount++;
	return(FALSE);
}

/*
 * 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;

	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);
	}
	net->netname = NOSTRING;
	net->namecount = 0;
	net->tempname = 0;
	net->arccount = net->refcount = net->portcount = net->buslinkcount = 0;
	net->parent = facet;
	net->globalnet = -1;
	net->buswidth = 1;
	net->firstvar = NOVARIABLE;
	net->numvar = 0;

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

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

	/* if this is a global network, mark the global signal "available" */
	if (net->globalnet >= 0)
	{
		if (net->globalnet < facet->globalnetcount)
			facet->globalnetworks[net->globalnet] = NONETWORK;
	}

	/* 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 INTBIG i;

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

	for(i=0; i<net->buswidth; 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->buswidth = 1;
}

void net_freenetwork(NETWORK *net, NODEPROTO *facet)
{
	if (net->namecount != 0) efree(net->netname);
	net->namecount = 0;
	net->netname = NOSTRING;
	if (net->buswidth > 1) efree((char *)net->networklist);
	net->buswidth = 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
 * true if ports were changed.  NOTE: merged busses must have same width
 * and this routine cannot handle that error condition properly.  Shouldn't
 * ever happen.
 */
BOOLEAN net_mergenet(NETWORK *oldnet, NETWORK *newnet)
{
	REGISTER char *pt;
	REGISTER INTBIG i;
	REGISTER BOOLEAN ret;
	REGISTER BOOLEAN portschanged;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER NETWORK *net;

	if (net_debug)
		ttyputmsg(M_("Merging old network %ld into %ld"), oldnet, newnet);

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

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

	/* 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;
		}
	}

	/* if old net is global, set that on new net */
	if (oldnet->globalnet >= 0)
		newnet->globalnet = oldnet->globalnet;

	/* 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->buswidth <= 1) continue;
			for(i=0; i<net->buswidth; 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(FALSE);
				}
			}
			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) return(FALSE);
		}
	}

	/* replace port references to the old network with the new one */
	portschanged = FALSE;
	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) return(TRUE);
		portschanged = TRUE;
	}

	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)
	{
		/* clear all library flags */
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			olib->userbits &= ~REDOFACETLIB;
		net_globalwork = TRUE;
	}

	/* 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, *arcname, *startpt;
	char *netname;
	REGISTER INTBIG k, found, len;
	UINTBIG descript[TEXTDESCRIPTSIZE];
	REGISTER NETWORK *net;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER VARIABLE *var;
	REGISTER void *infstr;

	/* 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)
	{
		name = net->netname;
		for(k=0; k<net->namecount; k++)
		{
			startpt = net_findnameinbus(newname, name);
			if (startpt != 0)
			{
				ttyputerr(_("Network name %s already exists in facet %s"), newname,
					describenodeproto(np));
				return;
			}
			name += strlen(name) + 1;
		}
	}

	/* substitute in all arcs */
	len = strlen(oldname);
	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_key);
		if (var == NOVARIABLE) continue;

		/* parse the network name */
		arcname = (char *)var->addr;
		found = 0;
		for(;;)
		{
			startpt = net_findnameinbus(oldname, arcname);
			if (startpt == 0) break;
			infstr = initinfstr();
			for(pt = arcname; pt < startpt; pt++)
				addtoinfstr(infstr, *pt);
			addstringtoinfstr(infstr, newname);
			pt += len;
			addstringtoinfstr(infstr, pt);
			arcname = returninfstr(infstr);
			found++;
		}
		if (found > 0)
		{
			/* rename the arc */
			(void)allocstring(&netname, arcname, el_tempcluster);
			TDCOPY(descript, var->textdescript);
			startobjectchange((INTBIG)ai, VARCINST);
			var = setvalkey((INTBIG)ai, VARCINST, el_arc_name_key, (INTBIG)netname, VSTRING|VDISPLAY);
			if (var == NOVARIABLE) continue;
			modifydescript((INTBIG)ai, VARCINST, var, descript);
			endobjectchange((INTBIG)ai, VARCINST);
			efree((char *)netname);
		}
	}

	/* substitute in all exports */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (namesamen(pp->protoname, oldname, len) != 0) continue;
		if (pp->protoname[len] != 0 && pp->protoname[len] != '[') continue;
		if (pp->protoname[len] == 0)
		{
			startobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
			setval((INTBIG)pp, VPORTPROTO, "protoname", (INTBIG)newname, VSTRING);
			endobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
		} else
		{
			infstr = initinfstr();
			addstringtoinfstr(infstr, newname);
			addstringtoinfstr(infstr, &pp->protoname[len]);
			startobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
			setval((INTBIG)pp, VPORTPROTO, "protoname", (INTBIG)returninfstr(infstr), VSTRING);
			endobjectchange((INTBIG)pp->subnodeinst, VNODEINST);
		}
	}

	ttyputmsg(_("Network %s renamed to %s"), oldname, newname);
}

/*
 * Routine to find the name "wantname" in the bus name "busname".
 * Returns the character position where the name starts (0 if not found).
 */
char *net_findnameinbus(char *wantname, char *busname)
{
	REGISTER char *pt;
	REGISTER INTBIG len;

	len = strlen(wantname);
	for(pt = busname; *pt != 0; pt++)
	{
		if (namesamen(pt, wantname, len) == 0)
		{
			if (pt[len] == 0 || pt[len] == '[' || pt[len] == ',')
				return(pt);
		}
		while (*pt != 0 && *pt != '[' && *pt != ',') pt++;
		while (*pt == '[')
		{
			pt++;
			while (*pt != 0 && *pt != ']') pt++;
			if (*pt == ']') pt++;
		}
		if (*pt == 0) break;
	}
	return(0);
}

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

#define MULTIDIMENSIONALBUS 1

/*
 * 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.
 */
INTBIG net_evalbusname(INTBIG funct, char *name, char ***strings,
	ARCINST *thisai, NODEPROTO *facet, INTBIG showerrors)
{
	REGISTER char *key, *cindex, *endindex, *errorstring, *busname;
	char *ptin, *savekey, ***mystrings;
	INTBIG count, *stringcount;
	REGISTER INTBIG ch1, ch2, perfect, dimension, dimstart, dimend, dimlen;
	REGISTER INTBIG indexval, endindexval, origfunct, i, index, range;
	REGISTER void *infstr;

	/* 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, &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 */
			if (thisai == NOARCINST)
			{
				(void)allocstring(&savekey, "", el_tempcluster);
			} else
			{
				busname = net_busnameofarc(thisai);
				if (busname == 0)
				{
					ttyputmsg(_("Facet %s, network '%s': cannot determine bus name to use"),
						describenodeproto(facet), name);
					busname = "XXX";
				}
				(void)allocstring(&savekey, busname, el_tempcluster);
			}
		}

		/* loop through the indexed entries */
		dimension = 0;
		dimstart = count;
		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))
			{
				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" */
				if (dimension > 0)
				{
					for(i=dimstart; i<dimend; i++)
					{
						infstr = initinfstr();
						formatinfstr(infstr, "%s[%s]", (*mystrings)[i], cindex);
						net_insertstring(returninfstr(infstr), dimend + dimlen + (i-dimstart)*(dimlen+1),
							&count, stringcount, mystrings);
					}
					dimlen++;
				} else
				{
					infstr = initinfstr();
					formatinfstr(infstr, "%s[%s]", savekey, cindex);
					net_addstring(returninfstr(infstr), &count, stringcount, mystrings);
				}
			} else
			{
				/* ':' 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, ",]");
				ch2 = 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))
				{
					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(index=indexval; index<=endindexval; index++)
					{
						if (dimension > 0)
						{
							for(i=dimstart; i<dimend; i++)
							{
								infstr = initinfstr();
								formatinfstr(infstr, "%s[%ld]", (*mystrings)[dimstart+i], index);
								net_insertstring(returninfstr(infstr), dimend + dimlen + i*(dimlen+1),
									&count, stringcount, mystrings);
							}
							dimlen++;
						} else
						{
							infstr = initinfstr();
							formatinfstr(infstr, "%s[%ld]", savekey, index);
							net_addstring(returninfstr(infstr), &count, stringcount, mystrings);
						}
					}
				} else
				{
					for(index=indexval; index>=endindexval; index--)
					{
						if (dimension > 0)
						{
							for(i=dimstart; i<dimend; i++)
							{
								infstr = initinfstr();
								formatinfstr(infstr, "%s[%ld]", (*mystrings)[dimstart+i], index);
								net_insertstring(returninfstr(infstr), dimend + dimlen + i*(dimlen+1),
									&count, stringcount, mystrings);
							}
							dimlen++;
						} else
						{
							infstr = initinfstr();
							formatinfstr(infstr, "%s[%ld]", savekey, index);
							net_addstring(returninfstr(infstr), &count, stringcount, mystrings);
						}
					}
				}
			}
			if (ch2 == ',') continue;

			/* at the "]", clean up multidimensional array */
			if (dimension > 0)
			{
				range = dimend - dimstart;
				for(i=dimend; i<count; i++)
				{
					key = (*mystrings)[i-range];
					(*mystrings)[i-range] = (*mystrings)[i];
					(*mystrings)[i] = key;
				}
				count-= range;
			}

			/* see if a "[" follows it for a multidimensional array */
			if (*ptin != '[') break;
			ptin++;
			dimend = count;
			dimension++;
			dimlen = 0;
#ifdef MULTIDIMENSIONALBUS
			continue;
#else
			break;
#endif
		}

		/* 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, &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"),
				describenodeproto(facet), name);
	}

	*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, TRUE);
		if (ch != 0) return(ch);
		ch = net_findnameofbus(ai, FALSE);
		if (ch != 0) return(ch);
	}
	return(0);
}

/*
 * 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 true.
 */
char *net_findnameofbus(ARCINST *ai, BOOLEAN justbus)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *oai;
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER INTBIG i, fun;
	REGISTER char *ch;
	REGISTER void *infstr;

	if (ai->proto == sch_busarc)
	{
		if (ai->network != NONETWORK)
		{
			if (ai->network->namecount > 0)
			{
				infstr = initinfstr();
				for(ch = ai->network->netname; *ch != 0;  ch++)
				{
					if (*ch == '[') break;
					addtoinfstr(infstr, *ch);
				}
				return(returninfstr(infstr));
			}
		}
	}
	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 && 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)
			{
				infstr = initinfstr();
				for(ch = pe->exportproto->protoname; *ch != 0;  ch++)
				{
					if (*ch == '[') break;
					addtoinfstr(infstr, *ch);
				}
				return(returninfstr(infstr));
			}
		}
	}
	return(0);
}

void net_insertstring(char *key, INTBIG index, INTBIG *count, INTBIG *stringcount, char ***mystrings)
{
	REGISTER char **newstrings, *lastone;
	REGISTER INTBIG i;

	if (*count >= *stringcount)
	{
		newstrings = (char **)emalloc(((sizeof (char *)) * (*count + 1)), net_tool->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_tool->cluster);
		if (*stringcount != 0)
			efree((char *)*mystrings);
		*stringcount = *count + 1;
		*mystrings = newstrings;
	}

	/* rearrange */
	lastone = (*mystrings)[*count];
	for(i = *count; i > index; i--)
		(*mystrings)[i] = (*mystrings)[i-1];
	(*mystrings)[index] = lastone;

	(void)reallocstring(&(*mystrings)[index], key, net_tool->cluster);
	(*count)++;
}

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

	if (*count >= *stringcount)
	{
		newstrings = (char **)emalloc(((sizeof (char *)) * (*count + 1)), net_tool->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_tool->cluster);
		if (*stringcount != 0)
			efree((char *)*mystrings);
		*stringcount = *count + 1;
		*mystrings = newstrings;
	}
	(void)reallocstring(&(*mystrings)[*count], key, net_tool->cluster);
	(*count)++;
}

/*
 * 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->buswidth <= 1) continue;

		/* make sure the bus has this network on it */
		for(j=0; j<oai->network->buswidth; 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->buswidth <= 1) continue;

		/* make sure the bus has this network on it */
		for(j=0; j<pp->network->buswidth; 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 ensure that network "net" (a bus) has a proper bus name if it is named temporarily.
 */
void net_ensuretempbusname(NETWORK *net)
{
	INTBIG count, base;
	REGISTER void *infstr;

	if (net->buswidth <= 1) return;
	if (net->tempname == 0) return;
	if (net->namecount != 1) return;
	count = net_buswidth(net->netname);
	if (count != 1) return;
	infstr = initinfstr();
	addstringtoinfstr(infstr, net->netname);
	if ((net_options&NETDEFBUSBASE1) == 0) base = 0; else base = 1;
	if ((net_options&NETDEFBUSBASEDESC) == 0)
	{
		formatinfstr(infstr, "[%ld:%ld]", base, net->buswidth-1+base);
	} else
	{
		formatinfstr(infstr, "[%ld:%ld]", net->buswidth-1+base, base);
	}
	efree(net->netname);
	net->namecount = 0;
	(void)net_namenet(returninfstr(infstr), net);
}

/*
 * Routine to evaluate the implied bus width of the string 'name'
 */
INTBIG net_buswidth(char *name)
{
	REGISTER INTBIG 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->buswidth;
		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 determine the width of bus arc "ai" by looking at facet instances that
 * connect to it.
 */
INTBIG net_buswidthofarc(ARCINST *ai)
{
	REGISTER INTBIG buswidth, width;
	REGISTER char *pt;
	REGISTER NODEINST *ni;

	if (ai->proto != sch_busarc) return(1);
	buswidth = 1;
	ni = ai->end[0].nodeinst;
	if (ni->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 (ni->arraysize > 1) buswidth *= ni->arraysize;
	}
	ni = ai->end[1].nodeinst;
	if (ni->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 (ni->arraysize > 1) width *= ni->arraysize;
			if (buswidth == 1) buswidth = width; else
				if (buswidth != width) buswidth = 1;
		}
	}
	return(buswidth);
}

#define FINDINTERBUSSHORT 1

/*
 * 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.
 */
BOOLEAN net_mergebuswires(NODEINST *ni)
{
	REGISTER NODEPROTO *np, *cnp;
	REGISTER PORTPROTO *pp, *opp, *cpp, *ocpp;
	REGISTER NETWORK *net, *onet, *cnet, *ocnet, *cmnet, *ocmnet, *topnet, *otopnet;
	REGISTER INTBIG j, k, a, nodewidth;
	REGISTER BOOLEAN recheck;

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

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

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

	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 at the current level */
		net = getnetonport(ni, pp);
		if (net == NONETWORK) continue;

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

		/* find the network inside of the contents */
		cpp = equivalentport(np, pp, cnp);
		if (cpp == NOPORTPROTO) continue;
		cnet = cpp->network;

		/* make sure the networks match width, inside and out */
		if (net->buswidth != cnet->buswidth && net->buswidth != cnet->buswidth * nodewidth)
		{
			if (nodewidth > 1)
			{
				ttyputmsg(_("Warning (facet %s): bus %s cannot connect to port %s of %ld-wide node %s (different width)"),
					describenodeproto(ni->parent), describenetwork(net), pp->protoname,
						nodewidth, describenodeinst(ni));
			} else
			{
				ttyputmsg(_("Warning (facet %s): bus %s cannot connect to port %s of node %s (different width)"),
					describenodeproto(ni->parent), describenetwork(net), pp->protoname,
						describenodeinst(ni));
			}
			continue;
		}

		/* now find another network attached to this node instance */
		for (opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
		{
#ifndef FINDINTERBUSSHORT
			if (opp == pp) continue;
#endif

			/* make sure this port has a different network attached to it at the current level */
			onet = getnetonport(ni, opp);
			if (onet == NONETWORK) continue;
#ifndef FINDINTERBUSSHORT
			if (onet == net) continue;
#endif

			/* find the network inside of the contents */
			ocpp = equivalentport(np, opp, cnp);
			if (ocpp == NOPORTPROTO) continue;
			ocnet = ocpp->network;

			/* make sure the networks match width, inside and out */
			if (onet->buswidth != ocnet->buswidth && onet->buswidth != ocnet->buswidth * nodewidth)
			{
				if (nodewidth > 1)
				{
					ttyputmsg(_("Warning (facet %s): bus %s cannot connect to port %s of %ld-wide node %s (different width)"),
						describenodeproto(ni->parent), describenetwork(onet), opp->protoname,
							nodewidth, describenodeinst(ni));
				} else
				{
					ttyputmsg(_("Warning (facet %s): bus %s cannot connect to port %s of node %s (different width)"),
						describenodeproto(ni->parent), describenetwork(onet), opp->protoname,
							describenodeinst(ni));
				}
				continue;
			}

#ifdef FINDINTERBUSSHORT
			/* when the same net, check for shorts that are internal to a bus */
			if (ocnet == cnet)
			{
				if (cnet->buswidth <= 1) continue;
				for(j=0; j<cnet->buswidth; j++)
				{
					cmnet = cnet->networklist[j];
					for (k=j+1; k<ocnet->buswidth; k++)
					{
						ocmnet = ocnet->networklist[k];
						if (cmnet != ocmnet) continue;

						/* merge found: propagate it to the top level */
						for(a=0; a<nodewidth; a++)
						{
							if (net->buswidth > cnet->buswidth)
								topnet = net->networklist[j + a*cnet->buswidth]; else
							{
								if (net->buswidth == 1) topnet = net; else
									topnet = net->networklist[j];
							}
							if (onet->buswidth > ocnet->buswidth)
								otopnet = onet->networklist[k + a*ocnet->buswidth]; else
							{
								if (onet->buswidth == 1) otopnet = onet; else
									otopnet = onet->networklist[k];
							}							
							(void)net_mergenet(topnet, otopnet);

							/* if neither arc is arrayed with the node, stop after 1 merge */
							if (net->buswidth == cnet->buswidth && onet->buswidth == ocnet->buswidth) break;
						}
					}
				}
				continue;
			}
#endif

			/* look for merges inside of the facet */
			for(j=0; j<cnet->buswidth; j++)
			{
				if (cnet->buswidth == 1) cmnet = cnet; else
					cmnet = cnet->networklist[j];

				for (k=0; k<ocnet->buswidth; k++)
				{
					if (ocnet->buswidth == 1) ocmnet = ocnet; else
						ocmnet = ocnet->networklist[k];
					if (cmnet != ocmnet) continue;

					/* merge found: propagate it to the top level */
					for(a=0; a<nodewidth; a++)
					{
						if (net->buswidth > cnet->buswidth)
							topnet = net->networklist[j + a*cnet->buswidth]; else
						{
							if (net->buswidth == 1) topnet = net; else
								topnet = net->networklist[j];
						}
						if (onet->buswidth > ocnet->buswidth)
							otopnet = onet->networklist[k + a*ocnet->buswidth]; else
						{
							if (onet->buswidth == 1) otopnet = onet; else
								otopnet = onet->networklist[k];
						}							
						(void)net_mergenet(topnet, otopnet);

						/* if neither arc is arrayed with the node, stop after 1 merge */
						if (net->buswidth == cnet->buswidth && onet->buswidth == ocnet->buswidth) break;
					}
				}
			}
		}
	}
	return(recheck);
}

/*
 * Routine to examine the network names on networks "net1" and "net2" and return
 * true if they share a common name.
 */
BOOLEAN net_samenetworkname(NETWORK *net1, NETWORK *net2)
{
	REGISTER char *pt1, *pt2;
	char **strings;
	REGISTER INTBIG n1, n2, wid1, wid2, i1, i2;

	pt1 = net1->netname;
	for(n1 = 0; n1 < net1->namecount; n1++)
	{
		wid1 = net_evalbusname(APBUS, pt1, &strings, NOARCINST, net1->parent, 0);
		if (wid1 > net_namecompstringtotal)
		{
			if (net_namecompstringtotal > 0) efree((char *)net_namecompstrings);
			net_namecompstringtotal = 0;
			net_namecompstrings = (char **)emalloc(wid1 * (sizeof (char *)), net_tool->cluster);
			if (net_namecompstrings == 0) return(TRUE);
			net_namecompstringtotal = wid1;
		}
		if (wid1 == 1) net_namecompstrings[0] = pt1; else
		{
			for(i1=0; i1<wid1; i1++)
				(void)allocstring(&net_namecompstrings[i1], strings[i1], net_tool->cluster);
		}

		pt2 = net2->netname;
		for(n2 = 0; n2 < net2->namecount; n2++)
		{
			wid2 = net_evalbusname(APBUS, pt2, &strings, NOARCINST, net2->parent, 0);
			for(i1=0; i1<wid1; i1++)
			{
				for(i2=0; i2<wid2; i2++)
				{
					if (namesame(net_namecompstrings[i1], strings[i2]) == 0) return(TRUE);
				}
			}
			pt2 += strlen(pt2) + 1;
		}
		if (wid1 > 1)
		{
			for(i1=0; i1<wid1; i1++)
				efree((char *)net_namecompstrings[i1]);
		}
		pt1 += strlen(pt1) + 1;
	}
	return(FALSE);
}

/*
 * Routine to add arc "ai", export "pp" and width "width" to the list of busses.
 */
void net_addtobuslist(ARCINST *ai, PORTPROTO *pp, INTBIG width)
{
	REGISTER INTBIG newtotal, i;
	REGISTER BUSLIST *newbuslists;

	if (net_buslistcount >= net_buslisttotal)
	{
		newtotal = net_buslisttotal * 2;
		if (newtotal <= net_buslistcount) newtotal = net_buslistcount + 5;
		newbuslists = (BUSLIST *)emalloc(newtotal * (sizeof (BUSLIST)), net_tool->cluster);
		if (newbuslists == 0) return;
		for(i=0; i<net_buslistcount; i++)
			newbuslists[i] = net_buslists[i];
		if (net_buslisttotal > 0) efree((char *)net_buslists);
		net_buslists = newbuslists;
		net_buslisttotal = newtotal;
	}
	net_buslists[net_buslistcount].ai = ai;
	net_buslists[net_buslistcount].pp = pp;
	net_buslists[net_buslistcount].width = width;
	net_buslistcount++;
}

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

/*
 * Routine to show the geometry on network "net".
 */
void net_showgeometry(NETWORK *net)
{
	REGISTER NODEPROTO *np;
	REGISTER INTBIG i, j, widest, len, lambda, total;
	REGISTER char *lname, *pad;
	REGISTER AREAPERIM *arpe, *firstarpe, **arpelist;
	TRANSISTORINFO *p_gate, *n_gate, *p_active, *n_active;
	float ratio;
	REGISTER void *infstr;

	/* gather geometry on this network */
	np = net->parent;
	firstarpe = net_gathergeometry(net, &p_gate, &n_gate, &p_active, &n_active, TRUE);

	/* copy the linked list to an array for sorting */
	total = 0;
	for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
		if (arpe->layer >= 0) total++;
	if (total == 0)
	{
		ttyputmsg(_("No geometry on network '%s' in facet %s"), describenetwork(net),
			describenodeproto(np));
		return;
	}
	arpelist = (AREAPERIM **)emalloc(total * (sizeof (AREAPERIM *)), net_tool->cluster);
	if (arpelist == 0) return;
	i = 0;
	for(arpe = firstarpe; arpe != NOAREAPERIM; arpe = arpe->nextareaperim)
		if (arpe->layer >= 0) arpelist[i++] = arpe;

	/* sort the layers */
	esort(arpelist, total, sizeof (AREAPERIM *), net_areaperimdepthascending);

	ttyputmsg(_("For network '%s' in facet %s:"), describenetwork(net),
		describenodeproto(np));
	lambda = lambdaoffacet(np);
	widest = 0;
	for(i=0; i<total; i++)
	{
		arpe = arpelist[i];
		lname = layername(arpe->tech, arpe->layer);
		len = strlen(lname);
		if (len > widest) widest = len;
	}
	for(i=0; i<total; i++)
	{
		arpe = arpelist[i];
		lname = layername(arpe->tech, arpe->layer);
		infstr = initinfstr();
		for(j=strlen(lname); j<widest; j++) addtoinfstr(infstr, ' ');
		pad = returninfstr(infstr);
		if (arpe->perimeter == 0)
		{
			ttyputmsg(_("Layer %s:%s area=%7g  half-perimeter=%s"), lname, pad,
				arpe->area/(float)lambda/(float)lambda, latoa(arpe->perimeter/2, 0));
		} else
		{
			ratio = (arpe->area / (float)lambda) / (float)(arpe->perimeter/2);
			ttyputmsg(_("Layer %s:%s area=%7g  half-perimeter=%s ratio=%g"), lname,
				pad, arpe->area/(float)lambda/(float)lambda,
					latoa(arpe->perimeter/2, 0), ratio);
		}
		efree((char *)arpelist[i]);
	}
	efree((char *)arpelist);
}

/*
 * Helper routine for "net_showgeometry()" to sort AREAPERIM objects by depth
 */
int net_areaperimdepthascending(const void *e1, const void *e2)
{
	REGISTER AREAPERIM *ap1, *ap2;
	REGISTER INTBIG fun1, fun2, depth1, depth2;

	ap1 = *((AREAPERIM **)e1);
	ap2 = *((AREAPERIM **)e2);
	fun1 = layerfunction(ap1->tech, ap1->layer);
	depth1 = layerfunctionheight(fun1);
	if (layeriscontact(fun1)) depth1 -= 1000;
	fun2 = layerfunction(ap2->tech, ap2->layer);
	depth2 = layerfunctionheight(fun2);
	if (layeriscontact(fun2)) depth2 -= 1000;
	return(depth2 - depth1);
}

/*
 * Helper routine for "net_reevaluatefacet()" to sort BUSLIST objects by width
 */
int net_buslistwidthascending(const void *e1, const void *e2)
{
	REGISTER BUSLIST *b1, *b2;

	b1 = (BUSLIST *)e1;
	b2 = (BUSLIST *)e2;
	return(b1->width - b2->width);
}

/*
 * Routine to gather the geometry on network "net".  Must deallocate all of the AREAPERIM
 * objects created by this routine.
 */
AREAPERIM *net_gathergeometry(NETWORK *net, TRANSISTORINFO **p_gate, TRANSISTORINFO **n_gate,
	TRANSISTORINFO **p_active, TRANSISTORINFO **n_active, BOOLEAN recurse)
{
	REGISTER NODEPROTO *np;
	REGISTER NETWORK *onet;

	np = net->parent;

	/* initialize polygon merging */
	net_merge = mergenew(net_tool->cluster);

	/* 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_cleartransistorinfo(&net_transistor_p_gate);
	net_cleartransistorinfo(&net_transistor_n_gate);
	net_cleartransistorinfo(&net_transistor_p_active);
	net_cleartransistorinfo(&net_transistor_n_active);
	net_firstareaperim = NOAREAPERIM;

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

	/* get back the total geometry */
	mergeextract(net_merge, net_geometrypolygon);
	mergedelete(net_merge);

#if 0		/* not necessary now that gate poly is on transistor layer */
	/* special adjustments */
	for(ap = net_firstareaperim; ap != NOAREAPERIM; ap = ap->nextareaperim)
	{
		fun = layerfunction(ap->tech, ap->layer);

		/* remove transistor area from polysilicon */
		if (layerispoly(fun))
		{
			ap->area -= net_transistor_p_gate.area + net_transistor_n_gate.area;
			ap->perimeter -= (net_transistor_p_gate.width + net_transistor_n_gate.width) * 2;
			break;
		}
	}
#endif

	/* store the transistor area information in the parameters */
	if (p_gate != 0) *p_gate = &net_transistor_p_gate;
	if (n_gate != 0) *n_gate = &net_transistor_n_gate;
	if (p_active != 0) *p_active = &net_transistor_p_active;
	if (n_active != 0) *n_active = &net_transistor_n_active;
	return(net_firstareaperim);
}

/*
 * 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, BOOLEAN recurse)
{
	static POLYGON *poly = NOPOLYGON;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnp;
	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;
	REGISTER INTBIG total, i, found;
	INTBIG length, width;

	/* get polygon */
	(void)needstaticpolygon(&poly, 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, NOWINDOWPART);
		for(i=0; i<total; i++)
		{
			shapearcpoly(ai, i, poly);
			xformpoly(poly, trans);
			mergeaddpolygon(net_merge, poly->layer, ai->proto->tech, poly);
		}
	}
	for(ni = facet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		/* ignore recursive references (showing icon in contents) */
		subnp = ni->proto;
		if (subnp->cell == facet->cell) continue;

		/* see if any selected networks touch this node */
		if (subnp->primindex == 0 && recurse == TRUE)
		{
			for(net = subnp->firstnetwork; net != NONETWORK; net = net->nextnetwork)
				net->temp1 = 0;
		} else
		{
			for(pp = subnp->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 (subnp->primindex == 0 && recurse == TRUE)
		{
			/* facet instance: recurse */
			makerot(ni, rot);
			maketrans(ni, trn);
			transmult(trn, rot, rottrn);
			transmult(rottrn, trans, subrot);
			net_propgeometry(subnp, subrot, recurse);
		} else
		{
			/* primitive: include layers that touch desired networks */
			makerot(ni, rot);
			transmult(rot, trans, subrot);
			ignorelayer = -1;
			nfun = nodefunction(ni);
			if (nfun == NPCONTACT)
			{
				/* find highest layer and ignore it */
				total = nodeEpolys(ni, 0, NOWINDOWPART);
				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, NOWINDOWPART);
			for(i=0; i<total; i++)
			{
				shapeEnodepoly(ni, i, poly);
				if (poly->layer == ignorelayer) continue;
				fun = layerfunction(subnp->tech, poly->layer);
				if (poly->portproto == NOPORTPROTO) continue;
				if (poly->portproto->network->temp1 == 0) continue;
				if ((fun&LFPSEUDO) != 0) continue;
				if (layerispoly(fun)) polys++;
				if ((fun&LFTYPE) == LFDIFF) diffs++;
				xformpoly(poly, subrot);
				mergeaddpolygon(net_merge, poly->layer, subnp->tech, poly);
			}
			if (nfun == NPTRANMOS)
			{
				transistorsize(ni, &length, &width);
				if (polys > 0)
					net_addtotransistorinfo(&net_transistor_n_gate, length, width);
				if (diffs > 0)
					net_addtotransistorinfo(&net_transistor_n_active, length, width);
			}
			if (nfun == NPTRAPMOS)
			{
				transistorsize(ni, &length, &width);
				if (polys > 0)
					net_addtotransistorinfo(&net_transistor_p_gate, length, width);
				if (diffs > 0)
					net_addtotransistorinfo(&net_transistor_p_active, length, width);
			}
		}
	}
}

/*
 * Helper routine to clear the TRANSISTORINFO structure "ti".
 */
void net_cleartransistorinfo(TRANSISTORINFO *ti)
{
	ti->count = 0;
	ti->area = 0;
	ti->width = 0;
	ti->length = 0;
}

/*
 * Helper routine to add a "length" and "width" transistor to the TRANSISTORINFO structure "ti".
 */
void net_addtotransistorinfo(TRANSISTORINFO *ti, INTBIG length, INTBIG width)
{
	INTBIG area;

	area = length * width;
	ti->count++;
	ti->area += area;
	ti->length += length;
	ti->width += width;
}

/*
 * Helper routine that is given merged geometry from the "network geometry" command.
 */
void net_geometrypolygon(INTBIG layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTBIG 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_tool->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 "telltool 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 "telltool 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, *subnp;
	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 */
		subnp = ni->proto;
		if (subnp->primindex != 0) continue;

		/* ignore recursive references (showing icon in contents) */
		if (subnp->cell == facet->cell) 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 return an array of selected networks, terminated by NONETWORK.
 */
NETWORK **net_gethighlightednets(BOOLEAN disperror)
{
	static NETWORK *onenet[2];
	REGISTER INTBIG i, fun;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER GEOM **geom;

	onenet[0] = onenet[1] = NONETWORK;
	if (el_curwindowpart == NOWINDOWPART) return(onenet);

#if SIMTOOL
	/* if current window is simulation, invade structures and find highlighted net */
	if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
	{
		onenet[0] = net_gethighlightednet(FALSE, disperror);
		return(onenet);
	}
#endif

	/* if current window is text, invade structures and find highlighted net name */
	if ((el_curwindowpart->state&WINDOWTYPE) == TEXTWINDOW ||
		(el_curwindowpart->state&WINDOWTYPE) == POPTEXTWINDOW)
	{
		onenet[0] = net_gethighlightednet(FALSE, disperror);
		return(onenet);
	}

	geom = (GEOM **)asktool(us_tool, "get-all-objects");
	if (geom[0] == NOGEOM)
	{
		if (disperror) ttyputerr(_("Find some objects first"));
		return(onenet);
	}

	/* gather all networks connected to selected objects */
	net_highnetscount = 0;
	for(i=0; geom[i] != NOGEOM; i++)
	{
		if (!geom[i]->entryisnode)
		{
			ai = geom[i]->entryaddr.ai;
			net_addnettolist(ai->network);
		} else
		{
			ni = geom[i]->entryaddr.ni;
			fun = nodefunction(ni);
			if (fun == NPPIN || fun == NPCONTACT ||
				fun == NPCONNECT || fun == NPNODE)
			{
				if (ni->firstportarcinst != NOPORTARCINST)
				{
					ai = ni->firstportarcinst->conarcinst;
					net_addnettolist(ai->network);
				}
			}
		}
	}
	net_addnettolist(NONETWORK);
	return(net_highnets);
}

/*
 * Routine to add network "net" to the global list of networks in "net_highnets".
 */
void net_addnettolist(NETWORK *net)
{
	REGISTER INTBIG i, newtotal;
	REGISTER NETWORK **newlist;

	/* stop if already in the list */
	for(i=0; i<net_highnetscount; i++)
		if (net == net_highnets[i]) return;

	/* ensure room in the list */
	if (net_highnetscount >= net_highnetstotal)
	{
		newtotal = net_highnetstotal * 2;
		if (net_highnetscount >= newtotal) newtotal = net_highnetscount + 5;
		newlist = (NETWORK **)emalloc(newtotal * (sizeof (NETWORK *)), net_tool->cluster);
		if (newlist == 0) return;
		for(i=0; i<net_highnetscount; i++)
			newlist[i] = net_highnets[i];
		if (net_highnetstotal > 0) efree((char *)net_highnets);
		net_highnets = newlist;
		net_highnetstotal = newtotal;
	}
	net_highnets[net_highnetscount] = net;
	net_highnetscount++;
}

/*
 * 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(BOOLEAN lookinside, BOOLEAN 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 SIMTOOL
	/* 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 *)asktool(us_tool, "get-object");
	if (geom == NOGEOM)
	{
		if (disperror) ttyputerr(_("Find a single object first"));
		return(NONETWORK);
	}
	if (!geom->entryisnode) return(geom->entryaddr.ai->network);

	ni = geom->entryaddr.ni;
	por = (PORTPROTO *)asktool(us_tool, "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) 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 && ni->proto->primindex == 0) return(por->network);

	if (disperror) 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;

	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 = getcomplexnetwork(name, np);
		if (net != NONETWORK) return(net);
	}

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

/*
 * Routine to specify highlighting of the arcs on network "net" in facet "np".
 * The highlighting is added to the infinite string.
 */
void net_highlightnet(void *infstr, NODEPROTO *np, NETWORK *net)
{
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *subnp;
	REGISTER PORTPROTO *pp;
	REGISTER INTBIG i, j, fun;

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

		/* handle busses according to the nature of the network being highlighted */
		if (net->buswidth <= 1)
		{
			/* network is single wire: look for its presence on a bus arc */
			if (ai->network->buswidth > 1)
			{
				for (i=0; i<ai->network->buswidth; i++)
					if (ai->network->networklist[i] == net)
				{
					formatinfstr(infstr, "FACET=%s FROM=0%lo;-1;0\n",
						describenodeproto(np), (INTBIG)ai->geom);
					break;
				}
			}
		} else
		{
			/* network is a bus: check the nature of this arc */
			if (ai->network->buswidth <= 1)
			{
				/* arc is single wire: see if it is on the network bus */
				for (i=0; i<net->buswidth; i++)
					if (net->networklist[i] == ai->network)
				{
					formatinfstr(infstr, "FACET=%s FROM=0%lo;-1;0\n",
						describenodeproto(np), (INTBIG)ai->geom);
					break;
				}
			} else
			{
				/* arc is bus: see if any of its signals are on network bus */
				for (i=0; i<net->buswidth; i++)
				{
					for (j=0; j<ai->network->buswidth; j++)
						if (ai->network->networklist[j] == net->networklist[i]) break;
					if (j < ai->network->buswidth) break;
				}
				if (i < net->buswidth)
				{
					formatinfstr(infstr, "FACET=%s LINE=%ld,%ld,%ld,%ld\n",
						describenodeproto(np), ai->end[0].xpos, ai->end[1].xpos,
							ai->end[0].ypos, ai->end[1].ypos);
				}
			}
		}
	}

	/* now highlight all pin-type nodes on the network */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (subnp->primindex == 0)
		{
			/* show network where it travels through a subfacet (layout only) */
			if (subnp->cellview == el_iconview) continue;

			/* see if the network hits the instance */
			net_highlightsubnet(infstr, np, ni, el_matid, net);
			continue;
		}
		fun = nodefunction(ni);
		if (fun != NPPIN && fun != NPCONTACT && fun != NPNODE && fun != NPCONNECT)
			continue;
		if (ni->firstportarcinst == NOPORTARCINST) continue;
		if (ni->firstportarcinst->conarcinst->network != net) continue;
		formatinfstr(infstr, "FACET=%s FROM=0%lo;-1;0\n",
			describenodeproto(np), (INTBIG)ni->geom);
	}

	/* finally highlight all exports on the network */
	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
	{
		if (pp->network == net)
		{
			formatinfstr(infstr, "FACET=%s TEXT=0%lo;0%lo;-\n",
				describenodeproto(np), (INTBIG)pp->subnodeinst->geom, (INTBIG)pp);
		}
		if (net->buswidth <= 1)
		{
			/* network is single wire: look for its presence on a bus export */
			if (pp->network->buswidth > 1)
			{
				for (i=0; i<pp->network->buswidth; i++)
					if (pp->network->networklist[i] == net)
				{
					formatinfstr(infstr, "FACET=%s TEXT=0%lo;0%lo;-\n",
						describenodeproto(np), (INTBIG)pp->subnodeinst->geom, (INTBIG)pp);
					break;
				}
			}
		} else
		{
			/* network is a bus: check the nature of this export */
			if (pp->network->buswidth <= 1)
			{
				/* export is single wire: see if it is on the network bus */
				for (i=0; i<net->buswidth; i++)
					if (net->networklist[i] == pp->network)
				{
					formatinfstr(infstr, "FACET=%s TEXT=0%lo;0%lo;-\n",
						describenodeproto(np), (INTBIG)pp->subnodeinst->geom, (INTBIG)pp);
					break;
				}
			} else
			{
				/* export is bus: see if any of its signals are on network bus */
				for (i=0; i<net->buswidth; i++)
				{
					for (j=0; j<pp->network->buswidth; j++)
						if (pp->network->networklist[j] == net->networklist[i]) break;
					if (j < pp->network->buswidth) break;
				}
				if (i < net->buswidth)
				{
					formatinfstr(infstr, "FACET=%s TEXT=0%lo;0%lo;-\n",
						describenodeproto(np), (INTBIG)pp->subnodeinst->geom, (INTBIG)pp);
				}
			}
		}
	}
}

/*
 * Routine to recursively highlight a subnet in a layout facet instance and add it to the infinite
 * string "infstr".  The top facet is "topnp" and the instance that is being expanded is "ni" (with
 * network "net" on that instance being highlighted).  The transformation matrix to that instance
 * (not including the instance) is "trans".
 */
void net_highlightsubnet(void *infstr, NODEPROTO *topnp, NODEINST *thisni, XARRAY trans, NETWORK *net)
{
	REGISTER PORTARCINST *pi;
	REGISTER PORTEXPINST *pe;
	REGISTER PORTPROTO *pp;
	REGISTER ARCINST *ai;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *subnp;
	REGISTER NETWORK *subnet;
	INTBIG x1, y1, x2, y2;
	XARRAY rot, tran, temptrans, thistrans;

	/* find out which port of the instance is on the net */
	pp = NOPORTPROTO;
	for(pi = thisni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		if (pi->conarcinst->network == net) break;
	if (pi != NOPORTARCINST) pp = pi->proto; else
	{
		for(pe = thisni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
			if (pe->exportproto->network == net) break;
		if (pe != NOPORTEXPINST) pp = pe->proto;
	}
	if (pp == NOPORTPROTO) return;

	/* prepare to display the net in the subfacet */
	maketrans(thisni, tran);
	makerot(thisni, rot);
	transmult(tran, rot, temptrans);
#if 1
	transmult(temptrans, trans, thistrans);
#else
	transmult(trans, temptrans, thistrans);
#endif
	subnet = pp->network;
	np = thisni->proto;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
	{
		if (ai->network != subnet) continue;
		x1 = ai->end[0].xpos;   y1 = ai->end[0].ypos;
		x2 = ai->end[1].xpos;   y2 = ai->end[1].ypos;
		xform(x1, y1, &x1, &y1, thistrans);
		xform(x2, y2, &x2, &y2, thistrans);
		formatinfstr(infstr, "FACET=%s LINE=%ld,%ld,%ld,%ld\n",
			describenodeproto(topnp), x1, x2, y1, y2);
	}
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		subnp = ni->proto;
		if (subnp->primindex != 0) continue;

		/* show network where it travels through a subfacet (layout only) */
		if (subnp->cellview == el_iconview) continue;

		/* see if the network hits the instance */
		net_highlightsubnet(infstr, topnp, ni, thistrans, subnet);
	}
}

/*
 * Helper routine to determine whether the string "name" is a number (but it may
 * end with network index characters ":", "]", or ",".
 */
BOOLEAN net_isanumber(char *name)
{
	REGISTER char *pt, save;
	BOOLEAN 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 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);
}

void net_setglobalnet(INTBIG *special, INTBIG newindex, char *netname, NETWORK *net,
	INTBIG characteristics, NODEPROTO *np)
{
	REGISTER INTBIG i, newsize, *newchar;
	REGISTER NETWORK **newnets;
	REGISTER char **newnetnames;

	/* make sure power and ground are in the list */
	if (np->globalnetcount == 0)
	{
		np->globalnetworks = (NETWORK **)emalloc(2 * (sizeof (NETWORK *)), np->cell->cluster);
		if (np->globalnetworks == 0) return;
		np->globalnetchar = (INTBIG *)emalloc(2 * SIZEOFINTBIG, np->cell->cluster);
		if (np->globalnetchar == 0) return;
		np->globalnetnames = (char **)emalloc(2 * (sizeof (char *)), np->cell->cluster);
		if (np->globalnetnames == 0) return;
		np->globalnetworks[0] = NONETWORK;
		(void)allocstring(&np->globalnetnames[0], _("Power"), np->cell->cluster);
		np->globalnetchar[0] = PWRPORT;
		np->globalnetworks[1] = NONETWORK;
		(void)allocstring(&np->globalnetnames[1], _("Ground"), np->cell->cluster);
		np->globalnetchar[1] = GNDPORT;
		np->globalnetcount = 2;
	}

	if (newindex < 0)
	{
		/* a global net: see if it is in the list */
		for(i=2; i<np->globalnetcount; i++)
		{
			if (*np->globalnetnames[i] == 0)
			{
				newindex = i;
				np->globalnetworks[newindex] = NONETWORK;
				continue;
			}
			if (namesame(netname, np->globalnetnames[i]) == 0)
			{
				newindex = i;
				break;
			}
		}
		if (newindex < 0)
		{
			/* expand the list */
			newsize = np->globalnetcount + 1;
			newnets = (NETWORK **)emalloc(newsize * (sizeof (NETWORK *)), np->cell->cluster);
			if (newnets == 0) return;
			newchar = (INTBIG *)emalloc(newsize * SIZEOFINTBIG, np->cell->cluster);
			if (newchar == 0) return;
			newnetnames = (char **)emalloc(newsize * (sizeof (char *)), np->cell->cluster);
			if (newnetnames == 0) return;
			for(i=0; i<np->globalnetcount; i++)
			{
				newnets[i] = np->globalnetworks[i];
				newchar[i] = np->globalnetchar[i];
				newnetnames[i] = np->globalnetnames[i];
			}
			newindex = np->globalnetcount;
			newnets[newindex] = NONETWORK;
			newchar[newindex] = 0;
			newnetnames[newindex] = 0;
			if (np->globalnetcount > 0)
			{
				efree((char *)np->globalnetworks);
				efree((char *)np->globalnetchar);
				efree((char *)np->globalnetnames);
			}
			np->globalnetworks = newnets;
			np->globalnetchar = newchar;
			np->globalnetnames = newnetnames;
			np->globalnetcount++;
		}
	}
	if (newindex >= 2)
	{
		if (np->globalnetnames[newindex] != 0)
			efree((char *)np->globalnetnames[newindex]);
		(void)allocstring(&np->globalnetnames[newindex], netname, np->cell->cluster);
	}

	if (*special < 0)
	{
		*special = newindex;
	} else
	{
		if (*special != newindex)
		{
			if (*special < np->globalnetcount)
			{
				ttyputerr(_("Warning (facet %s): global signals %s and %s are connected"),
					describenodeproto(np), np->globalnetnames[*special], np->globalnetnames[newindex]);
			} else
			{
				ttyputerr(_("Warning (facet %s): global signal %s is connected to another"),
					describenodeproto(np), np->globalnetnames[newindex]);
			}
		}
	}
	if (np->globalnetworks[newindex] != NONETWORK)
	{
		(void)net_mergenet(np->globalnetworks[newindex], net);
	}
	np->globalnetworks[newindex] = net;
	np->globalnetchar[newindex] = characteristics;
	net->globalnet = (INTSML)newindex;
}

/*
 * 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, *newvar;
	REGISTER INTBIG i, count, lowend;
	char **strings, **localstrings;
	REGISTER INTBIG lowx, highx, sepx, lowy, highy, sepy, stublen, lowxbus, lowybus, lambda;
	INTBIG sxw, syw, sxb, syb;

	ai = (ARCINST *)us_getobject(VARCINST, FALSE);
	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->buswidth <= 1)
	{
		ttyputerr(_("Bus must have multiple signals"));
		return;
	}

	/* determine which bus name to use */
	var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
	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);
	if (count == 0)
	{
		ttyputerr(_("Bus has zero-width"));
		return;
	}

	/* 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->buswidth-1) * lambda)
		{
			/* signals fit on grid */
			sepy = ((highy-lowy) / ((net->buswidth-1) * lambda)) * lambda;
			lowy = ((highy - lowy) - (sepy * (net->buswidth-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->buswidth-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->buswidth-1) * lambda)
		{
			/* signals fit on grid */
			sepx = ((highx-lowx) / ((net->buswidth-1) * lambda)) * lambda;
			lowx = ((highx - lowx) - (sepx * (net->buswidth-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->buswidth-1);
		}
		lowxbus = lowx;   lowybus = ai->end[0].ypos;
	} else
	{
		ttyputerr(_("Bus must be horizontal or vertical to be ripped out"));
		return;
	}

	/* copy names to a local array */
	localstrings = (char **)emalloc(count * (sizeof (char *)), net_tool->cluster);
	if (localstrings == 0) return;
	for(i=0; i<count; i++)
		(void)allocstring(&localstrings[i], strings[i], net_tool->cluster);

	/* 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;
		newvar = setvalkey((INTBIG)aiw, VARCINST, el_arc_name_key,
			(INTBIG)localstrings[i], VSTRING|VDISPLAY);
		if (newvar != NOVARIABLE)
			defaulttextsize(4, newvar->textdescript);
		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
		{
			/* LINTED "niblast" used in proper order */
			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)
	{
		newvar = setvalkey((INTBIG)aiw, VARCINST, el_arc_name_key, var->addr, VSTRING|VDISPLAY);
		if (newvar != NOVARIABLE)
			defaulttextsize(4, newvar->textdescript);
	}
	endobjectchange((INTBIG)aiw, VARCINST);

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

	/* free memory */
	for(i=0; i<count; i++)
		efree(localstrings[i]);
	efree((char *)localstrings);
}

/*
 * Routine to name all nodes in facet "np" that do not already have node names.
 * Returns the number of nodes that were named.
 */
INTBIG net_nameallnodes(NODEPROTO *np, BOOLEAN evenpins)
{
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;
	REGISTER char *name, *match, **shortnameoverride;
	REGISTER char *lastname, *thisname, *warnedname;
	char *shortnames[MAXNODEFUNCTION], **namelist;
	INTBIG named, count;
	REGISTER INTBIG i, len, abbrevlen;
	INTBIG cindex, fun, highpseudo[MAXNODEFUNCTION];
	REGISTER void *infstr;

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

	/* get the length of cell name abbreviations */
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_node_abbrevlen_key);
	if (var == NOVARIABLE) abbrevlen = NETDEFAULTABBREVLEN; else
		abbrevlen = var->addr;

	/* get the node function abbreviations */
	for(i=0; i<MAXNODEFUNCTION; i++)
	{
		highpseudo[i] = 0;
		shortnames[i] = nodefunctionshortname(i);
	}
	var = getvalkey((INTBIG)net_tool, VTOOL, VSTRING|VISARRAY, net_node_abbrev_key);
	if (var != NOVARIABLE)
	{
		shortnameoverride = (char **)var->addr;
		len = getlength(var);
		for(i=0; i<MAXNODEFUNCTION; i++)
		{
			if (i >= len) break;
			if (*shortnameoverride[i] != 0)
				shortnames[i] = shortnameoverride[i];
		}
	}

	/* get a list of existing names in this facet */
	count = net_gathernodenames(np, &namelist);

	/* issue warning if there are duplicate names */
	if (count > 1)
	{
		esort(namelist, count, sizeof (char *), sort_stringascending);
		lastname = namelist[0];
		warnedname = 0;
		for(i=1; i<count; i++)
		{
			thisname = (char *)namelist[i];
			if (lastname != 0 && thisname != 0)
			{
				if (namesame(lastname, thisname) == 0)
				{
					if (warnedname == 0 || namesame(warnedname, thisname) != 0)
					{
						ttyputerr(_("***Error: facet %s has multiple nodes with name '%s'"),
							describenodeproto(np), thisname);
					}
					warnedname = thisname;
				}
			}
			lastname = thisname;
		}
	}

	/* check all nodes to see if they need to be named */
	named = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto->primindex == 0) ni->proto->cell->temp1 = 0;
	}
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		fun = nodefunction(ni);
		if (evenpins == 0 && ni->firstportexpinst == NOPORTEXPINST)
		{
			if (fun == NPPIN || fun == NPART) continue;
		}

		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
		if (var != NOVARIABLE)
		{
			/* check for existing pseudo-names */
			name = (char *)var->addr;
			while (*name == ' ' || *name == '\t') name++;
			if (*name != 0)
			{
				if (ni->proto->primindex == 0)
				{
					infstr = initinfstr();
					addstringtoinfstr(infstr, ni->proto->cell->cellname);
					match = returninfstr(infstr);
					len = strlen(match);
					if (len > abbrevlen) match[abbrevlen] = 0;
					len = strlen(match);
					if (namesamen(name, match, len) == 0)
					{
						cindex = atoi(&name[len]);
						if (cindex > ni->proto->cell->temp1)
							ni->proto->cell->temp1 = cindex;
					}
				} else
				{
					match = shortnames[fun];
					len = strlen(match);
					if (namesamen(name, match, len) == 0)
					{
						cindex = atoi(&name[len]);
						if (cindex > highpseudo[fun]) highpseudo[fun] = cindex;
					}
				}
				continue;
			}
		}
		named++;
	}

	/* see if any naming needs to be done */
	if (named != 0)
	{
		/* make names for nodes without them */
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			fun = nodefunction(ni);
			if (evenpins == 0 && ni->firstportexpinst == NOPORTEXPINST)
			{
				if (fun == NPPIN || fun == NPART) continue;
			}

			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
			if (var != NOVARIABLE)
			{
				name = (char *)var->addr;
				while (*name == ' ' || *name == '\t') name++;
				if (*name != 0) continue;
			}
			for(;;)
			{
				if (ni->proto->primindex == 0)
				{
					ni->proto->cell->temp1++;
					infstr = initinfstr();
					addstringtoinfstr(infstr, ni->proto->cell->cellname);
					match = returninfstr(infstr);
					len = strlen(match);
					if (len > abbrevlen) match[abbrevlen] = 0;
					infstr = initinfstr();
					formatinfstr(infstr, "%s%ld", match, ni->proto->cell->temp1);
				} else
				{
					highpseudo[fun]++;
					infstr = initinfstr();
					formatinfstr(infstr, "%s%ld", shortnames[fun], highpseudo[fun]);
				}
				name = returninfstr(infstr);

				/* see if this name is in use */
				for(i=0; i<net_nodenamelistcount; i++)
					if (namesame(name, net_nodenamelist[i]) == 0) break;
				if (i >= net_nodenamelistcount) break;
			}
			var = setvalkey((INTBIG)ni, VNODEINST, el_node_name_key, (INTBIG)name, VSTRING);
			if (var != NOVARIABLE)
				if (net_addnodename((char *)var->addr)) break;
		}
	}

	return(named);
}

/*
 * Routine to gather all named nodes in facet "np" and fill the globals
 * "net_nodenamelistcount" and "net_nodenamelist" (also sets names "namelist"
 * and returns the count).
 */
INTBIG net_gathernodenames(NODEPROTO *np, char ***namelist)
{
	REGISTER NODEINST *ni;
	REGISTER VARIABLE *var;
	REGISTER char *pt, *name;
	REGISTER INTBIG count, i;
	char **strings;

	net_nodenamelistcount = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
		if (var == NOVARIABLE) continue;
		name = (char *)var->addr;
		for(pt = name; *pt != 0; pt++) if (*pt == '[') break;
		if (*pt == 0)
		{
			if (net_addnodename(name)) break;
		} else
		{
			/* array specification: break it up and include each name separately */
			count = net_evalbusname(APBUS, name, &strings, NOARCINST, np, 0);
			for(i=0; i<count; i++)
				if (net_addnodename(strings[i])) break;
		}
	}
	*namelist = net_nodenamelist;
	return(net_nodenamelistcount);
}

/*
 * Routine to add name "name" to the global list "net_nodenamelist" which is
 * "net_nodenamelistcount" long.  Returns true on error.
 */
BOOLEAN net_addnodename(char *name)
{
	REGISTER INTBIG i, newtotal;
	REGISTER char **newlist;

	if (net_nodenamelistcount >= net_nodenamelisttotal)
	{
		newtotal = net_nodenamelisttotal * 2;
		if (net_nodenamelistcount >= newtotal) newtotal = net_nodenamelistcount + 50;
		newlist = (char **)emalloc(newtotal * (sizeof (char *)), net_tool->cluster);
		if (newlist == 0) return(TRUE);
		for(i=0; i<net_nodenamelistcount; i++)
			newlist[i] = net_nodenamelist[i];
		for(i=net_nodenamelistcount; i<newtotal; i++)
			newlist[i] = 0;
		if (net_nodenamelisttotal > 0) efree((char *)net_nodenamelist);
		net_nodenamelist = newlist;
		net_nodenamelisttotal = newtotal;
	}
	if (net_nodenamelist[net_nodenamelistcount] == 0)
		(void)allocstring(&net_nodenamelist[net_nodenamelistcount], name, net_tool->cluster); else
			(void)reallocstring(&net_nodenamelist[net_nodenamelistcount], name, net_tool->cluster);
	net_nodenamelistcount++;
	return(FALSE);
}

INTBIG net_nameallnets(NODEPROTO *np)
{
	REGISTER ARCINST *ai;
	REGISTER NETWORK *net;
	REGISTER VARIABLE *var;
	REGISTER char *name;
	char newname[50];
	INTBIG cindex, highpseudo;
	INTBIG 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_key);
		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_key, (INTBIG)newname, VSTRING);
		if (var != NOVARIABLE)
		defaulttextsize(4, var->textdescript);
		named++;
	}
	return(named);
}

/*
 * routine to get the two facets to be compared.  These must be the only
 * two windows on the display.  Returns true if facets cannot be found.
 */
BOOLEAN net_getfacets(NODEPROTO **facet1, NODEPROTO **facet2)
{
	REGISTER WINDOWPART *w;

	/* get two windows */
	*facet1 = *facet2 = NONODEPROTO;
	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
	{
		if ((w->state&WINDOWTYPE) != DISPWINDOW &&
			(w->state&WINDOWTYPE) != DISP3DWINDOW) continue;
		if (*facet1 == NONODEPROTO) *facet1 = w->curnodeproto; else
		{
			if (*facet2 == NONODEPROTO)
			{
				if (w->curnodeproto != *facet1) *facet2 = w->curnodeproto;
			} else
			{
				if (w->curnodeproto != NONODEPROTO) return(TRUE);
			}
		}
	}
	if (*facet2 == NONODEPROTO) return(TRUE);
	return(FALSE);
}

/*
 * Routine to synchronize the schematic technology with the network tools idea of
 * resistor handling.
 */
void net_tellschematics(void)
{
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG total;

	/* see how many facets there are */
	total = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			total++;
	}

	if (asktech(sch_tech, "ignoring-resistor-topology") != 0)
	{
		/* currently ignoring resistors */
		if ((net_options&NETIGNORERESISTORS) == 0)
		{
			/* make it start including resistors */
			if (total > 0)
				ttyputmsg(_("Adjusting network topology for resistors"));
			(void)asktool(us_tool, "clear");
			(void)asktech(sch_tech, "include-resistor-topology");
			net_totalrenumber(NOLIBRARY);
		}
	} else
	{
		/* currently including resistors */
		if ((net_options&NETIGNORERESISTORS) != 0)
		{
			/* make it start ignoring resistors */
			if (total > 0)
				ttyputmsg(_("Adjusting network topology for resistors"));
			(void)asktool(us_tool, "clear");
			(void)asktech(sch_tech, "ignore-resistor-topology");
			net_totalrenumber(NOLIBRARY);
		}
	}
}

/****************************** NCC CONTROL DIALOG ******************************/

/* Network: NCC Control */
static DIALOGITEM net_nccoptionsdialogitems[] =
{
 /*  1 */ {0, {420,224,444,312}, BUTTON, N_("Do NCC")},
 /*  2 */ {0, {420,8,444,72}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {300,4,316,200}, CHECK, N_("Recurse through hierarchy")},
 /*  4 */ {0, {204,4,220,220}, CHECK, N_("Check export names")},
 /*  5 */ {0, {420,96,444,201}, BUTTON, N_("Preanalysis")},
 /*  6 */ {0, {364,4,380,216}, CHECK, N_("Show progress graphically")},
 /*  7 */ {0, {108,4,124,220}, CHECK, N_("Merge parallel components")},
 /*  8 */ {0, {180,4,196,220}, CHECK, N_("Ignore power and ground")},
 /*  9 */ {0, {60,4,76,217}, MESSAGE, N_("For all facets:")},
 /* 10 */ {0, {175,228,359,400}, SCROLL, ""},
 /* 11 */ {0, {53,220,412,221}, DIVIDELINE, ""},
 /* 12 */ {0, {28,312,44,352}, BUTTON, N_("Set")},
 /* 13 */ {0, {4,312,20,352}, BUTTON, N_("Set")},
 /* 14 */ {0, {28,136,44,305}, EDITTEXT, ""},
 /* 15 */ {0, {108,232,124,281}, RADIO, N_("Yes")},
 /* 16 */ {0, {108,284,124,329}, RADIO, N_("No")},
 /* 17 */ {0, {108,332,124,396}, RADIO, N_("Default")},
 /* 18 */ {0, {276,20,292,164}, MESSAGE, N_("Size tolerance (amt):")},
 /* 19 */ {0, {276,168,292,216}, EDITTEXT, ""},
 /* 20 */ {0, {52,4,53,401}, DIVIDELINE, ""},
 /* 21 */ {0, {60,228,76,400}, MESSAGE, N_("Individual facet overrides:")},
 /* 22 */ {0, {156,36,172,185}, BUTTON, N_("Clear valid NCC dates")},
 /* 23 */ {0, {228,4,244,220}, CHECK, N_("Check component sizes")},
 /* 24 */ {0, {4,136,20,305}, EDITTEXT, ""},
 /* 25 */ {0, {28,4,44,132}, MESSAGE, N_("With facet:")},
 /* 26 */ {0, {4,4,20,132}, MESSAGE, N_("Compare facet:")},
 /* 27 */ {0, {252,20,268,164}, MESSAGE, N_("Size tolerance (%):")},
 /* 28 */ {0, {252,168,268,216}, EDITTEXT, ""},
 /* 29 */ {0, {420,336,444,400}, BUTTON, N_("Save")},
 /* 30 */ {0, {412,4,413,400}, DIVIDELINE, ""},
 /* 31 */ {0, {388,240,404,389}, BUTTON, N_("Remove all overrides")},
 /* 32 */ {0, {132,232,148,281}, RADIO, N_("Yes")},
 /* 33 */ {0, {132,284,148,329}, RADIO, N_("No")},
 /* 34 */ {0, {132,332,148,396}, RADIO, N_("Default")},
 /* 35 */ {0, {132,4,148,220}, CHECK, N_("Merge series transistors")},
 /* 36 */ {0, {84,232,100,281}, RADIO, N_("Yes")},
 /* 37 */ {0, {84,284,100,329}, RADIO, N_("No")},
 /* 38 */ {0, {84,332,100,396}, RADIO, N_("Default")},
 /* 39 */ {0, {84,4,100,220}, CHECK, N_("Expand hierarchy")},
 /* 40 */ {0, {155,228,171,400}, POPUP, ""},
 /* 41 */ {0, {364,240,380,389}, BUTTON, N_("List all overrides")},
 /* 42 */ {0, {388,4,404,216}, CHECK, N_("Show 'NCCMatch' tags")},
 /* 43 */ {0, {4,356,20,400}, BUTTON, N_("Next")},
 /* 44 */ {0, {28,356,44,400}, BUTTON, N_("Next")},
 /* 45 */ {0, {324,4,340,152}, CHECK, N_("Experimental NCC")},
 /* 46 */ {0, {344,36,360,148}, MESSAGE, N_("Processors:")},
 /* 47 */ {0, {344,152,360,200}, EDITTEXT, ""}
};
#ifdef FORCESUNTOOLS
static DIALOG net_nccoptionsdialog = {{50,75,503,486}, N_("Network Consistency Checker"), 0, 47, net_nccoptionsdialogitems, 0, 0};
#else
static DIALOG net_nccoptionsdialog = {{50,75,503,486}, N_("Network Consistency Checker"), 0, 44, net_nccoptionsdialogitems, 0, 0};
#endif

/* special items for the "NCC Control" dialog: */
#define DNCO_DONCCNOW            1		/* Do NCC now (button) */
#define DNCO_RECURSIVENCC        3		/* Do NCC recursively (check) */
#define DNCO_CHECKEXPORTNAME     4		/* Check export names in NCC (check) */
#define DNCO_DOPREANALNOW        5		/* Do NCC Preanalysis now (button) */
#define DNCO_NCCGRAPHICPROGRESS  6		/* Show progress graphically (check) */
#define DNCO_MERGEPARALLEL       7		/* Merge parallel components in NCC (check) */
#define DNCO_IGNOREPG            8		/* Ignore power/ground in NCC (check) */
#define DNCO_FACETS             10		/* List of facets (scroll) */
#define DNCO_SETSECONDFACET     12		/* Set second facet to compare (button) */
#define DNCO_SETFIRSTFACET      13		/* Set first facet to compare (button) */
#define DNCO_SECONDFACET        14		/* Second facet to compare (edit text) */
#define DNCO_MERGEPARALLELYES   15		/* Yes to Merge parallel components (radio) */
#define DNCO_MERGEPARALLELNO    16		/* No to Merge parallel components (radio) */
#define DNCO_MERGEPARALLELDEF   17		/* Default to Merge parallel components (radio) */
#define DNCO_COMPMATCHTOLAMT    19		/* Component match tolerance (amt) (edit text) */
#define DNCO_CLEARVALIDNCCDATE  22		/* Clear valid NCC dates (button) */
#define DNCO_CHECKCOMPSIZE      23		/* Check comp. size in NCC (check) */
#define DNCO_FIRSTFACET         24		/* First facet to compare (edit text) */
#define DNCO_COMPMATCHTOLPER    28		/* Component match tolerance (%) (edit text) */
#define DNCO_SAVEOPTS           29		/* Save NCC options (button) */
#define DNCO_REMOVEOVERRIDES    31		/* Remove all overrides (button) */
#define DNCO_MERGESERIESYES     32		/* Yes to Merge series transistors (radio) */
#define DNCO_MERGESERIESNO      33		/* No to Merge series transistors (radio) */
#define DNCO_MERGESERIESDEF     34		/* Default to Merge series transistors (radio) */
#define DNCO_MERGESERIES        35		/* Merge series transistors in NCC (check) */
#define DNCO_EXPANDHIERYES      36		/* Yes to expand hierarchy (radio) */
#define DNCO_EXPANDHIERNO       37		/* No to expand hierarchy (radio) */
#define DNCO_EXPANDHIERDEF      38		/* Default to expand hierarchy (radio) */
#define DNCO_EXPANDHIERARCHY    39		/* Expand hierarchy (check) */
#define DNCO_LIBRARYSEL         40		/* Library of facets (popup) */
#define DNCO_LISTOVERRIDES      41		/* List all overrides (button) */
#define DNCO_SHOWMATCHTAGS      42		/* Show 'NCCMatch' tags (check) */
#define DNCO_NEXTFIRSTFACET     43		/* Choose next "first facet" (button) */
#define DNCO_NEXTSECONDFACET    44		/* Choose next "second facet" (button) */
#define DNCO_EXPERIMENTALNCC    45		/* Use experimental NCC (check) */
#define DNCO_NUMNCCPROCESSORS_L 46		/* NCC processors label (message) */
#define DNCO_NUMNCCPROCESSORS   47		/* Number of processors in experimental NCC (edit text) */

#define NUMOVERRIDES   3

static struct
{
	char *description;
	int   yes, no, def;
	int   overridebit, yesbit, nobit;
} net_nccoverridebuttons[NUMOVERRIDES] =
{
	{"hierarchy expansion",
		DNCO_EXPANDHIERYES,        DNCO_EXPANDHIERNO,      DNCO_EXPANDHIERDEF,
		NCCHIERARCHICALOVER,       NCCHIERARCHICAL,        0},
	{"parallel component merging",
		DNCO_MERGEPARALLELYES,     DNCO_MERGEPARALLELNO,   DNCO_MERGEPARALLELDEF,
		NCCNOMERGEPARALLELOVER,    0,                      NCCNOMERGEPARALLEL},
	{"series transistor merging",
		DNCO_MERGESERIESYES,       DNCO_MERGESERIESNO,     DNCO_MERGESERIESDEF,
		NCCMERGESERIESOVER,        NCCMERGESERIES,         0}
};

static char     **net_librarylist;
static LIBRARY   *net_library;
static NODEPROTO *net_posnodeprotos;

BOOLEAN net_topoffacets(char **c)
{
	net_posnodeprotos = net_library->firstnodeproto;
	return(TRUE);
}
char *net_nextfacets(void)
{
	REGISTER NODEPROTO *np;

	while (net_posnodeprotos != NONODEPROTO)
	{
		np = net_posnodeprotos;
		net_posnodeprotos = net_posnodeprotos->nextnodeproto;
		if (np->cellview == el_iconview) continue;
		return(nldescribenodeproto(np));
	}
	return(0);
}

void net_nccoptionsdlog(void)
{
	REGISTER VARIABLE *var;
	REGISTER INTBIG itemHit, i, options, oldoptions, clearvalidnccdates, tolerance,
		toleranceamt, libcount, oldfacetoptions;
#ifdef FORCESUNTOOLS
	REGISTER INTBIG numproc;
#endif
	REGISTER UINTBIG wanted;
	REGISTER LIBRARY *lib;
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER WINDOWPART *win, *winfirst, *winsecond;
	char buf[10], *paramstart[5];
	REGISTER NODEPROTO *np;
	NODEPROTO *facet1, *facet2;
	static NODEPROTO *firstfacet = NONODEPROTO;
	static NODEPROTO *secondfacet = NONODEPROTO;
	static LIBRARY *firstlibrary, *secondlibrary;
	REGISTER void *dia;

	dia = DiaInitDialog(&net_nccoptionsdialog);
	if (dia == 0) return;
	DiaUnDimItem(dia, DNCO_CLEARVALIDNCCDATE);

	if (!net_getfacets(&facet1, &facet2))
	{
		firstfacet = facet1;   firstlibrary = facet1->cell->lib;
		secondfacet = facet2;  secondlibrary = facet2->cell->lib;
	}
	winfirst = winsecond = el_curwindowpart;
	if (firstfacet != NONODEPROTO)
	{
		/* validate this facet */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			if (lib == firstlibrary) break;
		if (lib == NOLIBRARY) firstfacet = NONODEPROTO;
		if (firstfacet != NONODEPROTO)
		{
			for(np = firstlibrary->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				if (np == firstfacet) break;
			if (np == NONODEPROTO) firstfacet = NONODEPROTO;
		}
		if (firstfacet != NONODEPROTO)
		{
			DiaSetText(dia, DNCO_FIRSTFACET, describenodeproto(firstfacet));
			for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
				if (win->curnodeproto == firstfacet) break;
			if (win != NOWINDOWPART) winfirst = win;
		}
	}
	if (secondfacet != NONODEPROTO)
	{
		/* validate this facet */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			if (lib == secondlibrary) break;
		if (lib == NOLIBRARY) secondfacet = NONODEPROTO;
		if (secondfacet != NONODEPROTO)
		{
			for(np = secondlibrary->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				if (np == secondfacet) break;
			if (np == NONODEPROTO) secondfacet = NONODEPROTO;
		}
		if (secondfacet != NONODEPROTO)
		{
			DiaSetText(dia, DNCO_SECONDFACET, describenodeproto(secondfacet));
			for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
				if (win->curnodeproto == secondfacet) break;
			if (win != NOWINDOWPART) winsecond = win;
		}
	}

	/* list the libraries */
	libcount = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		if ((lib->userbits&HIDDENLIBRARY) == 0) libcount++;
	net_librarylist = (char **)emalloc(libcount * (sizeof (char *)), el_tempcluster);
	libcount = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		if ((lib->userbits&HIDDENLIBRARY) == 0)
			net_librarylist[libcount++] = lib->libname;
	esort(net_librarylist, libcount, sizeof (char *), sort_stringascending);
	DiaSetPopup(dia, DNCO_LIBRARYSEL, libcount, net_librarylist);
	for(i=0; i<libcount; i++)
		if (namesame(net_librarylist[i], el_curlib->libname) == 0) break;
	if (i < libcount) DiaSetPopupEntry(dia, DNCO_LIBRARYSEL, i);

	net_library = el_curlib;
	DiaInitTextDialog(dia, DNCO_FACETS, net_topoffacets, net_nextfacets, DiaNullDlogDone, 0,
		SCSELMOUSE|SCSELKEY|SCREPORT);
	(void)us_setscrolltocurrentfacet(DNCO_FACETS, FALSE, TRUE, FALSE, FALSE, dia);
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_optionskey);
	if (var == NOVARIABLE) options = 0; else options = var->addr;
	oldoptions = options;
	if ((options&NCCRECURSE) != 0) DiaSetControl(dia, DNCO_RECURSIVENCC, 1);
	if ((options&NCCHIERARCHICAL) != 0) DiaSetControl(dia, DNCO_EXPANDHIERARCHY, 1);
	if ((options&NCCGRAPHICPROGRESS) != 0) DiaSetControl(dia, DNCO_NCCGRAPHICPROGRESS, 1);
	if ((options&NCCIGNOREPWRGND) != 0) DiaSetControl(dia, DNCO_IGNOREPG, 1);
	if ((options&NCCNOMERGEPARALLEL) == 0) DiaSetControl(dia, DNCO_MERGEPARALLEL, 1);
	if ((options&NCCMERGESERIES) != 0) DiaSetControl(dia, DNCO_MERGESERIES, 1);
	if ((options&NCCCHECKEXPORTNAMES) != 0) DiaSetControl(dia, DNCO_CHECKEXPORTNAME, 1);
	if ((options&NCCCHECKSIZE) != 0) DiaSetControl(dia, DNCO_CHECKCOMPSIZE, 1);
	if ((options&NCCHIDEMATCHTAGS) == 0) DiaSetControl(dia, DNCO_SHOWMATCHTAGS, 1);
#ifdef FORCESUNTOOLS
	if ((options&NCCEXPERIMENTAL) != 0)
	{
		DiaSetControl(dia, DNCO_EXPERIMENTALNCC, 1);
		DiaUnDimItem(dia, DNCO_NUMNCCPROCESSORS_L);
		DiaUnDimItem(dia, DNCO_NUMNCCPROCESSORS);
	} else
	{
		DiaDimItem(dia, DNCO_NUMNCCPROCESSORS_L);
		DiaDimItem(dia, DNCO_NUMNCCPROCESSORS);
	}
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_processors_key);
	if (var == NOVARIABLE) numproc = 1; else numproc = var->addr;
	sprintf(buf, "%ld", numproc);
	DiaSetText(dia, DNCO_NUMNCCPROCESSORS, buf);
#endif

	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_comptolerancekey);
	if (var == NOVARIABLE) tolerance = 0; else tolerance = var->addr;
	sprintf(buf, "%ld", tolerance);
	DiaSetText(dia, DNCO_COMPMATCHTOLPER, buf);
	var = getvalkey((INTBIG)net_tool, VTOOL, VINTEGER, net_ncc_comptoleranceamtkey);
	if (var == NOVARIABLE) toleranceamt = 0; else toleranceamt = var->addr;
	DiaSetText(dia, DNCO_COMPMATCHTOLAMT, frtoa(toleranceamt));

	/* 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(dia);
	clearvalidnccdates = 0;

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit(dia);
		if (itemHit == DNCO_SAVEOPTS || itemHit == CANCEL) break;
		if (itemHit == DNCO_DONCCNOW || itemHit == DNCO_DOPREANALNOW)
		{
			if (firstfacet != NONODEPROTO && secondfacet != NONODEPROTO) break;
			DiaMessageInDialog(_("Must first specify two facets to compare"));
			continue;
		}
		if (itemHit == DNCO_MERGEPARALLEL || itemHit == DNCO_IGNOREPG ||
			itemHit == DNCO_CHECKEXPORTNAME || itemHit == DNCO_CHECKCOMPSIZE ||
			itemHit == DNCO_MERGESERIES || itemHit == DNCO_EXPANDHIERARCHY)
		{
			DiaSetControl(dia, itemHit, 1 - DiaGetControl(dia, itemHit));
			clearvalidnccdates = 1;
			DiaDimItem(dia, DNCO_CLEARVALIDNCCDATE);
			continue;
		}
#ifdef FORCESUNTOOLS
		if (itemHit == DNCO_EXPERIMENTALNCC)
		{
			i = 1 - DiaGetControl(dia, itemHit);
			DiaSetControl(dia, itemHit, i);
			if (i != 0)
			{
				DiaUnDimItem(dia, DNCO_NUMNCCPROCESSORS_L);
				DiaUnDimItem(dia, DNCO_NUMNCCPROCESSORS);
			} else
			{
				DiaDimItem(dia, DNCO_NUMNCCPROCESSORS_L);
				DiaDimItem(dia, DNCO_NUMNCCPROCESSORS);
			}
			clearvalidnccdates = 1;
			DiaDimItem(dia, DNCO_CLEARVALIDNCCDATE);
			continue;
		}
#endif
		if (itemHit == DNCO_NCCGRAPHICPROGRESS || itemHit == DNCO_RECURSIVENCC ||
			itemHit == DNCO_SHOWMATCHTAGS)
		{
			DiaSetControl(dia, itemHit, 1 - DiaGetControl(dia, itemHit));
			continue;
		}
		if (itemHit == DNCO_COMPMATCHTOLAMT || itemHit == DNCO_COMPMATCHTOLPER)
		{
			if (itemHit == DNCO_COMPMATCHTOLAMT)
			{
				i = atofr(DiaGetText(dia, DNCO_COMPMATCHTOLAMT));
				if (i == toleranceamt) continue;
			} else
			{
				i = atoi(DiaGetText(dia, DNCO_COMPMATCHTOLPER));
				if (i == tolerance) continue;
			}
			clearvalidnccdates = 1;
			DiaDimItem(dia, DNCO_CLEARVALIDNCCDATE);
			continue;
		}
		if (itemHit == DNCO_SETFIRSTFACET)
		{
			i = us_facetselect(_("First facet to NCC"), paramstart, 0);
			if (i == 0) continue;
			np = getnodeproto(paramstart[0]);
			if (np == NONODEPROTO) continue;
			firstfacet = np;
			DiaSetText(dia, DNCO_FIRSTFACET, describenodeproto(np));
			if (firstfacet != NONODEPROTO) firstlibrary = firstfacet->cell->lib;
			continue;
		}
		if (itemHit == DNCO_FIRSTFACET)
		{
			firstfacet = getnodeproto(DiaGetText(dia, DNCO_FIRSTFACET));
			if (firstfacet != NONODEPROTO) firstlibrary = firstfacet->cell->lib;
			continue;
		}
		if (itemHit == DNCO_NEXTFIRSTFACET)
		{
			/* cycle through the open windows, choosing the next "first facet" */
			if (winfirst == NOWINDOWPART) continue;
			winfirst = winfirst->nextwindowpart;
			if (winfirst == NOWINDOWPART) winfirst = el_topwindowpart;
			if (winfirst->curnodeproto != NONODEPROTO)
			{
				firstfacet = winfirst->curnodeproto;
				DiaSetText(dia, DNCO_FIRSTFACET, describenodeproto(firstfacet));
				if (firstfacet != NONODEPROTO) firstlibrary = firstfacet->cell->lib;
			}
			continue;
		}
		if (itemHit == DNCO_SETSECONDFACET)
		{
			i = us_facetselect(_("Second facet to NCC"), paramstart, 0);
			if (i == 0) continue;
			np = getnodeproto(paramstart[0]);
			if (np == NONODEPROTO) continue;
			secondfacet = np;
			DiaSetText(dia, DNCO_SECONDFACET, describenodeproto(np));
			if (secondfacet != NONODEPROTO) secondlibrary = secondfacet->cell->lib;
			continue;
		}
		if (itemHit == DNCO_SECONDFACET)
		{
			secondfacet = getnodeproto(DiaGetText(dia, DNCO_SECONDFACET));
			if (secondfacet != NONODEPROTO) secondlibrary = secondfacet->cell->lib;
			continue;
		}
		if (itemHit == DNCO_NEXTSECONDFACET)
		{
			/* cycle through the open windows, choosing the next "first facet" */
			if (winsecond == NOWINDOWPART) continue;
			winsecond = winsecond->nextwindowpart;
			if (winsecond == NOWINDOWPART) winsecond = el_topwindowpart;
			if (winsecond->curnodeproto != NONODEPROTO)
			{
				secondfacet = winsecond->curnodeproto;
				DiaSetText(dia, DNCO_SECONDFACET, describenodeproto(secondfacet));
				if (secondfacet != NONODEPROTO) secondlibrary = secondfacet->cell->lib;
			}
			continue;
		}
		if (itemHit == DNCO_LISTOVERRIDES)
		{
			net_listnccoverrides(TRUE);
			continue;
		}
		if (itemHit == DNCO_REMOVEOVERRIDES)
		{
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
				for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
					np->temp1 = 0;
			}
			continue;
		}
		for(i=0; i<NUMOVERRIDES; i++)
		{
			if (itemHit == net_nccoverridebuttons[i].yes ||
				itemHit == net_nccoverridebuttons[i].no ||
				itemHit == net_nccoverridebuttons[i].def)
			{
				DiaSetControl(dia, net_nccoverridebuttons[i].yes, 0);
				DiaSetControl(dia, net_nccoverridebuttons[i].no, 0);
				DiaSetControl(dia, net_nccoverridebuttons[i].def, 0);
				DiaSetControl(dia, itemHit, 1);

				np = net_nccdlgselectedfacet(dia);
				if (np == NONODEPROTO) break;
				np->temp1 &= ~(net_nccoverridebuttons[i].overridebit |
					net_nccoverridebuttons[i].yesbit | net_nccoverridebuttons[i].nobit);
				if (itemHit != net_nccoverridebuttons[i].def)
				{
					np->temp1 |= net_nccoverridebuttons[i].overridebit;
					if (itemHit == net_nccoverridebuttons[i].yes)
					{
						np->temp1 |= net_nccoverridebuttons[i].yesbit;
					} else
					{
						np->temp1 |= net_nccoverridebuttons[i].nobit;
					}
				}
				break;
			}
		}
		if (itemHit == DNCO_FACETS)
		{
			net_setnccoverrides(dia);
			continue;
		}
		if (itemHit == DNCO_CLEARVALIDNCCDATE)
		{
			/* clear valid NCC dates */
			clearvalidnccdates = 1;
			DiaDimItem(dia, DNCO_CLEARVALIDNCCDATE);
			continue;
		}
		if (itemHit == DNCO_LIBRARYSEL)
		{
			i = DiaGetPopupEntry(dia, DNCO_LIBRARYSEL);
			lib = getlibrary(net_librarylist[i]);
			if (lib == NOLIBRARY) continue;
			net_library = lib;
			DiaLoadTextDialog(dia, DNCO_FACETS, net_topoffacets, net_nextfacets, DiaNullDlogDone, 0);
			net_setnccoverrides(dia);
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		options = 0;
		if (DiaGetControl(dia, DNCO_RECURSIVENCC) != 0) options |= NCCRECURSE;
		if (DiaGetControl(dia, DNCO_EXPANDHIERARCHY) != 0) options |= NCCHIERARCHICAL;
		if (DiaGetControl(dia, DNCO_NCCGRAPHICPROGRESS) != 0) options |= NCCGRAPHICPROGRESS;
		if (DiaGetControl(dia, DNCO_MERGEPARALLEL) == 0) options |= NCCNOMERGEPARALLEL;
		if (DiaGetControl(dia, DNCO_MERGESERIES) != 0) options |= NCCMERGESERIES;
		if (DiaGetControl(dia, DNCO_IGNOREPG) != 0) options |= NCCIGNOREPWRGND;
		if (DiaGetControl(dia, DNCO_CHECKEXPORTNAME) != 0) options |= NCCCHECKEXPORTNAMES;
		if (DiaGetControl(dia, DNCO_CHECKCOMPSIZE) != 0) options |= NCCCHECKSIZE;
		if (DiaGetControl(dia, DNCO_SHOWMATCHTAGS) == 0) options |= NCCHIDEMATCHTAGS;
#ifdef FORCESUNTOOLS
		if (DiaGetControl(dia, DNCO_EXPERIMENTALNCC) != 0) options |= NCCEXPERIMENTAL;
#endif
		if (options != oldoptions)
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_optionskey, options, VINTEGER);

		i = atoi(DiaGetText(dia, DNCO_COMPMATCHTOLPER));
		if (i != tolerance)
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_comptolerancekey, i, VINTEGER);
		i = atofr(DiaGetText(dia, DNCO_COMPMATCHTOLAMT));
		if (i != toleranceamt)
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_comptoleranceamtkey, i, VINTEGER);
#ifdef FORCESUNTOOLS
		i = atoi(DiaGetText(dia, DNCO_NUMNCCPROCESSORS));
		if (i != numproc)
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_ncc_processors_key, i, VINTEGER);
#endif

		/* 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) oldfacetoptions = 0; else oldfacetoptions = var->addr;
			if (oldfacetoptions == np->temp1) continue;
			if (np->temp1 == 0) (void)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)
				{
					net_nccremovematches(np);
					for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
					{
						var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
						if (var != NOVARIABLE)
						{
							if (namesamen((char *)var->addr, "NCCmatch", 8) == 0)
							{
								startobjectchange((INTBIG)ni, VNODEINST);
								(void)delvalkey((INTBIG)ni, VNODEINST, el_node_name_key);
								endobjectchange((INTBIG)ni, VNODEINST);
							}
						}
						var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
						if (var != NOVARIABLE)
						{
							startobjectchange((INTBIG)ni, VNODEINST);
							(void)delvalkey((INTBIG)ni, VNODEINST, net_ncc_matchkey);
							endobjectchange((INTBIG)ni, VNODEINST);
						}	
					}
					for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
					{
						var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
						if (var != NOVARIABLE)
						{
							if (namesamen((char *)var->addr, "NCCmatch", 8) == 0)
							{
								startobjectchange((INTBIG)ai, VARCINST);
								(void)delvalkey((INTBIG)ai, VARCINST, el_arc_name_key);
								endobjectchange((INTBIG)ai, VARCINST);
							}
						}
						var = getvalkey((INTBIG)ai, VARCINST, VSTRING, net_ncc_matchkey);
						if (var != NOVARIABLE)
						{
							startobjectchange((INTBIG)ai, VARCINST);
							(void)delvalkey((INTBIG)ai, VARCINST, net_ncc_matchkey);
							endobjectchange((INTBIG)ai, VARCINST);
						}	
					}
				}
			}
			net_removeassociations();
		} else if ((options&NCCHIDEMATCHTAGS) != (oldoptions&NCCHIDEMATCHTAGS))
		{
			/* change visibility of "nccmatch" tags */
			if ((options&NCCHIDEMATCHTAGS) == 0) wanted = VDISPLAY; else wanted = 0;
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				{
					for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
					{
						var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
						if (var != NOVARIABLE)
						{
							if (namesamen((char *)var->addr, "NCCmatch", 8) == 0)
							{
								if ((var->type&VDISPLAY) != wanted)
								{
									startobjectchange((INTBIG)ni, VNODEINST);
									var->type = (var->type & ~VDISPLAY) | wanted;
									endobjectchange((INTBIG)ni, VNODEINST);
								}
							}
						}
						var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, net_ncc_matchkey);
						if (var != NOVARIABLE && (var->type&VDISPLAY) != wanted)
						{
							startobjectchange((INTBIG)ni, VNODEINST);
							var->type = (var->type & ~VDISPLAY) | wanted;
							endobjectchange((INTBIG)ni, VNODEINST);
						}	
					}
					for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
					{
						var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
						if (var != NOVARIABLE)
						{
							if (namesamen((char *)var->addr, "NCCmatch", 8) == 0)
							{
								if ((var->type&VDISPLAY) != wanted)
								{
									startobjectchange((INTBIG)ai, VARCINST);
									var->type = (var->type & ~VDISPLAY) | wanted;
									endobjectchange((INTBIG)ai, VARCINST);
								}
							}
						}
						var = getvalkey((INTBIG)ai, VARCINST, VSTRING, net_ncc_matchkey);
						if (var != NOVARIABLE && (var->type&VDISPLAY) != wanted)
						{
							startobjectchange((INTBIG)ai, VARCINST);
							var->type = (var->type & ~VDISPLAY) | wanted;
							endobjectchange((INTBIG)ai, VARCINST);
						}	
					}
				}
			}
		}
	}
	DiaDoneDialog(dia);
	efree((char *)net_librarylist);
	if (itemHit == DNCO_DONCCNOW)
	{
		net_compare(FALSE, TRUE, firstfacet, secondfacet);
	} else if (itemHit == DNCO_DOPREANALNOW)
	{
		net_compare(TRUE, TRUE, firstfacet, secondfacet);
	}
}

void net_setnccoverrides(void *dia)
{
	REGISTER INTBIG i, override;
	REGISTER NODEPROTO *np;

	np = net_nccdlgselectedfacet(dia);
	if (np == NONODEPROTO) return;

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

/*
 * Routine to list NCC overrides
 */
void net_listnccoverrides(BOOLEAN usetemp1)
{
	REGISTER LIBRARY *lib;
	REGISTER NODEPROTO *np;
	REGISTER INTBIG i, overridecount, bits;
	REGISTER char *state;
	REGISTER void *infstr;
	REGISTER VARIABLE *var;

	overridecount = 0;
	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
	{
		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		{
			if (usetemp1) bits = np->temp1; else
			{
				var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, net_ncc_optionskey);
				if (var == NOVARIABLE) bits = 0; else bits = var->addr;
			}
			for(i=0; i<NUMOVERRIDES; i++)
			{
				if ((bits&net_nccoverridebuttons[i].overridebit) == 0)
					continue;
				if (net_nccoverridebuttons[i].yesbit != 0)
				{
					if ((bits&net_nccoverridebuttons[i].yesbit) != 0)
						state = _("on"); else
							state = _("off");
				} else
				{
					if ((bits&net_nccoverridebuttons[i].nobit) != 0)
						state = _("off"); else
							state = _("on");
				}
				if (overridecount == 0)
					ttyputmsg(_("- Current facet overrides:"));
				infstr = initinfstr();
				formatinfstr(infstr, _("    Facet %s forces %s %s"),
					describenodeproto(np), net_nccoverridebuttons[i].description,
						state);
				ttyputmsg("%s", returninfstr(infstr));
				overridecount++;
			}
		}
	}
	if (overridecount == 0) ttyputmsg(_("- No facet overrides"));
}

/*
 * Routine to return the facet that is currently selected in the "Network Options"
 * dialog.  Returns NONODEPROTO if none can be determined.
 */
NODEPROTO *net_nccdlgselectedfacet(void *dia)
{
	REGISTER INTBIG which;
	REGISTER char *pt;
	REGISTER NODEPROTO *np;
	REGISTER LIBRARY *lib;
	REGISTER void *infstr;

	/* get the library */
	which = DiaGetPopupEntry(dia, DNCO_LIBRARYSEL);
	if (which < 0) return(NONODEPROTO);
	lib = getlibrary(net_librarylist[which]);
	if (lib == NOLIBRARY) return(NONODEPROTO);

	which = DiaGetCurLine(dia, DNCO_FACETS);
	if (which < 0) return(NONODEPROTO);
	pt = DiaGetScrollLine(dia, DNCO_FACETS, which);
	if (*pt == 0) return(NONODEPROTO);
	if (lib != el_curlib)
	{
		infstr = initinfstr();
		formatinfstr(infstr, "%s:%s", lib->libname, pt);
		pt = returninfstr(infstr);
	}
	np = getnodeproto(pt);
	return(np);
}

/****************************** NETWORK OPTIONS DIALOG ******************************/

/* Network: Options */
static DIALOGITEM net_optionsdialogitems[] =
{
 /*  1 */ {0, {240,132,264,196}, BUTTON, N_("OK")},
 /*  2 */ {0, {240,24,264,88}, BUTTON, N_("Cancel")},
 /*  3 */ {0, {28,20,44,226}, CHECK, N_("Unify Power and Ground")},
 /*  4 */ {0, {52,20,68,226}, CHECK, N_("Unify all like-named nets")},
 /*  5 */ {0, {4,4,20,165}, MESSAGE, N_("Network numbering:")},
 /*  6 */ {0, {180,36,196,201}, RADIO, N_("Ascending (0:N)")},
 /*  7 */ {0, {204,36,220,201}, RADIO, N_("Descending (N:0)")},
 /*  8 */ {0, {132,20,148,177}, MESSAGE, N_("Default starting index:")},
 /*  9 */ {0, {132,180,148,226}, POPUP, ""},
 /* 10 */ {0, {100,4,101,226}, DIVIDELINE, ""},
 /* 11 */ {0, {156,20,172,177}, MESSAGE, N_("Default order:")},
 /* 12 */ {0, {108,4,124,177}, MESSAGE, N_("For busses:")},
 /* 13 */ {0, {76,20,92,226}, CHECK, N_("Ignore Resistors")}
};
static DIALOG net_optionsdialog = {{50,75,323,311}, N_("Network Options"), 0, 13, net_optionsdialogitems, 0, 0};

/* special items for the "Network Options" dialog: */
#define DNTO_UNIFYPG             3		/* Unify Power and Ground (check) */
#define DNTO_UNIFYLIKENAMEDNETS  4		/* Unify all like-named nets (check) */
#define DNTO_UNNAMEDASCEND       6		/* Number unnamed busses ascending (radio) */
#define DNTO_UNNAMEDDESCEND      7		/* Number unnamed busses descending (radio) */
#define DNTO_UNNAMEDSTARTINDEX   9		/* Starting index of unnamed busses (popup) */
#define DNTO_IGNORERESISTORS    13		/* Ignore resistors (check) */

void net_optionsdlog(void)
{
	REGISTER INTBIG itemHit, i;
	char *choices[3];
	REGISTER void *dia;

	dia = DiaInitDialog(&net_optionsdialog);
	if (dia == 0) return;
	choices[0] = "0";   choices[1] = "1";
	DiaSetPopup(dia, DNTO_UNNAMEDSTARTINDEX, 2, choices);

	/* load general network options */
	if ((net_options&NETCONPWRGND) != 0) DiaSetControl(dia, DNTO_UNIFYPG, 1);
	if ((net_options&NETCONCOMMONNAME) != 0) DiaSetControl(dia, DNTO_UNIFYLIKENAMEDNETS, 1);
	if ((net_options&NETIGNORERESISTORS) != 0) DiaSetControl(dia, DNTO_IGNORERESISTORS, 1);

	/* load bus naming options */
	if ((net_options&NETDEFBUSBASE1) != 0)
	{
		DiaSetPopupEntry(dia, DNTO_UNNAMEDSTARTINDEX, 1);
		DiaSetText(dia, DNTO_UNNAMEDASCEND, _("Ascending (1:N)"));
		DiaSetText(dia, DNTO_UNNAMEDDESCEND, _("Descending (N:1)"));
	} else
	{
		DiaSetText(dia, DNTO_UNNAMEDASCEND, _("Ascending (0:N)"));
		DiaSetText(dia, DNTO_UNNAMEDDESCEND, _("Descending (N:0)"));
	}
	if ((net_options&NETDEFBUSBASEDESC) != 0) DiaSetControl(dia, DNTO_UNNAMEDDESCEND, 1); else
		DiaSetControl(dia, DNTO_UNNAMEDASCEND, 1);

	/* loop until done */
	for(;;)
	{
		itemHit = DiaNextHit(dia);
		if (itemHit == OK || itemHit == CANCEL) break;
		if (itemHit == DNTO_UNIFYPG || itemHit == DNTO_UNIFYLIKENAMEDNETS ||
			itemHit == DNTO_IGNORERESISTORS)
		{
			DiaSetControl(dia, itemHit, 1 - DiaGetControl(dia, itemHit));
			continue;
		}
		if (itemHit == DNTO_UNNAMEDDESCEND || itemHit == DNTO_UNNAMEDASCEND)
		{
			DiaSetControl(dia, DNTO_UNNAMEDASCEND, 0);
			DiaSetControl(dia, DNTO_UNNAMEDDESCEND, 0);
			DiaSetControl(dia, itemHit, 1);
			continue;
		}
		if (itemHit == DNTO_UNNAMEDSTARTINDEX)
		{
			if (DiaGetPopupEntry(dia, DNTO_UNNAMEDSTARTINDEX) != 0)
			{
				DiaSetText(dia, DNTO_UNNAMEDASCEND, _("Ascending (1:N)"));
				DiaSetText(dia, DNTO_UNNAMEDDESCEND, _("Descending (N:1)"));
			} else
			{
				DiaSetText(dia, DNTO_UNNAMEDASCEND, _("Ascending (0:N)"));
				DiaSetText(dia, DNTO_UNNAMEDDESCEND, _("Descending (N:0)"));
			}
			continue;
		}
	}

	if (itemHit != CANCEL)
	{
		i = net_options & ~(NETCONPWRGND | NETCONCOMMONNAME | NETDEFBUSBASE1 | NETDEFBUSBASEDESC |
			NETIGNORERESISTORS);
		if (DiaGetControl(dia, DNTO_UNIFYPG) != 0) i |= NETCONPWRGND;
		if (DiaGetControl(dia, DNTO_UNIFYLIKENAMEDNETS) != 0) i |= NETCONCOMMONNAME;
		if (DiaGetControl(dia, DNTO_IGNORERESISTORS) != 0) i |= NETIGNORERESISTORS;
		if (DiaGetPopupEntry(dia, DNTO_UNNAMEDSTARTINDEX) != 0) i |= NETDEFBUSBASE1;
		if (DiaGetControl(dia, DNTO_UNNAMEDDESCEND) != 0) i |= NETDEFBUSBASEDESC;
		if (i != net_options)
		{
			(void)setvalkey((INTBIG)net_tool, VTOOL, net_optionskey, i, VINTEGER);
			net_tellschematics();
			net_totalrenumber(NOLIBRARY);
		}
	}
	DiaDoneDialog(dia);
}
