/*
 * Electric(tm) VLSI Design System
 *
 * File: usrcomab.c
 * User interface aid: command handler for A through B
 * 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"

void us_arc(INTSML count, char *par[])
{
	REGISTER INTBIG negate, ang, toggle, acx, acy, r, j, i, l;
	INTBIG ix, iy, x1, y1, x2, y2, xcur, ycur, dx[2], dy[2];
	REGISTER ARCINST *ai;
	REGISTER NODEPROTO *np;
	REGISTER char *pp;
	REGISTER NETWORK *net;
	extern COMCOMP us_arcp, us_arcnotp, us_arcnamep, us_arctogp;
	REGISTER VARIABLE *var;
	REGISTER GEOM **list;
	static POLYGON *poly = NOPOLYGON;

	if (count == 0)
	{
		count = ttygetparam(_("ARC command: "), &us_arcp, MAXPARS, par);
		if (count == 0)
		{
			us_abortedmsg();
			return;
		}
	}
	l = strlen(pp = par[0]);
	negate = toggle = 0;

	if (namesamen(pp, "not", l) == 0 && l >= 2)
	{
		if (count == 1)
		{
			count = ttygetparam(_("Negation option: "), &us_arcnotp, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		count--;
		par++;
		l = strlen(pp = par[0]);
		negate = 1;
	} else if (namesamen(pp, "toggle", l) == 0 && l >= 2)
	{
		if (count == 1)
		{
			count = ttygetparam(_("Toggle option: "), &us_arctogp, MAXPARS-1, &par[1]) + 1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		count--;
		par++;
		l = strlen(pp = par[0]);
		toggle = 1;
	}

	/* get the list of all highlighted arcs */
	list = us_gethighlighted(OBJARCINST);
	if (list[0] == NOGEOM)
	{
		us_abortcommand(_("No arcs are selected"));
		return;
	}

	/* disallow arraying if lock is on */
	np = geomparent(list[0]);
	if (us_canedit(np, NONODEPROTO, 1) != 0) return;

	/* save and erase highlighting */
	us_pushhighlight();
	us_clearhighlightcount();

	if (namesamen(pp, "rigid", l) == 0 && l >= 2)
	{
		if (namesame(el_curconstraint->conname, "layout") != 0)
		{
			us_abortcommand(_("Use the layout constraint system for rigid arcs"));
			(void)us_pophighlight(0);
			return;
		}
		i = us_modarcbits((INTSML)((toggle != 0 ? 23 : (negate != 0 ? 1 : 0))), 0, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle rigidity") : (negate != 0 ? _("made un-rigid") : _("made rigid"))));
	} else if ((namesamen(pp, "manhattan", l) == 0 ||
		namesamen(pp, "fixed-angle", l) == 0) && l >= 1)
	{
		if (namesame(el_curconstraint->conname, "layout") != 0)
		{
			us_abortcommand(_("Use the layout constraint system for fixed-angle arcs"));
			(void)us_pophighlight(0);
			return;
		}
		if (namesamen(pp, "manhattan", l) == 0)
			ttyputmsg(_("It is now proper to use 'arc fixed-angle' instead of 'arc manhattan'"));
		i = us_modarcbits((INTSML)((toggle != 0 ? 24 : (negate != 0 ? 3 : 2))), 0, "", list);
		if (i >= 0)
			ttyputverbose("%d %s %s", i, makeplural(_("arc"), i), (toggle != 0 ? _("toggle fixed-angle") :
				(negate != 0 ? _("made not fixed-angle") : _("made fixed-angle"))));
	} else if (namesamen(pp, "slide", l) == 0 && l >= 2)
	{
		if (namesame(el_curconstraint->conname, "layout") != 0)
		{
			us_abortcommand(_("Use the layout constraint system for slidable arcs"));
			(void)us_pophighlight(0);
			return;
		}
		i = us_modarcbits((INTSML)((toggle != 0 ? 25 : (negate != 0 ? 5 : 4))), 0, "", list);
		if (i >= 0)
			ttyputverbose("%d %s %s", i, makeplural(_("arc"), i), (toggle != 0 ? _("toggle slidability") :
				(negate != 0 ? _("can not slide in ports") : _("can slide in ports"))));
	} else if (namesamen(pp, "temp-rigid", l) == 0 && l >= 1)
	{
		if (namesame(el_curconstraint->conname, "layout") != 0)
		{
			us_abortcommand(_("Use the layout constraint system for rigid arcs"));
			(void)us_pophighlight(0);
			return;
		}
		i = us_modarcbits((INTSML)((negate != 0 ? 7 : 6)), 0, "", list);
		if (i >= 0) ttyputverbose(_("%d %s made temporarily %srigid"), i, makeplural(_("arc"), i),
			(negate!=0 ? _("un-") : ""));
	} else if (namesamen(pp, "ends-extend", l) == 0 && l >= 1)
	{
		i = us_modarcbits((INTSML)((toggle != 0 ? 26 : (negate != 0 ? 9 : 8))), 1, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle end extension") :
				(negate != 0 ? _("given no end extension") : _("given full end extension"))));
	} else if (namesamen(pp, "directional", l) == 0 && l >= 1)
	{
		i = us_modarcbits((INTSML)((toggle != 0 ? 27 : (negate != 0 ? 11: 10))), 1, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle directionality") :
				(negate != 0 ? _("given no direction") : _("given direction"))));
	} else if (namesamen(pp, "negated", l) == 0 && l >= 2)
	{
		i = us_modarcbits((INTSML)((toggle != 0 ? 28 : (negate != 0 ? 13 : 12))), 1, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle negation") : (negate != 0 ? _("not negated") : _("negated"))));
	} else if (namesamen(pp, "skip-tail", l) == 0 && l >= 6)
	{
		i = us_modarcbits((INTSML)((toggle != 0 ? 29 : (negate != 0 ? 15 : 14))), 1, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle tail use") : (negate!=0 ? _("use tail") : _("do not use tail"))));
	} else if (namesamen(pp, "skip-head", l) == 0 && l >= 6)
	{
		i = us_modarcbits((INTSML)((toggle != 0 ? 30 : (negate != 0 ? 17 : 16))), 1, "", list);
		if (i >= 0) ttyputverbose("%d %s %s", i, makeplural(_("arc"), i),
			(toggle != 0 ? _("toggle head use") : (negate!=0 ? _("use head") : _("do not use head"))));
	} else if (namesamen(pp, "reverse", l) == 0 && l >= 2)
	{
		if (negate != 0) ttyputerr(_("'arc reverse' is a relative notion, 'not' ignored"));
		i = us_modarcbits(18, 1, "", list);
		if (i >= 0) ttyputverbose(_("%d %s direction reversed"), i, makeplural(_("arc"), i));
	} else if (namesamen(pp, "constraint", l) == 0 && l >= 2)
	{
		if (namesame(el_curconstraint->conname, "linear") != 0)
		{
			us_abortcommand(_("First switch to the linear inequalities constraint system"));
			(void)us_pophighlight(0);
			return;
		}
		if (count <= 1)
		{
			if (negate != 0) (void)us_modarcbits(19, 1, "", list); else
				(void)us_modarcbits(20, 0, "", list);
		} else
		{
			if (count > 2 && namesamen(par[2], "add", strlen(par[2])) == 0)
				i = us_modarcbits(22, 1, par[1], list); else
					i = us_modarcbits(21, 1, par[1], list);
		}
	} else if (namesamen(pp, "name", l) == 0 && l >= 2)
	{
		if (list[0]->entrytype != OBJARCINST || list[1] != NOGEOM)
		{
			us_abortcommand(_("Select only one arc when naming"));
			(void)us_pophighlight(0);
			return;
		}
		ai = list[0]->entryaddr.ai;

		if (negate == 0)
		{
			if (count < 2)
			{
				count = ttygetparam(_("ARC name: "), &us_arcnamep, MAXPARS-1, &par[1]) + 1;
				if (count == 1)
				{
					us_abortedmsg();
					(void)us_pophighlight(0);
					return;
				}
			}
			pp = par[1];

			/* get the former name on this arc */
			net = getnetwork(pp, ai->parent);
			if (net != NONETWORK)
			{
				if (ai->network == net)
					ttyputmsg(_("Network %s is already on this arc"), describenetwork(net));
			} else ttyputverbose(_("Network %s defined"), pp);
			us_setarcname(ai, pp);
		} else
		{
			var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
			if (var == NOVARIABLE)
			{
				us_abortcommand(_("This arc has no name"));
				(void)us_pophighlight(0);
				return;
			}
			if (var != NOVARIABLE) us_setarcname(ai, (char *)0);
			ttyputverbose(_("Arc name removed"));
		}
	} else if (namesamen(pp, "center", l) == 0 && l >= 2)
	{
		if (list[0]->entrytype != OBJARCINST || list[1] != NOGEOM)
		{
			us_abortcommand(_("Select only one arc when changing curvature"));
			(void)us_pophighlight(0);
			return;
		}
		ai = list[0]->entryaddr.ai;

		/* make sure the arc can handle it */
		if ((ai->proto->userbits&CANCURVE) == 0 && negate == 0)
		{
			us_abortcommand(_("This arc cannot curve"));
			(void)us_pophighlight(0);
			return;
		}

		startobjectchange((INTBIG)ai, VARCINST);

		/* change the arc center information */
		if (negate == 0)
		{
			if (us_demandxy(&xcur, &ycur))
			{
				(void)us_pophighlight(0);
				return;
			}

			/* get true center of arc through cursor */
			ang = (INTSML)(((ai->userbits&AANGLE) >> AANGLESH) * 10);
			acx = (ai->end[0].xpos + ai->end[1].xpos) / 2;
			acy = (ai->end[0].ypos + ai->end[1].ypos) / 2;
			(void)intersect(xcur, ycur, ang, acx, acy, (ang+900)%3600, &ix, &iy);
			r = computedistance(ai->end[0].xpos, ai->end[0].ypos, ix, iy);

			/* now see if this point will be re-created */
			(void)findcenters(r, ai->end[0].xpos,ai->end[0].ypos,
				ai->end[1].xpos, ai->end[1].ypos, ai->length, &x1,&y1, &x2,&y2);
			if (abs(x1-ix)+abs(y1-iy) < abs(x2-ix)+abs(y2-iy)) r = -r;

			(void)setvalkey((INTBIG)ai, VARCINST, el_arc_radius, r, VINTEGER);
		} else (void)delvalkey((INTBIG)ai, VARCINST, el_arc_radius);
		(void)modifyarcinst(ai, 0, 0, 0, 0, 0);

		/* restore highlighting */
		endobjectchange((INTBIG)ai, VARCINST);
	} else if (namesamen(pp, "shorten", l) == 0 && l >= 2)
	{
		/* get the necessary polygon */
		if (poly == NOPOLYGON) poly = allocstaticpolygon(4, us_aid->cluster);

		l = 0;
		for(i=0; list[i] != NOGEOM; i++)
		{
			ai = list[i]->entryaddr.ai;
			for(j=0; j<2; j++)
			{
				shapeportpoly(ai->end[j].nodeinst, ai->end[j].portarcinst->proto,
					poly, 0);
				ix = ai->end[1-j].xpos;   iy = ai->end[1-j].ypos;
				closestpoint(poly, &ix, &iy);
				dx[j] = ix - ai->end[j].xpos;
				dy[j] = iy - ai->end[j].ypos;
			}
			if (dx[0] != 0 || dy[0] != 0 || dx[1] != 0 || dy[1] != 0)
			{
				startobjectchange((INTBIG)ai, VARCINST);
				modifyarcinst(ai, 0, dx[0], dy[0], dx[1], dy[1]);
				endobjectchange((INTBIG)ai, VARCINST);
				l++;
			}
		}
		ttyputmsg("Shortened %d %s", l, makeplural(_("arc"), l));
	} else if (namesamen(pp, "curve", l) == 0 && l >= 2)
	{
		if (list[0]->entrytype != OBJARCINST || list[1] != NOGEOM)
		{
			us_abortcommand(_("Select only one arc when changing curvature"));
			(void)us_pophighlight(0);
			return;
		}
		ai = list[0]->entryaddr.ai;

		/* make sure the arc can handle it */
		if ((ai->proto->userbits&CANCURVE) == 0 && negate == 0)
		{
			us_abortcommand(_("This arc cannot curve"));
			(void)us_pophighlight(0);
			return;
		}

		startobjectchange((INTBIG)ai, VARCINST);

		/* change the arc center information */
		if (negate == 0)
		{
			REGISTER INTBIG r0x, r0y, r1x, r1y, r2x, r2y, rpx, rpy, r02x, r02y, rcx, rcy;
			REGISTER float u, v, t;

			if (us_demandxy(&xcur, &ycur))
			{
				(void)us_pophighlight(0);
				return;
			}

			/* compute the center position of the arc from three points */
			r0x = ai->end[0].xpos;   r0y = ai->end[0].ypos;
			r1x = ai->end[1].xpos;   r1y = ai->end[1].ypos;
			r2x = xcur;              r2y = ycur;
			r02x = r2x-r0x;          r02y = r2y-r0y;
			rpx = r0y-r1y;           rpy = r1x-r0x;
			u = (float)r02x;   u *= (r2x-r1x);
			v = (float)r02y;   v *= (r2y-r1y);
			t = u + v;
			u = (float)r02x;   u *= rpx;
			v = (float)r02y;   v *= rpy;
			t /= (u + v) * 2.0f;
			rcx = r0x + (INTBIG)((r1x-r0x)/2 + t*rpx);
			rcy = r0y + (INTBIG)((r1y-r0y)/2 + t*rpy);

			/* now see if this point will be re-created */
			r = computedistance(r0x,r0y, rcx,rcy);
			(void)findcenters(r, r0x,r0y, r1x,r1y, ai->length, &x1,&y1, &x2,&y2);
			if (abs(x1-rcx)+abs(y1-rcy) < abs(x2-rcx)+abs(y2-rcy)) r = -r;

			(void)setvalkey((INTBIG)ai, VARCINST, el_arc_radius, r, VINTEGER);
		} else (void)delvalkey((INTBIG)ai, VARCINST, el_arc_radius);
		(void)modifyarcinst(ai, 0, 0, 0, 0, 0);

		/* highlight the new arc */
		endobjectchange((INTBIG)ai, VARCINST);
	} else ttyputbadusage("arc");

	/* clean-up */
	(void)us_pophighlight(1);
}

void us_array(INTSML count, char *par[])
{
	REGISTER INTSML xsize, ysize, flipx, flipy, staggerx, staggery, ro, tr, noname, l,
		diagonal, first;
	REGISTER INTBIG x, y, xpos, ypos, nodecount, arccount, i, lx, hx, ly, hy, cx, cy,
		cx0, cy0, cx1, cy1, xoff0, xoff1, yoff0, yoff1, xoff, yoff, keepbits;
	REGISTER GEOM **list, *geom;
	INTBIG xoverlap, yoverlap, plx, ply, phx, phy;
	REGISTER char *pt1, *pt2, *portname;
	char totalname[200], *objname;
	REGISTER NODEINST *ni, *newno, **nodelist, *ni0, *ni1;
	REGISTER ARCINST *ai, **arclist, *newai;
	REGISTER PORTPROTO *pp;
	REGISTER NODEPROTO *np;
	REGISTER PORTEXPINST *pe;
	REGISTER VARIABLE *var;

	/* special case for the "array file" command */
	if (count > 0 && namesamen(par[0], "file", strlen(par[0])) == 0)
	{
		us_arrayfromfile(par[1]);
		return;
	}

	/* get the objects to be arrayed */
	list = us_gethighlighted(OBJNODEINST|OBJARCINST);
	if (list[0] == NOGEOM)
	{
		us_abortcommand(_("Must select circuitry before arraying it"));
		return;
	}
	np = geomparent(list[0]);

	/* disallow arraying if lock is on */
	for(i=0; list[i] != NOGEOM; i++)
	{
		if (list[i]->entrytype == OBJARCINST)
		{
			if (us_canedit(np, NONODEPROTO, 1) != 0) return;
		} else
		{
			if (us_canedit(np, list[i]->entryaddr.ni->proto, 1) != 0) return;
		}
	}

	/* see if the array is to be unnamed (last argument is "no-names") */
	noname = diagonal = 0;
	while (count > 0)
	{
		l = strlen(pt1 = par[count-1]);
		if (namesamen(pt1, "no-names", l) == 0)
		{
			noname = 1;
			count--;
		} else if (namesamen(pt1, "diagonal", l) == 0)
		{
			diagonal = 1;
			count--;
		} else break;
	}

	/* get the X extent of the array */
	if (count == 0)
	{
		ttyputusage("array XREP[f][s] [YREP[f][s] [XOVRLP [YOVRLP [no-names][diagonal]]]]");
		return;
	}
	pt1 = par[0];
	xsize = (INTSML)myatoi(pt1);
	flipx = staggerx = 0;
	for(i = strlen(pt1)-1; i > 0; i--)
	{
		if (pt1[i] == 'f') flipx = 1; else
			if (pt1[i] == 's') staggerx = 1; else
				break;
	}

	/* get the Y extent of the array */
	if (count == 1) pt2 = "1"; else pt2 = par[1];
	ysize = atoi(pt2);
	flipy = staggery = 0;
	for(i = strlen(pt2)-1; i > 0; i--)
	{
		if (pt2[i] == 'f') flipy = 1; else
			if (pt2[i] == 's') staggery = 1; else
				break;
	}

	/* check for nonsense */
	if (xsize <= 1 && ysize <= 1)
	{
		us_abortcommand(_("One dimension of the array must be greater than 1"));
		return;
	}
	if (diagonal != 0 && xsize != 1 && ysize != 1)
	{
		us_abortcommand(_("Diagonal arrays need one dimension to be 1"));
		return;
	}

	/* mark the list of nodes and arcs in the facet that will be arrayed */
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		ni->temp1 = 0;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		ai->temp1 = 0;
	for(i=0; list[i] != NOGEOM; i++)
	{
		geom = list[i];
		if (geom->entrytype == OBJNODEINST)
		{
			ni = geom->entryaddr.ni;
			ni->temp1 = 1;
		} else
		{
			ai = geom->entryaddr.ai;
			ai->temp1 = 1;
			ai->end[0].nodeinst->temp1 = ai->end[1].nodeinst->temp1 = 1;
		}
	}

	/* count the number of nodes and arcs that will be arrayed */
	nodecount = arccount = 0;
	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
		if (ni->temp1 != 0) nodecount++;
	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
		if (ai->temp1 != 0) arccount++;

	/* build arrays of nodes and arcs that will be arrayed */
	if (nodecount > 0)
	{
		nodelist = (NODEINST **)emalloc(nodecount * (sizeof (NODEINST *)), el_tempcluster);
		if (nodelist == 0) return;
		for(ni = np->firstnodeinst, i=nodecount-1; ni != NONODEINST; ni = ni->nextnodeinst)
			if (ni->temp1 != 0) nodelist[i--] = ni;
	}
	if (arccount > 0)
	{
		arclist = (ARCINST **)emalloc(arccount * (sizeof (ARCINST *)), el_tempcluster);
		if (arclist == 0)
		{
			if (nodecount > 0) efree((char *)nodelist);
			return;
		}
		for(ai = np->firstarcinst, i=0; ai != NOARCINST; ai = ai->nextarcinst)
			if (ai->temp1 != 0) arclist[i++] = ai;
	}

	/* determine spacing between arrayed objects */
	first = 1;
	for(i=0; i<nodecount; i++)
	{
		ni = nodelist[i];
		if (first != 0)
		{
			lx = ni->geom->lowx;   hx = ni->geom->highx;
			ly = ni->geom->lowy;   hy = ni->geom->highy;
			first = 0;
		} else
		{
			if (ni->geom->lowx < lx) lx = ni->geom->lowx;
			if (ni->geom->highx > hx) hx = ni->geom->highx;
			if (ni->geom->lowy < ly) ly = ni->geom->lowy;
			if (ni->geom->highy > hy) hy = ni->geom->highy;
		}
	}
	for(i=0; i<arccount; i++)
	{
		ai = arclist[i];
		if (first != 0)
		{
			lx = ai->geom->lowx;   hx = ai->geom->highx;
			ly = ai->geom->lowy;   hy = ai->geom->highy;
			first = 0;
		} else
		{
			if (ai->geom->lowx < lx) lx = ai->geom->lowx;
			if (ai->geom->highx > hx) hx = ai->geom->highx;
			if (ai->geom->lowy < ly) ly = ai->geom->lowy;
			if (ai->geom->highy > hy) hy = ai->geom->highy;
		}
	}
	xoverlap = hx - lx;   yoverlap = hy - ly;
	cx = (lx + hx) / 2;   cy = (ly + hy) / 2;

	/* get the overlap (if any) */
	if (count >= 3)
	{
		xoverlap -= atola(par[2]);
		if (count >= 4) yoverlap -= atola(par[3]);
	}

	/* check to ensure the array doesn't overflow the address space */
	for(x=0; x<xsize; x++)
	{
		plx = lx + xoverlap * x;
		phx = hx + xoverlap * x;
		if (staggery != 0) { plx += xoverlap/2;   phx += xoverlap/2; }
		if (xoverlap > 0)
		{
			if (phx < lx) break;
		} else
		{
			if (plx > hx) break;
		}
	}
	if (x < xsize)
	{
		us_abortcommand(_("Array is too wide...place elements closer or use fewer"));
		if (nodecount > 0) efree((char *)nodelist);
		if (arccount > 0) efree((char *)arclist);
		return;
	}
	for(y=0; y<ysize; y++)
	{
		ply = ly + yoverlap * y;
		phy = hy + yoverlap * y;
		if (staggerx != 0) { ply += yoverlap/2;   phy += yoverlap/2; }
		if (yoverlap > 0)
		{
			if (phy < ly) break;
		} else
		{
			if (ply > hy) break;
		}
	}
	if (y < ysize)
	{
		us_abortcommand(_("Array is too tall...place elements closer or use fewer"));
		if (nodecount > 0) efree((char *)nodelist);
		if (arccount > 0) efree((char *)arclist);
		return;
	}

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

	/* create the array */
	for(x=0; x<xsize; x++) for(y=0; y<ysize; y++)
	{
		if (stopping(STOPREASONARRAY)) break;
		if (x == 0 && y == 0) continue;

		/* first replicate the nodes */
		for(i=0; i<nodecount; i++)
		{
			ni = nodelist[i];
			if (diagonal != 0 && xsize == 1) xpos = cx + xoverlap * y; else
				xpos = cx + xoverlap * x;
			if (diagonal != 0 && ysize == 1) ypos = cy + yoverlap * x; else
				ypos = cy + yoverlap * y;
			xoff = (ni->lowx + ni->highx) / 2 - cx;
			yoff = (ni->lowy + ni->highy) / 2 - cy;
			if ((x&1) != 0 && staggerx != 0) ypos += yoverlap/2;
			if ((y&1) != 0 && staggery != 0) xpos += xoverlap/2;
			ro = ni->rotation;  tr = ni->transpose;
			if ((x&1) != 0 && flipx)
			{
				if (tr) ro += 2700; else ro += 900;
				ro %= 3600;
				tr = 1 - tr;
				xoff = -xoff;
			}
			if ((y&1) != 0 && flipy)
			{
				if (tr) ro += 900; else ro += 2700;
				ro %= 3600;
				tr = 1 - tr;
				yoff = -yoff;
			}
			xpos += xoff;   ypos += yoff;
			plx = xpos - (ni->highx - ni->lowx) / 2;   phx = plx + (ni->highx - ni->lowx);
			ply = ypos - (ni->highy - ni->lowy) / 2;   phy = ply + (ni->highy - ni->lowy);
			newno = newnodeinst(ni->proto, plx, phx, ply, phy, tr, ro, np);
			if (newno == NONODEINST)
			{
				ttyputerr(_("Problem creating array nodes"));
				if (nodecount > 0) efree((char *)nodelist);
				if (arccount > 0) efree((char *)arclist);
				(void)us_pophighlight(0);
				return;
			}
			keepbits = NEXPAND | HARDSELECTN;
			newno->userbits = (newno->userbits & ~keepbits) | (ni->userbits & keepbits);
			(void)copyvars((INTBIG)ni, VNODEINST, (INTBIG)newno, VNODEINST);
			if (noname == 0)
			{
				objname = "";
				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
				if (var != NOVARIABLE)
				{
					if (strcmp((char *)var->addr, "0") != 0 && strcmp((char *)var->addr, "0-0") != 0)
						objname = (char *)var->addr;
				}
				if (xsize <= 1 || ysize <= 1)
					(void)sprintf(totalname, "%s%ld", objname, x+y); else
						(void)sprintf(totalname, "%s%ld-%ld", objname, x, y);
				var = setvalkey((INTBIG)newno, VNODEINST, el_node_name,
					(INTBIG)totalname, VSTRING|VDISPLAY);

				/* shift text down if on a facet instance */
				if (var != NOVARIABLE && ni->proto->primindex == 0)
				{
					var->textdescript = us_setdescriptoffset(var->textdescript,
						0, (ni->highy-ni->lowy) / el_curlib->lambda[el_curtech->techindex]);
				}
			}

			/* copy the ports, too */
			if ((us_useroptions&DUPCOPIESPORTS) != 0)
			{
				for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
				{
					portname = us_uniqueportname(pe->exportproto->protoname, np);
					pp = newportproto(np, newno, pe->proto, portname);
					if (pp == NOPORTPROTO)
					{
						us_abortcommand(_("Cannot create port %s"), portname);
						return;
					}
					pp->userbits = pe->exportproto->userbits;
					pp->textdescript = pe->exportproto->textdescript;
					if (copyvars((INTBIG)pe->exportproto, VPORTPROTO, (INTBIG)pp, VPORTPROTO) != 0)
						return;
				}
			}

			endobjectchange((INTBIG)newno, VNODEINST);
			ni->temp1 = (UINTBIG)newno;
		}

		/* next replicate the arcs */
		for(i=0; i<arccount; i++)
		{
			ai = arclist[i];
			cx0 = (ai->end[0].nodeinst->lowx + ai->end[0].nodeinst->highx) / 2;
			cy0 = (ai->end[0].nodeinst->lowy + ai->end[0].nodeinst->highy) / 2;
			xoff0 = ai->end[0].xpos - cx0;
			yoff0 = ai->end[0].ypos - cy0;

			cx1 = (ai->end[1].nodeinst->lowx + ai->end[1].nodeinst->highx) / 2;
			cy1 = (ai->end[1].nodeinst->lowy + ai->end[1].nodeinst->highy) / 2;
			xoff1 = ai->end[1].xpos - cx1;
			yoff1 = ai->end[1].ypos - cy1;
			if ((x&1) != 0 && flipx)
			{
				xoff0 = -xoff0;
				xoff1 = -xoff1;
			}
			if ((y&1) != 0 && flipy)
			{
				yoff0 = -yoff0;
				yoff1 = -yoff1;
			}

			ni0 = (NODEINST *)ai->end[0].nodeinst->temp1;
			ni1 = (NODEINST *)ai->end[1].nodeinst->temp1;
			cx0 = (ni0->lowx + ni0->highx) / 2;
			cy0 = (ni0->lowy + ni0->highy) / 2;
			cx1 = (ni1->lowx + ni1->highx) / 2;
			cy1 = (ni1->lowy + ni1->highy) / 2;
			newai = newarcinst(ai->proto, ai->width, ai->userbits, ni0, ai->end[0].portarcinst->proto,
				cx0+xoff0, cy0+yoff0, ni1, ai->end[1].portarcinst->proto, cx1+xoff1, cy1+yoff1, np);
			if (newai == NOARCINST)
			{
				ttyputerr(_("Problem creating array arcs"));
				if (nodecount > 0) efree((char *)nodelist);
				if (arccount > 0) efree((char *)arclist);
				(void)us_pophighlight(0);
				return;
			}
			(void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newai, VARCINST);
			if (noname == 0)
			{
				objname = "";
				var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
				if (var != NOVARIABLE)
				{
					if (strcmp((char *)var->addr, "0") != 0 && strcmp((char *)var->addr, "0-0") != 0)
						objname = (char *)var->addr;
				}
				if (xsize <= 1 || ysize <= 1)
					(void)sprintf(totalname, "%s%ld", objname, x+y); else
						(void)sprintf(totalname, "%s%ld-%ld", objname, x, y);
				var = setvalkey((INTBIG)newai, VARCINST, el_arc_name,
					(INTBIG)totalname, VSTRING|VDISPLAY);
			}
			endobjectchange((INTBIG)newai, VARCINST);
		}
	}

	/* rename the replicated objects */
	if (noname == 0)
	{
		for(i=0; i<nodecount; i++)
		{
			ni = nodelist[i];
			startobjectchange((INTBIG)ni, VNODEINST);

			/* get former name of node */
			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name);
			objname = "";
			if (var != NOVARIABLE)
			{
				if (strcmp((char *)var->addr, "0") != 0 && strcmp((char *)var->addr, "0-0") != 0)
					objname = (char *)var->addr;
			}
			if (xsize <= 1 || ysize <= 1) (void)sprintf(totalname, "%s0", objname); else
				(void)sprintf(totalname, "%s0-0", objname);
			var = setvalkey((INTBIG)ni, VNODEINST, el_node_name, (INTBIG)totalname, VSTRING|VDISPLAY);

			/* shift text down if on a facet instance */
			if (var != NOVARIABLE && ni->proto->primindex == 0)
			{
				var->textdescript = us_setdescriptoffset(var->textdescript,
					0, (ni->highy-ni->lowy) / el_curlib->lambda[el_curtech->techindex]);
			}
			endobjectchange((INTBIG)ni, VNODEINST);
		}
		for(i=0; i<arccount; i++)
		{
			ai = arclist[i];
			startobjectchange((INTBIG)ai, VARCINST);

			/* get former name of node */
			var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name);
			objname = "";
			if (var != NOVARIABLE)
			{
				if (strcmp((char *)var->addr, "0") != 0 && strcmp((char *)var->addr, "0-0") != 0)
					objname = (char *)var->addr;
			}
			if (xsize <= 1 || ysize <= 1) (void)sprintf(totalname, "%s0", objname); else
				(void)sprintf(totalname, "%s0-0", objname);
			var = setvalkey((INTBIG)ai, VARCINST, el_arc_name, (INTBIG)totalname, VSTRING|VDISPLAY);
			endobjectchange((INTBIG)ai, VARCINST);
		}
	}

	/* restore highlighting */
	(void)us_pophighlight(0);
	if (nodecount > 0) efree((char *)nodelist);
	if (arccount > 0) efree((char *)arclist);
}

