/*
 * Electric(tm) VLSI Design System
 *
 * File: usrcomcd.c
 * User interface aid: command handler for C through D
 * 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 "usr.h"
#include "usrtrack.h"
#include "database.h"
#include "tecgen.h"
#include "tecart.h"
#include "tecschem.h"
#include "efunction.h"
#include "usrdiacom.h"
#include <math.h>

#define	MAXLINE	   200		/* max characters on color map input line */
struct {
	INTSML bits;
	INTSML set;
} us_overlappables[] =
{
	{LAYERT1, 0},
	{LAYERT2, 0},
	{LAYERT3, 0},
	{LAYERT4, 0},
	{LAYERT5, 0},
	{0, 0}
};

void us_color(INTSML count, char *par[])
{
	REGISTER INTSML i, j, k, l, high, totalcolor, max, red, green, blue,
		newmax, style, newraster;
	char line[MAXLINE], *layerlabel[5], *layerabbrev[5], *filename, *truename;
	INTBIG redt[256], greent[256], bluet[256], rr, rg, rb;
	float hue, sat, inten, amt;
	REGISTER VARIABLE *varred, *vargreen, *varblue;
	REGISTER char *pp, *orig, *s, *la;
	GRAPHICS *desc;
	REGISTER FILE *f;
	extern COMCOMP us_colorp, us_colorentryp, us_colorreadp, us_colorwritep;

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

	if (namesamen(pp, "black-background", l) == 0 && l >= 1)
	{
		us_getcolormap(el_curtech, COLORSBLACK, 1);
		ttyputverbose(_("Black-background colormap loaded"));
		return;
	}

	if (namesamen(pp, "white-background", l) == 0 && l >= 1)
	{
		us_getcolormap(el_curtech, COLORSWHITE, 1);
		ttyputverbose(_("White-background colormap loaded"));
		return;
	}

	if (namesamen(pp, "default", l) == 0 && l >= 1)
	{
		us_getcolormap(el_curtech, COLORSDEFAULT, 1);
		ttyputverbose(_("Default colormap loaded"));
		return;
	}

	if (namesamen(pp, "highlight", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;
		if (count < 2)
		{
			ttyputusage("color highlight LAYERLETTERS [AMOUNT]");
			return;
		}
		amt = 0.2f;
		if (count >= 3)
		{
			amt = (float)atofr(par[2]);
			amt /= WHOLE;
		}
		if (amt < 0.0 || amt > 1.0)
		{
			us_abortcommand(_("Highlight amount must be from 0 to 1"));
			return;
		}

		/* check the list of letters */
		for(k=0; us_overlappables[k].bits != 0; k++)
			us_overlappables[k].set = 0;
		for(i=0; par[1][i] != 0 && par[1][i] != '('; i++)
		{
			for(j=0; j<el_curtech->layercount; j++)
			{
				la = us_layerletters(el_curtech, j);
				for(k=0; la[k] != 0; k++) if (par[1][i] == la[k]) break;
				if (la[k] != 0) break;
			}
			if (j >= el_curtech->layercount)
			{
				us_abortcommand(_("Unknown layer letter: %c"), par[1][i]);
				return;
			}
			for(k=0; us_overlappables[k].bits != 0; k++)
				if (el_curtech->layers[j]->bits == us_overlappables[k].bits) break;
			if (us_overlappables[k].bits == 0)
			{
				us_abortcommand(_("Layer %s(%c) is not overlappable"), layername(el_curtech, j), par[1][i]);
				return;
			}
			us_overlappables[k].set = 1;
		}

		/* get the color map */
		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)
		{
			ttyputerr(_("Cannot get current color map"));
			return;
		}

		/* copy the color arrays from the variables */
		for(i=0; i<256; i++)
		{
			redt[i] = ((INTBIG *)varred->addr)[i];
			greent[i] = ((INTBIG *)vargreen->addr)[i];
			bluet[i] = ((INTBIG *)varblue->addr)[i];
		}

		/* adjust the color arrays */
		for(i=1; i<256; i++)
		{
			/* ignore if nonoverlappable, grid, or highlight */
			if ((i&(LAYERG|LAYERH|LAYEROE)) != 0) continue;

			/* skip if it is one of the highlighted layers */
			for(k=0; us_overlappables[k].bits != 0; k++)
				if ((us_overlappables[k].bits&i) != 0 && us_overlappables[k].set != 0) break;
			if (us_overlappables[k].bits != 0) continue;

			/* dim the layer */
			us_rgbtohsv((INTSML)redt[i], (INTSML)greent[i], (INTSML)bluet[i], &hue, &sat, &inten);
			sat *= amt;
			us_hsvtorgb(hue, sat, inten, &rr, &rg, &rb);
			redt[i] = rr;   greent[i] = rg;   bluet[i] = rb;
		}

		/* set the new color map */
		startobjectchange((INTBIG)us_aid, VAID);
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_red, (INTBIG)redt,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_green, (INTBIG)greent,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_blue, (INTBIG)bluet,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		endobjectchange((INTBIG)us_aid, VAID);
		return;
	}

	if ((namesamen(pp, "entry", l) == 0 ||
		namesamen(pp, "pattern", l) == 0) && l >= 1)
	{
		/* force pattern references on black&white display */
		style = *pp;
		if (us_needwindow()) return;

		/* get the entry number */
		if (count < 2)
		{
			count = ttygetparam(_("Entry: "), &us_colorentryp, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}

		if (style == 'p')
		{
			/* pattern specification */
			j = el_curtech->layercount;
			totalcolor = us_getcolormapentry(par[1], 1);
		} else
		{
			/* color map entry specification */
			j = el_maplength;
			totalcolor = us_getcolormapentry(par[1], 0);
		}
		if (totalcolor < 0) return;
		if (totalcolor >= j)
		{
			us_abortcommand(_("Entry must be from 0 to %d"), j-1);
			return;
		}

		/* get color arrays */
		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)
		{
			ttyputerr(_("Cannot get current color map"));
			return;
		}

		/* if more values specified, get new entry */
		if (count >= 3)
		{
			if (style == 'p')
			{
				/* get a stipple pattern for this layer */
				desc = el_curtech->layers[totalcolor];
				pp = par[2];
				for(j=0; j<8; j++)
				{
					if (*pp == 0) pp = par[2];
					if (*pp == '/')
					{
						pp++;
						continue;
					}
					newraster = (INTSML)myatoi(pp);
					(void)setind((INTBIG)desc, VGRAPHICS, "raster", j, newraster);
					while (*pp != '/' && *pp != 0) pp++;
					if (*pp == '/') pp++;
				}
			} else
			{
				/* get three colors for the map */
				if (count < 5)
				{
					ttyputusage("color entry INDEX [RED GREEN BLUE]");
					return;
				}
				us_setcolorentry(totalcolor, (INTSML)myatoi(par[2]), (INTSML)myatoi(par[3]),
					(INTSML)myatoi(par[4]), 0, 1);
			}
		}

		/* report the current color values */
		if (style == 'p')
		{
			ttyputmsg(_("Entry %d (%s) is bits:"), totalcolor,
				layername(el_curtech, totalcolor));
			desc = el_curtech->layers[totalcolor];
			for(j=0; j<8; j++)
			{
				(void)sprintf(line, "0x%04x |", desc->raster[j]&0xFFFF);
				for(k=0; k<16; k++)
					if ((desc->raster[j] & (1<<(15-k))) != 0)
						(void)strcat(line, "X"); else
							(void)strcat(line, " ");
				(void)strcat(line, "|");
				ttyputmsg("%s", line);
			}
		} else us_printcolorvalue(totalcolor);
		return;
	}

	if (namesamen(pp, "mix", l) == 0 && l >= 1)
	{
		if (us_needwindow()) return;
		for(i=0; i<5; i++) layerlabel[i] = 0;
		j = el_curtech->layercount;
		for(i=0; i<j; i++)
		{
			desc = el_curtech->layers[i];
			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) continue;
			if (layerlabel[k] != 0) continue;
			layerlabel[k] = layername(el_curtech, i);
			layerabbrev[k] = (char *)emalloc(2, el_tempcluster);
			layerabbrev[k][0] = *us_layerletters(el_curtech, i);
			layerabbrev[k][1] = 0;
		}
		for(i=0; i<5; i++) if (layerlabel[i] == 0)
		{
			layerlabel[i] = "UNUSED";
			(void)allocstring(&layerabbrev[i], "?", el_tempcluster);
		}
		us_colormixdlog(layerlabel);
		for(i=0; i<5; i++) efree(layerabbrev[i]);
		return;
	}

	if (namesamen(pp, "read", l) == 0 && l >= 1)
	{
		if (count < 2)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("color/");
			(void)addstringtoinfstr(_("Color Map File"));
			count = ttygetparam(returninfstr(), &us_colorreadp, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		orig = par[1];

		/* get color map from file */
		f = xopen(orig, us_filetypecolormap, el_libdir, &filename);
		if (f == NULL)
		{
			us_abortcommand(_("Cannot find %s"), orig);
			return;
		}
		ttyputmsg(_("Reading %s"), orig);

		/* see if technology name is right */
		(void)xfgets(line, MAXLINE, f);
		if (strncmp(line, "technology=", 11) != 0)
		{
			us_abortcommand(_("Invalid color map file"));
			xclose(f);
			return;
		}
		pp = &line[11];
		if (strcmp(pp, el_curtech->techname) != 0)
			ttyputverbose(_("Warning: color map is for %s technology"), pp);

		max = 0;
		for(;;)
		{
			if (xfgets(line, MAXLINE, f))
			{
				us_abortcommand(_("Map ends prematurely"));
				return;
			}
			(void)strcat(line, "\t\t\t\t");
			pp = line;   red = atoi(pp);
			while (*pp != '\t' && *pp != 0) pp++;  green = atoi(++pp);
			while (*pp != '\t' && *pp != 0) pp++;  blue = atoi(++pp);
			while (*pp != '\t' && *pp != 0) pp++;  newmax = atoi(++pp);
			if (newmax < max || newmax >= 256)
			{
				us_abortcommand(_("Bad map indices: %s"), line);
				xclose(f);
				return;
			}
			for(; max <= newmax; max++)
			{
				redt[max] = red&0377;
				greent[max] = green&0377;
				bluet[max] = blue&0377;
			}
			if (max >= 256) break;
		}

		/* set the color map */
		startobjectchange((INTBIG)us_aid, VAID);
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_red, (INTBIG)redt,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_green, (INTBIG)greent,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		(void)setvalkey((INTBIG)us_aid, VAID, us_colormap_blue, (INTBIG)bluet,
			VINTEGER|VISARRAY|(256<<VLENGTHSH));
		endobjectchange((INTBIG)us_aid, VAID);

		/* now get the bit-map information */
		if (xfgets(line, MAXLINE, f))
		{
			ttyputmsg(_("Warning: no bit-map information in file"));
			return;
		}
		j = atoi(line);
		high = el_curtech->layercount;
		for(i=0; i<j; i++)
		{
			if (xfgets(line, MAXLINE, f))
			{
				ttyputmsg(_("Warning: EOF during bit-map data"));
				break;
			}
			for(k=0; k<high; k++)
			{
				pp = us_layerletters(el_curtech, k);
				for(l=0; pp[l] != 0; l++) if (pp[l] == line[0]) break;
				if (pp[l] != 0)
				{
					desc = el_curtech->layers[k];
					pp = &line[1];
					for(l=0; l<8; l++)
					{
						while (*pp != ' ' && *pp != 0) pp++;
						newraster = (INTSML)myatoi(++pp);
						(void)setind((INTBIG)desc, VGRAPHICS, "raster", l, newraster);
					}
					break;
				}
			}
			if (k >= high) ttyputmsg(_("Warning: no layer '%c' in this technology"), line[0]);
		}
		xclose(f);

		ttyputmsg(_("Color map in %s read"), orig);
		return;
	}

	if (namesamen(pp, "write", l) == 0 && l >= 1)
	{
		if (count < 2)
		{
			(void)initinfstr();
			(void)addstringtoinfstr("color/");
			(void)addstringtoinfstr(_("Color map file name: "));
			count = ttygetparam(returninfstr(), &us_colorwritep, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		orig = par[1];

		/* get color arrays */
		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)
		{
			ttyputerr(_("Cannot get current color map"));
			return;
		}

		/* write color map to file */
		f = xcreate(orig, us_filetypecolormap, _("Color Map File"), &truename);
		if (f == NULL)
		{
			if (truename != 0) us_abortcommand(_("Cannot write %s"), truename);
			return;
		}
		xprintf(f, "technology=%s\n", el_curtech->techname);
		for(i=0; i<256; i++)
			if (i == 255 || ((INTBIG *)varred->addr)[i] != ((INTBIG *)varred->addr)[i+1] ||
				((INTBIG *)vargreen->addr)[i] != ((INTBIG *)vargreen->addr)[i+1] ||
					((INTBIG *)varblue->addr)[i] != ((INTBIG *)varblue->addr)[i+1])
		{
			xprintf(f, "%ld\t%ld\t%ld\t%d\n", ((INTBIG *)varred->addr)[i],
				((INTBIG *)vargreen->addr)[i], ((INTBIG *)varblue->addr)[i], i);
		}

		/* write the bit maps */
		high = el_curtech->layercount;
		xprintf(f, "%d\n", high);
		for(i=0; i<high; i++)
		{
			desc = el_curtech->layers[i];
			s = us_layerletters(el_curtech, i);
			xprintf(f, "%c", s[0]);
			for(j=0; j<8; j++)
				xprintf(f, " 0%o", desc->raster[j] & 0xFFFF);
			xprintf(f, "\n");
		}
		xclose(f);
		ttyputverbose(_("%s written"), truepath(orig));
		return;
	}

	ttyputbadusage("color");
}

void us_commandfile(INTSML count, char *par[])
{
	REGISTER FILE *in;
	REGISTER INTSML verbose;
	REGISTER char *pp;
	char *filename;
	REGISTER MACROPACK *lastmacropack;
	extern COMCOMP us_colorreadp;

	if (count >= 2 && namesamen(par[1], "verbose", strlen(par[1])) == 0) verbose = 1; else
		verbose = 0;

	if (count == 0)
	{
		(void)initinfstr();
		(void)addstringtoinfstr("macro/");
		(void)addstringtoinfstr(_("Command File: "));
		count = ttygetparam(returninfstr(), &us_colorreadp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}
	pp = par[0];

	in = xopen(pp, us_filetypemacro, el_libdir, &filename);
	if (in == NULL)
	{
		us_abortcommand(_("Command file %s not found"), pp);
		return;
	}

	/* create a new macro package entry */
	lastmacropack = us_curmacropack;
	us_curmacropack = us_newmacropack(pp);

	/* read the commands */
	us_docommands(in, verbose, "");
	xclose(in);

	/* now there is no macro package */
	us_curmacropack = lastmacropack;
}

void us_constraint(INTSML count, char *par[])
{
	REGISTER INTSML l, c;
	REGISTER char *pp;

	/* get the name of the constraint solver */
	if (count <= 1)
	{
		ttyputusage("constraint (use | tell) SOLVER [OPTIONS]");
		return;
	}
	for(c=0; el_constraints[c].conname != 0; c++)
		if (namesame(el_constraints[c].conname, par[1]) == 0) break;
	if (el_constraints[c].conname == 0)
	{
		us_abortcommand(_("No constraint solver called %s"), par[1]);
		return;
	}

	l = strlen(pp = par[0]);

	if (namesamen(pp, "tell", l) == 0 && l >= 1)
	{
		(*(el_constraints[c].setmode))((INTSML)(count-2), &par[2]);
		return;
	}

	if (namesamen(pp, "use", l) == 0 && l >= 1)
	{
		if (el_curconstraint == &el_constraints[c])
		{
			ttyputerr(_("Already using that constraint solver"));
			return;
		}
		us_clearhighlightcount();
		(void)setvalkey((INTBIG)us_aid, VAID, us_current_constraint,
			(INTBIG)&el_constraints[c], VCONSTRAINT|VDONTSAVE);
		ttyputmsg(_("Switching to %s"), _(el_constraints[c].condesc));
		return;
	}

	ttyputbadusage("constraint");
}

void us_copyfacet(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np, *onp;
	REGISTER LIBRARY *fromlib, *tolib;
	REGISTER char *pt, *ppt, *lastch;
	extern COMCOMP us_copyfacetp;
	REGISTER PORTPROTO *pp, *rpp, *npp;
	REGISTER NODEINST *ni, *newni;
	REGISTER VIEW *nview;
	REGISTER INTBIG xc, yc, lowx, highx, lowy, highy, i;
	REGISTER INTSML newang, newtran, save, quiet, move;
	XARRAY localtrans, ntrans, trans;
	INTBIG newx, newy;

	/* get name of old facet */
	if (count == 0)
	{
		count = ttygetparam(_("Old facet name: "), &us_copyfacetp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}
	np = getnodeproto(par[0]);
	if (np == NONODEPROTO)
	{
		us_abortcommand(_("Cannot find source facet %s"), par[0]);
		return;
	}
	if (np->primindex != 0)
	{
		us_abortcommand(_("Cannot copy primitives"));
		return;
	}
	fromlib = np->cell->lib;
	tolib = fromlib;

	/* get name of new facet */
	quiet = move = 0;
	if (count <= 1) pt = np->cell->cellname; else
	{
		pt = par[1];
		for(ppt = pt; *ppt != 0; ppt++) if (*ppt == ':') break;
		if (*ppt == ':')
		{
			*ppt++ = 0;
			tolib = getlibrary(pt);
			if (tolib == NOLIBRARY)
			{
				us_abortcommand(_("Unknown destination library: %s"), pt);
				return;
			}
			pt = ppt;
		}
		if (namesame(pt, "{ic}") == 0 || namesame(pt, "{sk}") == 0)
		{
			(void)initinfstr();
			(void)addstringtoinfstr(np->cell->cellname);
			(void)addstringtoinfstr(par[1]);
			pt = returninfstr();
		}
		while (count > 2)
		{
			i = strlen(ppt = par[2]);
			if (namesamen(ppt, "quiet", i) == 0) quiet = 1; else
			if (namesamen(ppt, "move", i) == 0) move = 1; else
			{
				ttyputbadusage("copyfacet");
				return;
			}
			count--;
			par++;
		}
	}

	/* see if icon generation is requested */
	i = strlen(pt);
	if (count > 1 && i > 4 && namesame(&pt[i-4], "{ic}") == 0 && np->cellview != el_iconview)
	{
		/* cannot iconize text-only views */
		if ((np->cellview->viewstate&TEXTVIEW) != 0)
		{
			us_abortcommand(_("Cannot iconize textual views: only layout and schematic"));
			return;
		}

		/* cannot cross libraries when iconizing */
		if (fromlib != tolib)
		{
			us_abortcommand(_("Can only generate icons within the same library"));
			return;
		}

		/* generate the icon */
		onp = us_makeiconfacet(np->firstportproto, np->cell->cellname, pt, np->tech, tolib);
		if (onp == NONODEPROTO) return;
		if (quiet == 0)
			ttyputmsg(_("Facet %s created with an iconic representation of %s"),
				describenodeproto(onp), describenodeproto(np));
		return;
	}

	/* see if skeletonization is requested */
	i = strlen(pt);
	if (count > 1 && i > 4 && namesame(&pt[i-4], "{sk}") == 0 && np->cellview != el_skeletonview)
	{
		/* cannot skeletonize text-only views */
		if ((np->cellview->viewstate&TEXTVIEW) != 0)
		{
			us_abortcommand(_("Cannot skeletonize textual views: only layout"));
			return;
		}

		/* warn if skeletonizing nonlayout views */
		if (np->cellview != el_unknownview && np->cellview != el_layoutview)
			ttyputmsg(_("Warning: skeletonization only makes sense for layout facets, not %s"),
				np->cellview->viewname);

		onp = np;
		np = newnodeproto(pt, tolib);
		if (np == NONODEPROTO)
		{
			us_abortcommand(_("Cannot create %s"), pt);
			return;
		}

		/* place all exports in the new facet */
		lowx = highx = (onp->lowx + onp->highx) / 2;
		lowy = highy = (onp->lowy + onp->highy) / 2;
		for(pp = onp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* make a transformation matrix for the node that is an export */
			ni = pp->subnodeinst;
			rpp = pp->subportproto;
			newang = ni->rotation;
			newtran = ni->transpose;
			makerot(ni, trans);
			while (ni->proto->primindex == 0)
			{
				maketrans(ni, localtrans);
				transmult(localtrans, trans, ntrans);
				ni = rpp->subnodeinst;
				rpp = rpp->subportproto;
				if (ni->transpose == 0) newang = ni->rotation + newang; else
					newang = ni->rotation + 3600 - newang;
				newtran = (newtran + ni->transpose) & 1;
				makerot(ni, localtrans);
				transmult(localtrans, ntrans, trans);
			}

			/* create this node */
			xc = (ni->lowx + ni->highx) / 2;   yc = (ni->lowy + ni->highy) / 2;
			xform(xc, yc, &newx, &newy, trans);
			newx -= (ni->highx - ni->lowx) / 2;
			newy -= (ni->highy - ni->lowy) / 2;
			newang = newang % 3600;   if (newang < 0) newang += 3600;
			newni = newnodeinst(ni->proto, newx, newx+ni->highx-ni->lowx,
				newy, newy+ni->highy-ni->lowy, newtran, newang, np);
			if (newni == NONODEINST)
			{
				us_abortcommand(_("Cannot create node in this facet"));
				return;
			}
			endobjectchange((INTBIG)newni, VNODEINST);
			lowx = mini(lowx, newx);
			highx = maxi(highx, newx+ni->highx-ni->lowx);
			lowy = mini(lowy, newy);
			highy = maxi(highy, newy+ni->highy-ni->lowy);

			/* export the port from the node */
			npp = newportproto(np, newni, rpp, pp->protoname);
			if (npp == NOPORTPROTO)
			{
				us_abortcommand(_("Could not create port %s"), pp->protoname);
				return;
			}
			npp->userbits = pp->userbits;
			npp->textdescript = pp->textdescript;
			if (copyvars((INTBIG)pp, VPORTPROTO, (INTBIG)npp, VPORTPROTO) != 0)
				return;
		}

		/* copy the facet center node if it exists */
		for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto != gen_facetcenterprim) continue;
			newni = newnodeinst(ni->proto, ni->lowx, ni->highx,
				ni->lowy, ni->highy, ni->transpose, ni->rotation, np);
			if (newni == NONODEINST)
			{
				us_abortcommand(_("Cannot create node in this facet"));
				return;
			}
			endobjectchange((INTBIG)newni, VNODEINST);
			lowx = mini(lowx, ni->lowx);
			highx = maxi(highx, ni->highx);
			lowy = mini(lowy, ni->lowy);
			highy = maxi(highy, ni->highy);
			break;
		}

		/* make sure facet is the same size */
		if (lowx > onp->lowx)
		{
			i = (onp->highy+onp->lowy)/2 - (gen_invispinprim->highy-gen_invispinprim->lowy)/2;
			(void)newnodeinst(gen_invispinprim, onp->lowx, onp->lowx+gen_invispinprim->highx-gen_invispinprim->lowx,
				i, i+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
		}
		if (highx < onp->highx)
		{
			i = (onp->highy+onp->lowy)/2 - (gen_invispinprim->highy-gen_invispinprim->lowy)/2;
			(void)newnodeinst(gen_invispinprim, onp->highx-(gen_invispinprim->highx-gen_invispinprim->lowx), onp->highx,
				i, i+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
		}
		if (lowy > onp->lowy)
		{
			i = (onp->highx+onp->lowx)/2 - (gen_invispinprim->highx-gen_invispinprim->lowx)/2;
			(void)newnodeinst(gen_invispinprim, i, i+gen_invispinprim->highx-gen_invispinprim->lowx,
				onp->lowy, onp->lowy+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, np);
		}
		if (highy < onp->highy)
		{
			i = (onp->highx+onp->lowx)/2 - (gen_invispinprim->highx-gen_invispinprim->lowx)/2;
			(void)newnodeinst(gen_invispinprim, i, i+gen_invispinprim->highx-gen_invispinprim->lowx,
				onp->highy-(gen_invispinprim->highy-gen_invispinprim->lowy), onp->highy, 0, 0,np);
		}

		/* place the actual origin of the facet inside */
		(void)initinfstr();
		(void)addstringtoinfstr(onp->cell->lib->libfile);
		(void)addtoinfstr(':');
		(void)addstringtoinfstr(nldescribenodeproto(onp));
		(void)setval((INTBIG)np, VNODEPROTO, "FACET_original_facet",
			(INTBIG)returninfstr(), VSTRING);

		if (quiet == 0)
			ttyputmsg(_("Facet %s created with a skeletal representation of %s"),
				describenodeproto(np), describenodeproto(onp));
		return;
	}

	/* if going within the same library, let the database do it */
	if (fromlib == tolib)
	{
		onp = copynodeproto(np, tolib, pt);
		if (onp == NONODEPROTO)
		{
			ttyputerr(_("Error copying facet"));
			return;
		}
		if (onp->cell != np->cell || onp->cellview != np->cellview)
		{
			if (quiet == 0)
				ttyputmsg(_("Facet %s copied to %s"), describenodeproto(np),
					describenodeproto(onp));
		} else
		{
			if (quiet == 0)
				ttyputmsg(_("New version of facet %s created, old is %s"),
					describenodeproto(onp), describenodeproto(np));

			/* set current nodeproto to itself to redisplay its name */
			if (np == getcurfacet())
				(void)setval((INTBIG)el_curlib, VLIBRARY, "curnodeproto", (INTBIG)np, VNODEPROTO);

			/* update display of any unexpanded old versions */
			for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
			{
				if ((ni->userbits&NEXPAND) != 0) continue;
				startobjectchange((INTBIG)ni, VNODEINST);
				endobjectchange((INTBIG)ni, VNODEINST);
			}
		}
		return;
	}

	/* get the new view type */
	nview = np->cellview;
	for(ppt = pt; *ppt != 0; ppt++) if (*ppt == '{') break;
	if (*ppt == '{')
	{
		lastch = &pt[strlen(pt)-1];
		if (*lastch != '}')
		{
			us_abortcommand(_("View name '%s'must end with a '}'"), &ppt[1]);
			return;
		}
		*lastch = 0;
		nview = getview(&ppt[1]);
		*lastch = '}';
		if (nview == NOVIEW)
		{
			us_abortcommand(_("Unknown view abbreviation: %s"), ppt);
			return;
		}
	}

	save = *ppt;
	*ppt = 0;
	onp = us_copyrecursively(np, pt, tolib, nview, 1, move, "");
	*ppt = (char)save;
}

void us_create(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np, *curfacet;
	REGISTER NODEINST *ni, *newno, *ni2;
	REGISTER PORTPROTO *pp, *ppt, *fpt, *pp1, *pp2;
	PORTPROTO *fromport, *toport;
	REGISTER ARCPROTO *ap;
	REGISTER ARCINST *ai, *newai;
	REGISTER TECHNOLOGY *tech;
	NODEINST *ani[2];
	REGISTER INTSML ang, l, remainhighlighted, ishor, isver, getcontents,
		pangle, join, waitfordown, angledcreate;
	REGISTER INTBIG bestdist, bestdist1, bestdist2, wid, truewid, i, j, k,
		bits1, bits2, dist, lx, hx, ly, hy, overcount;
	INTBIG px, py, nx, ny, cx, cy, x, y, xp[2], yp[2], xcur, ycur, otheralign, pxs, pys,
		cornerx[4], cornery[4], overx[2], overy[2];
	PORTPROTO *app[2];
	REGISTER VARIABLE *var;
	static POLYGON *poly = NOPOLYGON;
	ARCINST *alt1, *alt2;
	extern COMCOMP us_createxp, us_createyp;
	char *oldarcname;
	HIGHLIGHT newhigh;
	REGISTER GEOM *foundgeom, *geom;
	GEOM *fromgeom, *togeom;

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

	/* make sure there is a facet being edited */
	if (us_needwindow() != 0) return;
	if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW)
	{
		us_abortcommand(_("Can only place components in graphical editing windows"));
		return;
	}
	curfacet = us_needfacet();
	if (curfacet == NONODEPROTO) return;

	remainhighlighted = getcontents = waitfordown = angledcreate = join = 0;
	while (count > 0)
	{
		l = strlen(par[0]);
		if (namesamen(par[0], "remain-highlighted", l) == 0 && l >= 1)
		{
			remainhighlighted++;
			count--;
			par++;
			continue;
		}
		if (namesamen(par[0], "contents", l) == 0 && l >= 1)
		{
			getcontents++;
			count--;
			par++;
			continue;
		}
		if (namesamen(par[0], "wait-for-down", l) == 0 && l >= 1)
		{
			waitfordown++;
			count--;
			par++;
			continue;
		}
		if ((namesamen(par[0], "angle", l) == 0 ||
			namesamen(par[0], "join-angle", l) == 0) && l >= 1)
		{
			if (namesamen(par[0], "join-angle", l) == 0) join++;
			angledcreate++;
			count--;
			par++;
			if (count >= 1)
			{
				ang = atofr(par[0])*10/WHOLE;
				count--;
				par++;
			} else ang = (INTSML)(((us_curarcproto->userbits&AANGLEINC) >> AANGLEINCSH) * 10);
		}
		break;
	}

	/* create a node or arc */
	if (count == 0)
	{
		/* see if two objects are selected (and arc should connect them) */
		if (us_gettwoobjects(&fromgeom, &fromport, &togeom, &toport) == 0)
		{
			/* two objects highlighted: create an arc between them */
			if (us_demandxy(&xcur, &ycur)) return;
			gridalign(&xcur, &ycur, us_alignment);
			if (geomparent(fromgeom) != geomparent(togeom))
			{
				us_abortcommand(_("Cannot run an arc between different facets"));
				return;
			}

			if (us_canedit(curfacet, NONODEPROTO, 1) != 0) return;

			/* run an arc from "fromgeom" to "togeom" */
			if (remainhighlighted != 0) us_pushhighlight();
			us_clearhighlightcount();
			if (angledcreate == 0) ang = 900;
			ai = aconnect(fromgeom, fromport, togeom, toport, us_curarcproto,
				xcur, ycur, &alt1, &alt2, ang, 0);
			if (remainhighlighted != 0) (void)us_pophighlight(0);
			if (ai == NOARCINST) return;

			/* unless indicated, leave only one object highlighted */
			if (remainhighlighted == 0)
			{
				/* if an arc was highlighted and it got broken, fix the highlight */
				geom = togeom;
				if (geom->entrytype == OBJARCINST && (geom->entryaddr.ai->userbits&DEADA) != 0)
				{
					geom = fromgeom;
					if (geom->entrytype == OBJARCINST && (geom->entryaddr.ai->userbits&DEADA) != 0) return;
				}

				newhigh.status = HIGHFROM;
				newhigh.facet = geomparent(geom);
				newhigh.fromgeom = geom;
				newhigh.fromport = NOPORTPROTO;
				(void)us_addhighlight(&newhigh);
			}
			return;
		}

		/* if no "angle" specified, simply create a node */
		if (angledcreate == 0)
		{
			/* determine the node type being created */
			np = us_nodetocreate(getcontents, curfacet);
			if (np == NONODEPROTO) return;

			/* adjust the cursor position if selecting interactively */
			us_clearhighlightcount();
			if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(np);

			/* get placement angle */
			pangle = us_getplacementangle(np);

			if ((us_aid->aidstate&INTERACTIVE) != 0)
			{
				us_createinit(0, 0, np, pangle, 0);
				trackcursor(waitfordown, us_ignoreup, us_createbegin, us_dragdown,
					us_stoponchar, us_dragup, TRACKDRAGGING);
				if (el_pleasestop != 0) return;
			}
			if (us_demandxy(&xcur, &ycur)) return;
			gridalign(&xcur, &ycur, us_alignment);

			/* get current facet (in case interactive placement changed the window) */
			curfacet = us_needfacet();

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

			/* create and display the nodeinst */
			corneroffset(NONODEINST, np, (INTSML)(pangle%3600), (INTSML)(pangle/3600), &cx, &cy,
				(INTSML)((us_useroptions&CENTEREDPRIMITIVES) != 0 ? 1 : 0));
			xcur -= cx;   ycur -= cy;
			defaultnodesize(np, &pxs, &pys);
			newno = newnodeinst(np, xcur, xcur+pxs, ycur, ycur+pys,
				(INTSML)(pangle/3600), (INTSML)(pangle%3600), curfacet);
			if (newno == NONODEINST)
			{
				us_abortcommand(_("Cannot create node"));
				return;
			}
			if ((np->userbits&WANTNEXPAND) != 0) newno->userbits |= NEXPAND;
			if (np == gen_facetcenterprim) newno->userbits |= HARDSELECTN;

			/* copy "inheritable" attributes */
			us_inheritattributes(newno);
			endobjectchange((INTBIG)newno, VNODEINST);

			newhigh.status = HIGHFROM;
			newhigh.facet = newno->parent;
			newhigh.fromgeom = newno->geom;
			newhigh.fromport = NOPORTPROTO;
			newhigh.frompoint = 0;
			(void)us_addhighlight(&newhigh);
			return;
		}

		/* "create angle": get cursor co-ordinates */
		if (us_demandxy(&xcur, &ycur)) return;
		gridalign(&xcur, &ycur, us_alignment);

		/* get nodeinst from which to draw */
		ni = (NODEINST *)us_getobject(OBJNODEINST, 0);
		if (ni == NONODEINST) return;

		/* ensure that cursor is in proper window */
		if (ni->parent != curfacet)
		{
			us_abortcommand(_("Cursor must be in the same facet as the highlighted node"));
			return;
		}
		if (us_canedit(curfacet, NONODEPROTO, 1) != 0) return;

		/* find the closest port to the cursor */
		var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_highlighted);
		if (var != NOVARIABLE)
			(void)us_makehighlight(((char **)var->addr)[0], &newhigh);
		bestdist1 = bestdist2 = HUGEINT;
		ppt = fpt = NOPORTPROTO;
		for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			if (newhigh.fromport != NOPORTPROTO && newhigh.fromport != pp) continue;
			shapeportpoly(ni, pp, poly, 0);
			k = polydistance(poly, xcur, ycur);

			/* find the closest port to the cursor */
			if (k < bestdist1)
			{
				bestdist1 = k;
				ppt = pp;  /* pp is the closest port to (xcur, ycur) */
			}

			/* find the closest port that can connect with the current arc */
			if (k < bestdist2)
			{
				for (i=0; pp->connects[i] != NOARCPROTO; i++)
				{
					if (pp->connects[i] == us_curarcproto)
					{
						/* can connect */
						bestdist2 = k;
						fpt = pp;
					}
				}
			}
		}

		/* error if there is no port */
		if (ppt == NOPORTPROTO)
		{
			us_abortcommand(_("Cannot run wires out of this node"));
			return;
		}

		/*
		 * now ppt is the closest port, and fpt is the closest port that can
		 * connect with the current arc.
		 */
		if (fpt == NOPORTPROTO)
		{
			/* no arcproto of correct type: first reset all arcprotos */
			for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
				for(ap = tech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
					ap->userbits &= ~CANCONNECT;

			/* now set all arcs that can connect this node's closest port */
			for(i=0; ppt->connects[i] != NOARCPROTO; i++) ppt->connects[i]->userbits |= CANCONNECT;

			/* see if current arcproto is acceptable */
			if ((us_curarcproto->userbits&CANCONNECT) == 0)
			{
				/* search this technology for another arcproto */
				for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
				{
					if ((ap->userbits&CANCONNECT) == 0) continue;
					if (getpinproto(ap) == NONODEPROTO) return;
					us_setarcproto(ap, 0);
					break;
				}

				/* if nothing in this technology can connect, see what can */
				if (ap == NOARCPROTO && ppt->connects[0] != NOARCPROTO)
				{
					ap = ppt->connects[0];
					if (getpinproto(ap) == NONODEPROTO) return;
					us_setarcproto(ap, 0);
				}

				/* error if no acceptable arcprotos */
				if (ap == NOARCPROTO)
				{
					us_abortcommand(_("No ports on this node"));
					return;
				}
			}
		} else ppt = fpt;
		newhigh.fromport = ppt;

		/* turn off highlighting */
		us_clearhighlightcount();

		/* specify precise location for new port */
		poly->xv[0] = xcur;   poly->yv[0] = ycur;   poly->count = 1;
		shapeportpoly(ni, ppt, poly, 1);

		/* determine pin for making the connection */
		np = getpinproto(us_curarcproto);
		if (np == NONODEPROTO) return;

		/* get placement angle */
		pangle = us_getplacementangle(np);

		/* determine location of pin */
		defaultnodesize(np, &pxs, &pys);
		px = pxs / 2;   py = pys / 2;
		corneroffset(NONODEINST, np, (INTSML)(pangle%3600), (INTSML)(pangle/3600), &cx, &cy,
			(INTSML)((us_useroptions&CENTEREDPRIMITIVES) != 0 ? 1 : 0));

		/* adjust the cursor position if selecting interactively */
		switch (poly->style)
		{
			case FILLED:
			case FILLEDRECT:
			case CROSSED:
			case CROSS:
			case BIGCROSS:
			case TEXTCENT:
			case TEXTTOP:
			case TEXTBOT:
			case TEXTLEFT:
			case TEXTRIGHT:
			case TEXTTOPLEFT:
			case TEXTBOTLEFT:
			case TEXTTOPRIGHT:
			case TEXTBOTRIGHT:
			case TEXTBOX:
			case DISC:
				getcenter(poly, &x, &y);
				break;

			default:
				x = xcur;   y = ycur;
				closestpoint(poly, &x, &y);
				break;
		}
		if ((us_aid->aidstate&INTERACTIVE) != 0)
		{
			us_createinit(x-px+cx, y-py+cy, np, ang, join);
			trackcursor(waitfordown, us_ignoreup, us_createabegin, us_createadown,
				us_stoponchar, us_createaup, TRACKDRAGGING);
			if (el_pleasestop != 0) return;
			if (us_demandxy(&xcur, &ycur) != 0) return;
			gridalign(&xcur, &ycur, us_alignment);
			if (join != 0)
			{
				us_createajoinedobject(&togeom, &toport);
				if (togeom != NOGEOM)
				{
					/* user dragged over another object: connect them */
					fromgeom = ni->geom;
					if (us_getnodeonarcinst(&togeom, &toport, fromgeom,
						ppt, xcur, ycur) == NONODEINST) return;
					if (toport == NOPORTPROTO)
					{
						us_abortcommand(_("Cannot connect to %s: it has no ports"),
							geomname(togeom));
						return;
					}
					us_clearhighlightcount();

					/* change cursor according to port angle */
					lx = mini(fromgeom->lowx, togeom->lowx);
					hx = maxi(fromgeom->highx, togeom->highx);
					ly = mini(fromgeom->lowy, togeom->lowy);
					hy = maxi(fromgeom->highy, togeom->highy);
					dist = maxi(hx-lx, hy-ly);
					overcount = 0;
					if (((ppt->userbits&PORTARANGE) >> PORTARANGESH) != 180)
					{
						k = us_bottomrecurse(fromgeom->entryaddr.ni, ppt) % 3600;
						if (k < 0) k += 3600;
						overx[overcount] = (fromgeom->lowx+fromgeom->highx)/2 + mult(cosine((INTSML)k), dist);
						overy[overcount] = (fromgeom->lowy+fromgeom->highy)/2 + mult(sine((INTSML)k), dist);
						overcount++;
					}
					if (((toport->userbits&PORTARANGE) >> PORTARANGESH) != 180)
					{
						k = us_bottomrecurse(togeom->entryaddr.ni, toport) % 3600;
						if (k < 0) k += 3600;
						overx[overcount] = (togeom->lowx+togeom->highx)/2 + mult(cosine((INTSML)k), dist);
						overy[overcount] = (togeom->lowy+togeom->highy)/2 + mult(sine((INTSML)k), dist);
						overcount++;
					}
					if (overcount == 2)
					{
						pxs = (overx[0] + overx[1]) / 2;
						pys = (overy[0] + overy[1]) / 2;
					} else
					{
						pxs = (fromgeom->lowx + fromgeom->highx) / 2;
						pys = (togeom->lowy + togeom->highy) / 2;
						if (overcount > 0)
						{
							pxs = overx[0];   pys = overy[0];
						}
						cornerx[0] = lx-1;   cornery[0] = ly-1;
						cornerx[1] = lx-1;   cornery[1] = hy+1;
						cornerx[2] = hx+1;   cornery[2] = hy+1;
						cornerx[3] = hx+1;   cornery[3] = ly-1;
						for(i=0; i<4; i++)
						{
							dist = abs(cornerx[i]-pxs) + abs(cornery[i]-pys);
							if (i == 0 || dist < bestdist)
							{
								bestdist = dist;
								j = i;
							}
						}
						pxs = cornerx[j]*2 - (lx+hx)/2;
						pys = cornery[j]*2 - (ly+hy)/2;
					}

					ai = aconnect(fromgeom, ppt, togeom, toport, us_curarcproto,
						pxs, pys, &alt1, &alt2, ang, 0);
					if (ai == NOARCINST) return;

					/* if an arc was highlighted and it got broken, fix the highlight */
					if (togeom->entrytype == OBJARCINST && (togeom->entryaddr.ai->userbits&DEADA) != 0)
					{
						togeom = fromgeom;
						if (togeom->entrytype == OBJARCINST && (togeom->entryaddr.ai->userbits&DEADA) != 0) return;
					}

					newhigh.status = HIGHFROM;
					newhigh.facet = geomparent(togeom);
					newhigh.fromgeom = togeom;
					newhigh.fromport = NOPORTPROTO;
					(void)us_addhighlight(&newhigh);
					return;
				}
			}
		}

		/* determine the proper point on the polygon to draw radials */
		us_getslide(ang, x-px+cx, y-py+cy, xcur, ycur, &nx, &ny);

		/* adjust for the box corner */
		nx -= cx;
		ny -= cy;

		/* find the object under the cursor */
		if (join == 0) foundgeom = NOGEOM; else
			foundgeom = us_getclosest(nx+px, ny+py, ni->parent);
		if (foundgeom != NOGEOM)
		{
			/* found another object: connect to it */
			fpt = ppt = NOPORTPROTO;
			bestdist = bestdist2 = HUGEINT;
			if (foundgeom->entrytype == OBJNODEINST)
			{
				/* find the closest port */
				ni = foundgeom->entryaddr.ni;
				for(pp = ni->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					shapeportpoly(ni, pp, poly, 0);
					dist = polydistance(poly, xcur, ycur);

					/* find the closest port to the cursor */
					if (dist < bestdist)
					{
						bestdist = dist;
						fpt = pp;
					}

					/* find the closest port that can connect with the current arc */
					if (dist < bestdist2)
					{
						for (i=0; pp->connects[i] != NOARCPROTO; i++)
						{
							if (pp->connects[i] == us_curarcproto)
							{
								/* can connect */
								bestdist2 = dist;
								ppt = pp;
							}
						}
					}
				}
				if (fpt != ppt)
				{
					/* the closest port cannot connect, but maybe it can with a different arc */
					for(i=0; fpt->connects[i] != NOARCPROTO; i++)
					{
						if (fpt->connects[i]->tech == gen_tech) continue;
						for(j=0; newhigh.fromport->connects[j] != NOARCPROTO; j++)
						{
							if (fpt->connects[i] == newhigh.fromport->connects[j]) break;
						}
						if (newhigh.fromport->connects[j] != NOARCPROTO) break;
					}
					if (fpt->connects[i] != NOARCPROTO)
					{
						us_setarcproto(fpt->connects[i], 0);
						ppt = fpt;
					}
				}
				if (ppt != NOPORTPROTO) fpt = ppt;
			}
			(void)aconnect(newhigh.fromgeom, newhigh.fromport, foundgeom,
				fpt, us_curarcproto, xcur, ycur, &alt1, &alt2, ang, 1);

			/* if an arc will be highlighted and it broke, fix the highlight */
			if (foundgeom->entrytype == OBJARCINST &&
				(foundgeom->entryaddr.ai->userbits&DEADA) != 0) return;

			/* highlight the found object */
			newhigh.status = HIGHFROM;
			newhigh.facet = ni->parent;
			newhigh.fromgeom = foundgeom;
			newhigh.fromport = fpt;
			newhigh.frompoint = 0;
			(void)us_addhighlight(&newhigh);
			return;
		}

		/* determine arc width */
		truewid = maxi(defaultarcwidth(us_curarcproto), us_widestarcinst(us_curarcproto, ni, ppt));
		i = us_stretchtonodes(us_curarcproto, truewid, ni, ppt, xcur, ycur);
		if (i > truewid) truewid = i;
		wid = truewid - arcprotowidthoffset(us_curarcproto);

		/* see if arc edge alignment is appropriate */
		if (us_edgealignment != 0 && (x == nx || y == ny))
		{
			if (x != nx) isver = 0; else
			{
				isver = 1;
				ny = us_alignvalue(ny + wid/2, us_edgealignment, &otheralign) - wid/2;
			}
			if (y != ny) ishor = 0; else
			{
				ishor = 1;
				nx = us_alignvalue(nx + wid/2, us_edgealignment, &otheralign) - wid/2;
			}

			shapeportpoly(ni, ppt, poly, 0);
			i = us_alignvalue(x + wid/2, us_edgealignment, &otheralign) - wid/2;
			otheralign -= wid/2;
			if (isinside(i, y, poly) != 0)
			{
				if (isver != 0) nx = i;
				x = i;
			} else if (isinside(otheralign, y, poly) != 0)
			{
				if (isver != 0) nx = otheralign;
				x = otheralign;
			}

			i = us_alignvalue(y + wid/2, us_edgealignment, &otheralign) - wid/2;
			otheralign -= wid/2;
			if (isinside(x, i, poly) != 0)
			{
				if (ishor != 0) ny = i;
				y = i;
			} else if (isinside(x, otheralign, poly) != 0)
			{
				if (ishor != 0) ny = otheralign;
				y = otheralign;
			}
		}

		/* create the pin and the arcinst to it */
		defaultnodesize(np, &pxs, &pys);
		newno = newnodeinst(np, nx, nx+pxs, ny, ny+pys,
			(INTSML)(pangle/3600), (INTSML)(pangle%3600), curfacet);
		if (newno == NONODEINST)
		{
			us_abortcommand(_("Cannot create pin"));
			return;
		}
		nx += px;   ny += py;
		if ((np->userbits&WANTNEXPAND) != 0) newno->userbits |= NEXPAND;
		endobjectchange((INTBIG)newno, VNODEINST);
		if (((ppt->userbits&PORTARANGE) >> PORTARANGESH) == 180)
		{
			ai = us_runarcinst(ni, ppt, x, y, newno,
				newno->proto->firstportproto, nx, ny, us_curarcproto, truewid);
			if (ai != NOARCINST) ttyputmsg(_("Created 1 %s arc"), us_curarcproto->protoname);
		} else
		{
			k = us_bottomrecurse(ni, ppt) % 3600;
			if (k < 0) k += 3600;
			xcur = x + mult(cosine((INTSML)k), ni->highx-ni->lowx);
			ycur = y + mult(sine((INTSML)k), ni->highy-ni->lowy);
			(void)aconnect(ni->geom, ppt, newno->geom, newno->proto->firstportproto,
				us_curarcproto, xcur, ycur, &alt1, &alt2, ang, 1);
		}

		newhigh.status = HIGHFROM;
		newhigh.facet = newno->parent;
		newhigh.fromgeom = newno->geom;
		newhigh.fromport = NOPORTPROTO;
		newhigh.frompoint = 0;
		(void)us_addhighlight(&newhigh);
		return;
	}

	/* handle the absolute placement option */
	if (namesamen(par[0], "to", l) == 0 && l >= 1)
	{
		/* get angle from the nodeinst */
		if (count < 2)
		{
			count = ttygetparam(_("X position: "), &us_createxp, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		nx = atola(par[1]);
		if (count < 3)
		{
			count = ttygetparam(_("Y position: "), &us_createyp, MAXPARS-2, &par[2]) + 2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		ny = atola(par[2]);

		/* disallow copying if lock is on */
		np = us_nodetocreate(1, curfacet);
		if (np == NONODEPROTO) return;

		if (us_canedit(curfacet, np, 1) != 0) return;

		/* get placement angle */
		pangle = us_getplacementangle(np);

		corneroffset(NONODEINST, np, (INTSML)(pangle%3600), (INTSML)(pangle/3600), &cx, &cy,
			(INTSML)((us_useroptions&CENTEREDPRIMITIVES) != 0 ? 1 : 0));
		nx -= cx;   ny -= cy;
		defaultnodesize(np, &pxs, &pys);
		newno = newnodeinst(np, nx, nx+pxs, ny, ny+pys,
			(INTSML)(pangle/3600), (INTSML)(pangle%3600), curfacet);
		if (newno == NONODEINST)
		{
			us_abortcommand(_("Cannot create pin"));
			return;
		}
		if ((np->userbits&WANTNEXPAND) != 0) newno->userbits |= NEXPAND;
		if (np == gen_facetcenterprim) newno->userbits |= HARDSELECTN;
		endobjectchange((INTBIG)newno, VNODEINST);

		us_clearhighlightcount();
		newhigh.status = HIGHFROM;
		newhigh.facet = newno->parent;
		newhigh.fromgeom = newno->geom;
		newhigh.fromport = NOPORTPROTO;
		newhigh.frompoint = 0;
		(void)us_addhighlight(&newhigh);
		return;
	}

	if (namesamen(par[0], "insert", l) == 0 && l >= 1)
	{
		if (us_demandxy(&xcur, &ycur)) return;
		gridalign(&xcur, &ycur, us_alignment);

		/* get the highlighted arc */
		ai = (ARCINST *)us_getobject(OBJARCINST, 0);
		if (ai == NOARCINST) return;
		ap = ai->proto;

		/* get the node to insert */
		np = us_nodetocreate(1, curfacet);
		if (np == NONODEPROTO) return;

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

		/* turn off highlighting */
		us_clearhighlightcount();
		if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(np);

		/* adjust position interactively if possible */
		if ((us_aid->aidstate&INTERACTIVE) != 0)
		{
			us_createinsinit(ai, np);
			trackcursor(waitfordown, us_ignoreup, us_createabegin, us_createinsdown,
				us_stoponchar, us_dragup, TRACKDRAGGING);
			if (el_pleasestop != 0) return;
		}
		if (us_demandxy(&xcur, &ycur)) return;
		gridalign(&xcur, &ycur, us_alignment);

		/* find two ports on this node that connect to the arc */
		pp1 = pp2 = NOPORTPROTO;
		for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
		{
			/* make sure this port can connect to this arc */
			for(i=0; pp->connects[i] != NOARCPROTO; i++)
				if (pp->connects[i] == ap) break;
			if (pp->connects[i] == NOARCPROTO) continue;

			/* save the port */
			if (pp1 == NOPORTPROTO) pp1 = pp; else
				if (pp2 == NOPORTPROTO) pp2 = pp;
		}

		/* make sure there are two ports for connection */
		if (pp1 == NOPORTPROTO)
		{
			us_abortcommand(_("No ports on %s node connect to %s arcs"), describenodeproto(np),
				describearcproto(ap));
			return;
		}
		if (pp2 == NOPORTPROTO) pp2 = pp1;

		/* determine position of the new node */
		ang = (INTSML)(((ai->userbits&AANGLE) >> AANGLESH) * 10);
		(void)intersect(ai->end[0].xpos, ai->end[0].ypos, ang, xcur,
			ycur, (ang+900)%3600, &nx, &ny);
		cx = mini(ai->end[0].xpos, ai->end[1].xpos);
		if (nx < cx) nx = cx;
		cx = maxi(ai->end[0].xpos, ai->end[1].xpos);
		if (nx > cx) nx = cx;
		cy = mini(ai->end[0].ypos, ai->end[1].ypos);
		if (ny < cy) ny = cy;
		cy = maxi(ai->end[0].ypos, ai->end[1].ypos);
		if (ny > cy) ny = cy;
		defaultnodesize(np, &px, &py);
		x = nx - px/2;   y = ny - py/2;

		/* create the new node */
		ni = newnodeinst(np, x,x+px, y,y+py, 0, 0, curfacet);
		if (ni == NONODEINST)
		{
			us_abortcommand(_("Cannot create node %s"), describenodeproto(np));
			return;
		}
		endobjectchange((INTBIG)ni, VNODEINST);
		portposition(ni, pp1, &nx, &ny);
		portposition(ni, pp2, &cx, &cy);

		/* see if any rotation needs to be applied to this node */
		if (pp1 != pp2)
		{
			k = figureangle(nx, ny, cx, cy);
			if (k != ang)
			{
				if ((k+1800)%3600 == ang)
				{
					/* simple 180 degree rotation: reverse the ports */
					pp = pp1;   pp1 = pp2;   pp2 = pp;
				} else
				{
					/* complex rotation: rotate the node */
					ang -= (INTSML)k;   if (ang < 0) ang += 3600;
					startobjectchange((INTBIG)ni, VNODEINST);
					modifynodeinst(ni, 0, 0, 0, 0, ang, 0);
					endobjectchange((INTBIG)ni, VNODEINST);
					portposition(ni, pp1, &nx, &ny);
					portposition(ni, pp2, &cx, &cy);
				}
			}
		}

		/* see if node edge alignment is appropriate */
		if (us_edgealignment != 0 && (ai->end[0].xpos == ai->end[1].xpos ||
			ai->end[0].ypos == ai->end[1].ypos))
		{
			px = us_alignvalue(x, us_edgealignment, &otheralign);
			py = us_alignvalue(y, us_edgealignment, &otheralign);
			if (px != x || py != y)
			{
				/* shift the node and make sure the ports are still valid */
				startobjectchange((INTBIG)ni, VNODEINST);
				modifynodeinst(ni, px-x, py-y, px-x, py-y, 0, 0);
				endobjectchange((INTBIG)ni, VNODEINST);
				(void)shapeportpoly(ni, pp1, poly, 0);
				if (isinside(nx, ny, poly) == 0) getcenter(poly, &nx, &ny);
				(void)shapeportpoly(ni, pp2, poly, 0);
				if (isinside(cx, cy, poly) == 0) getcenter(poly, &cx, &cy);
			}
		}

		/* now save the arc information and delete it */
		for(i=0; i<2; i++)
		{
			xp[i] = ai->end[i].xpos;
			yp[i] = ai->end[i].ypos;
			app[i] = ai->end[i].portarcinst->proto;
			ani[i] = ai->end[i].nodeinst;
		}
		wid = ai->width;
		bits1 = bits2 = ai->userbits;
		if ((bits1&ISNEGATED) != 0)
		{
			if ((bits1&REVERSEEND) == 0) bits2 &= ~ISNEGATED; else
				bits1 &= ~ISNEGATED;
		}
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
		if (var == NOVARIABLE) oldarcname = 0; else
		{
			(void)allocstring(&oldarcname, (char *)var->addr, el_tempcluster);
			(void)askaid(net_aid, "delname", (INTBIG)oldarcname,
				(INTBIG)curfacet);
		}
		startobjectchange((INTBIG)ai, VARCINST);
		(void)killarcinst(ai);

		/* create the two new arcs */
		newai = newarcinst(ap, wid, bits1, ani[0], app[0], xp[0], yp[0], ni, pp1, nx, ny, curfacet);
		if (oldarcname != 0 && newai != NOARCINST)
		{
			(void)askaid(net_aid, "setname", (INTBIG)newai, (INTBIG)oldarcname);
			(void)setvalkey((INTBIG)newai, VARCINST, el_arc_name, (INTBIG)oldarcname, VSTRING|VDISPLAY);
			efree(oldarcname);
		}
		if (newai != NOARCINST) endobjectchange((INTBIG)newai, VARCINST);
		newai = newarcinst(ap, wid, bits2, ni, pp2, cx, cy, ani[1],app[1],xp[1],yp[1], curfacet);
		if (newai != NOARCINST) endobjectchange((INTBIG)newai, VARCINST);

		/* highlight the node */
		newhigh.status = HIGHFROM;
		newhigh.facet = curfacet;
		newhigh.fromgeom = ni->geom;
		newhigh.fromport = NOPORTPROTO;
		newhigh.frompoint = 0;
		(void)us_addhighlight(&newhigh);
		return;
	}

	if (namesamen(par[0], "breakpoint", l) == 0 && l >= 1)
	{
		/* get the highlighted arc */
		ai = (ARCINST *)us_getobject(OBJARCINST, 0);
		if (ai == NOARCINST) return;
		ap = ai->proto;

		/* get the pin to insert */
		np = getpinproto(ap);
		if (np == NONODEPROTO)
		{
			us_abortcommand(_("Cannot determine pin type to insert in arc"));
			return;
		}
		ppt = np->firstportproto;

		if (us_canedit(curfacet, np, 1) != 0) return;

		/* turn off highlighting */
		us_clearhighlightcount();
		if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(np);

		/* adjust position interactively if possible */
		if ((us_aid->aidstate&INTERACTIVE) != 0)
		{
			us_createinsinit(ai, np);
			trackcursor(waitfordown, us_ignoreup, us_createabegin, us_createinsdown,
				us_stoponchar, us_dragup, TRACKDRAGGING);
			if (el_pleasestop != 0) return;
		}
		if (us_demandxy(&xcur, &ycur)) return;
		gridalign(&xcur, &ycur, us_alignment);

		/* determine position of the new pins */
		ang = (INTSML)(((ai->userbits&AANGLE) >> AANGLESH) * 10);
		(void)intersect(ai->end[0].xpos, ai->end[0].ypos, ang, xcur, ycur, (ang+900)%3600, &nx, &ny);
		cx = mini(ai->end[0].xpos, ai->end[1].xpos);
		if (nx < cx) nx = cx;
		cx = maxi(ai->end[0].xpos, ai->end[1].xpos);
		if (nx > cx) nx = cx;
		cy = mini(ai->end[0].ypos, ai->end[1].ypos);
		if (ny < cy) ny = cy;
		cy = maxi(ai->end[0].ypos, ai->end[1].ypos);
		if (ny > cy) ny = cy;
		defaultnodesize(np, &px, &py);
		x = nx - px/2;   y = ny - py/2;

		/* create the break pins */
		ni = newnodeinst(np, x,x+px, y,y+py, 0, 0, curfacet);
		if (ni == NONODEINST)
		{
			us_abortcommand(_("Cannot create pin %s"), describenodeproto(np));
			return;
		}
		endobjectchange((INTBIG)ni, VNODEINST);
		ni2 = newnodeinst(np, x,x+px, y,y+py, 0, 0, curfacet);
		if (ni2 == NONODEINST)
		{
			us_abortcommand(_("Cannot create pin %s"), describenodeproto(np));
			return;
		}
		endobjectchange((INTBIG)ni2, VNODEINST);

		/* get location of connection to these pins */
		portposition(ni, ppt, &nx, &ny);

		/* see if edge alignment is appropriate */
		if (us_edgealignment != 0 && (ai->end[0].xpos == ai->end[1].xpos ||
			ai->end[0].ypos == ai->end[1].ypos))
		{
			px = us_alignvalue(x, us_edgealignment, &otheralign);
			py = us_alignvalue(y, us_edgealignment, &otheralign);
			if (px != x || py != y)
			{
				/* shift the nodes and make sure the ports are still valid */
				startobjectchange((INTBIG)ni, VNODEINST);
				modifynodeinst(ni, px-x, py-y, px-x, py-y, 0, 0);
				endobjectchange((INTBIG)ni, VNODEINST);
				startobjectchange((INTBIG)ni2, VNODEINST);
				modifynodeinst(ni2, px-x, py-y, px-x, py-y, 0, 0);
				endobjectchange((INTBIG)ni2, VNODEINST);
				(void)shapeportpoly(ni, ppt, poly, 0);
				if (isinside(nx, ny, poly) == 0) getcenter(poly, &nx, &ny);
			}
		}

		/* now save the arc information and delete it */
		for(i=0; i<2; i++)
		{
			xp[i] = ai->end[i].xpos;
			yp[i] = ai->end[i].ypos;
			app[i] = ai->end[i].portarcinst->proto;
			ani[i] = ai->end[i].nodeinst;
		}
		wid = ai->width;
		bits1 = bits2 = ai->userbits;
		if ((bits1&ISNEGATED) != 0)
		{
			if ((bits1&REVERSEEND) == 0) bits2 &= ~ISNEGATED; else
				bits1 &= ~ISNEGATED;
		}
		var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
		if (var == NOVARIABLE) oldarcname = 0; else
		{
			(void)allocstring(&oldarcname, (char *)var->addr, el_tempcluster);
			(void)askaid(net_aid, "delname", (INTBIG)oldarcname,
				(INTBIG)curfacet);
		}
		startobjectchange((INTBIG)ai, VARCINST);
		(void)killarcinst(ai);

		/* create the new arcs */
		newai = newarcinst(ap, wid, bits1, ani[0], app[0], xp[0], yp[0], ni, ppt, nx, ny, curfacet);
		if (oldarcname != 0 && newai != NOARCINST)
		{
			(void)askaid(net_aid, "setname", (INTBIG)newai, (INTBIG)oldarcname);
			(void)setvalkey((INTBIG)newai, VARCINST, el_arc_name, (INTBIG)oldarcname, VSTRING|VDISPLAY);
			efree(oldarcname);
		}
		if (newai != NOARCINST) endobjectchange((INTBIG)newai, VARCINST);
		newai = newarcinst(ap, wid, bits2, ni2, ppt, nx, ny, ani[1],app[1],xp[1],yp[1], curfacet);
		if (newai != NOARCINST) endobjectchange((INTBIG)newai, VARCINST);
		bits2 &= ~ISNEGATED;
		ang = (ang + 900) % 3600;
		bits2 = (bits2 & ~AANGLE) | (((ang+5)/10) << AANGLESH);
		newai = newarcinst(ap, wid, bits2, ni, ppt, nx, ny, ni2, ppt, nx, ny, curfacet);
		if (newai != NOARCINST) endobjectchange((INTBIG)newai, VARCINST);

		/* highlight the arc */
		newhigh.status = HIGHFROM;
		newhigh.facet = curfacet;
		newhigh.fromgeom = newai->geom;
		newhigh.fromport = NOPORTPROTO;
		newhigh.frompoint = 0;
		(void)us_addhighlight(&newhigh);
		return;
	}

	ttyputbadusage("create");
}

/* #define DEBUGMERGE 1 */

#ifdef DEBUGMERGE
INTBIG us_debugmergeoffset;
void us_debugwritepolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count);

void us_debugwritepolygon(INTSML layer, TECHNOLOGY *tech, INTBIG *x, INTBIG *y, INTSML count)
{
	REGISTER NODEPROTO *facet, *prim;
	REGISTER NODEINST *ni;
	REGISTER INTBIG lx, hx, ly, hy, cx, cy, *newlist;
	REGISTER INTSML i;
	HIGHLIGHT high;

	prim = getnodeproto("metal-1-node");
	facet = us_needfacet();
	if (facet == NONODEPROTO) return;
	lx = hx = x[0];
	ly = hy = y[0];
	for(i=1; i<count; i++)
	{
		if (x[i] < lx) lx = x[i];
		if (x[i] > hx) hx = x[i];
		if (y[i] < ly) ly = y[i];
		if (y[i] > hy) hy = y[i];
	}
	cx = (lx+hx) / 2;   cy = (ly+hy) / 2;
	ni = newnodeinst(prim, lx, hx, ly+us_debugmergeoffset, hy+us_debugmergeoffset, 0, 0, facet);
	newlist = (INTBIG *)emalloc(count * 2 * SIZEOFINTBIG, el_tempcluster);
	if (newlist == 0) return;
	for(i=0; i<count; i++)
	{
		newlist[i*2] = x[i] - cx;
		newlist[i*2+1] = y[i] - cy;
	}
	(void)setvalkey((INTBIG)ni, VNODEINST, el_trace, (INTBIG)newlist,
		VINTEGER|VISARRAY|((count*2)<<VLENGTHSH));
	endobjectchange((INTBIG)ni, VNODEINST);
	high.status = HIGHFROM;
	high.fromgeom = ni->geom;
	high.fromport = NOPORTPROTO;
	high.frompoint = 0;
	high.facet = facet;
	(void)us_addhighlight(&high);
}
#endif

void us_debug(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER PORTPROTO *pp;
	REGISTER NODEINST *ni;
	REGISTER PORTARCINST *pi;
	REGISTER ARCINST *ai;
	REGISTER LIBRARY *lib;
	REGISTER VARIABLE *var;
	REGISTER INTBIG i, j, l, let1, let2, let3;
	char line[200], *pt, *libname, *libfile;
	extern INTSML db_printerrors, db_minrtnodesize, db_maxrtnodesize;
	HIGHLIGHT newhigh;

	if (count == 0) return;
	l = strlen(par[0]);

	/********** THESE ARE SPECIAL CASES **********/
	if (namesamen(par[0], "convert-dashes", l) == 0)
	{
		i = 0;
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		{
			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			{
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					for(pt = pp->protoname; *pt != 0; pt++)
						if (*pt == '-') break;
					if (*pt != 0)
					{
						i++;
						strcpy(line, pp->protoname);
						for(pt = line; *pt != 0; pt++)
							if (*pt == '-') *pt = '_';
						pt = us_uniqueportname(line, np);
						(void)reallocstring(&pp->protoname, pt, us_aid->cluster);
					}
				}
			}
		}
		if (i == 0) ttyputmsg("No conversion necessary"); else
			ttyputmsg("Converted %d port names", i);
		return;
	}
#ifdef DEBUGMERGE
	if (namesamen(par[0], "merge", l) == 0)
	{
		static POLYGON *poly = NOPOLYGON;
		NODEINST **nilist;
		INTBIG total, done, thisx, thisy, lastx, lasty;

		if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		us_debugmergeoffset = np->highy - np->lowy +
			el_curlib->lambda[el_curtech->techindex]*4;
		total = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto->primindex == 0) continue;
			if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPNODE) continue;
			total++;
		}
		if (total == 0) return;
		nilist = (NODEINST **)emalloc(total * (sizeof (NODEINST *)), el_tempcluster);
		total = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->proto->primindex == 0) continue;
			if (((ni->proto->userbits&NFUNCTION) >> NFUNCTIONSH) != NPNODE) continue;
			nilist[total++] = ni;
		}
		done = 0;
		while (done == 0)
		{
			done = 1;
			for(i=1; i<total; i++)
			{
				thisx = (nilist[i]->lowx + nilist[i]->highx) / 2;
				thisy = (nilist[i]->lowy + nilist[i]->highy) / 2;
				lastx = (nilist[i-1]->lowx + nilist[i-1]->highx) / 2;
				lasty = (nilist[i-1]->lowy + nilist[i-1]->highy) / 2;
				if (lasty > thisy || (lasty == thisy && lastx <= thisx)) continue;
				ni = nilist[i];   nilist[i] = nilist[i-1];   nilist[i-1] = ni;
				done = 0;
			}
		}
		mergeinit();
		for(l=0; l<total; l++)
		{
			ni = nilist[l];
			j = nodepolys(ni, 0);
			for(i=0; i<j; i++)
			{
				shapenodepoly(ni, i, poly);
				mergestorepolygon(poly->layer, poly->tech, poly);
			}
		}
		mergedone(us_debugwritepolygon);
		efree((char *)nilist);
		return;
	}
