/*
 * Electric(tm) VLSI Design System
 *
 * File: usrcomek.c
 * User interface aid: command handler for E through K
 * 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 "usr.h"
#include "usrtrack.h"
#include "usrdiacom.h"
#include "efunction.h"
#include "tecart.h"
#include "tecgen.h"
#ifdef HAVE_UNISTD_H
#  include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#  include <fcntl.h>
#endif

#define	MAXLINE 500		/* maximum length of news/help file input lines */

/* working memory for "us_iterate()" */
static INTBIG *us_iterateaddr, *us_iteratetype;
static INTBIG  us_iteratelimit=0;

/*
 * Routine to free all memory associated with this module.
 */
void us_freecomekmemory(void)
{
	if (us_iteratelimit > 0)
	{
		efree((char *)us_iterateaddr);
		efree((char *)us_iteratetype);
	}
}

void us_echo(INTSML count, char *par[])
{
	REGISTER INTSML lastquiet, i, j;

	lastquiet = ttyquiet(0);
	i = initinfstr();
	for(j=0; j<count; j++)
	{
		i += addstringtoinfstr(par[j]);
		i += addtoinfstr(' ');
	}
	if (i != 0)
	{
		ttyputerr("No space for message!");
		return;
	}
	ttyputmsg("%s", returninfstr());
	(void)ttyquiet(lastquiet);
}

void us_editfacet(INTSML count, char *par[])
{
	REGISTER INTSML implicit, i, len, newwindow, nonredundant, newframe, push;
	INTBIG lx, hx, ly, hy;
	REGISTER NODEPROTO *np, *onp, *np1, *curfacet;
	REGISTER NODEINST *ni, *stacknodeinst;
	REGISTER LIBRARY *lib, *olib;
	REGISTER WINDOWPART *win;
	REGISTER char *pt;
	NODEINST *hini;
	PORTPROTO *hipp;

	/* get proper highlighting in subfacet if port is selected */
	us_findlowerport(&hini, &hipp);

	/* find the nodeinst in this window to go "down into" (if any) */
	newwindow = 0;
	nonredundant = 0;
	if (count == 0)
	{
		implicit = 1;
		ni = (NODEINST *)us_getobject(OBJNODEINST, 0);
		if (ni == NONODEINST) return;
		stacknodeinst = ni;
		np = ni->proto;

		/* translate this reference if this is an icon facet */
		if (np->cellview == el_iconview)
		{
			onp = contentsview(np);
			if (onp != NONODEPROTO)
			{
				np = onp;
				implicit = 0;
			}
		}
	} else
	{
		/* check for options */
		for(i=1; i<count; i++)
		{
			len = strlen(par[i]);
			if (namesamen(par[i], "new-window", len) == 0 && len > 1) newwindow++; else
			if (namesamen(par[i], "non-redundant", len) == 0 && len > 1) nonredundant++; else
			{
				us_abortcommand("Unknown EDITFACET parameter: %s", par[i]);
				return;
			}
		}

		/* see if specified facet exists */
		implicit = 0;
		np = getnodeproto(par[0]);

		/* if it is a new facet, create it */
		if (np == NONODEPROTO)
		{
			lib = el_curlib;
			for(pt = par[0]; *pt != 0; pt++) if (*pt == ':') break;
			if (*pt != ':') pt = par[0]; else
			{
				i = *pt;
				*pt = 0;
				lib = getlibrary(par[0]);
				*pt = (char)i;
				if (lib == NOLIBRARY)
				{
					us_abortcommand("Cannot find library for new facet %s", par[0]);
					return;
				}
				pt++;
			}
			np = newnodeproto(pt, lib);
			if (np == NONODEPROTO)
			{
				us_abortcommand("Cannot create facet %s", par[0]);
				return;
			}

			/* icon facets should always be expanded */
			if (np->cellview == el_iconview) np->userbits |= WANTNEXPAND;
			ttyputverbose("Editing new facet: %s", par[0]);
		}

		/* look through window for instances of this facet */
		stacknodeinst = NONODEINST;
		curfacet = getcurfacet();
		if (curfacet != NONODEPROTO)
		{
			for(ni = curfacet->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				if (ni->proto == np)
			{
				stacknodeinst = ni;
				break;
			}
		}
	}

	/* make sure nodeinst is not primitive and in proper technology */
	if (np->primindex != 0)
	{
		us_abortcommand("Cannot edit primitive nodes");
		return;
	}

	push = 0;
	if (stacknodeinst != NONODEINST)
	{
		push = 1;
		onp = contentsview(np);
		if (onp == NONODEPROTO) onp = np;
		(void)setvalkey((INTBIG)onp, VNODEPROTO, us_facet_descent_path, (INTBIG)stacknodeinst,
			VNODEINST|VDONTSAVE);
	}

	/* check dates of subfacets */
	if ((us_useroptions&CHECKDATE) != 0)
	{
		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
			for(np1 = olib->firstnodeproto; np1 != NONODEPROTO; np1 = np1->nextnodeproto)
				np1->temp1 = 0;
		us_check_facet_date(np, np->revisiondate);
	}

	/* if a nonredundant display is needed, see if it already exists */
	if (nonredundant != 0)
	{
		for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
			if (win->curnodeproto == np) break;
		if (win != NOWINDOWPART)
		{
			/* switch to window "win" */
			bringwindowtofront(win->frame);
			us_highlightwindow(win, 0);
			return;
		}
	}

	/* determine window area */
	if (el_curwindowpart == NOWINDOWPART)
	{
		lx = np->lowx;   hx = np->lowx;
		ly = np->lowy;   hy = np->lowy;
	} else
	{
		lx = el_curwindowpart->screenlx;   hx = el_curwindowpart->screenhx;
		ly = el_curwindowpart->screenly;   hy = el_curwindowpart->screenhy;
		if (el_curwindowpart->curnodeproto == NONODEPROTO)
		{
			lx = -el_curtech->deflambda * 25;
			hx =  el_curtech->deflambda * 25;
			ly = -el_curtech->deflambda * 25;
			hy =  el_curtech->deflambda * 25;
		}
	}
	if (implicit == 0)
	{
		/* make the new facet fill the window */
		us_fullview(np, &lx, &hx, &ly, &hy);
	} else
	{
		/* make the current facet be in the same place in the window */
		lx += np->lowx - stacknodeinst->lowx;
		hx += np->lowx - stacknodeinst->lowx;
		ly += np->lowy - stacknodeinst->lowy;
		hy += np->lowy - stacknodeinst->lowy;
	}

	/* edit the facet (creating a new frame/partition if requested) */
	if (newwindow != 0 || el_curwindowpart == NOWINDOWPART)
	{
		newframe = 1;
		push = 0;
	} else
	{
		newframe = 0;
	}
	us_switchtofacet(np, lx, hx, ly, hy, hini, hipp, newframe, push);
}

void us_erase(INTSML count, char *par[])
{
	REGISTER INTSML i, j, l, otherend, foundnode;
	REGISTER NODEINST *ni, *nextni, *ni1, *ni2;
	REGISTER ARCINST *ai;
	REGISTER PORTARCINST *pi;
	REGISTER NODEPROTO *np;
	HIGHLIGHT high;
	REGISTER char *pp;
	REGISTER GEOM **list;
	REGISTER VARIABLE *highvar;

	if (count > 0)
	{
		l = strlen(pp = par[0]);
		if (namesamen(pp, "clean-up-all", l) == 0 && l > 8)
		{
			us_clearhighlightcount();
			for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				us_cleanupfacet(np, 0);
			return;
		}
		if (namesamen(pp, "clean-up", l) == 0)
		{
			np = us_needfacet();
			if (np == NONODEPROTO) return;
			us_clearhighlightcount();
			us_cleanupfacet(np, 1);
			return;
		}
		if (namesamen(pp, "geometry", l) == 0)
		{
			np = us_needfacet();
			if (np == NONODEPROTO) return;
			us_erasegeometry(np);
			return;
		}
	}

	np = us_needfacet();
	if (np == NONODEPROTO) return;
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar == NOVARIABLE)
	{
		us_abortcommand("First highlight something to erase");
		return;
	}
	(void)us_makehighlight(((char **)highvar->addr)[0], &high);

	/* special case if a text object is highlighted */
	if (getlength(highvar) == 1 && (high.status&HIGHTYPE) == HIGHTEXT)
	{
		/* disallow erasing if lock is on */
		if (us_canedit(np, NONODEPROTO, 1) != 0) return;

		/* deleting variable on object */
		if (high.fromvar != NOVARIABLE)
		{
			us_clearhighlightcount();
			if (high.fromgeom->entrytype == OBJNODEINST)
			{
				ni = high.fromgeom->entryaddr.ni;
				startobjectchange((INTBIG)ni, VNODEINST);

				/* if deleting text on invisible pin, delete pin too */
				if (ni->proto == gen_invispinprim) us_erasenodeinst(ni); else
					(void)delval((INTBIG)ni, VNODEINST, makename(high.fromvar->key));
				endobjectchange((INTBIG)ni, VNODEINST);
			} else
			{
				ai = high.fromgeom->entryaddr.ai;
				startobjectchange((INTBIG)ai, VARCINST);
				(void)delval((INTBIG)ai, VARCINST, makename(high.fromvar->key));
				endobjectchange((INTBIG)ai, VARCINST);
			}
		} else if (high.fromport != NOPORTPROTO)
		{
			us_clearhighlightcount();
			ni = high.fromgeom->entryaddr.ni;
			startobjectchange((INTBIG)ni, VNODEINST);
			us_undoportproto((NODEINST *)ni, high.fromport);
			endobjectchange((INTBIG)ni, VNODEINST);
			high.status = HIGHFROM;
			(void)us_addhighlight(&high);
		} else if (high.fromgeom->entrytype == OBJNODEINST)
			us_abortcommand("Cannot delete facet name");
		return;
	}

	/* get list of highlighted objects to be erased */
	np = us_needfacet();
	if (np == NONODEPROTO)
	{
		us_abortcommand("No current facet");
		return;
	}
	list = us_gethighlighted(OBJARCINST|OBJNODEINST);
	if (list[0] == NOGEOM)
	{
		us_abortcommand("Find an object to erase");
		return;
	}

	/* remove from list if a node is locked */
	j = foundnode = 0;
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype == OBJNODEINST)
		{
			ni = list[i]->entryaddr.ni;
			if (us_canedit(np, ni->proto, 1)) continue;
			foundnode = 1;
		}
		list[j++] = list[i];
	}
	list[j] = NOGEOM;
	if (list[0] == NOGEOM)
	{
		us_abortcommand("All selected objects are locked");
		return;
	}

	if (foundnode == 0)
	{
		/* disallow erasing if lock is on */
		if (us_canedit(np, NONODEPROTO, 1) != 0) return;
	}

	/* look for option to re-connect arcs into an erased node */
	if (count > 0 && namesamen(par[0], "pass-through", strlen(par[0])) == 0)
	{
		if (list[1] != NOGEOM || list[0]->entrytype != OBJNODEINST)
		{
			us_abortcommand("Must erase a single node to pass arcs through");
			return;
		}
		us_clearhighlightcount();
		j = us_erasepassthru(list[0]->entryaddr.ni, 1);
		switch (j)
		{
			case 2:
				break;
			case -1:
				us_abortcommand("Arcs to this node are of different type");
				break;
			case -5:
				us_abortcommand("Cannot create connecting arc");
				break;
			default:
				us_abortcommand("Must be 2 arcs on this node (it has %d)", j);
				break;
		}
		return;
	}

	/* handle simple erasing */
	us_clearhighlightcount();

	/* mark all nodes touching arcs that are killed */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) ni->temp1 = 0;
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype != OBJARCINST) continue;
		ai = list[i]->entryaddr.ai;
		ai->end[0].nodeinst->temp1 = 1;
		ai->end[1].nodeinst->temp1 = 1;
	}

	/* also mark all nodes on arcs that will be erased */
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype != OBJNODEINST) continue;
		ni = list[i]->entryaddr.ni;
		if (ni->temp1 != 0) ni->temp1 = 2;
	}

	/* also mark all nodes on the other end of arcs connected to erased nodes */
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype != OBJNODEINST) continue;
		ni = list[i]->entryaddr.ni;
		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
		{
			ai = pi->conarcinst;
			if (ai->end[0].portarcinst == pi) otherend = 1; else
				otherend = 0;
			if (ai->end[otherend].nodeinst->temp1 == 0)
				ai->end[otherend].nodeinst->temp1 = 1;
		}
	}

	/* now kill all of the arcs */
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype != OBJARCINST) continue;
		ai = list[i]->entryaddr.ai;

		/* see if nodes need to be undrawn to account for "Steiner Point" changes */
		ni1 = ai->end[0].nodeinst;   ni2 = ai->end[1].nodeinst;
		if (ni1->temp1 == 1 && (ni1->proto->userbits&WIPEON1OR2) != 0)
			startobjectchange((INTBIG)ni1, VNODEINST);
		if (ni2->temp1 == 1 && (ni2->proto->userbits&WIPEON1OR2) != 0)
			startobjectchange((INTBIG)ni2, VNODEINST);

		startobjectchange((INTBIG)ai, VARCINST);
		if (killarcinst(ai)) ttyputerr("Error killing arc");

		/* see if nodes need to be redrawn to account for "Steiner Point" changes */
		if (ni1->temp1 == 1 && (ni1->proto->userbits&WIPEON1OR2) != 0)
			endobjectchange((INTBIG)ni1, VNODEINST);
		if (ni2->temp1 == 1 && (ni2->proto->userbits&WIPEON1OR2) != 0)
			endobjectchange((INTBIG)ni2, VNODEINST);
	}

	/* next kill all of the nodes */
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype != OBJNODEINST) continue;
		ni = list[i]->entryaddr.ni;
		us_erasenodeinst(ni);
	}

	/* kill all pin nodes that touched an arc and no longer do */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
	{
		nextni = ni->nextnodeinst;
		if (ni->temp1 == 0) continue;
		if (ni->proto->primindex == 0) continue;
		if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN) continue;
		if (ni->firstportarcinst != NOPORTARCINST || ni->firstportexpinst != NOPORTEXPINST)
			continue;
		us_erasenodeinst(ni);
	}

	/* kill all unexported pin or bus nodes left in the middle of arcs */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = nextni)
	{
		nextni = ni->nextnodeinst;
		if (ni->temp1 == 0) continue;
		if (ni->proto->primindex == 0) continue;
		if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPPIN) continue;
		if (ni->firstportexpinst != NOPORTEXPINST) continue;
		(void)us_erasepassthru(ni, 0);
	}
}

