/*
 * Electric(tm) VLSI Design System
 *
 * File: usredtecc.c
 * User interface technology editor: interactive technology library editing
 * 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 "egraphics.h"
#include "efunction.h"
#include "usr.h"
#include "tecgen.h"
#include "tecart.h"
#include "usredtec.h"
#include "usrdiacom.h"

#define	MAXNAMELEN 25		/* max chars in a new name */

INTSML us_teceddrclayers = 0;
char **us_teceddrclayernames = 0;

/* the known technology variables */
TECHVAR us_knownvars[] =
{
	{"DRC_ecad_deck",               NOTECHVAR, 0, 0, 0.0, "", VSTRING|VISARRAY,
		"Dracula design-rule deck"},
	{"IO_cif_polypoints",           NOTECHVAR, 0, 0, 0.0, "", VINTEGER,
		"Maximum points in a CIF polygon"},
	{"IO_cif_resolution",           NOTECHVAR, 0, 0, 0.0, "", VINTEGER,
		"Minimum resolution of CIF coordinates"},
	{"IO_gds_polypoints",           NOTECHVAR, 0, 0, 0.0, "", VINTEGER,
		"Maximum points in a GDS-II polygon"},
	{"SIM_spice_min_resistance",    NOTECHVAR, 0, 0, 0.0, "", VFLOAT,
		"Minimum resistance of SPICE elements"},
	{"SIM_spice_min_capacitance",   NOTECHVAR, 0, 0, 0.0, "", VFLOAT,
		"Minimum capacitance of SPICE elements"},
	{"SIM_spice_mask_scale",        NOTECHVAR, 0, 0, 0.0, "", VFLOAT,
		"Scaling factor for SPICE decks"},
	{"SIM_spice_header_level1",     NOTECHVAR, 0, 0, 0.0, "", VSTRING|VISARRAY,
		"Level 1 header for SPICE decks"},
	{"SIM_spice_header_level2",     NOTECHVAR, 0, 0, 0.0, "", VSTRING|VISARRAY,
		"Level 2 header for SPICE decks"},
	{"SIM_spice_header_level3",     NOTECHVAR, 0, 0, 0.0, "", VSTRING|VISARRAY,
		"Level 3 header for SPICE decks"},
	{"SIM_spice_model_file",        NOTECHVAR, 0, 0, 0.0, "", VSTRING,
		"Disk file with SPICE header cards"},
	{"SIM_spice_trailer_file",      NOTECHVAR, 0, 0, 0.0, "", VSTRING,
		"Disk file with SPICE trailer cards"},
	{NULL, NULL,0, 0, 0.0, NULL, 0, NULL}  /* 0 */
};

/* these must correspond to the layer functions in "efunction.h" */
LIST us_teclayer_functions[] =
{
	{"unknown",       "LFUNKNOWN",     LFUNKNOWN},
	{"metal-1",       "LFMETAL1",      LFMETAL1},
	{"metal-2",       "LFMETAL2",      LFMETAL2},
	{"metal-3",       "LFMETAL3",      LFMETAL3},
	{"metal-4",       "LFMETAL4",      LFMETAL4},
	{"metal-5",       "LFMETAL5",      LFMETAL5},
	{"metal-6",       "LFMETAL6",      LFMETAL6},
	{"metal-7",       "LFMETAL7",      LFMETAL7},
	{"metal-8",       "LFMETAL8",      LFMETAL8},
	{"poly-1",        "LFPOLY1",       LFPOLY1},
	{"poly-2",        "LFPOLY2",       LFPOLY2},
	{"poly-3",        "LFPOLY3",       LFPOLY3},
	{"gate",          "LFGATE",        LFGATE},
	{"diffusion",     "LFDIFF",        LFDIFF},
	{"implant",       "LFIMPLANT",     LFIMPLANT},
	{"contact-1",     "LFCONTACT1",    LFCONTACT1},
	{"contact-2",     "LFCONTACT2",    LFCONTACT2},
	{"contact-3",     "LFCONTACT3",    LFCONTACT3},
	{"contact-4",     "LFCONTACT4",    LFCONTACT4},
	{"contact-5",     "LFCONTACT5",    LFCONTACT5},
	{"contact-6",     "LFCONTACT6",    LFCONTACT6},
	{"plug",          "LFPLUG",        LFPLUG},
	{"overglass",     "LFOVERGLASS",   LFOVERGLASS},
	{"resistor",      "LFRESISTOR",    LFRESISTOR},
	{"capacitor",     "LFCAP",         LFCAP},
	{"transistor",    "LFTRANSISTOR",  LFTRANSISTOR},
	{"emitter",       "LFEMITTER",     LFEMITTER},
	{"base",          "LFBASE",        LFBASE},
	{"collector",     "LFCOLLECTOR",   LFCOLLECTOR},
	{"substrate",     "LFSUBSTRATE",   LFSUBSTRATE},
	{"well",          "LFWELL",        LFWELL},
	{"guard",         "LFGUARD",       LFGUARD},
	{"isolation",     "LFISOLATION",   LFISOLATION},
	{"bus",           "LFBUS",         LFBUS},
	{"art",           "LFART",         LFART},
	{"control",       "LFCONTROL",     LFCONTROL},

	{"p-type",        "LFPTYPE",       LFPTYPE},
	{"n-type",        "LFNTYPE",       LFNTYPE},
	{"depletion",     "LFDEPLETION",   LFDEPLETION},
	{"enhancement",   "LFENHANCEMENT", LFENHANCEMENT},
	{"light",         "LFLIGHT",       LFLIGHT},
	{"heavy",         "LFHEAVY",       LFHEAVY},
	{"pseudo",        "LFPSEUDO",      LFPSEUDO},
	{"nonelectrical", "LFNONELEC",     LFNONELEC},
	{NULL, NULL, 0}
};

/* these must correspond to the layer functions in "efunction.h" */
LIST us_tecarc_functions[] =
{
	{"unknown",             "APUNKNOWN",  APUNKNOWN},
	{"metal-1",             "APMETAL1",   APMETAL1},
	{"metal-2",             "APMETAL2",   APMETAL2},
	{"metal-3",             "APMETAL3",   APMETAL3},
	{"metal-4",             "APMETAL4",   APMETAL4},
	{"metal-5",             "APMETAL5",   APMETAL5},
	{"metal-6",             "APMETAL6",   APMETAL6},
	{"metal-7",             "APMETAL7",   APMETAL7},
	{"metal-8",             "APMETAL8",   APMETAL8},
	{"polysilicon-1",       "APPOLY1",    APPOLY1},
	{"polysilicon-2",       "APPOLY2",    APPOLY2},
	{"polysilicon-3",       "APPOLY3",    APPOLY3},
	{"diffusion",           "APDIFF",     APDIFF},
	{"p-Diffusion",         "APDIFFP",    APDIFFP},
	{"n-Diffusion",         "APDIFFN",    APDIFFN},
	{"substrate-Diffusion", "APDIFFS",    APDIFFS},
	{"well-Diffusion",      "APDIFFW",    APDIFFW},
	{"bus",                 "APBUS",      APBUS},
	{"unrouted",            "APUNROUTED", APUNROUTED},
	{"nonelectrical",       "APNONELEC",  APNONELEC},
	{NULL, NULL, 0}
};

/* these must correspond to the layer functions in "efunction.h" */
LIST us_tecnode_functions[] =
{
	{"unknown",                       "NPUNKNOWN",   NPUNKNOWN},
	{"pin",                           "NPPIN",       NPPIN},
	{"contact",                       "NPCONTACT",   NPCONTACT},
	{"pure-layer-node",               "NPNODE",      NPNODE},
	{"connection",                    "NPCONNECT",   NPCONNECT},
	{"nMOS-transistor",               "NPTRANMOS",   NPTRANMOS},
	{"DMOS-transistor",               "NPTRADMOS",   NPTRADMOS},
	{"PMOS-transistor",               "NPTRAPMOS",   NPTRAPMOS},
	{"NPN-transistor",                "NPTRANPN",    NPTRANPN},
	{"PNP-transistor",                "NPTRAPNP",    NPTRAPNP},
	{"n-type-JFET-transistor",        "NPTRANJFET",  NPTRANJFET},
	{"p-type-JFET-transistor",        "NPTRAPJFET",  NPTRAPJFET},
	{"depletion-mesfet",              "NPTRADMES",   NPTRADMES},
	{"enhancement-mesfet",            "NPTRAEMES",   NPTRAEMES},
	{"dual-emitter-transistor",       "NPTRANE2L",   NPTRANE2L},
	{"prototype-defined-transistor",  "NPTRANSREF",  NPTRANSREF},
	{"transistor",                    "NPTRANS",     NPTRANS},
	{"resistor",                      "NPRESIST",    NPRESIST},
	{"capacitor",                     "NPCAPAC",     NPCAPAC},
	{"electrolytic-capacitor",        "NPECAPAC",    NPECAPAC},
	{"diode",                         "NPDIODE",     NPDIODE},
	{"zener-diode",                   "NPDIODEZ",    NPDIODEZ},
	{"inductor",                      "NPINDUCT",    NPINDUCT},
	{"meter",                         "NPMETER",     NPMETER},
	{"base",                          "NPBASE",      NPBASE},
	{"emitter",                       "NPEMIT",      NPEMIT},
	{"collector",                     "NPCOLLECT",   NPCOLLECT},
	{"buffer",                        "NPBUFFER",    NPBUFFER},
	{"AND-gate",                      "NPGATEAND",   NPGATEAND},
	{"OR-gate",                       "NPGATEOR",    NPGATEOR},
	{"XOR-gate",                      "NPGATEXOR",   NPGATEXOR},
	{"flip-flop",                     "NPFLIPFLOP",  NPFLIPFLOP},
	{"multiplexor",                   "NPMUX",       NPMUX},
	{"power",                         "NPCONPOWER",  NPCONPOWER},
	{"ground",                        "NPCONGROUND", NPCONGROUND},
	{"source",                        "NPSOURCE",    NPSOURCE},
	{"source-voltage",                "NPSOURCEV",   NPSOURCEV},
	{"source-current",                "NPSOURCEC",   NPSOURCEC},
	{"source-current-meter",          "NPSOURCECM",  NPSOURCECM},
	{"source-tran-anal",              "NPSOURCET",   NPSOURCET},
	{"source-dc-anal",                "NPSOURCEDC",  NPSOURCEDC},
	{"source-ac-anal",                "NPSOURCEAC",  NPSOURCEAC},
	{"source-nodeset",                "NPSOURCEN",   NPSOURCEN},
	{"source-extension",              "NPSOURCEX",   NPSOURCEX},
	{"source-bulk",                   "NPSOURCEB",   NPSOURCEB},
	{"source-special",                "NPSOURCES",   NPSOURCES},
	{"substrate",                     "NPSUBSTRATE", NPSUBSTRATE},
	{"well",                          "NPWELL",      NPWELL},
	{"artwork",                       "NPART",       NPART},
	{"array",                         "NPARRAY",     NPARRAY},
	{"align",                         "NPALIGN",     NPALIGN},
	{"ccvs",                          "NPCCVS",      NPCCVS},
	{"cccs",                          "NPCCCS",      NPCCCS},
	{"vcvs",                          "NPVCVS",      NPVCVS},
	{"vccs",                          "NPVCCS",      NPVCCS},
	{"transmission-line",             "NPTLINE",     NPTLINE},
	{NULL, NULL, 0}
};