void us_bind(INTSML count, char *par[])
{
	REGISTER INTSML i, j, l, negated, bindex, newcount, key, but, menu, popup,
		x, y, backgroundcolor, swap, lastchar;
	REGISTER INTBIG varkey;
	INTSML important, menumessagesize;
	REGISTER USERCOM *rb;
	REGISTER char *pp, *letter, *pt, *popname, *str;
	NODEPROTO *nodeglyph;
	ARCPROTO *arcglyph;
	POPUPMENU *pm;
	REGISTER VARIABLE *var;
	COMMANDBINDING commandbinding;
	char partial[80], *a[MAXPARS+1], *menumessage;
	extern COMCOMP us_bindp, us_bindsetp, us_bindbuttonp, us_bindkeyp,
		us_bindmenuryp, us_bindmenuxp, us_userp, us_bindpopnp, us_bindpoprep;

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

	if (namesamen(pp, "not", l) == 0 && l >= 1)
	{
		negated = 1;
		if (count <= 1)
		{
			ttyputusage("bind not verbose");
			return;
		}
		l = strlen(pp = par[1]);
	}

	if (namesamen(pp, "verbose", l) == 0 && l >= 1)
	{
		if (negated == 0)
		{
			us_aid->aidstate |= ECHOBIND;
			ttyputverbose(_("Key/menu/button commands echoed"));
		} else
		{
			us_aid->aidstate &= ~ECHOBIND;
			ttyputverbose(_("Key/menu/button commands not echoed"));
		}
		return;
	}

	if (namesamen(pp, "get", l) == 0 && l >= 1)
	{
		if (count < 2)
		{
			ttyputusage("bind get LETTER OBJECT");
			return;
		}
		letter = par[1];
		if (!isalpha(*letter) || letter[1] != 0)
		{
			us_abortcommand(_("A single letter must be used, not '%s'"), letter);
			return;
		}

		l = strlen(pp = par[2]);
		if (namesamen(pp, "button", l) == 0 && l >= 1)
		{
			if (count < 4)
			{
				ttyputusage("bind get LETTER button BUTTON");
				return;
			}
			pp = par[3];
			for(but=0; but<buttoncount(); but++)
			{
				(void)strcpy(partial, buttonname(but, &important));
				for(j=0; j<important; j++)
				{
					if (isupper(pp[j])) pp[j] = tolower(pp[j]);
					if (isupper(partial[j])) partial[j] = tolower(partial[j]);
					if (pp[j] != partial[j]) break;
				}
				if (j >= important) break;
			}
			if (j < important)
			{
				us_abortcommand(_("Button %s unknown"), pp);
				return;
			}
			var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_binding_buttons);
			if (var == NOVARIABLE) return;
			us_parsebinding(((char **)var->addr)[but], &commandbinding);
			if (*commandbinding.command == 0) commandbinding.command = "-";
			(void)setval((INTBIG)us_aid, VAID, us_commandvarname(*letter),
				(INTBIG)commandbinding.command, VSTRING|VDONTSAVE);
			us_freebindingparse(&commandbinding);
			return;
		}
		if (namesamen(pp, "key", l) == 0 && l >= 1)
		{
			if (count < 4)
			{
				ttyputusage("bind get LETTER key KEY");
				return;
			}
			pp = par[3];
			key = *pp & 255;
			var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_binding_keys);
			if (var == NOVARIABLE) return;
			us_parsebinding(((char **)var->addr)[key], &commandbinding);
			if (*commandbinding.command == 0) commandbinding.command = "-";
			(void)setval((INTBIG)us_aid, VAID, us_commandvarname(*letter),
				(INTBIG)commandbinding.command, VSTRING|VDONTSAVE);
			us_freebindingparse(&commandbinding);
			return;
		}
		if (namesamen(pp, "menu", l) == 0 && l >= 1)
		{
			if (count < 5)
			{
				ttyputusage("bind get LETTER menu ROW COLUMN");
				return;
			}
			y = (INTSML)myatoi(pp = par[3]);
			x = (INTSML)myatoi(pp = par[4]);
			if (x < 0 || y < 0 || x >= us_menux || y >= us_menuy)
			{
				us_abortcommand(_("Index of menu entry out of range"));
				return;
			}
			var = getvalkey((INTBIG)us_aid, VAID, VSTRING|VISARRAY, us_binding_menu);
			if (var == NOVARIABLE) return;
			if (us_menupos <= 1)
				str = ((char **)var->addr)[y * us_menux + x]; else
					str = ((char **)var->addr)[x * us_menuy + y];
			us_parsebinding(str, &commandbinding);
			if (*commandbinding.command == 0) commandbinding.command = "-";
			(void)setval((INTBIG)us_aid, VAID, us_commandvarname(*letter),
				(INTBIG)commandbinding.command, VSTRING|VDONTSAVE);
			us_freebindingparse(&commandbinding);
			return;
		}
		if (namesamen(pp, "popup", l) == 0 && l >= 1)
		{
			if (count < 5)
			{
				ttyputusage("bind get LETTER popup MENU ENTRY");
				return;
			}
			pm = us_getpopupmenu(par[3]);
			if (pm == NOPOPUPMENU)
			{
				us_abortcommand(_("No popup menu called %s"), par[3]);
				return;
			}
			popup = myatoi(par[4]) + 1;
			if (popup <= 0 || popup > pm->total)
			{
				us_abortcommand(_("Popup menu entry must be from 0 to %d"), pm->total-1);
				return;
			}
			rb = pm->list[popup-1].response;

			/* make sure there is a command there */
			if (rb == NOUSERCOM || rb->active < 0) pp = "-"; else
			{
				(void)initinfstr();
				(void)addstringtoinfstr(rb->comname);
				(void)us_appendargs(rb);
				pp = returninfstr();
			}
			(void)setval((INTBIG)us_aid, VAID, us_commandvarname(*letter),
				(INTBIG)pp, VSTRING|VDONTSAVE);
			return;
		}
		ttyputusage("bind get LETTER key|button|menu|popup ENTRY");
		return;
	}

	if (namesamen(pp, "set", l) == 0 && l >= 1)
	{
		if (count < 2)
		{
			count = ttygetparam(_("Key, Menu, Button, or Popup: "), &us_bindsetp, MAXPARS-1, &par[1])+1;
			if (count == 1)
			{
				us_abortedmsg();
				return;
			}
		}
		l = strlen(pp = par[1]);
		important = 0;
		popup = menu = key = 0;
		nodeglyph = NONODEPROTO;
		arcglyph = NOARCPROTO;
		backgroundcolor = 0;
		menumessage = 0;
		menumessagesize = TXTLARGE;

		if (namesamen(pp, "button", l) == 0 && l >= 1)
		{
			if (count < 3)
			{
				count = ttygetparam(_("Button name: "), &us_bindbuttonp, MAXPARS-2, &par[2])+2;
				if (count == 2)
				{
					us_abortedmsg();
					return;
				}
			}
			pp = par[2];
			for(but=0; but<buttoncount(); but++)
			{
				(void)strcpy(partial, buttonname(but, &important));
				for(j=0; j<important; j++)
				{
					if (isupper(pp[j])) pp[j] = tolower(pp[j]);
					if (isupper(partial[j])) partial[j] = tolower(partial[j]);
					if (pp[j] != partial[j]) break;
				}
				if (j >= important) break;
			}
			if (j < important)
			{
				us_abortcommand(_("Button %s unknown"), pp);
				return;
			}
			important = 3;
			varkey = us_binding_buttons;
			bindex = but;
		} else if (namesamen(pp, "key", l) == 0 && l >= 1)
		{
			if (count < 3)
			{
				count = ttygetparam(_("Key name: "), &us_bindkeyp, MAXPARS-2, &par[2])+2;
				if (count == 2)
				{
					us_abortedmsg();
					return;
				}
			}
			pp = par[2];
			if (namesamen(pp, "m", 1) == 0 && pp[1] == '-')
			{
				/* the form M-X implies the meta key (high bit) */
				key = pp[2] | 0200;
			} else key = *pp & 255;
			varkey = us_binding_keys;
			bindex = key;
			important = 3;
		} else if ((namesamen(pp, "popup", l) == 0 ||
			namesamen(pp, "input-popup", l) == 0) && l >= 1)
		{
			if (count < 3)
			{
				count = ttygetparam(_("Popup menu name: "), &us_bindpopnp, MAXPARS-2, &par[2])+2;
				if (count == 2)
				{
					us_abortedmsg();
					return;
				}
			}
			popname = par[2];
			pm = us_getpopupmenu(popname);
			if (pm == NOPOPUPMENU)
			{
				us_abortcommand(_("No popup menu called %s"), par[2]);
				return;
			}
			(void)initinfstr();
			(void)addstringtoinfstr("USER_binding_popup_");
			(void)addstringtoinfstr(popname);
			varkey = makekey(returninfstr());

			/* look for possible message indication */
			if (count >= 5 && namesamen(par[3], "message", strlen(par[3])) == 0)
			{
				menumessage = par[4];
				count -= 2;
				par += 2;
			}

			if (count < 4)
			{
				count = ttygetparam(_("Popup menu entry: "), &us_bindpoprep, MAXPARS-3, &par[3])+3;
				if (count == 3)
				{
					us_abortedmsg();
					return;
				}
			}
			popup = myatoi(par[3]) + 1;
			if (popup <= 0 || popup > pm->total)
			{
				us_abortcommand(_("Popup menu entry must be from 0 to %d"), pm->total-1);
				return;
			}
			if (pp[0] == 'i') popup = -popup;
			important = 4;
			bindex = abs(popup);
		} else if (namesamen(pp, "menu", l) == 0 && l >= 1)
		{
			/* look for possible glyph/message/background indication */
			while (count >= 4)
			{
				l = strlen(pp = par[2]);
				if (namesamen(pp, "glyph", l) == 0)
				{
					nodeglyph = getnodeproto(par[3]);
					if (nodeglyph == NONODEPROTO)
					{
						us_abortcommand(_("Cannot find %s"), par[3]);
						return;
					}
					count -= 2;
					par += 2;
					continue;
				}
				if (namesamen(pp, "message", l) == 0)
				{
					menumessage = par[3];
					count -= 2;
					par += 2;
					continue;
				}
				if (namesamen(pp, "textsize", l) == 0)
				{
					menumessagesize = (INTSML)us_gettextsize(par[3], TXTLARGE);
					count -= 2;
					par += 2;
					continue;
				}
				if (namesamen(pp, "background", l) == 0)
				{
					backgroundcolor = getecolor(par[3]);
					count -= 2;
					par += 2;
					continue;
				}
				break;
			}

			/* get the menu row number */
			if (count < 3)
			{
				count = ttygetparam(_("Menu row: "), &us_bindmenuryp, MAXPARS-2, &par[2]) + 2;
				if (count == 2)
				{
					us_abortedmsg();
					return;
				}
			}
			y = (INTSML)myatoi(pp = par[2]);
			if (count < 4)
			{
				count = ttygetparam(_("Menu column: "), &us_bindmenuxp, MAXPARS+3, &par[3])+3;
				if (count == 3)
				{
					us_abortedmsg();
					return;
				}
			}
			x = (INTSML)myatoi(pp = par[3]);

			/* reorder the meaning if the optional "w" character is appended */
			lastchar = par[2][ strlen(par[2])-1 ];
			if (lastchar == 'w' && us_menuy > us_menux)
			{
				swap = x;   x = y;   y = swap;
			} else
			{
				lastchar = par[3][ strlen(par[3])-1 ];
				if (lastchar == 'w' && us_menux > us_menuy)
				{
					swap = x;   x = y;   y = swap;
				}
			}

			/* check validity */
			if (x < 0 || y < 0 || x >= us_menux || y >= us_menuy)
			{
				us_abortcommand(_("Index of menu entry out of range"));
				return;
			}
			varkey = us_binding_menu;
			if (us_menupos <= 1) bindex = y * us_menux + x; else
				bindex = x * us_menuy + y;
			important = 4;
			menu++;
		}

		if (important == 0)
		{
			ttyputusage("bind set key|button|menu|[input-]popup COMMAND");
			return;
		}

		if (count <= important)
			count = ttygetparam(_("Command: "), &us_userp, (INTSML)(MAXPARS-important),
				&par[important]) + important;
		(void)initinfstr();
		for(i=important; i<count; i++)
		{
			(void)addstringtoinfstr(par[i]);
			(void)addtoinfstr(' ');
		}

		newcount = us_parsecommand(returninfstr(), a);
		if (newcount <= 0)
		{
			us_abortedmsg();
			return;
		}

		/* handle un-binding */
		if (strcmp(a[0], "-") == 0)
		{
			if (key != 0 && us_islasteval(key))
			{
				us_abortcommand(_("Cannot un-bind the last 'tellaid user' key"));
				return;
			}
			(void)initinfstr();
			if (popup != 0)
			{
				if (popup < 0) (void)addstringtoinfstr("inputpopup="); else
					(void)addstringtoinfstr("popup=");
				(void)addstringtoinfstr(popname);
				(void)addtoinfstr(' ');
			}
			if (menumessage != 0)
			{
				(void)addstringtoinfstr("message=\"");
				for(pt = menumessage; *pt != 0; pt++)
				{
					if (*pt == '"') (void)addtoinfstr('^');
					(void)addtoinfstr(*pt);
				}
				(void)addstringtoinfstr("\" ");
			}
			(void)addstringtoinfstr("command=");
			(void)setindkey((INTBIG)us_aid, VAID, varkey, bindex, (INTBIG)returninfstr());
			return;
		}

		i = parse(a[0], &us_userp, 1);
		if (i < 0)
		{
			us_unknowncommand();
			return;
		}

		/* special case: cannot rebind the last "tellaid user" */
		if (i >= us_longcount || strcmp(us_lcommand[i].name, "tellaid") != 0 ||
			newcount != 1 || strcmp(a[1], "user") != 0)
		{
			if (key != 0 && us_islasteval(key))
			{
				us_abortcommand(_("Cannot re-bind the last 'tellaid user' key"));
				return;
			}
		}

		/* special case for menu binds of "getproto" command */
		if (menu != 0 && namesame(par[important], "getproto") == 0)
		{
			if (newcount > 2 && namesame(a[1], "node") == 0) nodeglyph = getnodeproto(a[2]); else
				if (newcount > 2 && namesame(a[1], "arc") == 0) arcglyph = getarcproto(a[2]); else
					if (newcount >= 2) nodeglyph = getnodeproto(a[1]);
		}

		/* set the variable that describes this new binding */
		(void)initinfstr();
		if (nodeglyph != NONODEPROTO)
		{
			(void)addstringtoinfstr("node=");
			if (nodeglyph->primindex != 0)
			{
				(void)addstringtoinfstr(nodeglyph->tech->techname);
				(void)addtoinfstr(':');
				(void)addstringtoinfstr(nodeglyph->primname);
			} else (void)addstringtoinfstr(describenodeproto(nodeglyph));
			(void)addtoinfstr(' ');
		}
		if (arcglyph != NOARCPROTO)
		{
			(void)addstringtoinfstr("arc=");
			(void)addstringtoinfstr(arcglyph->tech->techname);
			(void)addtoinfstr(':');
			(void)addstringtoinfstr(arcglyph->protoname);
			(void)addtoinfstr(' ');
		}
		if (backgroundcolor != 0)
		{
			(void)sprintf(partial, "background=%d ", backgroundcolor);
			(void)addstringtoinfstr(partial);
		}
		if (menumessage != 0)
		{
			(void)addstringtoinfstr("message=\"");
			for(pt = menumessage; *pt != 0; pt++)
			{
				if (*pt == '"') (void)addtoinfstr('^');
				(void)addtoinfstr(*pt);
			}
			(void)addstringtoinfstr("\" ");
			if (menumessagesize != 0)
			{
				(void)sprintf(partial, "messagesize=%d ", menumessagesize);
				(void)addstringtoinfstr(partial);
			}
		}
		if (popup != 0)
		{
			if (popup < 0) (void)addstringtoinfstr("inputpopup="); else
				(void)addstringtoinfstr("popup=");
			(void)addstringtoinfstr(popname);
			(void)addtoinfstr(' ');
		}
		(void)addstringtoinfstr("command=");
		for(j=important; j<count; j++)
		{
			if (j != important) (void)addtoinfstr(' ');
			(void)addstringtoinfstr(par[j]);
		}
		(void)setindkey((INTBIG)us_aid, VAID, varkey, bindex, (INTBIG)returninfstr());
		if (popup > 0) us_setkeyequiv(pm, bindex);
		return;
	}

	ttyputbadusage("bind");
}