#endif

	if (namesamen(par[0], "prepare-tsmc-io", l) == 0 && l >= 2)
	{
		INTSML total;
		char **filelist, libfile[200], libname[50];
		LIBRARY *leflib, *gdslib, *prepfile;
		extern AIDENTRY *dr_aid;

#define TSMCELECDIRECTORY "E:\\DevelE\\Artisan\\TPZ872G_150C\\ELIB\\"
#define TSMCGDSDIRECTORY  "E:\\DevelE\\Artisan\\TPZ872G_150C\\GDS\\"
#define TSMCLEFFILE       "E:\\DevelE\\Artisan\\TPZ872G_150C\\LEF\\tpz872g_4lm.lef"
#define TSMCPREPFILE      "E:\\DevelE\\Artisan\\tsmc25m4.txt"

		/* turn off DRC */
		aidturnoff(dr_aid, 1);

		/* read the TSMC preparation file */
		strcpy(libname, "prep");
		strcpy(libfile, TSMCPREPFILE);
		prepfile = newlibrary(libname, libfile);
		ttyputmsg("Reading Preparation file %s", libfile);
		if (askaid(io_aid, "read", (INTBIG)prepfile, (INTBIG)"text", 0) != 0)
		{
			ttyputerr("Cannot find TSMC preparation file %s", libfile);
			return;
		}

		/* read the LEF library with ports */
		strcpy(libname, "lef");
		strcpy(libfile, TSMCLEFFILE);
		leflib = newlibrary(libname, libfile);
		ttyputmsg("Reading LEF file %s", libfile);
		if (askaid(io_aid, "read", (INTBIG)leflib, (INTBIG)"lef", 0) != 0)
		{
			ttyputerr("Cannot find LEF file %s", libfile);
			return;
		}

		/* create a library for the GDS files */
		strcpy(libname, "gds");
		strcpy(libfile, "gds.elib");
		gdslib = newlibrary(libname, libfile);
		selectlibrary(gdslib);

		total = filesindirectory(TSMCGDSDIRECTORY, &filelist);
		for(i=0; i<total; i++)
		{
			if (filelist[i][0] == '.') continue;

			/* read the GDS file */
			eraselibrary(gdslib);
			strcpy(libfile, TSMCGDSDIRECTORY);
			strcat(libfile, filelist[i]);
			(void)reallocstring(&gdslib->libfile, libfile, gdslib->cluster);
			ttyputmsg("Reading GDS file %s", libfile);
			if (askaid(io_aid, "read", (INTBIG)gdslib, (INTBIG)"gds", 0) != 0) break;

			/* synchronize the ports */
			us_portsynchronize(leflib);

			/* mark all facets as part of a "cell library" */
			for(np = gdslib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
				np->userbits |= INCELLLIBRARY;

			/* write the Electric library */
			strcpy(libfile, TSMCELECDIRECTORY);
			strcat(libfile, filelist[i]);
			j = strlen(libfile);
			if (namesame(&libfile[j-4], ".gds") == 0) libfile[j-4] = 0;
			(void)reallocstring(&gdslib->libfile, libfile, gdslib->cluster);
			gdslib->userbits |= READFROMDISK;
			if (askaid(io_aid, "write", (INTBIG)gdslib, (INTBIG)"binary") != 0) break;
		}
		prepfile->userbits &= ~(LIBCHANGEDMAJOR|LIBCHANGEDMINOR);
		leflib->userbits &= ~(LIBCHANGEDMAJOR|LIBCHANGEDMINOR);
		return;
	}

	if (namesamen(par[0], "erase-bits", l) == 0 && l >= 2)
	{
		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
		{
			/* clear the library bits */
			lib->temp1 = lib->temp2 = 0;
			lib->userbits &= (LIBCHANGEDMAJOR | LIBCHANGEDMINOR | LIBUNITS);

			/* look at every facet in the library */
			for(np=lib->firstnodeproto; np!=NONODEPROTO; np=np->nextnodeproto)
			{
				/* clear the node prototype bits */
				np->temp1 = np->temp2 = 0;
				np->userbits &= WANTNEXPAND;

				/* clear the port prototype bits */
				for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
				{
					pp->temp1 = pp->temp2 = 0;
					pp->userbits &= (STATEBITS|PORTANGLE|PORTARANGE);
				}

				/* clear the node instance bits */
				for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
				{
					ni->temp1 = ni->temp2 = 0;
					ni->userbits &= NEXPAND;

					/* compute WIPED bit on node instance */
					if ((ni->proto->userbits&ARCSWIPE) != 0)
					{
						for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
						{
							if ((pi->conarcinst->proto->userbits&CANWIPE) != 0)
							{
								ni->userbits |= WIPED;
								break;
							}
						}
					}
				}

				/* clear the arc instance bits */
				for(ai=np->firstarcinst; ai!=NOARCINST; ai=ai->nextarcinst)
				{
					ai->temp1 = ai->temp2 = 0;
					ai->userbits &= (FIXED|FIXANG|ISNEGATED|NOEXTEND|NOTEND0|NOTEND1|REVERSEEND|CANTSLIDE);
					determineangle(ai);
					(void)setshrinkvalue(ai, 0);
				}
			}
		}
		ttyputmsg("All aid words reset");
		return;
	}

	if (namesamen(par[0], "label-facet", l) == 0 && l >= 1)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		us_clearhighlightcount();
		let1 = 'A';   let2 = let3 = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			line[0] = (char)let1;   line[1] = (char)let2;
			line[2] = (char)let3;   line[3] = 0;
			(void)setvalkey((INTBIG)ni, VNODEINST, el_node_name, (INTBIG)line, VSTRING|VDISPLAY);
			let1++;
			if (let1 > 'Z')
			{
				let1 = 'A';
				if (let2 == 0) let2 = 'A'; else let2++;
				if (let2 > 'Z')
				{
					let2 = 'A';
					if (let3 == 0) let3 = 'A'; else let3++;
				}
			}
		}
		i = 1;
		for(ai = np->firstarcinst; ai != NOARCINST;
			ai = ai->nextarcinst)
		{
			(void)sprintf(line, "%d", i++);
			(void)setvalkey((INTBIG)ai, VARCINST, el_arc_name, (INTBIG)line,
				VSTRING|VDISPLAY);
		}
		us_redisplay(el_curwindowpart);
		return;
	}

	if (namesamen(par[0], "out-of-bounds", l) == 0)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		i = 0;
		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		{
			if (ni->geom->lowx > -HUGEINT/2 && ni->geom->highx < HUGEINT/2 &&
				ni->geom->lowy > -HUGEINT/2 && ni->geom->highy < HUGEINT/2) 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++;
		}
		for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		{
			if (ai->geom->lowx > -HUGEINT/2 && ai->geom->highx < HUGEINT/2 &&
				ai->geom->lowy > -HUGEINT/2 && ai->geom->highy < HUGEINT/2) continue;
			newhigh.status = HIGHFROM;
			newhigh.facet = np;
			newhigh.fromgeom = ai->geom;
			newhigh.fromport = NOPORTPROTO;
			newhigh.frompoint = 0;
			(void)us_addhighlight(&newhigh);
			i++;
		}
		if (i == 0) ttyputmsg("No objects are out of bounds"); else
			ttyputmsg("%d objects are out of bounds", i);
		return;
	}

	/********** THESE ARE STANDARD **********/

	if (namesamen(par[0], "arena", l) == 0 && l >= 1)
	{
		if (count > 1) db_printclusterarena(par[1]); else
			db_printclusterarena("");
		return;
	}

	if (namesamen(par[0], "check-database", l) == 0 && l >= 1)
	{
		if (count > 1 && namesamen(par[1], "verbose", strlen(par[1])) == 0)
			us_checkdatabase(1); else
				us_checkdatabase(0);
		return;
	}

	if (namesamen(par[0], "examine-options", l) == 0 && l >= 2)
	{
		libname = us_tempoptionslibraryname();
		libfile = optionsfilepath();
		if (fileexistence(truepath(libfile)) == 1)
		{
			lib = newlibrary(libname, libfile);
			if (lib == NOLIBRARY) return;
			(void)askaid(io_aid, "read", (INTBIG)lib, (INTBIG)"binary");
			(void)askaid(io_aid, "write", (INTBIG)lib, (INTBIG)"text");
			killlibrary(lib);
		}
		return;
	}

	if (namesamen(par[0], "freeze-user-interface", l) == 0 && l >= 1)
	{
		j = l = 0;
		for(i=0; i<us_aid->numvar; i++)
		{
			var = &us_aid->firstvar[i];
			pt = makename(var->key);
			if (namesamen(pt, "USER_binding_", 13) == 0)
			{
				if (namesame(pt, "USER_binding_menu") == 0) continue;
				var->type &= ~VDONTSAVE;
				j++;
			} else if (namesamen(pt, "USER_macro_", 11) == 0)
			{
				var->type &= ~VDONTSAVE;

				/*
				 * when saving macros to disk, must erase entry 2, which
				 * is the address of the parsing structures: these cannot
				 * be valid when read back in.
				 */
				setindkey((INTBIG)us_aid, VAID, var->key, 2, (INTBIG)"");
				l++;
			}
		}
		ttyputmsg("Made %d macros and %d bindings permanent", l, j);
		return;
	}

	if (namesamen(par[0], "internal-errors", l) == 0 && l >= 1)
	{
		db_printerrors = !db_printerrors;
		ttyputmsg("Internal database errors will %sbe printed", (db_printerrors==0 ? "not " : ""));
		return;
	}

	if (namesamen(par[0], "namespace", l) == 0 && l >= 1)
	{
		ttyputmsg("%ld names in global namespace:", el_numnames);
		for(i=0; i<el_numnames; i++) ttyputmsg("'%s'", el_namespace[i]);
		return;
	}

	if (namesamen(par[0], "options-changed", l) == 0 && l >= 1)
	{
		explainoptionchanges();
		return;
	}

	if (namesamen(par[0], "rtree", l) == 0 && l >= 2)
	{
		np = us_needfacet();
		if (np == NONODEPROTO) return;
		ttyputmsg("R-tree for facet %s (nodes hold from %d to %d entries)",
			describenodeproto(np), db_minrtnodesize, db_maxrtnodesize);
		db_printrtree(NORTNODE, np->rtree, 0);
		return;
	}

	if (namesamen(par[0], "undo", l) == 0 && l >= 1)
	{
		db_undodlog();
		return;
	}

	ttyputbadusage("debug");
}