static INTSML us_teceddrc;			/* nonzero if editing DRC */
static void (*us_edtecoldptermhandler)(WINDOWPART*);	/* former pallet term. routine */

GRAPHICS us_edtechigh = {LAYERH, HIGHLIT, SOLIDC, SOLIDC,
	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};

/* prototypes for local routines */
void us_teceditdrc(void);
void us_teceditcolormap(void);
void us_teceditcreat(INTSML, char*[]);
void us_teceditidentify(INTSML);
void us_teceditinquire(void);
void us_teceditmodobject(INTSML, char*[]);
void us_tecedlayer3dheight(NODEINST*, INTSML, char*[]);
void us_tecedlayer3dthick(NODEINST*, INTSML, char*[]);
void us_tecedlayercolor(NODEINST*, INTSML, char*[]);
void us_tecedlayerdrcminwid(NODEINST*, INTSML, char*[]);
void us_tecedlayerstyle(NODEINST*, INTSML, char*[]);
void us_tecedlayercif(NODEINST*, INTSML, char*[]);
void us_tecedlayerdxf(NODEINST*, INTSML, char*[]);
void us_tecedlayergds(NODEINST*, INTSML, char*[]);
void us_tecedlayerspires(NODEINST*, INTSML, char*[]);
void us_tecedlayerspicap(NODEINST*, INTSML, char*[]);
void us_tecedlayerspiecap(NODEINST*, INTSML, char*[]);
void us_tecedlayerfunction(NODEINST*, INTSML, char*[]);
void us_tecedlayerletters(NODEINST*, INTSML, char*[]);
void us_tecedlayerpattern(NODEINST*);
void us_tecedlayertype(NODEINST*, INTSML, char*[]);
void us_tecedmodport(NODEINST*, INTSML, char*[]);
void us_tecedarcfunction(NODEINST*, INTSML, char*[]);
void us_tecedarcfixang(NODEINST*, INTSML, char*[]);
void us_tecedarcwipes(NODEINST*, INTSML, char*[]);
void us_tecedarcnoextend(NODEINST*, INTSML, char*[]);
void us_tecedarcinc(NODEINST*, INTSML, char*[]);
void us_tecednodefunction(NODEINST*, INTSML, char*[]);
void us_tecednodeserpentine(NODEINST*, INTSML, char*[]);
void us_tecednodesquare(NODEINST*, INTSML, char*[]);
void us_tecednodewipes(NODEINST*, INTSML, char*[]);
void us_tecednodelockable(NODEINST*, INTSML, char*[]);
void us_tecedinfolambda(NODEINST*, INTSML, char*[]);
void us_tecedinfodescript(NODEINST*, INTSML, char*[]);
void us_tecedinfounits(NODEINST*, INTSML, char*[]);
void us_tecedsetnode(NODEINST*, char*);
void us_teceddrcchange(WINDOWPART*, INTSML, char*, char*, INTBIG);
void us_teceddrcterm(WINDOWPART*);
NODEPROTO *us_tecedenterfacet(char*);
void us_tecedredolayergraphics(NODEPROTO*);
void us_tecedloadlibmap(LIBRARY*);
INTSML us_teceditparsefun(char*);

/*
 * the entry routine for all technology editing
 */
void us_tecedentry(INTSML count, char *par[])
{
	REGISTER TECHNOLOGY *tech;
	REGISTER LIBRARY *lib;
	LIBRARY **dependentlibs;
	NODEPROTO **sequence;
	REGISTER char *pp, **dependentlist;
	char *facetname, *newpar[2];
	UINTSML stip[8];
	REGISTER INTSML i, l, dependentlibcount;
	REGISTER NODEPROTO *np;
	REGISTER VARIABLE *var;

	if (count == 0)
	{
		ttyputusage("technology edit OPTION");
		return;
	}

	l = strlen(pp = par[0]);
	if (namesamen(pp, "library-to-tech-and-c", l) == 0 && l >= 17)
	{
		if (us_teceddrc != 0)
		{
			us_abortcommand(_("Cannot generate technology while editing DRC"));
			return;
		}
		if (count <= 1) pp = 0; else
			pp = par[1];
		us_tecfromlibinit(el_curlib, pp, 1);
		return;
	}
	if (namesamen(pp, "library-to-tech", l) == 0)
	{
		if (us_teceddrc != 0)
		{
			us_abortcommand(_("Cannot generate technology while editing DRC"));
			return;
		}
		if (count <= 1) pp = 0; else
			pp = par[1];
		us_tecfromlibinit(el_curlib, pp, 0);
		return;
	}
	if (namesamen(pp, "tech-to-library", l) == 0)
	{
		if (count == 1) tech = el_curtech; else
		{
			tech = gettechnology(par[1]);
			if (tech == NOTECHNOLOGY)
			{
				us_abortcommand(_("Technology '%s' unknown"), par[1]);
				return;
			}
		}
		if ((tech->userbits&NONSTANDARD) != 0)
		{
			us_abortcommand(_("Cannot convert technology '%s', it is nonstandard"), tech->techname);
			return;
		}

		/* see if there is already such a library */
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
			if (namesame(lib->libname, tech->techname) == 0) break;
		if (lib != NOLIBRARY)
			ttyputmsg(_("Already a library called %s, using that"), lib->libname); else
				lib = us_tecedmakelibfromtech(tech);
		if (lib != NOLIBRARY)
		{
			newpar[0] = "use";
			newpar[1] = lib->libname;
			us_library(2, newpar);
			us_tecedloadlibmap(lib);
		}
		return;
	}
	if (namesamen(pp, "inquire-layer", l) == 0 && l >= 2)
	{
		us_teceditinquire();
		return;
	}
	if (namesamen(pp, "place-layer", l) == 0)
	{
		if (count < 2)
		{
			ttyputusage("technology edit place-layer SHAPE");
			return;
		}

		us_teceditcreat((INTSML)(count-1), &par[1]);
		return;
	}
	if (namesamen(pp, "change", l) == 0)
	{
		us_teceditmodobject((INTSML)(count-1), &par[1]);
		return;
	}
	if (namesamen(pp, "edit-node", l) == 0 && l >= 6)
	{
		if (count < 2)
		{
			ttyputusage("technology edit edit-node NODENAME");
			return;
		}
		(void)initinfstr();
		(void)addstringtoinfstr("node-");
		(void)addstringtoinfstr(par[1]);
		(void)allocstring(&facetname, returninfstr(), el_tempcluster);

		/* first make sure all fields exist */
		np = getnodeproto(facetname);
		if (np != NONODEPROTO)
		{
			us_tecedmakenode(np, NPUNKNOWN, 0, 0, 0, 0);
			(*el_curconstraint->solve)(np);
		}

		np = us_tecedenterfacet(facetname);
		efree(facetname);
		if (np == NONODEPROTO) return;
		us_tecedmakenode(np, NPUNKNOWN, 0, 0, 0, 0);
		(*el_curconstraint->solve)(np);
		(void)us_tecedenterfacet(describenodeproto(np));
		return;
	}
	if (namesamen(pp, "edit-arc", l) == 0 && l >= 6)
	{
		if (count < 2)
		{
			ttyputusage("technology edit edit-arc ARCNAME");
			return;
		}
		(void)initinfstr();
		(void)addstringtoinfstr("arc-");
		(void)addstringtoinfstr(par[1]);
		(void)allocstring(&facetname, returninfstr(), el_tempcluster);
		np = us_tecedenterfacet(facetname);
		efree(facetname);
		if (np == NONODEPROTO) return;
		us_tecedmakearc(np, APUNKNOWN, 1, 1, 0, 90);
		(*el_curconstraint->solve)(np);
		(void)us_tecedenterfacet(describenodeproto(np));
		return;
	}
	if (namesamen(pp, "edit-layer", l) == 0 && l >= 6)
	{
		if (count < 2)
		{
			ttyputusage("technology edit edit-layer LAYERNAME");
			return;
		}
		(void)initinfstr();
		(void)addstringtoinfstr("layer-");
		(void)addstringtoinfstr(par[1]);
		(void)allocstring(&facetname, returninfstr(), el_tempcluster);

		/* first make sure all fields exist */
		for(i=0; i<8; i++) stip[i] = 0;
		np = getnodeproto(facetname);
		if (np != NONODEPROTO)
		{
			us_tecedmakelayer(np, COLORT1, stip, SOLIDC, "XX", LFUNKNOWN, "x", "",
				-1, 0.0, 0.0, 0.0, -1, 0, 0);
			(*el_curconstraint->solve)(np);
		}

		np = us_tecedenterfacet(facetname);
		efree(facetname);
		if (np == NONODEPROTO) return;
		us_tecedmakelayer(np, COLORT1, stip, SOLIDC, "XX", LFUNKNOWN, "x", "",
			-1, 0.0, 0.0, 0.0, -1, 0, 0);
		(*el_curconstraint->solve)(np);
		(void)us_tecedenterfacet(describenodeproto(np));
		return;
	}
	if (namesamen(pp, "edit-subsequent", l) == 0 && l >= 6)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		if (namesamen(np->cell->cellname, "node-", 5) == 0)
		{
			dependentlibcount = us_teceditgetdependents(el_curlib, &dependentlibs);
			i = us_teceditfindsequence(dependentlibs, dependentlibcount, "node-",
				"EDTEC_nodesequence", &sequence);
			if (i == 0) return;
			for(l=0; l<i; l++) if (sequence[l] == np)
			{
				if (l == i-1) np = sequence[0]; else np = sequence[l+1];
				(void)us_tecedenterfacet(describenodeproto(np));
				break;
			}
			efree((char *)sequence);
			return;
		}
		if (namesamen(np->cell->cellname, "arc-", 4) == 0)
		{
			dependentlibcount = us_teceditgetdependents(el_curlib, &dependentlibs);
			i = us_teceditfindsequence(dependentlibs, dependentlibcount, "arc-",
				"EDTEC_arcsequence", &sequence);
			if (i == 0) return;
			for(l=0; l<i; l++) if (sequence[l] == np)
			{
				if (l == i-1) np = sequence[0]; else np = sequence[l+1];
				(void)us_tecedenterfacet(describenodeproto(np));
				break;
			}
			efree((char *)sequence);
			return;
		}
		if (namesamen(np->cell->cellname, "layer-", 6) == 0)
		{
			dependentlibcount = us_teceditgetdependents(el_curlib, &dependentlibs);
			i = us_teceditfindsequence(dependentlibs, dependentlibcount, "layer-",
				"EDTEC_layersequence", &sequence);
			if (i == 0) return;
			for(l=0; l<i; l++) if (sequence[l] == np)
			{
				if (l == i-1) np = sequence[0]; else np = sequence[l+1];
				(void)us_tecedenterfacet(describenodeproto(np));
				break;
			}
			efree((char *)sequence);
			return;
		}
		ttyputerr(_("Must be editing a layer, node, or arc to advance to the next"));
		return;
	}
	if (namesamen(pp, "edit-colors", l) == 0 && l >= 6)
	{
		us_teceditcolormap();
		return;
	}
	if (namesamen(pp, "edit-design-rules", l) == 0 && l >= 6)
	{
		us_teceditdrc();
		return;
	}
	if (namesamen(pp, "edit-misc-information", l) == 0 && l >= 6)
	{
		/* first make sure all fields exist */
		np = getnodeproto("factors");
		if (np != NONODEPROTO)
		{
			us_tecedmakeinfo(np, 2000, el_curlib->libname);
			(*el_curconstraint->solve)(np);
		}

		/* now edit the facet */
		np = us_tecedenterfacet("factors");
		if (np == NONODEPROTO) return;
		us_tecedmakeinfo(np, 2000, el_curlib->libname);
		(*el_curconstraint->solve)(np);
		(void)us_tecedenterfacet(describenodeproto(np));
		return;
	}
	if (namesamen(pp, "identify-layers", l) == 0 && l >= 10)
	{
		us_teceditidentify(0);
		return;
	}
	if (namesamen(pp, "identify-ports", l) == 0 && l >= 10)
	{
		us_teceditidentify(1);
		return;
	}
	if (namesamen(pp, "dependent-libraries", l) == 0 && l >= 2)
	{
		if (count < 2)
		{
			/* display dependent library names */
			var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING|VISARRAY, "EDTEC_dependent_libraries");
			if (var == NOVARIABLE) ttyputmsg(_("There are no dependent libraries")); else
			{
				i = (INTSML)getlength(var);
				ttyputmsg(_("%d dependent %s:"), i, makeplural("library", i));
				for(l=0; l<i; l++)
				{
					pp = ((char **)var->addr)[l];
					lib = getlibrary(pp);
					ttyputmsg("    %s%s", pp, (lib == NOLIBRARY ? _(" (not read in)") : ""));
				}
			}
			return;
		}

		/* clear list if just "-" is given */
		if (count == 2 && strcmp(par[1], "-") == 0)
		{
			var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING|VISARRAY, "EDTEC_dependent_libraries");
			if (var != NOVARIABLE)
				(void)delval((INTBIG)el_curlib, VLIBRARY, "EDTEC_dependent_libraries");
			return;
		}

		/* create a list */
		dependentlist = (char **)emalloc((count-1) * (sizeof (char *)), el_tempcluster);
		if (dependentlist == 0) return;
		for(i=1; i<count; i++) dependentlist[i-1] = par[i];
		(void)setval((INTBIG)el_curlib, VLIBRARY, "EDTEC_dependent_libraries", (INTBIG)dependentlist,
			VSTRING|VISARRAY|((count-1)<<VLENGTHSH));
		efree((char *)dependentlist);
		return;
	}
	ttyputbadusage("technology edit");
}