void us_find(INTSML count, char *par[])
{
	REGISTER INTSML findport, l, findpoint, findexclusively, findmore, findangle,
		findwithin, findstill, extrainfo, findnobox, findspecial, size, findconstrained,
		findeasy, findhard;
	REGISTER INTBIG i, total, type, addr;
	INTBIG xcur, ycur;
	XARRAY trans;
	REGISTER INTBIG areasizex, areasizey, x, y, *newlist;
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *onp;
	REGISTER LIBRARY *lib;
	REGISTER GEOM *geom, **glist;
	REGISTER ARCINST *ai;
	REGISTER PORTPROTO *pp;
	REGISTER NETWORK *net;
	REGISTER VARIABLE *var, *highvar;
	REGISTER char *pt, **list;
	HIGHLIGHT newhigh;

	if (count >= 1)
	{
		l = strlen(pt = par[0]);
		if (namesamen(pt, "constraint-angle", l) == 0 && l >= 12)
		{
			if (count >= 2)
			{
				(void)setvalkey((INTBIG)us_aid, VAID, us_interactiveangle,
					atofr(par[1])*10/WHOLE, VINTEGER|VDONTSAVE);
			}
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_interactiveangle);
			if (var == NOVARIABLE) findangle = 0; else
				findangle = (INTSML)var->addr;
			if (findangle == 0)
			{
				ttyputverbose("Interactive dragging done with no constraints");
			} else
			{
				ttyputverbose("Interactive dragging constrained to %d degree angles",
					findangle);
			}
			return;
		}
		if (namesamen(pt, "constraint-default", l) == 0 && l >= 12)
		{
			if (count >= 2)
			{
				l = strlen(pt = par[1]);
				if (namesamen(pt, "on", l) == 0 && l >= 2)
				{
					(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
						us_useroptions | FLIPINTERACTIVECON, VINTEGER);
				}
				if (namesamen(pt, "off", l) == 0 && l >= 2)
				{
					(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
						us_useroptions & ~FLIPINTERACTIVECON, VINTEGER);
				}
			}
			if ((us_useroptions&FLIPINTERACTIVECON) != 0)
			{
				ttyputverbose("Interactive selection constrains to one axis");
			} else
			{
				ttyputverbose("Interactive selection allows motion in either axis");
			}
			return;
		}
	}

	/* make sure there is a facet being edited */
	np = us_needfacet();
	if (np == NONODEPROTO) return;
	if ((np->cellview->viewstate&TEXTVIEW) != 0)
	{
		us_abortcommand("There are no components to select in a text-only facet");
		return;
	}
	if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
	{
		us_abortcommand("Cannot select objects in a 3D window");
		return;
	}

	/* establish the default highlight environment */
	highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (highvar != NOVARIABLE)
		(void)us_makehighlight(((char **)highvar->addr)[0], &newhigh); else
	{
		newhigh.status = 0;
		newhigh.fromgeom = NOGEOM;
		newhigh.fromport = NOPORTPROTO;
		newhigh.fromvar = NOVARIABLE;
		newhigh.frompoint = 0;
	}

	/* look for qualifiers */
	findport = findpoint = findexclusively = findwithin = findmore = 0;
	findstill = findspecial = findnobox = findconstrained = 0;
	extrainfo = 0;
	while (count > 0)
	{
		l = strlen(pt = par[0]);
		if (namesamen(pt, "extra-info", l) == 0 && l >= 3)
		{
			extrainfo = HIGHEXTRA;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "more", l) == 0 && l >= 1)
		{
			findmore++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "no-box", l) == 0 && l >= 3)
		{
			findnobox++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "special", l) == 0 && l >= 2)
		{
			findspecial++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "still", l) == 0 && l >= 2)
		{
			findstill++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "exclusively", l) == 0 && l >= 3)
		{
			findexclusively++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "constrained", l) == 0 && l >= 10)
		{
			findconstrained = 1;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "within", l) == 0 && l >= 1)
		{
			if (newhigh.status == 0)
			{
				us_abortcommand("Find an object before working 'within' it");
				return;
			}
			if ((newhigh.status&HIGHTYPE) != HIGHFROM || newhigh.fromgeom->entrytype != OBJNODEINST)
			{
				us_abortcommand("Must find a node before working 'within'");
				return;
			}

			findwithin++;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "port", l) == 0 && l >= 1)
		{
			findport = 1;
			count--;   par++;   continue;
		}
		if (namesamen(pt, "vertex", l) == 0 && l >= 1)
		{
			findpoint = 1;
			count--;   par++;   continue;
		}
		break;
	}

	if (count >= 1)
	{
		l = strlen(pt = par[0]);

		if (namesamen(pt, "all", l) == 0 && l >= 2)
		{
			if (findpoint != 0 || findwithin != 0 || findmore != 0 ||
				findexclusively != 0 || findstill != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find all'");
				return;
			}
			findeasy = findhard = 1;
			if (count >= 2)
			{
				l = strlen(pt = par[1]);
				if (namesamen(pt, "easy", l) == 0) findhard = 0; else
				if (namesamen(pt, "hard", l) == 0) findeasy = 0; else
				{
					us_abortcommand("Usage: find all [easy|hard]");
					return;
				}
			}
			total = 0;
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) total++;
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) total++;
			if (total == 0) return;
			list = (char **)emalloc((total * (sizeof (char *))), el_tempcluster);
			if (list == 0) return;

			newhigh.status = HIGHFROM | extrainfo;
			if (findnobox != 0) newhigh.status |= HIGHNOBOX;
			newhigh.facet = np;
			newhigh.frompoint = 0;
			newhigh.fromvar = NOVARIABLE;
			total = 0;
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (ni->proto->primindex == 0 && (us_useroptions&NOINSTANCESELECT) != 0)
				{
					/* facet instance that is hard to select */
					if (findhard == 0) continue;
				} else
				{
					/* regular node: see if it should be selected */
					if (findeasy == 0 && (ni->userbits&HARDSELECTN) == 0) continue;
					if (findhard == 0 && (ni->userbits&HARDSELECTN) != 0) continue;
				}
				newhigh.fromgeom = ni->geom;
				newhigh.fromport = NOPORTPROTO;
				if (findport != 0)
				{
					pp = ni->proto->firstportproto;
					if (pp != NOPORTPROTO && pp->nextportproto == NOPORTPROTO)
						newhigh.fromport = pp;
				}
				pt = us_makehighlightstring(&newhigh);
				(void)allocstring(&list[total], pt, el_tempcluster);
				total++;
			}
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				if (findeasy == 0 && (ai->userbits&HARDSELECTA) == 0) continue;
				if (findhard == 0 && (ai->userbits&HARDSELECTA) != 0) continue;
				newhigh.fromgeom = ai->geom;
				newhigh.fromport = NOPORTPROTO;
				pt = us_makehighlightstring(&newhigh);
				(void)allocstring(&list[total], pt, el_tempcluster);
				total++;
			}
			if (total == 0) us_clearhighlightcount(); else
			{
				(void)setvalkey((INTBIG)us_aid, VAID, us_highlighted, (INTBIG)list,
					VSTRING|VISARRAY|(total<<VLENGTHSH)|VDONTSAVE);
			}
			for(i=0; i<total; i++) efree(list[i]);
			efree((char *)list);
			us_showallhighlight();
			if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO);
			return;
		}

		if (namesamen(pt, "arc", l) == 0 && l >= 3)
		{
			if (count <= 1)
			{
				us_abortcommand("Usage: find arc ARCNAME");
				return;
			}
			if (findport != 0 || findpoint != 0 || findwithin != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find arc'");
				return;
			}
			if (findexclusively != 0 && us_curarcproto == NOARCPROTO)
			{
				us_abortcommand("Must select an arc for exclusive finding");
				return;
			}
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				if (findexclusively != 0 && ai->proto != us_curarcproto) continue;
				var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
				if (var == NOVARIABLE) continue;
				if (namesame((char *)var->addr, par[1]) == 0) break;
			}
			if (ai == NOARCINST)
			{
				us_abortcommand("Sorry, no %s arc named '%s' in this facet",
					(findexclusively==0 ? "" : describearcproto(us_curarcproto)), par[1]);
				return;
			}
			newhigh.status = HIGHFROM;
			newhigh.fromgeom = ai->geom;
			newhigh.facet = np;
			us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
			return;
		}

		if (namesamen(pt, "area-define", l) == 0 && l >= 6)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
				findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find area-define'");
				return;
			}

			us_clearhighlightcount();
			trackcursor(0, us_ignoreup, us_finddbegin, us_stretchdown, us_stoponchar,
				us_invertdragup, TRACKDRAGGING);
			if (el_pleasestop != 0) return;
			us_finddterm(&newhigh.stalx, &newhigh.staly);
			if (us_demandxy(&xcur, &ycur) != 0) return;
			if (xcur >= newhigh.stalx) newhigh.stahx = xcur; else
			{
				newhigh.stahx = newhigh.stalx;   newhigh.stalx = xcur;
			}
			if (ycur >= newhigh.staly) newhigh.stahy = ycur; else
			{
				newhigh.stahy = newhigh.staly;   newhigh.staly = ycur;
			}
			newhigh.status = HIGHBBOX;
			newhigh.facet = np;
			(void)us_addhighlight(&newhigh);
			return;
		}

		if (namesamen(pt, "area-move", l) == 0 && l >= 6)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
				findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find area-move'");
				return;
			}
			if (us_demandxy(&xcur, &ycur)) return;
			gridalign(&xcur, &ycur, us_alignment);

			/* set the highlight */
			if ((newhigh.status&HIGHTYPE) == HIGHBBOX)
			{
				areasizex = newhigh.stahx - newhigh.stalx;
				areasizey = newhigh.stahy - newhigh.staly;
			} else
			{
				areasizex = (el_curwindowpart->screenhx-el_curwindowpart->screenlx) / 5;
				areasizey = (el_curwindowpart->screenhy-el_curwindowpart->screenly) / 5;
			}

			us_clearhighlightcount();

			/* adjust the cursor position if selecting interactively */
			if ((us_aid->aidstate&INTERACTIVE) != 0)
			{
				us_findinit(areasizex, areasizey);
				trackcursor(0, us_ignoreup, us_findmbegin, us_dragdown, us_stoponchar,
					us_dragup, TRACKDRAGGING);
				if (el_pleasestop != 0) return;
				if (us_demandxy(&xcur, &ycur) != 0) return;
				gridalign(&xcur, &ycur, us_alignment);
			}
			newhigh.status = HIGHBBOX;
			newhigh.facet = np;
			newhigh.stalx = xcur;   newhigh.stahx = xcur + areasizex;
			newhigh.staly = ycur;   newhigh.stahy = ycur + areasizey;
			(void)us_addhighlight(&newhigh);
			return;
		}

		if (namesamen(pt, "area-size", l) == 0 && l >= 6)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
				findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find area-size'");
				return;
			}
			if (us_demandxy(&xcur, &ycur)) return;
			gridalign(&xcur, &ycur, us_alignment);

			if (newhigh.status != HIGHBBOX)
			{
				us_abortcommand("Use 'find area-move' first, then this");
				return;
			}
			if (np != newhigh.facet)
			{
				us_abortcommand("Not in same facet as highlight area");
				return;
			}

			us_clearhighlightcount();

			/* adjust the cursor position if selecting interactively */
			if ((us_aid->aidstate&INTERACTIVE) != 0)
			{
				us_findinit(newhigh.stalx, newhigh.staly);
				trackcursor(0, us_ignoreup, us_findsbegin, us_stretchdown,
					us_stoponchar, us_invertdragup, TRACKDRAGGING);
				if (el_pleasestop != 0) return;
				if (us_demandxy(&xcur, &ycur) != 0) return;
				gridalign(&xcur, &ycur, us_alignment);
			}
			if (xcur >= newhigh.stalx) newhigh.stahx = xcur; else
			{
				newhigh.stahx = newhigh.stalx;   newhigh.stalx = xcur;
			}
			if (ycur >= newhigh.staly) newhigh.stahy = ycur; else
			{
				newhigh.stahy = newhigh.staly;   newhigh.staly = ycur;
			}
			(void)us_addhighlight(&newhigh);
			return;
		}

		if (namesamen(pt, "clear", l) == 0 && l >= 2)
		{
			us_clearhighlightcount();
			return;
		}

		if (namesamen(pt, "comp-interactive", l) == 0 && l >= 3)
		{
			if (findpoint != 0 || findexclusively != 0 || extrainfo != 0)
			{
				us_abortcommand("Illegal options given to 'find comp-interactive'");
				return;
			}

			if ((us_useroptions&FLIPINTERACTIVECON) != 0)
				findconstrained = 1 - findconstrained;
			if (findconstrained == 0) findangle = 0; else
			{
				var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_interactiveangle);
				if (var == NOVARIABLE) findangle = 0; else
					findangle = (INTSML)var->addr;
			}				
			us_findiinit(findport, extrainfo, findangle, (INTSML)(1-findmore), findstill,
				findnobox, findspecial);
			trackcursor(0, us_ignoreup, us_findcibegin, us_findidown, us_stoponchar,
				us_findiup, TRACKDRAGGING);
			return;
		}

		if (namesamen(pt, "deselect-arcs", l) == 0 && l >= 2)
		{
			glist = us_gethighlighted(OBJARCINST);
			for(i=0; glist[i] != NOGEOM; i++)
			{
				newhigh.status = HIGHFROM;
				newhigh.fromgeom = glist[i];
				newhigh.fromport = NOPORTPROTO;
				us_delhighlight(&newhigh);
			}
			return;
		}

		if (namesamen(pt, "down-stack", l) == 0 && l >= 2)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find down-stack'");
				return;
			}
			us_pushhighlight();
			ttyputverbose("Pushed");
			return;
		}

		if (namesamen(pt, "export", l) == 0 && l >= 3)
		{
			if (count <= 1)
			{
				us_abortcommand("Usage: find export PORTNAME");
				return;
			}
			if (findport != 0 || findpoint != 0 || findexclusively != 0 || findwithin != 0 ||
				findspecial != 0)
			{
				us_abortcommand("'find export' cannot accept other control");
				return;
			}
			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				if (namesame(par[1], pp->protoname) == 0) break;
			if (pp == NOPORTPROTO)
			{
				us_abortcommand("Sorry, no exported port named '%s' in this facet", par[1]);
				return;
			}
			newhigh.status = HIGHFROM;
			newhigh.fromgeom = pp->subnodeinst->geom;
			newhigh.fromport = pp->subportproto;
			newhigh.facet = np;
			us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
			return;
		}

		if (namesamen(pt, "interactive", l) == 0)
		{
			if (findexclusively != 0)
			{
				us_abortcommand("Illegal options given to 'find interactively'");
				return;
			}

			/* special case: "find within vertex interactive" for polygon-editing */
			if (findpoint != 0 && findwithin != 0)
			{
				ni = newhigh.fromgeom->entryaddr.ni;
				us_pointinit(ni, 0);
				trackcursor(0, us_ignoreup, us_findpointbegin, us_movepdown,
					us_stoponchar, us_dragup, TRACKDRAGGING);
				if (el_pleasestop != 0) return;

				highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
				if (highvar == NOVARIABLE) return;
				(void)us_makehighlight(((char **)highvar->addr)[0], &newhigh);
				if (us_demandxy(&xcur, &ycur) != 0) return;
				gridalign(&xcur, &ycur, us_alignment);
				var = gettrace(ni);
				if (var == NOVARIABLE) return;
				size = getlength(var) / 2;
				newlist = (INTBIG *)emalloc((size*2*SIZEOFINTBIG), el_tempcluster);
				if (newlist == 0) return;
				makerot(ni, trans);
				x = (ni->highx + ni->lowx) / 2;
				y = (ni->highy + ni->lowy) / 2;
				for(i=0; i<size; i++)
				{
					if (i+1 == newhigh.frompoint)
					{
						newlist[i*2] = xcur;
						newlist[i*2+1] = ycur;
					} else xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y,
						&newlist[i*2], &newlist[i*2+1], trans);
				}

				/* now re-draw this trace */
				us_pushhighlight();
				us_clearhighlightcount();
				us_settrace(ni, newlist, size);
				(void)us_pophighlight(0);
				efree((char *)newlist);
				return;
			}

			/* traditional interactive selection */
			if ((us_useroptions&FLIPINTERACTIVECON) != 0)
				findconstrained = 1 - findconstrained;
			if (findconstrained == 0) findangle = 0; else
			{
				var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_interactiveangle);
				if (var == NOVARIABLE) findangle = 0; else
					findangle = (INTSML)var->addr;
			}				
			us_findiinit(findport, extrainfo, findangle, (INTSML)(1-findmore), findstill,
				findnobox, findspecial);
			trackcursor(0, us_ignoreup, us_findibegin, us_findidown, us_stoponchar,
				us_findiup, TRACKDRAGGING);
			return;
		}

		if (namesamen(pt, "name", l) == 0 && l >= 2)
		{
			if (count <= 1)
			{
				us_abortcommand("Usage: find name HIGHLIGHTNAME");
				return;
			}
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				findwithin != 0 || findmore != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find name'");
				return;
			}
			(void)initinfstr();
			(void)addstringtoinfstr("USER_highlight_");
			(void)addstringtoinfstr(par[1]);
			var = getval((INTBIG)us_aid, VAID, VSTRING|VISARRAY, returninfstr());
			if (var == NOVARIABLE)
			{
				us_abortcommand("Cannot find saved highlight '%s'", par[1]);
				return;
			}

			(void)setvalkey((INTBIG)us_aid, VAID, us_highlighted, var->addr, var->type|VDONTSAVE);
			if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO);
			return;
		}

		if (namesamen(pt, "node", l) == 0 && l >= 3)
		{
			if (count <= 1)
			{
				us_abortcommand("Usage: find node NODENAME");
				return;
			}
			if (findport != 0 || findwithin != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find node'");
				return;
			}
			if (findexclusively != 0 && us_curnodeproto == NONODEPROTO)
			{
				us_abortcommand("Must select a node for exclusive finding");
				return;
			}
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if (findexclusively != 0 && ni->proto != us_curnodeproto) continue;
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
				if (var == NOVARIABLE) continue;
				if (namesame((char *)var->addr, par[1]) == 0) break;
			}
			if (ni == NONODEINST)
			{
				us_abortcommand("Sorry, no %s node named '%s' in this facet",
					(findexclusively==0 ? "" : describenodeproto(us_curnodeproto)), par[1]);
				return;
			}
			newhigh.status = HIGHFROM;
			newhigh.fromgeom = ni->geom;
			newhigh.fromport = NOPORTPROTO;
			newhigh.facet = np;
			us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
			return;
		}

		if (namesamen(pt, "nonmanhattan", l) == 0 && l >= 3)
		{
			i = 0;
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
					onp->temp1 = 0;
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
				{
					for(ai = onp->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
					{
						if (ai->proto->tech == gen_tech || ai->proto->tech == art_tech)
							continue;
						var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius);
						if (var != NOVARIABLE || (ai->end[0].xpos != ai->end[1].xpos &&
							ai->end[0].ypos != ai->end[1].ypos)) onp->temp1++;
					}
					for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
					{
						if ((ni->rotation % 900) != 0) onp->temp1++;
					}
				}
			}
			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
			{
				if (ai->proto->tech == gen_tech || ai->proto->tech == art_tech)
					continue;
				var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius);
				if (var == NOVARIABLE && (ai->end[0].xpos == ai->end[1].xpos ||
					ai->end[0].ypos == ai->end[1].ypos)) continue;
				if (i == 0) us_clearhighlightcount();
				newhigh.status = HIGHFROM;
				newhigh.facet = np;
				newhigh.fromgeom = ai->geom;
				newhigh.fromport = NOPORTPROTO;
				newhigh.frompoint = 0;
				(void)us_addhighlight(&newhigh);
				i++;
			}
			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
			{
				if ((ni->rotation % 900) == 0) continue;
				if (i == 0) us_clearhighlightcount();
				newhigh.status = HIGHFROM;
				newhigh.facet = np;
				newhigh.fromgeom = ni->geom;
				newhigh.fromport = NOPORTPROTO;
				newhigh.frompoint = 0;
				(void)us_addhighlight(&newhigh);
				i++;
			}
			if (i == 0) ttyputmsg("No nonmanhattan objects in this facet"); else
				ttyputmsg("%ld objects are not manhattan in this facet", i);
			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			{
				if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
				i = 0;
				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
					if (onp != np) i += onp->temp1;
				if (i == 0) continue;
				if (lib == el_curlib)
				{
					l = 0;
					(void)initinfstr();
					for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
					{
						if (onp == np || onp->temp1 == 0) continue;
						(void)addtoinfstr(' ');
						(void)addstringtoinfstr(describenodeproto(onp));
						l++;
					}
					if (l == 1)
					{
						ttyputmsg("Found nonmanhattan geometry in facet%s", returninfstr());
					} else
					{
						ttyputmsg("Found nonmanhattan geometry in these facets:%s",
							returninfstr());
					}
				} else
				{
					ttyputmsg("Found nonmanhattan geometry in library %s", lib->libname);
				}
			}
			return;
		}

		if (namesamen(pt, "object", l) == 0)
		{
			if (count < 2)
			{
				us_abortcommand("Usage: find object (TYPE ADDRESS | TYPEADDRESS)");
				return;
			}
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				findwithin != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find object'");
				return;
			}

			/* determine type and address to highlight */
			if (count == 3)
			{
				type = us_variabletypevalue(par[1]);
				addr = myatoi(par[2]);
			} else
			{
				type = VUNKNOWN;
				if (namesamen(par[1], "node", 4) == 0)
				{
					type = VNODEINST;
					addr = myatoi(&par[1][4]);
				}
				if (namesamen(par[1], "arc", 3) == 0)
				{
					type = VARCINST;
					addr = myatoi(&par[1][3]);
				}
				if (namesamen(par[1], "port", 4) == 0)
				{
					type = VPORTPROTO;
					addr = myatoi(&par[1][4]);
				}
				if (namesamen(par[1], "network", 7) == 0)
				{
					type = VNETWORK;
					addr = myatoi(&par[1][7]);
				}
			}
			if (type == VUNKNOWN)
			{
				us_abortcommand("Unknown object type in 'find object' command");
				return;
			}
			switch (type)
			{
				case VNODEINST:
					ni = (NODEINST *)addr;
					if (ni == 0 || ni == NONODEINST) return;
					if (ni->parent != np)
					{
						us_abortcommand("Cannot find node %ld in this facet", addr);
						return;
					}
					newhigh.status = HIGHFROM;
					newhigh.fromgeom = ni->geom;
					newhigh.fromport = NOPORTPROTO;
					newhigh.facet = np;
					us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
					break;
				case VARCINST:
					ai = (ARCINST *)addr;
					if (ai == 0 || ai == NOARCINST) return;
					if (ai->parent != np)
					{
						us_abortcommand("Cannot find arc %ld in this facet", addr);
						return;
					}
					newhigh.status = HIGHFROM;
					newhigh.fromgeom = ai->geom;
					newhigh.fromport = NOPORTPROTO;
					newhigh.facet = np;
					us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
					break;
				case VNETWORK:
					net = (NETWORK *)addr;
					if (net == 0 || net == NONETWORK) return;
					if (net->parent != np)
					{
						us_abortcommand("Cannot find network %ld in this facet", addr);
						return;
					}
					for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
					{
						if (ai->network != net) continue;
						newhigh.status = HIGHFROM;
						newhigh.fromgeom = ai->geom;
						newhigh.fromport = NOPORTPROTO;
						newhigh.facet = np;
						us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
					}
					for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
					{
						if (pp->network != net) continue;
						newhigh.status = HIGHFROM;
						newhigh.fromgeom = pp->subnodeinst->geom;
						newhigh.fromport = pp->subportproto;
						newhigh.facet = np;
						us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
					}
					break;
				case VPORTPROTO:
					pp = (PORTPROTO *)addr;
					if (pp == 0 || pp == NOPORTPROTO) return;
					if (pp->parent != np)
					{
						us_abortcommand("Cannot find port %ld in this facet", addr);
						return;
					}
					newhigh.status = HIGHFROM;
					newhigh.fromgeom = pp->subnodeinst->geom;
					newhigh.fromport = pp->subportproto;
					newhigh.facet = np;
					us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
					break;
				default:
					us_abortcommand("Cannot highlight objects of type %s",
						us_variabletypename(type));
					return;
			}
			return;
		}

		if (namesamen(pt, "save", l) == 0 && l >= 2)
		{
			if (count <= 1)
			{
				us_abortcommand("Usage: find save HIGHLIGHTNAME");
				return;
			}
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find save'");
				return;
			}
			var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
			if (var == NOVARIABLE)
			{
				us_abortcommand("Highlight something before saving highlight");
				return;
			}
			(void)initinfstr();
			(void)addstringtoinfstr("USER_highlight_");
			(void)addstringtoinfstr(par[1]);
			(void)setval((INTBIG)us_aid, VAID, returninfstr(), var->addr, var->type|VDONTSAVE);
			ttyputverbose("%s saved", par[1]);
			return;
		}

		if (namesamen(pt, "set-easy-selection", l) == 0 && l >= 5)
		{
			glist = us_gethighlighted(OBJARCINST|OBJNODEINST);
			if (glist[0] == NOGEOM)
			{
				us_abortcommand("Select something before making it easy-to-select");
				return;
			}
			for(i=0; glist[i] != NOGEOM; i++)
			{
				geom = glist[i];
				if (geom->entrytype == OBJARCINST)
				{
					ai = geom->entryaddr.ai;
					ai->userbits &= ~HARDSELECTA;
				} else
				{
					ni = geom->entryaddr.ni;
					ni->userbits &= ~HARDSELECTN;
				}
			}
			return;
		}

		if (namesamen(pt, "set-hard-selection", l) == 0 && l >= 5)
		{
			glist = us_gethighlighted(OBJARCINST|OBJNODEINST);
			if (glist[0] == NOGEOM)
			{
				us_abortcommand("Select something before making it easy-to-select");
				return;
			}
			for(i=0; glist[i] != NOGEOM; i++)
			{
				geom = glist[i];
				if (geom->entrytype == OBJARCINST)
				{
					ai = geom->entryaddr.ai;
					ai->userbits |= HARDSELECTA;
				} else
				{
					ni = geom->entryaddr.ni;
					ni->userbits |= HARDSELECTN;
				}
			}
			return;
		}

		if (namesamen(pt, "snap-mode", l) == 0 && l >= 2)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find snap-mode'");
				return;
			}

			if (count < 2)
			{
				switch (us_state&SNAPMODE)
				{
					case SNAPMODENONE:     ttyputmsg("Snapping mode: none");              break;
					case SNAPMODECENTER:   ttyputmsg("Snapping mode: center");            break;
					case SNAPMODEMIDPOINT: ttyputmsg("Snapping mode: midpoint");          break;
					case SNAPMODEENDPOINT: ttyputmsg("Snapping mode: end point");         break;
					case SNAPMODETANGENT:  ttyputmsg("Snapping mode: tangent");           break;
					case SNAPMODEPERP:     ttyputmsg("Snapping mode: perpendicular");     break;
					case SNAPMODEQUAD:     ttyputmsg("Snapping mode: quadrant");          break;
					case SNAPMODEINTER:    ttyputmsg("Snapping mode: any intersection");  break;
				}
				return;
			}
			l = strlen(pt = par[1]);
			if (namesamen(pt, "none", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODENONE;
				ttyputverbose("Snapping mode: none");
				return;
			}
			if (namesamen(pt, "center", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODECENTER;
				ttyputverbose("Snapping mode: center");
				return;
			}
			if (namesamen(pt, "midpoint", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODEMIDPOINT;
				ttyputverbose("Snapping mode: midpoint");
				return;
			}
			if (namesamen(pt, "endpoint", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODEENDPOINT;
				ttyputverbose("Snapping mode: endpoint");
				return;
			}
			if (namesamen(pt, "tangent", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODETANGENT;
				ttyputverbose("Snapping mode: tangent");
				return;
			}
			if (namesamen(pt, "perpendicular", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODEPERP;
				ttyputverbose("Snapping mode: perpendicular");
				return;
			}
			if (namesamen(pt, "quadrant", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODEQUAD;
				ttyputverbose("Snapping mode: quadrant");
				return;
			}
			if (namesamen(pt, "intersection", l) == 0)
			{
				us_state = (us_state & ~SNAPMODE) | SNAPMODEINTER;
				ttyputverbose("Snapping mode: any intersection");
				return;
			}
			us_abortcommand("Unknown snapping mode: %s", pt);
			return;
		}

		if (namesamen(pt, "up-stack", l) == 0 && l >= 1)
		{
			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
			{
				us_abortcommand("Illegal options given to 'find up-stack'");
				return;
			}
			if (us_pophighlight(0) != 0) us_abortcommand("The stack is empty");
			return;
		}
		us_abortcommand("Bad FIND option: %s", pt);
		return;
	}

	/* get the cursor co-ordinates */
	if (us_demandxy(&xcur, &ycur)) return;

	/* find the closest object to the cursor */
	if (findwithin == 0)
		us_findobject(xcur, ycur, el_curwindowpart, &newhigh, findexclusively, 0, findport, 0, findspecial);
	if (newhigh.status == 0) return;
	if (findport == 0) newhigh.fromport = NOPORTPROTO;
	newhigh.facet = np;
	us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
}

void us_getproto(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER ARCPROTO *ap, *lat;
	REGISTER INTSML doarc, l;
	REGISTER char *pp;
	HIGHLIGHT high;
	INTBIG xcur, ycur;
	extern COMCOMP us_getproto1p;
	REGISTER VARIABLE *highvar;
	GEOM *fromgeom, *togeom;
	PORTPROTO *fromport, *toport;

	if (count == 0)
	{
		count = ttygetparam("Getproto option: ", &us_getproto1p, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}
	l = strlen(pp = par[0]);

	if (namesamen(pp, "node", l) == 0 && l >= 2 && count > 1)
	{
		np = getnodeproto(par[1]);
		if (np == NONODEPROTO)
		{
			us_abortcommand("Cannot find node '%s'", par[1]);
			return;
		}
		us_setnodeproto(np);
		return;
	}
	if (namesamen(pp, "arc", l) == 0 && l >= 1 && count > 1)
	{
		ap = getarcproto(par[1]);
		if (ap == NOARCPROTO) us_abortcommand("Cannot find arc '%s'", par[1]); else
			us_setarcproto(ap, 1);
		return;
	}

	if (namesamen(pp, "this-proto", l) == 0 && l >= 1)
	{
		if (us_demandxy(&xcur, &ycur)) return;
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		us_setnodeproto(np);
		return;
	}

	/* decide whether arcs are the default */
	doarc = 0;
	if (us_gettwoobjects(&fromgeom, &fromport, &togeom, &toport) == 0) doarc = 1; else
	{
		highvar = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
		if (highvar != NOVARIABLE && getlength(highvar) == 1)
		{
			(void)us_makehighlight(((char **)highvar->addr)[0], &high);
			if ((high.status&HIGHFROM) != 0 && high.fromgeom->entrytype == OBJARCINST) doarc++;
		}
	}

	if (namesamen(pp, "next-proto", l) == 0 && l >= 2)
	{
		if (doarc)
		{
			/* advance to the next arcproto */
			ap = us_curarcproto->nextarcproto;
			if (ap == NOARCPROTO) ap = el_curtech->firstarcproto;
			us_setarcproto(ap, 1);
		} else
		{
			/* advance to the next nodeproto */
			np = us_curnodeproto;
			if (np->primindex == 0) np = el_curtech->firstnodeproto; else
			{
				/* advance to next after "np" */
				np = np->nextnodeproto;
				if (np == NONODEPROTO) np = el_curtech->firstnodeproto;
			}
			us_setnodeproto(np);
		}
		return;
	}

	if (namesamen(pp, "prev-proto", l) == 0 && l >= 1)
	{
		if (doarc)
		{
			/* backup to the previous arcproto */
			for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
				if (ap->nextarcproto == us_curarcproto) break;
			if (ap == NOARCPROTO)
				for(lat = el_curtech->firstarcproto; lat != NOARCPROTO; lat = lat->nextarcproto)
					ap = lat;
			us_setarcproto(ap, 1);
		} else
		{
			/* backup to the previous nodeproto */
			np = us_curnodeproto;
			if (np->primindex == 0) np = el_curtech->firstnodeproto; else
			{
				/* back up to previous of "np" */
				np = np->lastnodeproto;
				if (np == NONODEPROTO)
					for(np = el_curtech->firstnodeproto; np->nextnodeproto != NONODEPROTO;
						np = np->nextnodeproto) ;
			}
			us_setnodeproto(np);
		}
		return;
	}

	/* must be a prototype name */
	if (doarc != 0)
	{
		ap = getarcproto(pp);
		if (ap != NOARCPROTO)
		{
			us_setarcproto(ap, 1);
			return;
		}
	}

	np = getnodeproto(pp);
	if (np == NONODEPROTO)
	{
		if (doarc != 0) us_abortcommand("Cannot find node or arc '%s'", pp); else
			us_abortcommand("Cannot find node '%s'", pp);
		return;
	}
	us_setnodeproto(np);
}

void us_grid(INTSML count, char *par[])
{
	REGISTER INTBIG i, j;
	REGISTER INTSML l;
	REGISTER char *pp;

	/* no arguments: toggle the grid state */
	if (count == 0)
	{
		if (us_needwindow()) return;

		/* save highlight */
		us_pushhighlight();
		us_clearhighlightcount();

		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		us_gridset(el_curwindowpart, ~el_curwindowpart->state);
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* restore highlighting */
		(void)us_pophighlight(0);
		return;
	}

	l = strlen(pp = par[0]);
	if (namesamen(pp, "alignment", l) == 0 && l >= 1)
	{
		if (count >= 2)
		{
			i = atola(par[1]);
			if (i < 0)
			{
				us_abortcommand("Alignment must be positive");
				return;
			}
			(void)setvalkey((INTBIG)us_aid, VAID, us_alignment_obj, i, VINTEGER|VDONTSAVE);
		}
		ttyputverbose("Cursor alignment is %s lambda", latoa(us_alignment));
		return;
	}

	if (namesamen(pp, "edges", l) == 0 && l >= 1)
	{
		if (count >= 2)
		{
			i = atola(par[1]);
			if (i < 0)
			{
				us_abortcommand("Alignment must be positive");
				return;
			}
			(void)setvalkey((INTBIG)us_aid, VAID, us_alignment_edge, i, VINTEGER|VDONTSAVE);
		}
		if (us_edgealignment == 0) ttyputverbose("No edge alignment done"); else
			ttyputverbose("Edge alignment is %s lambda", latoa(us_edgealignment));
		return;
	}

	if (namesamen(pp, "size", l) == 0 && l >= 1)
	{
		if (count < 2)
		{
			us_abortcommand("Usage: grid size X [Y]");
			return;
		}
		i = atola(par[1]);
		if (i&1) i++;
		if (count >= 3)
		{
			j = atola(par[2]);
			if (j&1) j++;
		} else j = i;
		if (i <= 0 || j <= 0)
		{
			us_abortcommand("Invalid grid spacing");
			return;
		}

		if (us_needwindow()) return;

		/* save highlight */
		us_pushhighlight();
		us_clearhighlightcount();
		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);

		/* turn grid off if on */
		if ((el_curwindowpart->state&GRIDON) != 0)
			(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "state",
				el_curwindowpart->state & ~GRIDON, VINTEGER);

		/* adjust grid */
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "gridx", i, VINTEGER);
		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, "gridy", j, VINTEGER);

		/* show new grid */
		us_gridset(el_curwindowpart, GRIDON);

		/* restore highlighting */
		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
		(void)us_pophighlight(0);
		return;
	}
	us_abortcommand("Bad GRID option: %s", pp);
}

#define	NEWSFILE   "newsfile"			/* file with news */

#define	NEWSDATE   ".electricnews"		/* file with date of last news */

void us_help(INTSML count, char *par[])
{
	REGISTER INTSML len;
	char *paramstart[MAXPARS];
	REGISTER char *pp;
	REGISTER INTSML lastquiet, nday, nmonth, nyear, on;
	INTSML day, month, year, hour, minute, second;
	FILE *in;
	char line[256], sep[2], *filename, *hd;
	UINTBIG clock;

	if (count > 0) len = strlen(pp = par[0]);

	/* show the user manual */
	if (count >= 1 && namesamen(pp, "manual", len) == 0)
	{
		strcpy(line, el_libdir);
		len = strlen(line);
		if (line[len-1] == DIRSEP) line[len-1] = 0;
		len = strlen(line);
		if (namesame(&line[len-3], "lib") == 0) line[len-3] = 0;
		strcat(line, "html");
		sep[0] = DIRSEP;   sep[1] = 0;
		strcat(line, sep);
		strcat(line, "manual");
		strcat(line, sep);
		strcat(line, "index.html");

		if (browsefile(line) != 0)
		{
			us_abortcommand("Cannot bring up the user's manual on this system");
		}
		return;
	}

	/* print news */
	if (count >= 1 && namesamen(pp, "news", len) == 0)
	{
		/* determine last date of news reading */
		(void)initinfstr();
		hd = hashomedir();
		if (hd == 0) (void)addstringtoinfstr(el_libdir); else
			(void)addstringtoinfstr(hd);
		(void)addstringtoinfstr(NEWSDATE);
		pp = truepath(returninfstr());
		in = fopen(pp, "r");
		if (in == 0) year = month = day = 0; else
		{
			fclose(in);
			clock = filedate(pp);
			parsetime(clock, &year, &month, &day, &hour, &minute, &second);
			month++;
		}

		/* get the news file */
		(void)initinfstr();
		(void)addstringtoinfstr(el_libdir);
		(void)addstringtoinfstr(NEWSFILE);
		pp = truepath(returninfstr());
		in = xopen(pp, FILETYPENEWS, "", &filename);
		if (in == NULL)
		{
			ttyputerr("Sorry, cannot find the news file: %s", pp);
			return;
		}

		/* read the file */
		on = 0;

		/* enable messages (if they were off) */
		lastquiet = ttyquiet(0);
		for(;;)
		{
			if (xfgets(line, MAXLINE, in) != 0) break;
			if (on != 0)
			{
				ttyputmsg("%s", line);
				continue;
			}
			if (line[0] == ' ' || line[0] == 0) continue;

			/* line with date, see if it is current */
			pp = line;
			nmonth = atoi(pp);
			while (*pp != '/' && *pp != 0) pp++;
			if (*pp == '/') pp++;
			nday = atoi(pp);
			while (*pp != '/' && *pp != 0) pp++;
			if (*pp == '/') pp++;
			nyear = atoi(pp);
			if (nyear < year) continue; else if (nyear > year) on = 1;
			if (nmonth < month) continue; else if (nmonth > month) on = 1;
			if (nday >= day) on = 1;
			if (on != 0) ttyputmsg("%s", line);
		}
		xclose(in);

		/* restore message output state */
		(void)ttyquiet(lastquiet);

		if (on == 0) ttyputmsg("No news");

		/* now mark the current date */
		(void)initinfstr();
		hd = hashomedir();
		if (hd == 0) (void)addstringtoinfstr(el_libdir); else
			(void)addstringtoinfstr(hd);
		(void)addstringtoinfstr(NEWSDATE);
		xclose(xcreate(truepath(returninfstr()), FILETYPENEWS, 0, 0));
		return;
	}

	/* illustrate commands */
	if (count >= 1 && namesamen(pp, "illustrate", len) == 0)
	{
		us_illustratecommandset();
		return;
	}

	/* general dialog-based help on command-line */
	(void)us_helpdlog("CL", paramstart);
}

void us_if(INTSML count, char *par[])
{
	REGISTER INTBIG term1, term1type, term2, term2type;
	REGISTER INTSML relation;
	REGISTER USERCOM *com;

	/* make sure the proper number of parameters is given */
	if (count < 4)
	{
		us_abortcommand("Usage: if TERM1 RELATION TERM2 COMMAND");
		return;
	}

	/* get term 1 */
	if (isanumber(par[0]) != 0)
	{
		term1 = myatoi(par[0]);
		term1type = VINTEGER;
	} else
	{
		term1 = (INTBIG)par[0];
		term1type = VSTRING;
	}

	/* get term 2 */
	if (isanumber(par[2]) != 0)
	{
		term2 = myatoi(par[2]);
		term2type = VINTEGER;
	} else
	{
		term2 = (INTBIG)par[2];
		term2type = VSTRING;
	}

	/* make sure the two terms are comparable */
	if (term1type != term2type)
	{
		if (term1 == VINTEGER)
		{
			term1 = (INTBIG)par[0];
			term1type = VSTRING;
		} else
		{
			term2 = (INTBIG)par[1];
			term2type = VSTRING;
		}
	}

	/* determine the relation being tested */
	relation = -1;
	if (strcmp(par[1], "==") == 0) relation = 0;
	if (strcmp(par[1], "!=") == 0) relation = 1;
	if (strcmp(par[1], "<")  == 0) relation = 2;
	if (strcmp(par[1], "<=") == 0) relation = 3;
	if (strcmp(par[1], ">")  == 0) relation = 4;
	if (strcmp(par[1], ">=") == 0) relation = 5;
	if (relation < 0)
	{
		us_abortcommand("Unknown relation: %s", par[1]);
		return;
	}

	/* make sure that qualitative comparison is done on numbers */
	if (relation > 1 && term1type != VINTEGER)
	{
		us_abortcommand("Inequality comparisons must be done on numbers");
		return;
	}

	/* see if the command should be executed */
	switch (relation)
	{
		case 0:		/* == */
			if (term1type == VINTEGER)
			{
				if (term1 != term2) return;
			} else
			{
				if (namesame((char *)term1, (char *)term2) != 0) return;
			}
			break;
		case 1:		/* != */
			if (term1type == VINTEGER)
			{
				if (term1 == term2) return;
			} else
			{
				if (namesame((char *)term1, (char *)term2) == 0) return;
			}
			break;
		case 2:		/* < */
			if (term1 >= term2) return;
			break;
		case 3:		/* <= */
			if (term1 > term2) return;
			break;
		case 4:		/* > */
			if (term1 <= term2) return;
			break;
		case 5:		/* >= */
			if (term1 < term2) return;
			break;
	}

	/* condition is true: create the command to execute */
	com = us_buildcommand((INTSML)(count-3), &par[3]);
	if (com == NOUSERCOM)
	{
		us_abortcommand("Condition true but there is no command to execute");
		return;
	}
	us_execute(com, 0, 0, 0);
	us_freeusercom(com);
}

void us_interpret(INTSML count, char *par[])
{
	char *retstr;
	REGISTER char *pp;
	REGISTER INTBIG language, len;
	REGISTER INTSML fromfile;

	language = VLISP;
	fromfile = 0;
	if (namesamen(par[0], "file", strlen(par[0])) == 0)
	{
		count--;
		par++;
		fromfile = 1;
	}

	if (count > 0)
	{
		len = strlen(pp = par[0]);
		if (namesamen(pp, "lisp", len) == 0)
		{
			language = VLISP;
			count--;
			par++;
		} else if (namesamen(pp, "tcl", len) == 0)
		{
			language = VTCL;
			count--;
			par++;
		} else if (namesamen(pp, "java", len) == 0)
		{
			language = VJAVA;
			count--;
			par++;
		}
	}

	/* handle "file" option */
	if (fromfile != 0)
	{
		if (count <= 0)
		{
			us_abortcommand("Usage: interpret file LANGUAGE FILENAME");
			return;
		}
		loadcode(par[0], language);
		return;
	}

	/* with no parameters, simply drop into the interpreter loop */
	if (count == 0)
	{
		/* "languageconverse" returns 0 if it wants to continue conversation */
		if (languageconverse(language) == 0) us_state |= LANGLOOP; else
			ttyputmsg("Back to Electric");
		return;
	}

	if (count > 1)
	{
		us_abortcommand("Please provide only one parameter to be interpreted");
		return;
	}

	if (doquerry(par[0], language, VSTRING, (INTBIG *)&retstr) != 0) ttyputmsg("%s", par[0]); else
		ttyputmsg("%s => %s", par[0], retstr);
}

void us_iterate(INTSML count, char *par[])
{
	REGISTER INTSML i, j, times, repeatcommand;
	REGISTER USERCOM *uc;
	REGISTER INTBIG len, total;
	INTBIG objaddr, objtype;
	VARIABLE *var, fvar;
	char *qual, parnames[30];
	INTSML cindex, comvar;
	REGISTER USERCOM *com;

	/* see if this is the "repeat last command" form */
	repeatcommand = 0;
	if (count == 0)
	{
		repeatcommand = 1;
		times = 1;
	} else
	{
		if (isanumber(par[0]) != 0)
		{
			repeatcommand = 1;
			times = atoi(par[0]);
		}
	}

	if (repeatcommand != 0)
	{
		/* make sure there was a valid previous command */
		if (us_lastcom == NOUSERCOM || us_lastcom->active < 0)
		{
			us_abortcommand("No last command to repeat");
			return;
		}

		/* copy last command into new one */
		uc = us_allocusercom();
		if (uc == NOUSERCOM)
		{
			ttyputerr("No memory");
			return;
		}
		for(i=0; i<us_lastcom->count; i++)
			if (allocstring(&uc->word[i], us_lastcom->word[i], us_aid->cluster) != 0)
		{
			ttyputerr("No memory");
			return;
		}
		uc->active = us_lastcom->active;
		uc->count = us_lastcom->count;
		(void)allocstring(&uc->comname, us_lastcom->comname, us_aid->cluster);
		uc->menu = us_lastcom->menu;

		/* execute this command */
		for(j=0; j<times; j++) us_execute(uc, 1, 0, 1);
		us_freeusercom(uc);
	} else
	{
		/* implement the iterate over array-variable form */
		if (count < 2)
		{
			us_abortcommand("Usage: iterate ARRAY-VARIABLE MACRO");
			return;
		}
		if (us_getvar(par[0], &objaddr, &objtype, &qual, &comvar, &cindex) != 0)
		{
			us_abortcommand("Incorrect iterator variable name: %s", par[0]);
			return;
		}
		if (*qual != 0)
		{
			var = getval(objaddr, objtype, -1, qual);
			if (var == NOVARIABLE)
			{
				us_abortcommand("Cannot find iterator variable: %s", par[0]);
				return;
			}
		} else
		{
			fvar.addr = objaddr;
			fvar.type = objtype;
			var = &fvar;
		}
		len = getlength(var);
		if (len < 0) len = 1;
		if (us_expandaddrtypearray(&us_iteratelimit, &us_iterateaddr,
			&us_iteratetype, len) != 0) return;
		if ((var->type&VISARRAY) == 0)
		{
			us_iterateaddr[0] = var->addr;
			us_iteratetype[0] = var->type;
			total = 1;
		} else
		{
			if ((var->type&VTYPE) == VGENERAL)
			{
				for(i=0; i<len; i += 2)
				{
					us_iterateaddr[i/2] = ((INTBIG *)var->addr)[i];
					us_iteratetype[i/2] = ((INTBIG *)var->addr)[i+1];
				}
				total = len / 2;
			} else
			{
				for(i=0; i<len; i++)
				{
					us_iterateaddr[i] = ((INTBIG *)var->addr)[i];
					us_iteratetype[i] = var->type;
				}
				total = len;
			}
		}

		/* now iterate with this value */
		for(i=0; i<total; i++)
		{
			(void)initinfstr();
			(void)addstringtoinfstr(par[1]);
			(void)sprintf(parnames, " %s 0%lo", us_variabletypename(us_iteratetype[i]),
				us_iterateaddr[i]);
			(void)addstringtoinfstr(parnames);
			com = us_makecommand(returninfstr());
			if (com->active < 0) { us_freeusercom(com);   break; }
			us_execute(com, 0, 0, 0);
			us_freeusercom(com);
			if (stopping("iteration") != 0) break;
		}
	}
}

void us_killfacet(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np, *onp;
	REGISTER NODEINST *ni;
	REGISTER LIBRARY *lib;
	extern COMCOMP us_showdp;

	if (count == 0)
	{
		count = ttygetparam("Facet name: ", &us_showdp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}

	np = getnodeproto(par[0]);
	if (np == NONODEPROTO)
	{
		us_abortcommand("No facet called %s", par[0]);
		return;
	}
	if (np->primindex != 0)
	{
		us_abortcommand("Can only kill facets");
		return;
	}

	/* disallow killing if lock is on */
	if (us_canedit(np, NONODEPROTO, 1) != 0) return;

	/* if there are still instances of the facet, mention them */
	if (np->firstinst != NONODEINST)
	{
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
				onp->temp1 = 0;
		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
			ni->parent->temp1++;
		ttyputerr("Erase all of the following instances of this facet first:");
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
				if (onp->temp1 != 0)
					ttyputmsg("  %ld instance(s) in facet %s", onp->temp1, describenodeproto(onp));
		return;
	}

	/* kill the facet */
	us_dokillfacet(np);
	ttyputmsg("Facet %s deleted", par[0]);
}