void us_defarc(INTSML count, char *par[])
{
	REGISTER char *aname, *pt;
	REGISTER INTSML l, negate;
	REGISTER INTBIG i, wid, style, bits;
	REGISTER ARCPROTO *ap;
	REGISTER PORTPROTO *pp;
	REGISTER NODEPROTO *np;
	extern COMCOMP us_defarcsp, us_defarcnotp, us_defarcwidp, us_defarcpinp, us_defarcangp;
	REGISTER VARIABLE *var;

	if (count == 0)
	{
		count = ttygetparam(_("Arc prototype: "), &us_defarcsp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}

	/* get arc prototype name */
	if (strcmp(par[0], "*") == 0)
	{
		ap = NOARCPROTO;
		aname = "all";
	} else
	{
		ap = getarcproto(par[0]);
		if (ap == NOARCPROTO)
		{
			us_abortcommand(_("Unknown arc prototype: %s"), par[0]);
			return;
		}
		aname = ap->protoname;
	}

	if (count <= 1)
	{
		/* get style to report */
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = -1;
			if (style == -1)
			{
				ttyputmsg(_("No overriding defaults set for arcs"));
				return;
			}
		}
		(void)initinfstr();
		(void)formatinfstr(_("%s arcs drawn "), aname);
		if ((style&WANTFIX) != 0) (void)addstringtoinfstr(_("rigid")); else
		{
			(void)addstringtoinfstr(_("nonrigid, "));
			if ((style&WANTFIXANG) == 0) (void)addstringtoinfstr(_("not fixed angle, ")); else
				(void)addstringtoinfstr(_("fixed angle, "));
			if ((style&WANTCANTSLIDE) != 0) (void)addstringtoinfstr(_("nonslidable")); else
				(void)addstringtoinfstr(_("slidable"));
		}
		if ((style&WANTDIRECTIONAL) != 0) (void)addstringtoinfstr(_(", directional"));
		if ((style&WANTNEGATED) != 0) (void)addstringtoinfstr(_(", negated"));
		(void)addstringtoinfstr(", ");
		if ((style&WANTNOEXTEND) != 0) (void)addstringtoinfstr(_("not ends-extended")); else
			(void)addstringtoinfstr(_("ends-extended"));
		if (ap != NOARCPROTO)
			(void)formatinfstr(_(", width %s, angle %ld"), latoa(defaultarcwidth(ap)-arcprotowidthoffset(ap)),
				(ap->userbits&AANGLEINC) >> AANGLEINCSH);
		ttyputmsg("%s", returninfstr());
		return;
	}

	l = strlen(pt = par[1]);
	negate = 0;

	if (namesamen(pt, "not", l) == 0 && l >= 2)
	{
		if (count <= 2)
		{
			count = ttygetparam(_("Defarc option: "), &us_defarcnotp, MAXPARS-2, &par[2])+2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		count--;
		par++;
		negate++;
		l = strlen(pt = par[1]);
	}

	if (namesamen(pt, "default", l) == 0 && l >= 2)
	{
		if (ap != NOARCPROTO)
		{
			(void)delvalkey((INTBIG)ap, VARCPROTO, us_arcstyle);
			ttyputverbose(_("%s arcs drawn in default style"), describearcproto(ap));
			return;
		}
		(void)delvalkey((INTBIG)us_aid, VAID, us_arcstyle);
		ttyputverbose(_("All arcs drawn in default style"));
		return;
	}

	if ((namesamen(pt, "manhattan", l) == 0 || namesamen(pt, "fixed-angle", l) == 0) && l >= 1)
	{
		if (namesamen(pt, "manhattan", l) == 0)
			ttyputmsg(_("It is now proper to use 'defarc fixed-angle' instead of 'defarc manhattan'"));
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate != 0) style &= ~WANTFIXANG; else style |= WANTFIXANG;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = 0;
			if (negate != 0) style &= ~WANTFIXANG; else style |= WANTFIXANG;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs drawn not fixed-angle"), aname); else
			ttyputverbose(_("%s arcs drawn fixed-angle"), aname);
		return;
	}

	if (namesamen(pt, "rigid", l) == 0 && l >= 1)
	{
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate != 0) style &= ~WANTFIX; else style |= WANTFIX;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = WANTFIXANG;
			if (negate != 0) style &= ~WANTFIX; else style |= WANTFIX;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs drawn un-rigid"), aname); else
			ttyputverbose(_("%s arcs drawn rigid"), aname);
		return;
	}

	if (namesamen(pt, "directional", l) == 0 && l >= 2)
	{
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate != 0) style &= ~WANTDIRECTIONAL; else
				style |= WANTDIRECTIONAL;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = WANTFIXANG;
			if (negate != 0) style &= ~WANTDIRECTIONAL; else
				style |= WANTDIRECTIONAL;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs drawn nondirectional"), aname); else
			ttyputverbose(_("%s arcs drawn directional"), aname);
		return;
	}

	if (namesamen(pt, "slide", l) == 0 && l >= 1)
	{
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate == 0) style &= ~WANTCANTSLIDE; else
				style |= WANTCANTSLIDE;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = WANTFIXANG;
			if (negate == 0) style &= ~WANTCANTSLIDE; else
				style |= WANTCANTSLIDE;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs can not slide in their ports"), aname); else
			ttyputverbose(_("%s arcs can slide in their ports"), aname);
		return;
	}

	if (namesamen(pt, "negated", l) == 0 && l >= 2)
	{
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate != 0) style &= ~WANTNEGATED; else style |= WANTNEGATED;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = WANTFIXANG;
			if (negate != 0) style &= ~WANTNEGATED; else style |= WANTNEGATED;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs drawn un-negated"), aname); else
			ttyputverbose(_("%s arcs drawn negated"), aname);
		return;
	}

	if (namesamen(pt, "ends-extend", l) == 0 && l >= 1)
	{
		if (ap != NOARCPROTO)
		{
			var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = ap->userbits;
			if (negate != 0) style |= WANTNOEXTEND; else style &= ~WANTNOEXTEND;
			(void)setvalkey((INTBIG)ap, VARCPROTO, us_arcstyle, style, VINTEGER);
		} else
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_arcstyle);
			if (var != NOVARIABLE) style = var->addr; else style = WANTFIXANG;
			if (negate != 0) style |= WANTNOEXTEND; else style &= ~WANTNOEXTEND;
			(void)setvalkey((INTBIG)us_aid, VAID, us_arcstyle, style, VINTEGER|VDONTSAVE);
		}
		if (negate != 0) ttyputverbose(_("%s arcs drawn with ends not extended"), aname); else
			ttyputverbose(_("%s arcs drawn with ends extended by half-width"), aname);
		return;
	}

	if (namesamen(pt, "width", l) == 0 && l >= 1)
	{
		if (ap == NOARCPROTO)
		{
			us_abortcommand(_("Default width requires specific arc prototypes"));
			return;
		}
		if (count <= 2)
		{
			count = ttygetparam(_("Width: "), &us_defarcwidp, MAXPARS-2, &par[2]) + 2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		pt = par[2];
		i = atola(pt);
		if (i&1) i++;
		if (i < 0)
		{
			us_abortcommand(_("Width must not be negative"));
			return;
		}
		wid = arcprotowidthoffset(ap) + i;
		(void)setvalkey((INTBIG)ap, VARCPROTO, el_arc_default_width, wid, VINTEGER);
		ttyputverbose(_("%s arcs default to %s wide"), aname, latoa(i));
		return;
	}

	if (namesamen(pt, "angle", l) == 0 && l >= 1)
	{
		if (ap == NOARCPROTO)
		{
			us_abortcommand(_("Default angle increment requires specific arc prototypes"));
			return;
		}
		if (count <= 2)
		{
			count = ttygetparam(_("Angle: "), &us_defarcangp, MAXPARS-2, &par[2]) + 2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		pt = par[2];
		i = atoi(pt) % 360;
		if (i < 0)
		{
			us_abortcommand(_("Angle increment must not be negative"));
			return;
		}
		bits = (ap->userbits & ~AANGLEINC) | (i << AANGLEINCSH);
		(void)setval((INTBIG)ap, VARCPROTO, "userbits", bits, VINTEGER);
		ttyputverbose(_("%s arcs will run at %d degree angle increments"), aname, i);
		return;
	}

	if (namesamen(pt, "pin", l) == 0 && l >= 1)
	{
		if (ap == NOARCPROTO)
		{
			us_abortcommand(_("Default pin requires specific arc prototypes"));
			return;
		}
		if (count <= 2)
		{
			count = ttygetparam(_("Pin: "), &us_defarcpinp, MAXPARS-2, &par[2]) + 2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		np = getnodeproto(par[2]);
		if (np == NONODEPROTO)
		{
			us_abortcommand(_("Cannot find primitive %s"), par[2]);
			return;
		}
		pp = np->firstportproto;
		for(i=0; pp->connects[i] != NOARCPROTO; i++)
			if (pp->connects[i] == ap) break;
		if (pp->connects[i] == NOARCPROTO)
		{
			us_abortcommand(_("Cannot: the %s node must connect to %s arcs on its first port (%s)"),
				describenodeproto(np), aname, pp->protoname);
			return;
		}
		(void)setval((INTBIG)ap, VARCPROTO, "ARC_Default_Pin", (INTBIG)np, VNODEPROTO|VDONTSAVE);
		ttyputverbose(_("Default pins for arc %s will be %s"), aname, describenodeproto(np));
		return;
	}

	ttyputbadusage("defarc");
}

void us_defnode(INTSML count, char *par[])
{
	REGISTER char *pp, *nname;
	REGISTER INTSML l, negated, pangle;
	REGISTER INTBIG i, j;
	INTBIG plx, ply, phx, phy, pxs, pys, nodesize[2];
	REGISTER VARIABLE *var;
	REGISTER NODEPROTO *np, *onp;
	extern COMCOMP us_defnodesp, us_defnodexsp, us_defnodeysp;

	if (count == 0)
	{
		count = ttygetparam(_("Node prototype: "), &us_defnodesp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}

	/* get node prototype name */
	if (strcmp(par[0], "*") == 0)
	{
		np = NONODEPROTO;
		nname = _("All complex");
	} else
	{
		np = getnodeproto(par[0]);
		if (np == NONODEPROTO)
		{
			us_abortcommand(_("Unknown node prototype: %s"), par[0]);
			return;
		}
		nname = describenodeproto(np);
	}

	if (count <= 1)
	{
		if (np == NONODEPROTO)
		{
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_placement_angle);
			if (var == NOVARIABLE) pangle = 0; else pangle = (INTSML)var->addr;
			ttyputmsg(_("Nodes are created with %s degrees rotation%s"),
				frtoa(pangle%3600*WHOLE/10), (pangle >= 3600 ? _(", transposed") : ""));
			ttyputmsg(_("Facet revision dates are %sbeing checked"),
				(us_useroptions&CHECKDATE) == 0 ? _("not ") : "");
			ttyputmsg(_("Facet changes are %sallowed"),
				(us_useroptions&NOFACETCHANGES) != 0 ? _("dis") : "");
			ttyputmsg(_("Locked primitive changes are %sallowed"),
				(us_useroptions&NOPRIMCHANGES) != 0 ? _("dis") : "");
			ttyputmsg(_("Primitive placement is %s"),
				(us_useroptions&CENTEREDPRIMITIVES) != 0 ? _("centered") : _("edge-based"));
			ttyputmsg(_("Ports are %scopied during array/duplicate/yanknode"),
				(us_useroptions&DUPCOPIESPORTS) != 0 ? "" : _("not "));
		} else
		{
			if (np->primindex == 0)
			{
				ttyputmsg(_("Newly created %s facets are %sexpanded"), describenodeproto(np),
					(np->userbits&WANTNEXPAND) == 0 ? _("un") : "");
				ttyputmsg(_("Instances in facet %s %s be changed"), describenodeproto(np),
					(np->userbits&NILOCKED) != 0 ? _("cannot") : _("can"));
				ttyputmsg(_("Facet %s is %spart of a cell library"), describenodeproto(np),
					(np->userbits&INCELLLIBRARY) != 0 ? "" : _("not "));
			} else
			{
				nodeprotosizeoffset(np, &plx, &ply, &phx, &phy);
				defaultnodesize(np, &pxs, &pys);
				ttyputmsg(_("Default %s size is %sx%s"), np->primname,
					latoa(pxs-phx-plx), latoa(pys-phy-ply));
			}
			var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, us_placement_angle);
			if (var != NOVARIABLE)
			{
				ttyputmsg(_("%s nodes are created with %s degrees rotation%s"),
					frtoa(var->addr%3600*WHOLE/10), (var->addr >= 3600 ? _(", transposed") : ""));
			}
		}
		return;
	}

	l = strlen(pp = par[1]);

	/* handle negation */
	if (namesamen(pp, "not", l) == 0 && l >= 1)
	{
		negated = 1;
		count--;
		par++;
		l = strlen(pp = par[1]);
	} else negated = 0;

	if (namesamen(pp, "from-cell-library", l) == 0)
	{
		if (np != NONODEPROTO && np->primindex != 0)
		{
			us_abortcommand(_("Cell library marking can only be done for facets"));
			return;
		}
		for(onp = el_curlib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
		{
			if (np != NONODEPROTO && np != onp) continue;
			if (negated == 0)
			{
				if ((onp->userbits&INCELLLIBRARY) != 0)
				{
					ttyputverbose(_("Facet %s is already part of a cell library"),
						describenodeproto(onp));
				} else
				{
					ttyputverbose(_("Facet %s is now part of a cell library"),
						describenodeproto(onp));
				}
				(void)setval((INTBIG)onp, VNODEPROTO, "userbits",
					onp->userbits | INCELLLIBRARY, VINTEGER);
			} else
			{
				if ((onp->userbits&INCELLLIBRARY) == 0)
				{
					ttyputverbose(_("Facet %s is already not part of a cell library"),
						describenodeproto(onp));
				} else
				{
					ttyputverbose(_("Facet %s is now not part of a cell library"),
						describenodeproto(onp));
				}
				(void)setval((INTBIG)onp, VNODEPROTO, "userbits",
					onp->userbits & ~INCELLLIBRARY, VINTEGER);
			}
		}
		return;
	}

	if (namesamen(pp, "check-dates", l) == 0 && l >= 2)
	{
		if (np != NONODEPROTO)
		{
			us_abortcommand(_("Date checking must be done for all facets (*)"));
			return;
		}
		if (negated == 0)
		{
			if ((us_useroptions&CHECKDATE) != 0)
				ttyputverbose(_("Facet revision dates already being checked")); else
					ttyputverbose(_("Facet revision date errors will be identified"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions | CHECKDATE, VINTEGER);
		} else
		{
			if ((us_useroptions&CHECKDATE) == 0)
				ttyputverbose(_("Facet revision dates already being ignored")); else
					ttyputverbose(_("Facet revision date errors will be ignored"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions & ~CHECKDATE, VINTEGER);
		}
		return;
	}

	if (namesamen(pp, "centered-primitives", l) == 0 && l >= 2)
	{
		if (np != NONODEPROTO)
		{
			us_abortcommand(_("Primitive centering must be done for all nodes (*)"));
			return;
		}
		if (negated == 0)
		{
			if ((us_useroptions&CENTEREDPRIMITIVES) != 0)
				ttyputverbose(_("Primitives are already being placed by their center")); else
					ttyputverbose(_("Primitives will be placed by their center"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions | CENTEREDPRIMITIVES, VINTEGER);
		} else
		{
			if ((us_useroptions&CENTEREDPRIMITIVES) == 0)
				ttyputverbose(_("Primitives are already being placed by edges")); else
					ttyputverbose(_("Primitives will be placed by edges"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions & ~CENTEREDPRIMITIVES, VINTEGER);
		}
		return;
	}

	if (namesamen(pp, "copy-ports", l) == 0 && l >= 2)
	{
		if (np != NONODEPROTO)
		{
			us_abortcommand(_("Port copying must be done for all nodes (*)"));
			return;
		}
		if (negated == 0)
		{
			if ((us_useroptions&DUPCOPIESPORTS) != 0)
				ttyputverbose(_("Ports are already being copied by duplicate/array/yanknode")); else
					ttyputverbose(_("Ports will be copied by duplicate/array/yanknode"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions | DUPCOPIESPORTS, VINTEGER);
		} else
		{
			if ((us_useroptions&DUPCOPIESPORTS) == 0)
				ttyputverbose(_("Ports are already not being copied by duplicate/array/yanknode")); else
					ttyputverbose(_("Ports will not be copied by duplicate/array/yanknode"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions & ~DUPCOPIESPORTS, VINTEGER);
		}
		return;
	}

	if (namesamen(pp, "alterable", l) == 0 && l >= 1)
	{
		if (np != NONODEPROTO)
		{
			/* set alterability of facets */
			if (negated == 0)
			{
				if ((np->userbits&NLOCKED) == 0)
				{
					ttyputverbose(_("Contents of facet %s are already able to be freely altered"),
						describenodeproto(np));
				} else
				{
					ttyputverbose(_("Contents of facet %s may now be freely edited"),
						describenodeproto(np));
				}
				(void)setval((INTBIG)np, VNODEPROTO, "userbits",
					np->userbits & ~NLOCKED, VINTEGER);
			} else
			{
				if ((np->userbits&NLOCKED) != 0)
				{
					ttyputverbose(_("Contents of facet %s are already locked"),
						describenodeproto(np));
				} else
				{
					ttyputverbose(_("Contents of facet %s will not be allowed to change"),
						describenodeproto(np));
				}
				(void)setval((INTBIG)np, VNODEPROTO, "userbits",
					np->userbits | NLOCKED, VINTEGER);
			}
		} else
		{
			us_abortcommand(_("Must set alterability for a specific facet"));
		}
		return;
	}

	if (namesamen(pp, "instances-locked", l) == 0 && l >= 1)
	{
		if (np != NONODEPROTO)
		{
			/* set alterability of facet instances */
			if (negated != 0)
			{
				if ((np->userbits&NILOCKED) == 0)
				{
					ttyputverbose(_("Instances in facet %s are already able to be freely altered"),
						describenodeproto(np));
				} else
				{
					ttyputverbose(_("Instances in facet %s may now be freely edited"),
						describenodeproto(np));
				}
				(void)setval((INTBIG)np, VNODEPROTO, "userbits",
					np->userbits & ~NILOCKED, VINTEGER);
			} else
			{
				if ((np->userbits&NILOCKED) != 0)
				{
					ttyputverbose(_("Instances in facet %s are already locked"),
						describenodeproto(np));
				} else
				{
					ttyputverbose(_("Instances in facet %s will not be allowed to change"),
						describenodeproto(np));
				}
				(void)setval((INTBIG)np, VNODEPROTO, "userbits",
					np->userbits | NILOCKED, VINTEGER);
			}
		} else
		{
			/* set alterability of all facet instances */
			if (negated != 0)
			{
				if ((us_useroptions&NOFACETCHANGES) == 0)
					ttyputverbose(_("Facets are already able to be freely altered")); else
						ttyputverbose(_("Facets may now be freely edited"));
				(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
					us_useroptions & ~NOFACETCHANGES, VINTEGER);
			} else
			{
				if ((us_useroptions&NOFACETCHANGES) != 0)
					ttyputverbose(_("Facet changes are already being disallowed")); else
						ttyputverbose(_("Facet changes will not be allowed"));
				(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
					us_useroptions | NOFACETCHANGES, VINTEGER);
			}
		}
		return;
	}

	if (namesamen(pp, "locked-primitives", l) == 0 && l >= 1)
	{
		if (np != NONODEPROTO)
		{
			us_abortcommand(_("Primitive lock must be done for all prototypes (*)"));
			return;
		}
		if (negated != 0)
		{
			if ((us_useroptions&NOPRIMCHANGES) == 0)
				ttyputverbose(_("Locked primitives are already able to be freely altered")); else
					ttyputverbose(_("Locked primitives may now be freely edited"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions & ~NOPRIMCHANGES, VINTEGER);
		} else
		{
			if ((us_useroptions&NOPRIMCHANGES) != 0)
				ttyputverbose(_("Locked primitive changes are already being disallowed")); else
					ttyputverbose(_("Locked primitive changes will not be allowed"));
			(void)setvalkey((INTBIG)us_aid, VAID, us_optionflags,
				us_useroptions | NOPRIMCHANGES, VINTEGER);
		}
		return;
	}

	if (namesamen(pp, "expanded", l) == 0 && l >= 1)
	{
		if (np == NONODEPROTO)
		{
			for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
			{
				if (negated == 0) (void)setval((INTBIG)np, VNODEPROTO, "userbits",
					np->userbits | WANTNEXPAND, VINTEGER); else
						(void)setval((INTBIG)np, VNODEPROTO, "userbits",
							np->userbits & ~WANTNEXPAND, VINTEGER);
			}
		} else
		{
			if (np->primindex != 0)
			{
				if (negated == 0) us_abortcommand(_("Primitives are expanded by definition")); else
					us_abortcommand(_("Cannot un-expand primitives"));
				return;
			}
			if (negated == 0) (void)setval((INTBIG)np, VNODEPROTO, "userbits",
				np->userbits | WANTNEXPAND, VINTEGER); else
					(void)setval((INTBIG)np, VNODEPROTO, "userbits",
						np->userbits & ~WANTNEXPAND, VINTEGER);
		}
		ttyputverbose(_("%s node prototypes will be %sexpanded on creation"), nname, (negated==0 ? "" : _("un-")));
		return;
	}

	if (namesamen(pp, "size", l) == 0 && l >= 1)
	{
		if (np == NONODEPROTO)
		{
			us_abortcommand(_("Must change size on a single node"));
			return;
		}
		if (np->primindex == 0)
		{
			us_abortcommand(_("Can only change default size on primitives"));
			return;
		}
		if (count <= 2)
		{
			count = ttygetparam(_("X size: "), &us_defnodexsp, MAXPARS-2, &par[2]) + 2;
			if (count == 2)
			{
				us_abortedmsg();
				return;
			}
		}
		pp = par[2];
		i = atola(pp);
		if (i&1) i++;
		if (count <= 3)
		{
			count = ttygetparam(_("Y size: "), &us_defnodeysp, MAXPARS-3, &par[3]) + 3;
			if (count == 3)
			{
				us_abortedmsg();
				return;
			}
		}
		pp = par[3];
		j = atola(pp);
		if (j&1) j++;
		if (i < 0 || j < 0)
		{
			us_abortcommand(_("Sizes must not be negative"));
			return;
		}
		nodeprotosizeoffset(np, &plx, &ply, &phx, &phy);
		nodesize[0] = i+plx+phx;
		nodesize[1] = j+ply+phy;
		(void)setvalkey((INTBIG)np, VNODEPROTO, el_node_default_size,
			(INTBIG)nodesize, VINTEGER|VISARRAY|(2<<VLENGTHSH));
		ttyputverbose(_("%s nodes will be created %sx%s in size"), describenodeproto(np),
			latoa(i), latoa(j));
		return;
	}

	if (namesamen(pp, "placement", l) == 0 && l >= 1)
	{
		if (np != NONODEPROTO)
		{
			/* get placement angle for this node */
			var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, us_placement_angle);
			if (var == NOVARIABLE) pangle = 0; else pangle = (INTSML)var->addr;

			if (count > 2)
			{
				pangle = (atofr(par[2])*10/WHOLE) % 3600;
				if (pangle < 0) pangle += 3600;
				if (namesame(&par[2][strlen(par[2])-1], "t") == 0) pangle += 3600;
			} else if (pangle >= 6300) pangle = 0; else
				pangle += 900;
			(void)setvalkey((INTBIG)np, VNODEPROTO, us_placement_angle, pangle, VINTEGER);
		} else
		{
			/* get placement angle for all nodes */
			var = getvalkey((INTBIG)us_aid, VAID, VINTEGER, us_placement_angle);
			if (var == NOVARIABLE) pangle = 0; else pangle = (INTSML)var->addr;

			if (count > 2)
			{
				pangle = (atofr(par[2])*10/WHOLE) % 3600;
				if (pangle < 0) pangle += 3600;
				if (namesame(&par[2][strlen(par[2])-1], "t") == 0) pangle += 3600;
			} else if (pangle >= 6300) pangle = 0; else
				pangle += 900;
			(void)setvalkey((INTBIG)us_aid, VAID, us_placement_angle, pangle, VINTEGER);
		}
		return;
	}

	ttyputbadusage("defnode");
}

void us_duplicate(INTSML count, char *par[])
{
	REGISTER NODEPROTO *np;
	REGISTER GEOM **list;
	REGISTER INTSML interactiveplace;

	/* get object */
	np = us_needfacet();
	if (np == NONODEPROTO) return;
	list = us_gethighlighted(OBJNODEINST | OBJARCINST);
	if (list[0] == NOGEOM)
	{
		us_abortcommand(_("First select objects to duplicate"));
		return;
	}

	if ((us_useroptions&NOMOVEAFTERDUP) != 0) interactiveplace = 0; else
		interactiveplace = 1;
	us_copylisttofacet(list, np, np, 1, interactiveplace);
}

#if 0		/* test code for John Wood */

#define NUMTRANSISTOR 5
#define DEFTRANSISTORWIDTH 3
#define TRANSISTORWIDTH 30
#define FACETNAME "multifingertransistor{lay}"

void us_debugjohn(void)
{
	NODEPROTO *facet, *tra, *contact;
	NODEINST *transni, *contactni, *lastcontactni;
	PORTPROTO *topport, *botport, *conport;
	ARCPROTO *active;
	ARCINST *ai;
	char *par[2];
	INTBIG lx, hx, ly, hy, sizex, sizey, sep, lambda, sizeycontact, bits,
		tpx, tpy, cpx, cpy, wid, i;

	lambda = el_curlib->lambda[el_curtech->techindex];
	facet = newnodeproto(FACETNAME, el_curlib);
	if (facet == NONODEPROTO) return;

	/* get the objects we need */
	tra = getnodeproto("mocmossub:N-Transistor");
	if (tra == NONODEPROTO) return;
	contact = getnodeproto("mocmossub:Metal-1-N-Active-Con");
	conport = contact->firstportproto;
	topport = getportproto(tra, "n-trans-diff-top");
	botport = getportproto(tra, "n-trans-diff-bot");
	active = getarcproto("mocmossub:N-Active");

	/* get information for layout */
	sizex = tra->highx - tra->lowx;
	sizey = tra->highy - tra->lowy;
	sizeycontact = contact->highy - contact->lowy;
	sep = sizey * 2 - 32 * lambda;
	bits = us_makearcuserbits(active);
	wid = active->nominalwidth;

	/* lay down the transistors */
	for(i=0; i<NUMTRANSISTOR; i++)
	{
		lx = 0;   hx = sizex + (TRANSISTORWIDTH - DEFTRANSISTORWIDTH) * lambda;
		ly = i*sep;   hy = ly + sizey;
		transni = newnodeinst(tra, lx, hx, ly, hy, 0, 0, facet);
		endobjectchange((INTBIG)transni, VNODEINST);
		if (i < NUMTRANSISTOR-1)
		{
			ly = hy - lambda * 29 / 2;   hy = ly + sizeycontact;
			contactni = newnodeinst(contact, lx, hx, ly, hy, 0, 0, facet);
			endobjectchange((INTBIG)contactni, VNODEINST);

			/* wire to the transistor */
			portposition(transni, topport, &tpx, &tpy);
			portposition(contactni, conport, &cpx, &cpy);
			ai = newarcinst(active, wid, bits, transni, topport, tpx, tpy,
				contactni, conport, cpx, cpy, facet);
			endobjectchange((INTBIG)ai, VARCINST);
		}
		if (i > 0)
		{
			portposition(transni, botport, &tpx, &tpy);
			portposition(lastcontactni, conport, &cpx, &cpy);
			ai = newarcinst(active, wid, bits, transni, botport, tpx, tpy,
				lastcontactni, conport, cpx, cpy, facet);
			endobjectchange((INTBIG)ai, VARCINST);
		}
		lastcontactni = contactni;
	}

	/* display the result */
	par[0] = FACETNAME;
	us_editfacet(1, par);
}
#endif