/*
 * routine for editing the DRC tables
 */
void us_teceditdrc(void)
{
	REGISTER WINDOWPART *w;
	REGISTER EDITOR *e;
	INTSML dummy;
	REGISTER INTSML i, l;
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np;

	if (us_teceddrc != 0)
	{
		ttyputerr(_("Already editing design rules"));
		return;
	}

	/* free any former layer name information */
	if (us_teceddrclayernames != 0)
	{
		for(i=0; i<us_teceddrclayers; i++) efree(us_teceddrclayernames[i]);
		efree((char *)us_teceddrclayernames);
		us_teceddrclayernames = 0;
	}

	/* count the number of layers */
	us_teceddrclayers = 0;
	for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (namesamen(np->cell->cellname, "layer-", 6) == 0) us_teceddrclayers++;
	if (us_teceddrclayers <= 0)
	{
		ttyputerr(_("Define layers before creating design rules"));
		return;
	}

	/* build and fill array of layers for DRC parsing */
	us_teceddrclayernames = (char **)emalloc(us_teceddrclayers * (sizeof (char *)), us_aid->cluster);
	if (us_teceddrclayernames == 0) return;
	for(i = 0, np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (namesamen(np->cell->cellname, "layer-", 6) == 0)
			(void)allocstring(&us_teceddrclayernames[i++], &np->cell->cellname[6], us_aid->cluster);

	/* make the editor window */
	w = us_wantnewwindow(0);
	if (w == NOWINDOWPART) return;

	if (us_makeeditor(w, _("Design Rules (Format: [C]SPACING LAYER LAYER)"), &dummy,
		&dummy) == NOWINDOWPART) return;
	e = w->editor;
	if (e == NOEDITOR) return;
	w->termhandler = us_teceddrcterm;
	us_teceddrc = 1;

	/* load in the existing design rules */
	var = getval((INTBIG)el_curlib, VLIBRARY, VSTRING|VISARRAY, "EDTEC_DRC");
	if (var != NOVARIABLE)
	{
		us_suspendgraphics(w);
		l = (INTSML)getlength(var);
		for(i=0; i<l; i++)
			us_addline(w, i, ((char **)var->addr)[i]);
		us_resumegraphics(w);
	}
	w->changehandler = us_teceddrcchange;
	ttyputverbose(_("Editing design rules"));
}

/*
 * routine for manipulating color maps
 */
void us_teceditcolormap(void)
{
	REGISTER INTSML i, k;
	INTBIG func, gds, drcminwid, height3d, thick3d;
	char *layerlabel[5], *layerabbrev[5], *cif, *layerletters, *dxf;
	GRAPHICS desc;
	REGISTER NODEPROTO *np;
	float spires, spicap, spiecap;
	REGISTER INTBIG *mapptr, *newmap;
	REGISTER VARIABLE *varred, *vargreen, *varblue;

	if (us_needwindow()) return;

	/* load the color map of the technology */
	us_tecedloadlibmap(el_curlib);

	/* now fill in real layer names if known */
	for(i=0; i<5; i++) layerlabel[i] = 0;
	for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (namesamen(np->cell->cellname, "layer-", 6) != 0) continue;
		cif = layerletters = 0;
		if (us_teceditgetlayerinfo(np, &desc, &cif, &func, &layerletters,
			&dxf, &gds, &spires, &spicap, &spiecap, &drcminwid, &height3d,
				&thick3d) != 0) return;
		switch (desc.bits)
		{
			case LAYERT1: k = 0;   break;
			case LAYERT2: k = 1;   break;
			case LAYERT3: k = 2;   break;
			case LAYERT4: k = 3;   break;
			case LAYERT5: k = 4;   break;
			default:      k = -1;  break;
		}
		if (k >= 0)
		{
			if (layerlabel[k] == 0)
			{
				layerlabel[k] = &np->cell->cellname[6];
				layerabbrev[k] = (char *)emalloc(2, el_tempcluster);
				layerabbrev[k][0] = *layerletters;
				layerabbrev[k][1] = 0;
			}
		}
		if (cif != 0) efree(cif);
		if (layerletters != 0) efree(layerletters);
	}

	/* set defaults */
	if (layerlabel[0] == 0)
	{
		layerlabel[0] = _("Ovrlap 1");
		(void)allocstring(&layerabbrev[0], "1", el_tempcluster);
	}
	if (layerlabel[1] == 0)
	{
		layerlabel[1] = _("Ovrlap 2");
		(void)allocstring(&layerabbrev[1], "2", el_tempcluster);
	}
	if (layerlabel[2] == 0)
	{
		layerlabel[2] = _("Ovrlap 3");
		(void)allocstring(&layerabbrev[2], "3", el_tempcluster);
	}
	if (layerlabel[3] == 0)
	{
		layerlabel[3] = _("Ovrlap 4");
		(void)allocstring(&layerabbrev[3], "4", el_tempcluster);
	}
	if (layerlabel[4] == 0)
	{
		layerlabel[4] = _("Ovrlap 5");
		(void)allocstring(&layerabbrev[4], "5", el_tempcluster);
	}

	/* run the color mixing palette */
	us_colormixdlog(layerlabel);
	for(i=0; i<5; i++) efree(layerabbrev[i]);

	/* save the map on the library */
	varred = getvalkey((INTBIG)us_aid, VAID, VINTEGER|VISARRAY, us_colormap_red);
	vargreen = getvalkey((INTBIG)us_aid, VAID, VINTEGER|VISARRAY, us_colormap_green);
	varblue = getvalkey((INTBIG)us_aid, VAID, VINTEGER|VISARRAY, us_colormap_blue);
	if (varred != NOVARIABLE && vargreen != NOVARIABLE && varblue != NOVARIABLE)
	{
		newmap = emalloc(256*3*SIZEOFINTBIG, el_tempcluster);
		mapptr = newmap;
		for(i=0; i<256; i++)
		{
			*mapptr++ = ((INTBIG *)varred->addr)[i];
			*mapptr++ = ((INTBIG *)vargreen->addr)[i];
			*mapptr++ = ((INTBIG *)varblue->addr)[i];
		}
		(void)setval((INTBIG)el_curlib, VLIBRARY, "EDTEC_colormap", (INTBIG)newmap,
			VINTEGER|VISARRAY|((256*3)<<VLENGTHSH));
		efree((char *)newmap);
	}
}

/*
 * routine for creating a new layer with shape "pp"
 */
void us_teceditcreat(INTSML count, char *par[])
{
	REGISTER INTSML l;
	REGISTER char *name, *pp;
	char *dummy[1];
	REGISTER NODEINST *ni;
	REGISTER NODEPROTO *np, *savenp, *facet;
	HIGHLIGHT high;
	REGISTER VARIABLE *var;

	l = strlen(pp = par[0]);
	np = NONODEPROTO;
	if (namesamen(pp, "port", l) == 0 && l >= 1) np = gen_portprim;
	if (namesamen(pp, "highlight", l) == 0 && l >= 1) np = art_boxprim;
	if (namesamen(pp, "rectangle-filled", l) == 0 && l >= 11) np = art_filledboxprim;
	if (namesamen(pp, "rectangle-outline", l) == 0 && l >= 11) np = art_boxprim;
	if (namesamen(pp, "rectangle-crossed", l) == 0 && l >= 11) np = art_crossedboxprim;
	if (namesamen(pp, "polygon-filled", l) == 0 && l >= 9) np = art_filledpolygonprim;
	if (namesamen(pp, "polygon-outline", l) == 0 && l >= 9) np = art_closedpolygonprim;
	if (namesamen(pp, "lines-solid", l) == 0 && l >= 7) np = art_openedpolygonprim;
	if (namesamen(pp, "lines-dotted", l) == 0 && l >= 8) np = art_openeddottedpolygonprim;
	if (namesamen(pp, "lines-dashed", l) == 0 && l >= 8) np = art_openeddashedpolygonprim;
	if (namesamen(pp, "lines-far-dotted", l) == 0 && l >= 7) np = art_openedfardottepolygonprim;
	if (namesamen(pp, "circle-outline", l) == 0 && l >= 8) np = art_circleprim;
	if (namesamen(pp, "circle-filled", l) == 0 && l >= 8) np = art_filledcircleprim;
	if (namesamen(pp, "circle-half", l) == 0 && l >= 8) np = art_circleprim;
	if (namesamen(pp, "circle-arc", l) == 0 && l >= 8) np = art_circleprim;
	if (namesamen(pp, "text", l) == 0 && l >= 1) np = gen_invispinprim;

	if (np == NONODEPROTO)
	{
		ttyputerr(_("Unrecoginzed shape: '%s'"), pp);
		return;
	}

	/* make sure the facet is right */
	facet = us_needfacet();
	if (facet == NONODEPROTO) return;
	if (namesamen(facet->cell->cellname, "node-", 5) != 0 &&
		namesamen(facet->cell->cellname, "arc-", 4) != 0)
	{
		us_abortcommand(_("Must be editing a node or arc to place geometry"));
		if ((us_aid->aidstate&NODETAILS) == 0)
			ttyputmsg(_("Use 'edit-node' or 'edit-arc' options"));
		return;
	}
	if (np == gen_portprim &&
		namesamen(facet->cell->cellname, "node-", 5) != 0)
	{
		us_abortcommand(_("Can only place ports in node descriptions"));
		if ((us_aid->aidstate&NODETAILS) == 0)
			ttyputmsg(_("Use the 'edit-node' options"));
		return;
	}

	/* create the node */
	us_clearhighlightcount();
	savenp = us_curnodeproto;
	us_curnodeproto = np;
	dummy[0] = "wait-for-down";
	us_create(1, dummy);
	us_curnodeproto = savenp;

	var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
	if (var == NOVARIABLE) return;
	(void)us_makehighlight(((char **)var->addr)[0], &high);
	if (high.fromgeom == NOGEOM) return;
	if (high.fromgeom->entrytype != OBJNODEINST) return;
	ni = high.fromgeom->entryaddr.ni;
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_option", LAYERPATCH, VINTEGER);

	/* postprocessing on the nodes */
	if (namesamen(pp, "port", l) == 0 && l >= 1)
	{
		/* a port layer */
		if (count == 1)
		{
			name = ttygetline(_("Port name: "));
			if (name == 0 || name[0] == 0)
			{
				us_abortedmsg();
				return;
			}
		} else name = par[1];
		var = setval((INTBIG)ni, VNODEINST, "EDTEC_portname", (INTBIG)name, VSTRING|VDISPLAY);
		if (var != NOVARIABLE) var->textdescript = defaulttextdescript(NOGEOM);
		if ((us_aid->aidstate&NODETAILS) == 0)
			ttyputmsg(_("Use 'change' option to set arc connectivity and port angle"));
	}
	if (namesamen(pp, "highlight", l) == 0 && l >= 1)
	{
		/* a highlight layer */
		us_teceditsetpatch(ni, &us_edtechigh);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer", (INTBIG)NONODEPROTO, VNODEPROTO);
		ttyputmsg(_("Keep highlight a constant distance from the example edge"));
	}
	if (namesamen(pp, "circle-half", l) == 0 && l >= 8)
		setarcdegrees(ni, 0.0, 180.0*EPI/180.0);
	if ((us_aid->aidstate&NODETAILS) == 0)
	{
		if (namesamen(pp, "rectangle-", 10) == 0)
			ttyputmsg(_("Use 'change' option to set a layer for this shape"));
		if (namesamen(pp, "polygon-", 8) == 0)
		{
			ttyputmsg(_("Use 'change' option to set a layer for this shape"));
			ttyputmsg(_("Use 'polygonedit' command to describe polygonal shape"));
		}
		if (namesamen(pp, "lines-", 6) == 0)
			ttyputmsg(_("Use 'change' option to set a layer for this line"));
		if (namesamen(pp, "circle-", 7) == 0)
			ttyputmsg(_("Use 'change' option to set a layer for this circle"));
		if (namesamen(pp, "text", l) == 0 && l >= 1)
		{
			ttyputmsg(_("Use 'change' option to set a layer for this text"));
			ttyputmsg(_("Use 'var textedit ~.ART_message' command to set text"));
			ttyputmsg(_("Then use 'var change ~.ART_message display' command"));
		}
	}
	if (namesamen(pp, "circle-arc", l) == 0 && l >= 8)
	{
		setarcdegrees(ni, 0.0, 45.0*EPI/180.0);
		if ((us_aid->aidstate&NODETAILS) == 0)
			ttyputmsg(_("Use 'setarc' command to set portion of circle"));
	}
}

/*
 * routine to highlight information about all layers (or ports if "doports" is nonzero)
 */
void us_teceditidentify(INTSML doports)
{
	REGISTER NODEPROTO *np;
	REGISTER VARIABLE *var;
	REGISTER INTSML total, qtotal, i, j, bestrot, indent;
	REGISTER INTBIG xsep, ysep, *xpos, *ypos, dist, bestdist, *style;
	INTBIG lx, hx, ly, hy;
	REGISTER EXAMPLE *nelist;
	REGISTER SAMPLE *ns, **whichsam;
	static POLYGON *poly = NOPOLYGON;
	extern GRAPHICS us_hbox;

	np = us_needfacet();
	if (np == NONODEPROTO) return;

	if (doports != 0)
	{
		if (namesamen(np->cell->cellname, "node-", 5) != 0)
		{
			us_abortcommand(_("Must be editing a node to identify ports"));
			if ((us_aid->aidstate&NODETAILS) == 0)
				ttyputmsg(_("Use the 'edit-node' option"));
			return;
		}
	} else
	{
		if (namesamen(np->cell->cellname, "node-", 5) != 0 &&
			namesamen(np->cell->cellname, "arc-", 4) != 0)
		{
			us_abortcommand(_("Must be editing a node or arc to identify layers"));
			if ((us_aid->aidstate&NODETAILS) == 0)
				ttyputmsg(_("Use 'edit-node' or 'edit-arc' options"));
			return;
		}
	}

	/* get examples */
	if (namesamen(np->cell->cellname, "node-", 5) == 0)
		nelist = us_tecedgetexamples(np, 1); else
			nelist = us_tecedgetexamples(np, 0);
	if (nelist == NOEXAMPLE) return;

	/* count the number of appropriate samples in the main example */
	total = 0;
	for(ns = nelist->firstsample; ns != NOSAMPLE; ns = ns->nextsample)
	{
		if (doports == 0)
		{
			if (ns->layer != gen_portprim) total++;
		} else
		{
			if (ns->layer == gen_portprim) total++;
		}
	}
	if (total == 0)
	{
		us_tecedfreeexamples(nelist);
		us_abortcommand(_("There are no %s to identify"), (doports == 0 ? _("layers") : _("ports")));
		return;
	}

	/* make arrays for position and association */
	xpos = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
	if (xpos == 0) return;
	ypos = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
	if (ypos == 0) return;
	style = (INTBIG *)emalloc(total * SIZEOFINTBIG, el_tempcluster);
	if (style == 0) return;
	whichsam = (SAMPLE **)emalloc(total * (sizeof (SAMPLE *)), el_tempcluster);
	if (whichsam == 0) return;

	/* fill in label positions */
	qtotal = (total+3) / 4;
	ysep = (el_curwindowpart->screenhy-el_curwindowpart->screenly) / qtotal;
	xsep = (el_curwindowpart->screenhx-el_curwindowpart->screenlx) / qtotal;
	indent = (el_curwindowpart->screenhy-el_curwindowpart->screenly) / 15;
	for(i=0; i<qtotal; i++)
	{
		/* label on the left side */
		xpos[i] = el_curwindowpart->screenlx + indent;
		ypos[i] = el_curwindowpart->screenly + ysep * i + ysep/2;
		style[i] = TEXTLEFT;
		if (i+qtotal < total)
		{
			/* label on the top side */
			xpos[i+qtotal] = el_curwindowpart->screenlx + xsep * i + xsep/2;
			ypos[i+qtotal] = el_curwindowpart->screenhy - indent;
			style[i+qtotal] = TEXTTOP;
		}
		if (i+qtotal*2 < total)
		{
			/* label on the right side */
			xpos[i+qtotal*2] = el_curwindowpart->screenhx - indent;
			ypos[i+qtotal*2] = el_curwindowpart->screenly + ysep * i + ysep/2;
			style[i+qtotal*2] = TEXTRIGHT;
		}
		if (i+qtotal*3 < total)
		{
			/* label on the bottom side */
			xpos[i+qtotal*3] = el_curwindowpart->screenlx + xsep * i + xsep/2;
			ypos[i+qtotal*3] = el_curwindowpart->screenly + indent;
			style[i+qtotal*3] = TEXTBOT;
		}
	}

	/* fill in sample associations */
	i = 0;
	for(ns = nelist->firstsample; ns != NOSAMPLE; ns = ns->nextsample)
	{
		if (doports == 0)
		{
			if (ns->layer != gen_portprim) whichsam[i++] = ns;
		} else
		{
			if (ns->layer == gen_portprim) whichsam[i++] = ns;
		}
	}

	/* rotate through all configurations, finding least distance */
	bestdist = HUGEINT;
	for(i=0; i<total; i++)
	{
		/* find distance from each label to its sample center */
		dist = 0;
		for(j=0; j<total; j++)
			dist += computedistance(xpos[j], ypos[j], whichsam[j]->xpos, whichsam[j]->ypos);
		if (dist < bestdist)
		{
			bestdist = dist;
			bestrot = i;
		}

		/* rotate the samples */
		ns = whichsam[0];
		for(j=1; j<total; j++) whichsam[j-1] = whichsam[j];
		whichsam[total-1] = ns;
	}

	/* rotate back to the best orientation */
	for(i=0; i<bestrot; i++)
	{
		ns = whichsam[0];
		for(j=1; j<total; j++) whichsam[j-1] = whichsam[j];
		whichsam[total-1] = ns;
	}

	/* get polygon */
	if (poly == NOPOLYGON) poly = allocstaticpolygon(2, us_aid->cluster);

	/* draw the highlighting */
	us_clearhighlightcount();
	for(i=0; i<total; i++)
	{
		ns = whichsam[i];
		poly->xv[0] = xpos[i];
		poly->yv[0] = ypos[i];
		poly->count = 1;
		if (ns->layer == NONODEPROTO)
		{
			poly->string = "HIGHLIGHT";
		} else if (ns->layer == gen_facetcenterprim)
		{
			poly->string = "GRAB";
		} else if (ns->layer == gen_portprim)
		{
			var = getval((INTBIG)ns->node, VNODEINST, VSTRING, "EDTEC_portname");
			if (var == NOVARIABLE) poly->string = "?"; else
				poly->string = (char *)var->addr;
		} else poly->string = &ns->layer->cell->cellname[6];
		poly->desc = &us_hbox;
		poly->style = (INTSML)style[i];
		poly->font = TXTSMALL;
		poly->tech = el_curtech;
		us_hbox.col = HIGHLIT;

		nodesizeoffset(ns->node, &lx, &ly, &hx, &hy);
		switch (poly->style)
		{
			case TEXTLEFT:
				poly->xv[1] = ns->node->lowx+lx;
				poly->yv[1] = (ns->node->lowy + ns->node->highy) / 2;
				poly->style = TEXTBOTLEFT;
				break;
			case TEXTRIGHT:
				poly->xv[1] = ns->node->highx-hx;
				poly->yv[1] = (ns->node->lowy + ns->node->highy) / 2;
				poly->style = TEXTBOTRIGHT;
				break;
			case TEXTTOP:
				poly->xv[1] = (ns->node->lowx + ns->node->highx) / 2;
				poly->yv[1] = ns->node->highy-hy;
				poly->style = TEXTTOPLEFT;
				break;
			case TEXTBOT:
				poly->xv[1] = (ns->node->lowx + ns->node->highx) / 2;
				poly->yv[1] = ns->node->lowy+ly;
				poly->style = TEXTBOTLEFT;
				break;
		}
		(void)us_showpoly(poly, el_curwindowpart);

		/* now draw the vector polygon */
		poly->count = 2;
		poly->style = VECTORS;
		(void)us_showpoly(poly, el_curwindowpart);
	}

	/* free rotation arrays */
	efree((char *)xpos);
	efree((char *)ypos);
	efree((char *)style);
	efree((char *)whichsam);

	/* free all examples */
	us_tecedfreeexamples(nelist);
}

/*
 * routine to print information about selected object
 */
void us_teceditinquire(void)
{
	REGISTER NODEINST *ni;
	REGISTER ARCINST *ai;
	REGISTER NODEPROTO *np;
	REGISTER VARIABLE *var, *var2;
	REGISTER INTSML opt;
	REGISTER HIGHLIGHT *high;

	high = us_getonehighlight();
	if ((high->status&HIGHTYPE) != HIGHTEXT && (high->status&HIGHTYPE) != HIGHFROM)
	{
		us_abortcommand(_("Must select a single object for inquiry"));
		return;
	}
	if ((high->status&HIGHTYPE) == HIGHFROM && high->fromgeom->entrytype == OBJARCINST)
	{
		/* describe currently highlighted arc */
		ai = high->fromgeom->entryaddr.ai;
		if (ai->proto != gen_universalarc)
		{
			ttyputmsg(_("This is an unimportant %s arc"), describearcproto(ai->proto));
			return;
		}
		if (ai->end[0].nodeinst->proto != gen_portprim ||
			ai->end[1].nodeinst->proto != gen_portprim)
		{
			ttyputmsg(_("This arc makes an unimportant connection"));
			return;
		}
		var = getval((INTBIG)ai->end[0].nodeinst, VNODEINST, VSTRING, "EDTEC_portname");
		var2 = getval((INTBIG)ai->end[1].nodeinst, VNODEINST, VSTRING, "EDTEC_portname");
		if (var == NOVARIABLE || var2 == NOVARIABLE)
			ttyputmsg(_("This arc connects two port objects")); else
				ttyputmsg(_("This arc connects ports '%s' and '%s'"),
					(char *)var->addr, (char *)var2->addr);
		return;
	}
	ni = high->fromgeom->entryaddr.ni;
	np = ni->parent;
	opt = us_tecedgetoption(ni);
	if (opt < 0)
	{
		ttyputmsg(_("This object has no relevance to technology editing"));
		return;
	}

	switch (opt)
	{
		case ARCFIXANG:
			ttyputmsg(_("This object defines the fixed-angle factor of %s"), describenodeproto(np));
			break;
		case ARCFUNCTION:
			ttyputmsg(_("This object defines the function of %s"), describenodeproto(np));
			break;
		case ARCINC:
			ttyputmsg(_("This object defines the prefered angle increment of %s"), describenodeproto(np));
			break;
		case ARCNOEXTEND:
			ttyputmsg(_("This object defines the arc extension of %s"), describenodeproto(np));
			break;
		case ARCWIPESPINS:
			ttyputmsg(_("This object defines the arc coverage of %s"), describenodeproto(np));
			break;
		case CENTEROBJ:
			ttyputmsg(_("This object identifies the grab point of %s"), describenodeproto(np));
			break;
		case LAYER3DHEIGHT:
			ttyputmsg(_("This object defines the 3D height of %s"), describenodeproto(np));
			break;
		case LAYER3DTHICK:
			ttyputmsg(_("This object defines the 3D thickness of %s"), describenodeproto(np));
			break;
		case LAYERCIF:
			ttyputmsg(_("This object defines the CIF name of %s"), describenodeproto(np));
			break;
		case LAYERCOLOR:
			ttyputmsg(_("This object defines the color of %s"), describenodeproto(np));
			break;
		case LAYERDXF:
			ttyputmsg(_("This object defines the DXF name(s) of %s"), describenodeproto(np));
			break;
		case LAYERDRCMINWID:
			ttyputmsg(_("This object defines the minimum DRC width of %s"), describenodeproto(np));
			break;
		case LAYERFUNCTION:
			ttyputmsg(_("This object defines the function of %s"), describenodeproto(np));
			break;
		case LAYERGDS:
			ttyputmsg(_("This object defines the Calma GDS-II number of %s"), describenodeproto(np));
			break;
		case LAYERLETTERS:
			ttyputmsg(_("This object defines the letters to use for %s"), describenodeproto(np));
			break;
		case LAYERPATTERN:
			ttyputmsg(_("This is one of the bitmap squares in %s"), describenodeproto(np));
			break;
		case LAYERSPICAP:
			ttyputmsg(_("This object defines the SPICE capacitance of %s"), describenodeproto(np));
			break;
		case LAYERSPIECAP:
			ttyputmsg(_("This object defines the SPICE edge capacitance of %s"), describenodeproto(np));
			break;
		case LAYERSPIRES:
			ttyputmsg(_("This object defines the SPICE resistance of %s"), describenodeproto(np));
			break;
		case LAYERSTYLE:
			ttyputmsg(_("This object defines the style of %s"), describenodeproto(np));
			break;
		case LAYERPATCH:
		case HIGHLIGHTOBJ:
			var = getval((INTBIG)ni, VNODEINST, VNODEPROTO, "EDTEC_layer");
			if (var == NOVARIABLE)
				ttyputerr(_("This is an object with no valid layer!")); else
			{
				np = (NODEPROTO *)var->addr;
				if (np == NONODEPROTO) ttyputmsg(_("This is a highlight box")); else
					ttyputmsg(_("This is a '%s' layer"), &np->cell->cellname[6]);
				var = getval((INTBIG)ni, VNODEINST, VSTRING, "EDTEC_minbox");
				if (var != NOVARIABLE)
					ttyputmsg(_("   It is at minimum size"));
			}
			break;
		case NODEFUNCTION:
			ttyputmsg(_("This object defines the function of %s"), describenodeproto(np));
			break;
		case NODELOCKABLE:
			ttyputmsg(_("This object tells if %s can be locked (used in array technologies)"),
				describenodeproto(np));
			break;
		case NODESERPENTINE:
			ttyputmsg(_("This object tells if %s is a serpentine transistor"), describenodeproto(np));
			break;
		case NODESQUARE:
			ttyputmsg(_("This object tells if %s is square"), describenodeproto(np));
			break;
		case NODEWIPES:
			ttyputmsg(_("This object tells if %s disappears when conencted to one or two arcs"),
				describenodeproto(np));
			break;
		case PORTOBJ:
			var = getval((INTBIG)ni, VNODEINST, VSTRING, "EDTEC_portname");
			if (var == NOVARIABLE) ttyputmsg(_("This is a port object")); else
				ttyputmsg(_("This is port '%s'"), (char *)var->addr);
			break;
		case TECHDESCRIPT:
			ttyputmsg(_("This object contains the technology description"));
			break;
		case TECHLAMBDA:
			ttyputmsg(_("This object defines the value of lambda"));
			break;
		default:
			ttyputerr(_("This object has unknown information"));
			break;
	}
}

/*
 * routine for modifying the selected object.  If two are selected, connect them.
 */
void us_teceditmodobject(INTSML count, char *par[])
{
	REGISTER NODEINST *ni;
	GEOM *fromgeom, *togeom;
	PORTPROTO *fromport, *toport;
	INTSML opt;
	REGISTER HIGHLIGHT *high;
	HIGHLIGHT newhigh;
	char *newpar[2];

	/* make sure something is highlighted */
	high = us_getonehighlight();
	if (high == NOHIGHLIGHT) return;

	/* if two are highlighted, connect them */
	if (us_gettwoobjects(&fromgeom, &fromport, &togeom, &toport) == 0)
	{
		newpar[0] = "angle";   newpar[1] = "0";
		us_create(2, newpar);
		return;
	}

	/* must be one node highlighted */
	ni = (NODEINST *)us_getobject(OBJNODEINST, 1);
	if (ni == NONODEINST) return;

	/* determine technology editor relevance */
	opt = us_tecedgetoption(ni);

	/* special case for pattern blocks: node is replaced */
	if (opt == LAYERPATTERN)
	{
		us_clearhighlightcount();
		us_tecedlayerpattern(ni);
		return;
	}

	/* special case for port modification: reset highlighting by hand */
	if (opt == PORTOBJ)
	{
		/* pick up old highlight values and then remove highlighting */
		newhigh = *high;
		us_clearhighlightcount();

		/* modify the port */
		us_tecedmodport(ni, count, par);

		/* set new highlighting variable */
		newhigh.fromvar = getval((INTBIG)ni, VNODEINST, VSTRING, "EDTEC_portname");
		us_addhighlight(&newhigh);
		return;
	}

	/* ignore if no parameter given */
	if (count <= 0) return;

	/* handle other cases */
	us_pushhighlight();
	us_clearhighlightcount();
	switch (opt)
	{
		case ARCFIXANG:      us_tecedarcfixang(ni, count, par);      break;
		case ARCFUNCTION:    us_tecedarcfunction(ni, count, par);    break;
		case ARCINC:         us_tecedarcinc(ni, count, par);         break;
		case ARCNOEXTEND:    us_tecedarcnoextend(ni, count, par);    break;
		case ARCWIPESPINS:   us_tecedarcwipes(ni, count, par);       break;
		case LAYER3DHEIGHT:  us_tecedlayer3dheight(ni, count, par);  break;
		case LAYER3DTHICK:   us_tecedlayer3dthick(ni, count, par);   break;
		case LAYERCIF:       us_tecedlayercif(ni, count, par);       break;
		case LAYERCOLOR:     us_tecedlayercolor(ni, count, par);     break;
		case LAYERDXF:       us_tecedlayerdxf(ni, count, par);       break;
		case LAYERDRCMINWID: us_tecedlayerdrcminwid(ni, count, par); break;
		case LAYERFUNCTION:  us_tecedlayerfunction(ni, count, par);  break;
		case LAYERGDS:       us_tecedlayergds(ni, count, par);       break;
		case LAYERLETTERS:   us_tecedlayerletters(ni, count, par);   break;
		case LAYERPATCH:     us_tecedlayertype(ni, count, par);      break;
		case LAYERSPICAP:    us_tecedlayerspicap(ni, count, par);    break;
		case LAYERSPIECAP:   us_tecedlayerspiecap(ni, count, par);   break;
		case LAYERSPIRES:    us_tecedlayerspires(ni, count, par);    break;
		case LAYERSTYLE:     us_tecedlayerstyle(ni, count, par);     break;
		case NODEFUNCTION:   us_tecednodefunction(ni, count, par);   break;
		case NODELOCKABLE:   us_tecednodelockable(ni, count, par);   break;
		case NODESERPENTINE: us_tecednodeserpentine(ni, count, par); break;
		case NODESQUARE:     us_tecednodesquare(ni, count, par);     break;
		case NODEWIPES:      us_tecednodewipes(ni, count, par);      break;
		case TECHDESCRIPT:   us_tecedinfodescript(ni, count, par);   break;
		case TECHLAMBDA:     us_tecedinfolambda(ni, count, par);     break;
		default:             us_abortcommand(_("Cannot modify this object"));   break;
	}
	(void)us_pophighlight(0);
}

/***************************** OBJECT MODIFICATION *****************************/

void us_tecedlayer3dheight(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the height of the layer when viewed in 3D"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXT3DHEIGHT);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayer3dthick(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the thickness of the layer when viewed in 3D"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXT3DTHICK);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerdrcminwid(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the minimum DRC width (negative for none)"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTDRCMINWID);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayercolor(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("New color required"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTCOLOR);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());

	/* redraw the demo layer in this facet */
	us_tecedredolayergraphics(ni->parent);
}

void us_tecedlayerstyle(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Layer style required"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTSTYLE);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());

	/* redraw the demo layer in this facet */
	us_tecedredolayergraphics(ni->parent);
}

void us_tecedlayercif(NODEINST *ni, INTSML count, char *par[])
{
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTCIF);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerdxf(NODEINST *ni, INTSML count, char *par[])
{
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTDXF);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayergds(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the GDS layer number (-1 for none)"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTGDS);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerspires(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the SPICE layer resistance"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTSPICERES);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerspicap(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the SPICE layer capacitance"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTSPICECAP);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerspiecap(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Enter the SPICE layer edge capacitance"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTSPICEECAP);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerfunction(NODEINST *ni, INTSML count, char *par[])
{
	REGISTER INTBIG func, newfunc;
	REGISTER char *str;
	REGISTER VARIABLE *var;

	if (par[0][0] == 0)
	{
		us_abortcommand(_("Layer function required"));
		return;
	}

	/* get the existing function */
	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, art_messagekey);
	func = 0;
	if (var != NOVARIABLE)
	{
		str = (char *)var->addr;
		if (namesamen(str, TECEDNODETEXTFUNCTION, strlen(TECEDNODETEXTFUNCTION)) == 0)
			func = us_teceditparsefun(&str[10]);
	}

	/* add in the new function */
	newfunc = us_teceditparsefun(par[0]);
	if (newfunc <= LFTYPE) func = newfunc; else func |= newfunc;

	/* build the string corresponding to this function */
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
	us_tecedaddfunstring(func);

	/* rewrite the node with the new function */
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerletters(NODEINST *ni, INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER char *pt;
	REGISTER INTBIG i;
	char *cif, *layerletters, *dxf;
	GRAPHICS desc;
	float spires, spicap, spiecap;
	INTBIG func, gds, drcminwid, height3d, thick3d;

	if (par[0][0] == 0)
	{
		us_abortcommand(_("Layer letter(s) required"));
		return;
	}

	/* check layer letters for uniqueness */
	for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
	{
		if (namesamen(np->cell->cellname, "layer-", 6) != 0) continue;
		cif = layerletters = 0;
		if (us_teceditgetlayerinfo(np, &desc, &cif, &func, &layerletters,
			&dxf, &gds, &spires, &spicap, &spiecap, &drcminwid, &height3d,
				&thick3d) != 0) return;
		if (cif != 0) efree(cif);
		if (layerletters == 0) continue;

		/* check these layer letters for uniqueness */
		for(pt = layerletters; *pt != 0; pt++)
		{
			for(i=0; par[0][i] != 0; i++) if (par[0][i] == *pt)
			{
				us_abortcommand(_("Cannot use letter '%c', it is in %s"), *pt, describenodeproto(np));
				efree(layerletters);
				return;
			}
		}
		efree(layerletters);
	}

	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTLETTERS);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedlayerpattern(NODEINST *ni)
{
	REGISTER NODEINST *newni;
	HIGHLIGHT high;
	REGISTER VARIABLE *var;
	INTBIG i;
	UINTSML col[8], color;

	if (ni->proto == art_boxprim)
	{
		startobjectchange((INTBIG)ni, VNODEINST);
		newni = replacenodeinst(ni, art_filledboxprim, 0);
		if (newni == NONODEINST) return;
		endobjectchange((INTBIG)newni, VNODEINST);
		ni = newni;
	} else
	{
		if (ni->proto == art_filledboxprim)
		{
			startobjectchange((INTBIG)ni, VNODEINST);
			var = getvalkey((INTBIG)ni, VNODEINST, VSHORT|VISARRAY, art_patternkey);
			if (var == NOVARIABLE) color = 0xFFFF; else color = ((INTSML *)var->addr)[0];
			for(i=0; i<8; i++) col[i] = ~color;
			(void)setvalkey((INTBIG)ni, VNODEINST, art_patternkey, (INTBIG)col,
				VSHORT|VISARRAY|(8<<VLENGTHSH));
			endobjectchange((INTBIG)ni, VNODEINST);
		} else return;
	}

	high.status = HIGHFROM;
	high.facet = ni->parent;
	high.fromgeom = ni->geom;
	high.fromport = NOPORTPROTO;
	high.frompoint = 0;
	high.fromvar = NOVARIABLE;
	(void)us_addhighlight(&high);

	/* redraw the demo layer in this facet */
	us_tecedredolayergraphics(ni->parent);
}

/*
 * routine to modify the layer information in node "ni".
 */
void us_tecedlayertype(NODEINST *ni, INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	char *cif, *layerletters, *dxf;
	REGISTER char *name;
	GRAPHICS desc;
	float spires, spicap, spiecap;
	INTBIG func, gds, drcminwid, height3d, thick3d;

	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a layer name"));
		return;
	}

	np = us_needfacet();
	if (np == NONODEPROTO) return;
	if (namesame(par[0], "SET-MINIMUM-SIZE") == 0)
	{
		if (namesamen(np->cell->cellname, "node-", 5) != 0)
		{
			us_abortcommand(_("Can only set minimum size in node descriptions"));
			if ((us_aid->aidstate&NODETAILS) == 0) ttyputmsg(_("Use 'edit-node' option"));
			return;
		}
		startobjectchange((INTBIG)ni, VNODEINST);
		(void)setval((INTBIG)ni, VNODEINST, "EDTEC_minbox", (INTBIG)"MIN", VSTRING|VDISPLAY);
		endobjectchange((INTBIG)ni, VNODEINST);
		return;
	}

	if (namesame(par[0], "CLEAR-MINIMUM-SIZE") == 0)
	{
		if (getval((INTBIG)ni, VNODEINST, VSTRING, "EDTEC_minbox") == NOVARIABLE)
		{
			ttyputmsg(_("Minimum size is not set on this layer"));
			return;
		}
		startobjectchange((INTBIG)ni, VNODEINST);
		(void)delval((INTBIG)ni, VNODEINST, "EDTEC_minbox");
		endobjectchange((INTBIG)ni, VNODEINST);
		return;
	}

	/* find the actual facet with that layer specification */
	(void)initinfstr();
	(void)addstringtoinfstr("layer-");
	(void)addstringtoinfstr(par[0]);
	name = returninfstr();
	np = getnodeproto(name);
	if (np == NONODEPROTO)
	{
		ttyputerr(_("Cannot find layer primitive %s"), name);
		return;
	}

	/* get the characteristics of that layer */
	cif = layerletters = 0;
	if (us_teceditgetlayerinfo(np, &desc, &cif, &func, &layerletters,
		&dxf, &gds, &spires, &spicap, &spiecap, &drcminwid, &height3d,
			&thick3d) != 0) return;
	if (cif != 0) efree(cif);
	if (layerletters != 0) efree(layerletters);

	startobjectchange((INTBIG)ni, VNODEINST);
	us_teceditsetpatch(ni, &desc);
	(void)setval((INTBIG)ni, VNODEINST, "EDTEC_layer", (INTBIG)np, VNODEPROTO);
	endobjectchange((INTBIG)ni, VNODEINST);
}

/*
 * routine to modify port characteristics
 */
void us_tecedmodport(NODEINST *ni, INTSML count, char *par[])
{
	REGISTER INTSML total, i, len, j, changed, yes, *yesno;
	REGISTER NODEPROTO *np, **conlist;
	REGISTER VARIABLE *var;

	/* build an array of arc connections */
	for(total = 0, np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (namesamen(np->cell->cellname, "arc-", 4) == 0) total++;
	conlist = (NODEPROTO **)emalloc(total * (sizeof (NODEPROTO *)), el_tempcluster);
	if (conlist == 0) return;
	for(total = 0, np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
		if (namesamen(np->cell->cellname, "arc-", 4) == 0) conlist[total++] = np;
	yesno = (INTSML *)emalloc(total * SIZEOFINTSML, el_tempcluster);
	if (yesno == 0) return;
	for(i=0; i<total; i++) yesno[i] = 0;

	/* put current list into the array */
	var = getval((INTBIG)ni, VNODEINST, VNODEPROTO|VISARRAY, "EDTEC_connects");
	if (var != NOVARIABLE)
	{
		len = (INTSML)getlength(var);
		for(j=0; j<len; j++)
		{
			for(i=0; i<total; i++)
				if (conlist[i] == ((NODEPROTO **)var->addr)[j]) break;
			if (i < total) yesno[i] = 1;
		}
	}

	/* parse the command parameters */
	changed = 0;
	for(i=0; i<count-1; i += 2)
	{
		/* search for an arc name */
		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			if (namesamen(np->cell->cellname, "arc-", 4) == 0 &&
				namesame(&np->cell->cellname[4], par[i]) == 0) break;
		if (np != NONODEPROTO)
		{
			for(j=0; j<total; j++) if (conlist[j] == np)
			{
				if (*par[i+1] == 'y' || *par[i+1] == 'Y') yesno[j] = 1; else
					yesno[j] = 0;
				changed++;
				break;
			}
			continue;
		}

		if (namesame(par[i], "PORT-ANGLE") == 0)
		{
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_portangle", myatoi(par[i+1]), VINTEGER);
			continue;
		}
		if (namesame(par[i], "PORT-ANGLE-RANGE") == 0)
		{
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_portrange", myatoi(par[i+1]), VINTEGER);
			continue;
		}
	}

	/* store list back if it was changed */
	if (changed != 0)
	{
		yes = 0;
		for(i=0; i<total; i++)
		{
			if (yesno[i] == 0) continue;
			conlist[yes++] = conlist[i];
		}
		if (yes == 0 && var != NOVARIABLE)
			(void)delval((INTBIG)ni, VNODEINST, "EDTEC_connects"); else
		{
			(void)setval((INTBIG)ni, VNODEINST, "EDTEC_connects", (INTBIG)conlist,
				VNODEPROTO|VISARRAY|(yes<<VLENGTHSH));
		}
	}
	efree((char *)conlist);
	efree((char *)yesno);
}

void us_tecedarcfunction(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a layer function"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedarcfixang(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Fixed-angle: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedarcwipes(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Wipes pins: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedarcnoextend(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Extend arcs: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedarcinc(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires an angle increment in degrees"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Angle increment: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecednodefunction(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a node function"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr(TECEDNODETEXTFUNCTION);
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecednodeserpentine(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Serpentine transistor: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecednodesquare(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Square node: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecednodewipes(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Invisible with 1 or 2 arcs: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecednodelockable(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a yes or no"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Lockable: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedinfolambda(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a value of lambda"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Lambda: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedinfodescript(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires a technology description"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Description: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

void us_tecedinfounits(NODEINST *ni, INTSML count, char *par[])
{
	if (par[0][0] == 0)
	{
		us_abortcommand(_("Requires units for this technology"));
		return;
	}
	(void)initinfstr();
	(void)addstringtoinfstr("Units: ");
	(void)addstringtoinfstr(par[0]);
	us_tecedsetnode(ni, returninfstr());
}

/****************************** UTILITIES ******************************/

void us_tecedsetnode(NODEINST *ni, char *chr)
{
	REGISTER INTBIG descript;
	REGISTER VARIABLE *var;

	startobjectchange((INTBIG)ni, VNODEINST);
	var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, art_messagekey);
	if (var == NOVARIABLE) descript = 0; else descript = var->textdescript;
	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)chr, VSTRING|VDISPLAY);
	if (var != NOVARIABLE) modifydescript((INTBIG)ni, VNODEINST, var, descript);
	endobjectchange((INTBIG)ni, VNODEINST);
}

/*
 * routine to handle changes to the DRC window.
 */
void us_teceddrcchange(WINDOWPART *w, INTSML nature, char *oldline, char *newline, INTBIG changed)
{
	INTSML connected, layer1, layer2;
	INTBIG amt;
	REGISTER INTBIG i;
	REGISTER char **newlist;

	/* ignore deletions and insertions */
	if (nature == DELETETEXTLINE || nature == INSERTTEXTLINE) return;

	/* handle entire replacements */
	if (nature == REPLACEALLTEXT)
	{
		newlist = (char **)newline;
		for(i=0; i<changed; i++)
			us_teceddrcchange(w, REPLACETEXTLINE, "", newlist[i], i);
		return;
	}

	/* parse the new line and print an error if found */
	while (*newline == ' ') newline++;
	if (*newline == 0) return;
	if (us_tecedgetdrc(newline, &connected, &amt, &layer1, &layer2,
		us_teceddrclayers, us_teceddrclayernames) != 0)
			ttyputmsg(_("DRC line is '%s'"), newline);
}

/*
 * routine to handle termination of the DRC window
 */
void us_teceddrcterm(WINDOWPART *w)
{
	REGISTER INTSML i, j, l;
	REGISTER char **lines, *pt;
	INTSML connected, layer1, layer2;
	INTBIG amt;

	us_teceddrc = 0;

	/* get rules back into library */
	l = (INTSML)us_totallines(w);
	if (l != 0)
	{
		lines = (char **)emalloc(l * (sizeof (char *)), el_tempcluster);
		if (lines != 0)
		{
			for(i=j=0; i<l; i++)
			{
				pt = us_getline(w, i);
				while (*pt == ' ') pt++;
				if (*pt == 0) continue;
				if (us_tecedgetdrc(pt, &connected, &amt, &layer1, &layer2, us_teceddrclayers,
					us_teceddrclayernames) != 0) ttyputmsg(_("DRC line is '%s'"), pt);
				(void)allocstring(&lines[j++], pt, el_tempcluster);
			}
			if (j > 0)
				(void)setval((INTBIG)el_curlib, VLIBRARY, "EDTEC_DRC", (INTBIG)lines,
					VSTRING|VISARRAY|(j<<VLENGTHSH));
			for(i=0; i<j; i++) efree(lines[i]);
			efree((char *)lines);
		}
	}

	/* now do the rest of the termination of this window */
	us_editorterm(w);
}

/*
 * routine to call up the facet "facetname" (either create it or reedit it)
 * returns NONODEPROTO if there is an error or the facet exists
 */
NODEPROTO *us_tecedenterfacet(char *facetname)
{
	REGISTER NODEPROTO *np;
	char *newpar[2];

	np = getnodeproto(facetname);
	if (np != NONODEPROTO && np->primindex == 0)
	{
		newpar[0] = "editfacet";
		newpar[1] = facetname;
		(void)tellaid(us_aid, 2, newpar);
		return(NONODEPROTO);
	}

	/* create the facet */
	np = newnodeproto(facetname, el_curlib);
	if (np == NONODEPROTO) return(NONODEPROTO);

	/* now edit the facet */
	newpar[0] = "editfacet";
	newpar[1] = facetname;
	(void)tellaid(us_aid, 2, newpar);
	return(np);
}

/*
 * routine to redraw the demo layer in "layer" facet "np"
 */
void us_tecedredolayergraphics(NODEPROTO *np)
{
	REGISTER VARIABLE *var;
	REGISTER NODEINST *ni;
	GRAPHICS desc;
	REGISTER NODEPROTO *onp;
	char *cif, *layerletters, *dxf;
	INTBIG func, gds, drcminwid, height3d, thick3d;
	float spires, spicap, spiecap;

	/* find the demo patch in this facet */
	var = getval((INTBIG)np, VNODEPROTO, VNODEINST, "EDTEC_colornode");
	if (var == NOVARIABLE) return;
	ni = (NODEINST *)var->addr;

	/* get the current description of this layer */
	cif = layerletters = 0;
	if (us_teceditgetlayerinfo(np, &desc, &cif, &func, &layerletters,
		&dxf, &gds, &spires, &spicap, &spiecap, &drcminwid, &height3d,
			&thick3d) != 0) return;
	if (cif != 0) efree(cif);
	if (layerletters != 0) efree(layerletters);

	/* modify the demo patch to reflect the color and pattern */
	startobjectchange((INTBIG)ni, VNODEINST);
	us_teceditsetpatch(ni, &desc);
	endobjectchange((INTBIG)ni, VNODEINST);

	/* now do this to all layers in all facets! */
	for(onp = el_curlib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
	{
		if (namesamen(onp->cell->cellname, "arc-", 4) != 0 &&
			namesamen(onp->cell->cellname, "node-", 5) != 0) continue;
		for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (us_tecedgetoption(ni) != LAYERPATCH) continue;
			var = getval((INTBIG)ni, VNODEINST, VNODEPROTO, "EDTEC_layer");
			if (var == NOVARIABLE) continue;
			if ((NODEPROTO *)var->addr != np) continue;
			us_teceditsetpatch(ni, &desc);
		}
	}
}

void us_teceditsetpatch(NODEINST *ni, GRAPHICS *desc)
{
	REGISTER INTSML i;
	UINTBIG pattern[8];
	UINTSML spattern[8];

	(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, desc->col, VINTEGER);
	if ((desc->colstyle&NATURE) == PATTERNED)
	{
		if ((desc->colstyle&OUTLINEPAT) == 0)
		{
			for(i=0; i<8; i++) pattern[i] = desc->raster[i];
			(void)setvalkey((INTBIG)ni, VNODEINST, art_patternkey, (INTBIG)pattern,
				VINTEGER|VISARRAY|(8<<VLENGTHSH));
		} else
		{
			for(i=0; i<8; i++) spattern[i] = desc->raster[i];
			(void)setvalkey((INTBIG)ni, VNODEINST, art_patternkey, (INTBIG)spattern,
				VSHORT|VISARRAY|(8<<VLENGTHSH));
		}
	} else
	{
		if (getvalkey((INTBIG)ni, VNODEINST, -1, art_patternkey) != NOVARIABLE)
			(void)delvalkey((INTBIG)ni, VNODEINST, art_patternkey);
	}
}

/*
 * routine to load the color map associated with library "lib"
 */
void us_tecedloadlibmap(LIBRARY *lib)
{
	REGISTER VARIABLE *var;
	REGISTER INTSML i;
	REGISTER INTBIG *mapptr;
	INTBIG redmap[256], greenmap[256], bluemap[256];

	var = getval((INTBIG)lib, VLIBRARY, VINTEGER|VISARRAY, "EDTEC_colormap");
	if (var != NOVARIABLE)
	{
		mapptr = (INTBIG *)var->addr;
		for(i=0; i<256; i++)
		{
			redmap[i] = *mapptr++;
			greenmap[i] = *mapptr++;
			bluemap[i] = *mapptr++;
		}

		/* disable option tracking */
		(void)setvalkey((INTBIG)us_aid, VAID, us_ignoreoptionchanges, 1,
			VINTEGER|VDONTSAVE);

		startobjectchange((INTBIG)us_aid, VAID);
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_red, (INTBIG)redmap,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_green, (INTBIG)greenmap,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_blue, (INTBIG)bluemap,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		endobjectchange((INTBIG)us_aid, VAID);

		/* re-enable option tracking */
		var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_ignoreoptionchanges);
		if (var != NOVARIABLE)
			delvalkey((INTBIG)us_aid, VAID, us_ignoreoptionchanges);
	}
}

/*
 * routine to parse the layer facet in "np" and fill these reference descriptors:
 *   "desc" (a GRAPHICS structure)
 *   "cif" (the name of the CIF layer)
 *   "dxf" (the name of the DXF layer)
 *   "func" (the integer function number)
 *   "layerletters" (the letters associated with this layer),
 *   "gds" (the Calma GDS-II layer number)
 *   "spires" (the SPICE resistance)
 *   "spicap" (the SPICE capacitance)
 *   "spiecap" (the SPICE edge capacitance)
 *   "drcminwid" (the DRC minimum width)
 *   "height3d" (the 3D height)
 *   "thick3d" (the 3D thickness)
 * All of the reference parameters except "func", "gds", "spires", "spicap", and "spiecap"
 * get allocated.  Returns nonzero on error.
 */
INTSML us_teceditgetlayerinfo(NODEPROTO *np, GRAPHICS *desc, char **cif, INTBIG *func,
	char **layerletters, char **dxf, INTBIG *gds, float *spires, float *spicap,
	float *spiecap, INTBIG *drcminwid, INTBIG *height3d, INTBIG *thick3d)
{
	REGISTER NODEINST *ni;
	REGISTER INTSML patterncount, i, color, l;
	REGISTER INTBIG lowx, highx, lowy, highy, x, y;
	REGISTER char *str;
	REGISTER VARIABLE *var;

	/* create and initialize the GRAPHICS structure */
	desc->colstyle = SOLIDC;
	desc->bwstyle = PATTERNED;
	for(i=0; i<8; i++) desc->raster[i] = 0;

	/* look at all nodes in the layer description facet */
	patterncount = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->proto == gen_invispinprim)
		{
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, art_messagekey);
			if (var == NOVARIABLE) continue;
			str = (char *)var->addr;
			l = strlen(TECEDNODETEXTCOLOR);
			if (namesamen(str, TECEDNODETEXTCOLOR, l) == 0)
			{
				color = getecolor(&str[l]);
				if (color < 0)
				{
					ttyputerr(_("Unknown color '%s' in %s"), &str[l], describenodeproto(np));
					return(1);
				}
				desc->col = color;
				switch (color)
				{
					case COLORT1: desc->bits = LAYERT1; break;
					case COLORT2: desc->bits = LAYERT2; break;
					case COLORT3: desc->bits = LAYERT3; break;
					case COLORT4: desc->bits = LAYERT4; break;
					case COLORT5: desc->bits = LAYERT5; break;
					default:      desc->bits = LAYERO;  break;
				}
				continue;
			}
			l = strlen(TECEDNODETEXTSTYLE);
			if (namesamen(str, TECEDNODETEXTSTYLE, l) == 0)
			{
				if (namesame(&str[l], "solid") == 0)
					desc->colstyle = desc->bwstyle = SOLIDC;
				if (namesame(&str[l], "patterned") == 0)
					desc->colstyle = desc->bwstyle = PATTERNED;
				if (namesame(&str[l], "patterned/outlined") == 0)
					desc->colstyle = desc->bwstyle = PATTERNED | OUTLINEPAT;
				continue;
			}
			l = strlen(TECEDNODETEXTCIF);
			if (namesamen(str, TECEDNODETEXTCIF, l) == 0)
			{
				if (allocstring(cif, &str[l], us_aid->cluster) != 0) return(1);
				continue;
			}
			l = strlen(TECEDNODETEXTDXF);
			if (namesamen(str, TECEDNODETEXTDXF, l) == 0)
			{
				if (allocstring(dxf, &str[l], us_aid->cluster) != 0) return(1);
				continue;
			}
			l = strlen(TECEDNODETEXTGDS);
			if (namesamen(str, TECEDNODETEXTGDS, l) == 0)
			{
				*gds = myatoi(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTGDSOLD);
			if (namesamen(str, TECEDNODETEXTGDSOLD, l) == 0)
			{
				*gds = myatoi(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTFUNCTION);
			if (namesamen(str, TECEDNODETEXTFUNCTION, l) == 0)
			{
				*func = us_teceditparsefun(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTLETTERS);
			if (namesamen(str, TECEDNODETEXTLETTERS, l) == 0)
			{
				if (allocstring(layerletters, &str[l], us_aid->cluster) != 0) return(1);
				continue;
			}
			l = strlen(TECEDNODETEXTSPICERES);
			if (namesamen(str, TECEDNODETEXTSPICERES, l) == 0)
			{
				*spires = (float)atof(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTSPICECAP);
			if (namesamen(str, TECEDNODETEXTSPICECAP, l) == 0)
			{
				*spicap = (float)atof(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTSPICEECAP);
			if (namesamen(str, TECEDNODETEXTSPICEECAP, l) == 0)
			{
				*spiecap = (float)atof(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXTDRCMINWID);
			if (namesamen(str, TECEDNODETEXTDRCMINWID, l) == 0)
			{
				*drcminwid = atofr(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXT3DHEIGHT);
			if (namesamen(str, TECEDNODETEXT3DHEIGHT, l) == 0)
			{
				*height3d = myatoi(&str[l]);
				continue;
			}
			l = strlen(TECEDNODETEXT3DTHICK);
			if (namesamen(str, TECEDNODETEXT3DTHICK, l) == 0)
			{
				*thick3d = myatoi(&str[l]);
				continue;
			}
			continue;
		}

		if (ni->proto == art_boxprim || ni->proto == art_filledboxprim)
		{
			/* see if this box is just 1x1 */
			if (ni->highx-ni->lowx != 2000 || ni->highy-ni->lowy != 2000) continue;
			if (patterncount == 0)
			{
				lowx = ni->lowx;   highx = ni->highx;
				lowy = ni->lowy;   highy = ni->highy;
			} else
			{
				if (ni->lowx < lowx) lowx = ni->lowx;
				if (ni->highx > highx) highx = ni->highx;
				if (ni->lowy < lowy) lowy = ni->lowy;
				if (ni->highy > highy) highy = ni->highy;
			}
			patterncount++;
		}
	}

	if (patterncount != 16*8)
	{
		ttyputerr(_("Incorrect number of pattern boxes in %s (has %d, not %d)"),
			describenodeproto(np), patterncount, 16*8);
		return(1);
	}

	/* construct the pattern */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
	{
		if (ni->highx-ni->lowx != 2000 || ni->highy-ni->lowy != 2000) continue;
		if (ni->proto != art_filledboxprim) continue;
		var = getvalkey((INTBIG)ni, VNODEINST, VSHORT|VISARRAY, art_patternkey);
		if (var != NOVARIABLE)
		{
			for(i=0; i<8; i++) if (((INTSML *)var->addr)[i] != 0) break;
			if (i >= 8) continue;
		}
		x = (ni->lowx - lowx) / 2000;
		y = (highy - ni->highy) / 2000;
		desc->raster[y] |= (1 << (15-x));
	}
	return(0);
}

/*
 * routine to parse the layer function string "str" and return the
 * actual function codes
 */
INTSML us_teceditparsefun(char *str)
{
	REGISTER INTBIG func, save, i;
	REGISTER char *pt;

	func = 0;
	for(;;)
	{
		/* find the next layer function name */
		pt = str;
		while (*pt != 0 && *pt != ',') pt++;

		/* parse the name */
		save = *pt;
		*pt = 0;
		for(i=0; us_teclayer_functions[i].name != 0; i++)
			if (namesame(str, us_teclayer_functions[i].name) == 0) break;
		*pt = (char)save;
		if (us_teclayer_functions[i].name == 0)
		{
			ttyputerr(_("Unknown layer function: %s"), str);
			return(0);
		}

		/* mix in the layer function */
		if (us_teclayer_functions[i].value <= LFTYPE)
		{
			if (func != 0)
			{
				ttyputerr(_("Cannot be both function %s and %s"),
					us_teclayer_functions[func&LFTYPE].name, us_teclayer_functions[i].name);
				func = 0;
			}
			func = us_teclayer_functions[i].value;
		} else func |= us_teclayer_functions[i].value;

		/* advance to the next layer function name */
		if (*pt == 0) break;
		str = pt + 1;
	}
	return((INTSML)func);
}

/*
 * routine to return the option index of node "ni"
 */
INTSML us_tecedgetoption(NODEINST *ni)
{
	REGISTER VARIABLE *var, *var2;
	REGISTER NODEPROTO *np;

	/* port objects are readily identifiable */
	if (ni->proto == gen_portprim) return(PORTOBJ);

	/* center objects are also readily identifiable */
	if (ni->proto == gen_facetcenterprim) return(CENTEROBJ);

	var = getval((INTBIG)ni, VNODEINST, VINTEGER, "EDTEC_option");
	if (var == NOVARIABLE) return(-1);
	if (var->addr == LAYERPATCH)
	{
		/* may be a highlight object */
		var2 = getval((INTBIG)ni, VNODEINST, VNODEPROTO, "EDTEC_layer");
		if (var2 != NOVARIABLE)
		{
			np = (NODEPROTO *)var2->addr;
			if (np == NONODEPROTO) return(HIGHLIGHTOBJ);
		}
	}
	return((INTSML)var->addr);
}

/******************** SUPPORT FOR "usredtecp.c" ROUTINES ********************/

/*
 * routine to return the actual bounding box of layer node "ni" in the
 * reference variables "lx", "hx", "ly", and "hy"
 */
void us_tecedgetbbox(NODEINST *ni, INTBIG *lx, INTBIG *hx, INTBIG *ly, INTBIG *hy)
{
	*lx = ni->geom->lowx;
	*hx = ni->geom->highx;
	*ly = ni->geom->lowy;
	*hy = ni->geom->highy;
	if (ni->proto != gen_portprim) return;
	*lx += 4000;   *hx -= 4000;
	*ly += 4000;   *hy -= 4000;
}

void us_tecedpointout(NODEINST *ni, NODEPROTO *np)
{
	REGISTER WINDOWPART *w;
	char *newpar[2];

	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
		if (w->curnodeproto == np) break;
	if (w == NOWINDOWPART)
	{
		newpar[0] = describenodeproto(np);
		us_editfacet(1, newpar);
	}
	if (ni != NONODEINST)
	{
		us_clearhighlightcount();
		(void)askaid(us_aid, "show-object", (INTBIG)ni->geom);
	}
}

/*
 * routine to swap entries "p1" and "p2" of the port list in "tlist"
 */
void us_tecedswapports(INTSML *p1, INTSML *p2, TECH_NODES *tlist)
{
	REGISTER INTBIG temp, *templ;
	REGISTER char *tempc;

	templ = tlist->portlist[*p1].portarcs;
	tlist->portlist[*p1].portarcs = tlist->portlist[*p2].portarcs;
	tlist->portlist[*p2].portarcs = templ;

	tempc = tlist->portlist[*p1].protoname;
	tlist->portlist[*p1].protoname = tlist->portlist[*p2].protoname;
	tlist->portlist[*p2].protoname = tempc;

	temp = tlist->portlist[*p1].initialbits;
	tlist->portlist[*p1].initialbits = tlist->portlist[*p2].initialbits;
	tlist->portlist[*p2].initialbits = temp;

	temp = tlist->portlist[*p1].lowxmul;
	tlist->portlist[*p1].lowxmul = tlist->portlist[*p2].lowxmul;
	tlist->portlist[*p2].lowxmul = (INTSML)temp;
	temp = tlist->portlist[*p1].lowxsum;
	tlist->portlist[*p1].lowxsum = tlist->portlist[*p2].lowxsum;
	tlist->portlist[*p2].lowxsum = (INTSML)temp;

	temp = tlist->portlist[*p1].lowymul;
	tlist->portlist[*p1].lowymul = tlist->portlist[*p2].lowymul;
	tlist->portlist[*p2].lowymul = (INTSML)temp;
	temp = tlist->portlist[*p1].lowysum;
	tlist->portlist[*p1].lowysum = tlist->portlist[*p2].lowysum;
	tlist->portlist[*p2].lowysum = (INTSML)temp;

	temp = tlist->portlist[*p1].highxmul;
	tlist->portlist[*p1].highxmul = tlist->portlist[*p2].highxmul;
	tlist->portlist[*p2].highxmul = (INTSML)temp;
	temp = tlist->portlist[*p1].highxsum;
	tlist->portlist[*p1].highxsum = tlist->portlist[*p2].highxsum;
	tlist->portlist[*p2].highxsum = (INTSML)temp;

	temp = tlist->portlist[*p1].highymul;
	tlist->portlist[*p1].highymul = tlist->portlist[*p2].highymul;
	tlist->portlist[*p2].highymul = (INTSML)temp;
	temp = tlist->portlist[*p1].highysum;
	tlist->portlist[*p1].highysum = tlist->portlist[*p2].highysum;
	tlist->portlist[*p2].highysum = (INTSML)temp;

	/* finally, swap the actual identifiers */
	temp = *p1;   *p1 = *p2;   *p2 = (INTSML)temp;
}

char *us_tecedsamplename(NODEPROTO *layernp)
{
	if (layernp == gen_portprim) return("PORT");
	if (layernp == gen_facetcenterprim) return("GRAB");
	if (layernp == NONODEPROTO) return("HIGHLIGHT");
	return(&layernp->cell->cellname[6]);
}
