1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrcomek.c
6  * User interface tool: command handler for E through K
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "usr.h"
34 #include "usrtrack.h"
35 #include "usrdiacom.h"
36 #include "efunction.h"
37 #include "edialogs.h"
38 #include "tecart.h"
39 #include "tecgen.h"
40 #include "tecschem.h"
41 #include "sim.h"
42 #include "network.h"
43 #ifdef HAVE_UNISTD_H
44 #  include <unistd.h>
45 #endif
46 #ifdef HAVE_FCNTL_H
47 #  include <fcntl.h>
48 #endif
49 
50 #define MAXLINE 500		/* maximum length of news/help file input lines */
51 
52 /* working memory for "us_iterate()" */
53 static INTBIG *us_iterateaddr, *us_iteratetype;
54 static INTBIG  us_iteratelimit=0;
55 
56 /*
57  * Routine to free all memory associated with this module.
58  */
us_freecomekmemory(void)59 void us_freecomekmemory(void)
60 {
61 	if (us_iteratelimit > 0)
62 	{
63 		efree((CHAR *)us_iterateaddr);
64 		efree((CHAR *)us_iteratetype);
65 	}
66 }
67 
us_echo(INTBIG count,CHAR * par[])68 void us_echo(INTBIG count, CHAR *par[])
69 {
70 	REGISTER INTBIG lastquiet, j;
71 	REGISTER void *infstr;
72 
73 	lastquiet = ttyquiet(0);
74 	infstr = initinfstr();
75 	for(j=0; j<count; j++)
76 	{
77 		addstringtoinfstr(infstr, par[j]);
78 		addtoinfstr(infstr, ' ');
79 	}
80 	ttyputmsg(x_("%s"), returninfstr(infstr));
81 	(void)ttyquiet(lastquiet);
82 }
83 
84 /* Simulation: Select Node Index */
85 static DIALOGITEM us_selinddialogitems[] =
86 {
87  /*  1 */ {0, {76,120,100,200}, BUTTON, N_("OK")},
88  /*  2 */ {0, {76,16,100,96}, BUTTON, N_("Cancel")},
89  /*  3 */ {0, {4,4,20,216}, MESSAGE, N_("This node is arrayed")},
90  /*  4 */ {0, {24,4,40,216}, MESSAGE, N_("Which entry should be entered?")},
91  /*  5 */ {0, {48,4,64,216}, POPUP, x_("")}
92 };
93 static DIALOG us_selinddialog = {{75,75,184,301}, N_("Select Node Index"), 0, 5, us_selinddialogitems, 0, 0};
94 
95 /* special items for the "Select Node Index" dialog: */
96 #define DSNI_CHOICES       5		/* List of indices (popup) */
97 
us_editcell(INTBIG count,CHAR * par[])98 void us_editcell(INTBIG count, CHAR *par[])
99 {
100 	REGISTER INTBIG implicit, i, len, newwindow, nonredundant, intoicon, lambda, inplace, index,
101 		sigcount, itemHit;
102 	BOOLEAN newframe, push, exact;
103 	INTBIG lx, hx, ly, hy;
104 	INTBIG viewinfo[22];
105 	REGISTER NODEPROTO *np, *onp, *np1, *curcell;
106 	REGISTER NODEINST *ni, *stacknodeinst;
107 	REGISTER LIBRARY *lib, *olib;
108 	REGISTER VARIABLE *var;
109 	REGISTER void *dia;
110 	REGISTER WINDOWPART *win;
111 	REGISTER CHAR *pt;
112 	CHAR **nodenames;
113 	NODEINST *hini;
114 	XARRAY rotarray, transarray, xfarray, newarray;
115 	PORTPROTO *hipp;
116 
117 	/* get proper highlighting in subcell if port is selected */
118 	us_findlowerport(&hini, &hipp);
119 
120 	/* find the nodeinst in this window to go "down into" (if any) */
121 	newwindow = 0;
122 	nonredundant = 0;
123 	intoicon = 0;
124 	inplace = 0;
125 	index = 0;
126 	if (count == 1)
127 	{
128 		len = estrlen(par[0]);
129 		if (len == 0) count--; else
130 		{
131 			if (namesamen(par[0], x_("in-place"), len) == 0)
132 			{
133 				if (el_curwindowpart->inplacedepth >= MAXINPLACEDEPTH)
134 				{
135 					ttyputerr(_("Can only go down %ld levels in-place"), MAXINPLACEDEPTH);
136 					return;
137 				}
138 				inplace = 1;
139 				count--;
140 			}
141 		}
142 	}
143 	if (count == 0)
144 	{
145 		implicit = 1;
146 		ni = (NODEINST *)us_getobject(VNODEINST, FALSE);
147 		if (ni == NONODEINST) return;
148 		stacknodeinst = ni;
149 		np = ni->proto;
150 
151 		/* translate this reference if this is an icon cell */
152 		if (np->cellview == el_iconview)
153 		{
154 			implicit = 0;
155 			if (!isiconof(np, ni->parent))
156 			{
157 				onp = contentsview(np);
158 				if (onp != NONODEPROTO) np = onp;
159 			} else
160 			{
161 				intoicon = 1;
162 			}
163 		}
164 	} else
165 	{
166 		/* check for options */
167 		for(i=1; i<count; i++)
168 		{
169 			len = estrlen(par[i]);
170 			if (namesamen(par[i], x_("new-window"), len) == 0 && len > 1) newwindow++; else
171 			if (namesamen(par[i], x_("non-redundant"), len) == 0 && len > 1) nonredundant++; else
172 			{
173 				ttyputbadusage(x_("editcell"));
174 				return;
175 			}
176 		}
177 
178 		/* see if specified cell exists */
179 		implicit = 0;
180 		np = getnodeproto(par[0]);
181 
182 		/* if it is a new cell, create it */
183 		if (np == NONODEPROTO)
184 		{
185 			lib = el_curlib;
186 			for(pt = par[0]; *pt != 0; pt++) if (*pt == ':') break;
187 			if (*pt != ':') pt = par[0]; else
188 			{
189 				i = *pt;
190 				*pt = 0;
191 				lib = getlibrary(par[0]);
192 				*pt = (CHAR)i;
193 				if (lib == NOLIBRARY)
194 				{
195 					us_abortcommand(_("Cannot find library for new cell %s"), par[0]);
196 					return;
197 				}
198 				pt++;
199 			}
200 			np = us_newnodeproto(pt, lib);
201 			if (np == NONODEPROTO)
202 			{
203 				us_abortcommand(_("Cannot create cell %s"), par[0]);
204 				return;
205 			}
206 			ttyputverbose(M_("Editing new cell: %s"), par[0]);
207 		}
208 
209 		/* look through window for instances of this cell */
210 		stacknodeinst = NONODEINST;
211 		curcell = getcurcell();
212 		if (curcell != NONODEPROTO)
213 		{
214 			for(ni = curcell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
215 				if (ni->proto == np)
216 			{
217 				stacknodeinst = ni;
218 				break;
219 			}
220 		}
221 	}
222 
223 	/* make sure nodeinst is not primitive and in proper technology */
224 	if (np->primindex != 0)
225 	{
226 		us_abortcommand(_("Cannot edit primitive nodes"));
227 		return;
228 	}
229 
230 	push = FALSE;
231 	exact = FALSE;
232 	if (stacknodeinst != NONODEINST)
233 	{
234 		/* if in waveform window, switch to associated circuit window */
235 		if ((el_curwindowpart->state&WINDOWTYPE) == WAVEFORMWINDOW)
236 		{
237 			for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
238 				if (win != el_curwindowpart && win->curnodeproto == stacknodeinst->parent) break;
239 			if (win == NOWINDOWPART)
240 			{
241 				us_abortcommand(_("Cannot go down the hierarchy from this waveform window"));
242 				return;
243 			}
244 			(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_window_key, (INTBIG)win,
245 				VWINDOWPART|VDONTSAVE);
246 		}
247 
248 		/* if simulating, and node is arrayed, find out the index */
249 		if (stacknodeinst->arraysize > 1 && el_curwindowpart != NOWINDOWPART &&
250 			(el_curwindowpart->state&WINDOWMODE) == WINDOWSIMMODE)
251 		{
252 			var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
253 			if (var != NOVARIABLE)
254 			{
255 				/* must find out which index is being traversed */
256 				sigcount = net_evalbusname(APBUS, (CHAR *)var->addr, &nodenames,
257 					NOARCINST, NONODEPROTO, 0);
258 				dia = DiaInitDialog(&us_selinddialog);
259 				DiaSetPopup(dia, DSNI_CHOICES, sigcount, nodenames);
260 				for(;;)
261 				{
262 					itemHit = DiaNextHit(dia);
263 					if (itemHit == CANCEL || itemHit == OK) break;
264 				}
265 				if (itemHit != CANCEL)
266 					index = DiaGetPopupEntry(dia, DSNI_CHOICES);
267 				DiaDoneDialog(dia);
268 			}
269 		}
270 
271 		viewinfo[0] = el_curwindowpart->screenlx;
272 		viewinfo[1] = el_curwindowpart->screenhx;
273 		viewinfo[2] = el_curwindowpart->screenly;
274 		viewinfo[3] = el_curwindowpart->screenhy;
275 		viewinfo[4] = el_curwindowpart->intocell[0][0];
276 		viewinfo[5] = el_curwindowpart->intocell[0][1];
277 		viewinfo[6] = el_curwindowpart->intocell[0][2];
278 		viewinfo[7] = el_curwindowpart->intocell[1][0];
279 		viewinfo[8] = el_curwindowpart->intocell[1][1];
280 		viewinfo[9] = el_curwindowpart->intocell[1][2];
281 		viewinfo[10] = el_curwindowpart->intocell[2][0];
282 		viewinfo[11] = el_curwindowpart->intocell[2][1];
283 		viewinfo[12] = el_curwindowpart->intocell[2][2];
284 		viewinfo[13] = el_curwindowpart->outofcell[0][0];
285 		viewinfo[14] = el_curwindowpart->outofcell[0][1];
286 		viewinfo[15] = el_curwindowpart->outofcell[0][2];
287 		viewinfo[16] = el_curwindowpart->outofcell[1][0];
288 		viewinfo[17] = el_curwindowpart->outofcell[1][1];
289 		viewinfo[18] = el_curwindowpart->outofcell[1][2];
290 		viewinfo[19] = el_curwindowpart->outofcell[2][0];
291 		viewinfo[20] = el_curwindowpart->outofcell[2][1];
292 		viewinfo[21] = el_curwindowpart->outofcell[2][2];
293 		push = TRUE;
294 		exact = TRUE;
295 		onp = contentsview(np);
296 		if (onp == NONODEPROTO || intoicon != 0) onp = np;
297 		sethierarchicalparent(onp, stacknodeinst, el_curwindowpart, index, viewinfo);
298 	}
299 
300 	/* check dates of subcells */
301 	if ((us_useroptions&CHECKDATE) != 0)
302 	{
303 		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
304 			for(np1 = olib->firstnodeproto; np1 != NONODEPROTO; np1 = np1->nextnodeproto)
305 				np1->temp1 = 0;
306 		us_check_cell_date(np, np->revisiondate);
307 	}
308 
309 	/* if a nonredundant display is needed, see if it already exists */
310 	if (nonredundant != 0)
311 	{
312 		for(win = el_topwindowpart; win != NOWINDOWPART; win = win->nextwindowpart)
313 			if (win->curnodeproto == np) break;
314 		if (win != NOWINDOWPART)
315 		{
316 			/* switch to window "win" */
317 			bringwindowtofront(win->frame);
318 			us_highlightwindow(win, FALSE);
319 			return;
320 		}
321 	}
322 
323 	/* determine window area */
324 	if (el_curwindowpart == NOWINDOWPART)
325 	{
326 		lx = np->lowx;   hx = np->lowx;
327 		ly = np->lowy;   hy = np->lowy;
328 	} else
329 	{
330 		lx = el_curwindowpart->screenlx;   hx = el_curwindowpart->screenhx;
331 		ly = el_curwindowpart->screenly;   hy = el_curwindowpart->screenhy;
332 		if (el_curwindowpart->curnodeproto == NONODEPROTO)
333 		{
334 			lambda = el_curlib->lambda[el_curtech->techindex];
335 			lx = -lambda * 25;
336 			hx =  lambda * 25;
337 			ly = -lambda * 25;
338 			hy =  lambda * 25;
339 		}
340 	}
341 	if (implicit == 0)
342 	{
343 		/* make the new cell fill the window */
344 		us_fullview(np, &lx, &hx, &ly, &hy);
345 		exact = FALSE;
346 	} else
347 	{
348 		/* make the current cell be in the same place in the window */
349 		if (inplace != 0)
350 		{
351 			lx = el_curwindowpart->screenlx;   hx = el_curwindowpart->screenhx;
352 			ly = el_curwindowpart->screenly;   hy = el_curwindowpart->screenhy;
353 		} else
354 		{
355 			lx += np->lowx - stacknodeinst->lowx;
356 			hx += np->lowx - stacknodeinst->lowx;
357 			ly += np->lowy - stacknodeinst->lowy;
358 			hy += np->lowy - stacknodeinst->lowy;
359 		}
360 	}
361 
362 	if (stacknodeinst != NONODEINST)
363 	{
364 		/* if simulating, coordinate this hierarchy traversal with the waveform */
365 		if (el_curwindowpart != NOWINDOWPART &&
366 			(el_curwindowpart->state&WINDOWMODE) == WINDOWSIMMODE)
367 		{
368 			asktool(sim_tool, "traverse-down", (INTBIG)stacknodeinst, np);
369 		}
370 	}
371 
372 	/* edit the cell (creating a new frame/partition if requested) */
373 	if (newwindow != 0 || el_curwindowpart == NOWINDOWPART)
374 	{
375 		newframe = TRUE;
376 		push = FALSE;
377 	} else
378 	{
379 		newframe = FALSE;
380 	}
381 	if (implicit != 0 && inplace != 0)
382 	{
383 		makerotI(stacknodeinst, rotarray);
384 		maketransI(stacknodeinst, transarray);
385 		transmult(rotarray, transarray, xfarray);
386 		transmult(el_curwindowpart->intocell, xfarray, newarray);
387 		us_setxarray((INTBIG)el_curwindowpart, VWINDOWPART, x_("intocell"), newarray);
388 
389 		makerot(stacknodeinst, rotarray);
390 		maketrans(stacknodeinst, transarray);
391 		transmult(transarray, rotarray, xfarray);
392 		transmult(xfarray, el_curwindowpart->outofcell, newarray);
393 		us_setxarray((INTBIG)el_curwindowpart, VWINDOWPART, x_("outofcell"), newarray);
394 		if ((el_curwindowpart->state&INPLACEEDIT) == 0)
395 		{
396 			(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("inplacedepth"), 0, VINTEGER);
397 			(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("topnodeproto"),
398 				(INTBIG)el_curwindowpart->curnodeproto, VNODEPROTO);
399 		}
400 		(void)setind((INTBIG)el_curwindowpart, VWINDOWPART, x_("inplacestack"),
401 			el_curwindowpart->inplacedepth, (INTBIG)stacknodeinst);
402 		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("inplacedepth"),
403 			el_curwindowpart->inplacedepth+1, VINTEGER);
404 		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("state"),
405 			el_curwindowpart->state | INPLACEEDIT, VINTEGER);
406 	} else
407 	{
408 		if (el_curwindowpart != NOWINDOWPART)
409 		{
410 			us_setxarray((INTBIG)el_curwindowpart, VWINDOWPART, x_("intocell"), el_matid);
411 			us_setxarray((INTBIG)el_curwindowpart, VWINDOWPART, x_("outofcell"), el_matid);
412 			(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("state"),
413 				el_curwindowpart->state & ~INPLACEEDIT, VINTEGER);
414 		}
415 	}
416 	us_switchtocell(np, lx, hx, ly, hy, hini, hipp, newframe, push, exact);
417 }
418 
us_erase(INTBIG count,CHAR * par[])419 void us_erase(INTBIG count, CHAR *par[])
420 {
421 	REGISTER INTBIG i, j, l;
422 	INTBIG textcount;
423 	CHAR **textinfo;
424 	BOOLEAN allvisible, foundnode, cleaned;
425 	REGISTER NODEINST *ni;
426 	ARCINST *ai;
427 	REGISTER NODEPROTO *np;
428 	HIGHLIGHT high;
429 	REGISTER CHAR *pt;
430 	REGISTER GEOM **list;
431 	REGISTER LIBRARY *lib;
432 	REGISTER WINDOWPART *w;
433 	HIGHLIGHT newhigh;
434 	REGISTER void *infstr;
435 
436 	if (count > 0)
437 	{
438 		l = estrlen(pt = par[0]);
439 		if (namesamen(pt, x_("clean-up-all"), l) == 0 && l > 8)
440 		{
441 			us_clearhighlightcount();
442 			cleaned = FALSE;
443 			for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
444 				if (us_cleanupcell(np, FALSE)) cleaned = TRUE;
445 			if (!cleaned) ttyputmsg(_("Nothing to clean"));
446 			return;
447 		}
448 		if (namesamen(pt, x_("clean-up"), l) == 0)
449 		{
450 			np = us_needcell();
451 			if (np == NONODEPROTO) return;
452 			us_clearhighlightcount();
453 			(void)us_cleanupcell(np, TRUE);
454 			return;
455 		}
456 		if (namesamen(pt, x_("geometry"), l) == 0)
457 		{
458 			np = us_needcell();
459 			if (np == NONODEPROTO) return;
460 			us_erasegeometry(np);
461 			return;
462 		}
463 	}
464 
465 	/* get list of highlighted objects to be erased */
466 	np = us_needcell();
467 	if (np == NONODEPROTO)
468 	{
469 		us_abortcommand(_("No current cell"));
470 		return;
471 	}
472 	list = us_gethighlighted(WANTARCINST|WANTNODEINST, &textcount, &textinfo);
473 	if (list[0] == NOGEOM && textcount == 0)
474 	{
475 		us_abortcommand(_("Find an object to erase"));
476 		return;
477 	}
478 
479 	/* if in outline-edit mode, delete the selected point */
480 	if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
481 	{
482 		par[0] = x_("trace");
483 		par[1] = x_("delete-point");
484 		us_node(2, par);
485 		return;
486 	}
487 
488 	/* make sure that all requested objects are displayed */
489 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
490 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
491 			np->temp1 = 0;
492 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
493 		if (w->curnodeproto != NONODEPROTO)
494 			w->curnodeproto->temp1 = 1;
495 	allvisible = TRUE;
496 	for(i=0; list[i] != NOGEOM; i++)
497 	{
498 		np = geomparent(list[i]);
499 		if (np->temp1 == 0) allvisible = FALSE;
500 	}
501 	for(i=0; i<textcount; i++)
502 	{
503 		(void)us_makehighlight(textinfo[i], &high);
504 		np = high.cell;
505 		if (np->temp1 == 0) allvisible = FALSE;
506 	}
507 	if (!allvisible)
508 	{
509 		infstr = initinfstr();
510 		addstringtoinfstr(infstr, _("Some of the objects to be deleted are not displayed.  Delete anyway?"));
511 		addstringtoinfstr(infstr, _(" (click 'no' to delete only visible objects, 'yes' to delete all selected)"));
512 		j = us_noyescanceldlog(returninfstr(infstr), par);
513 		if (j == 0) return;
514 		if (namesame(par[0], x_("cancel")) == 0) return;
515 		if (namesame(par[0], x_("no")) == 0)
516 		{
517 			j = 0;
518 			for(i=0; list[i] != NOGEOM; i++)
519 			{
520 				np = geomparent(list[i]);
521 				if (np->temp1 == 0) continue;
522 				list[j++] = list[i];
523 			}
524 			if (j == 0)
525 			{
526 				us_abortcommand(_("No displayed objects to delete"));
527 				return;
528 			}
529 			list[j] = NOGEOM;
530 		}
531 	}
532 
533 	/* remove from list if a node is locked */
534 	j = 0;
535 	foundnode = FALSE;
536 	for(i=0; list[i] != NOGEOM; i++)
537 	{
538 		if (list[i]->entryisnode)
539 		{
540 			ni = list[i]->entryaddr.ni;
541 			if (us_cantedit(np, ni, TRUE)) continue;
542 			foundnode = TRUE;
543 		}
544 		list[j++] = list[i];
545 	}
546 	list[j] = NOGEOM;
547 	if (list[0] == NOGEOM && textcount == 0)
548 	{
549 		us_abortcommand(_("All selected objects are locked"));
550 		return;
551 	}
552 
553 	if (!foundnode)
554 	{
555 		/* disallow erasing if lock is on */
556 		if (us_cantedit(np, NONODEINST, TRUE)) return;
557 	}
558 
559 	/* unhighlight */
560 	us_clearhighlightcount();
561 
562 	/* if one node is selected, see if it can be handled with reconnection */
563 	if (list[0] != NOGEOM && list[0]->entryisnode && list[1] == NOGEOM && textcount == 0)
564 	{
565 		j = us_erasepassthru(ni, FALSE, &ai);
566 		if (j == 2)
567 		{
568 			/* worked: highlight the arc */
569 			newhigh.status = HIGHFROM;
570 			newhigh.fromgeom = ai->geom;
571 			newhigh.cell = np;
572 			us_setfind(&newhigh, 0, 1, 0, 0);
573 			return;
574 		}
575 	}
576 
577 	/* delete the text */
578 	for(i=0; i<textcount; i++)
579 	{
580 		(void)us_makehighlight(textinfo[i], &high);
581 
582 		/* disallow erasing if lock is on */
583 		np = high.cell;
584 		if (np != NONODEPROTO)
585 		{
586 			if (us_cantedit(np, NONODEINST, TRUE)) continue;
587 		}
588 
589 		/* do not deal with text on an object if the object is already in the list */
590 		if (high.fromgeom != NOGEOM)
591 		{
592 			for(j=0; list[j] != NOGEOM; j++)
593 				if (list[j] == high.fromgeom) break;
594 			if (list[j] != NOGEOM) continue;
595 		}
596 
597 		/* deleting variable on object */
598 		if (high.fromvar != NOVARIABLE)
599 		{
600 			if (high.fromgeom == NOGEOM)
601 			{
602 				us_undrawcellvariable(high.fromvar, np);
603 				(void)delval((INTBIG)np, VNODEPROTO, makename(high.fromvar->key));
604 			} else if (high.fromgeom->entryisnode)
605 			{
606 				ni = high.fromgeom->entryaddr.ni;
607 				startobjectchange((INTBIG)ni, VNODEINST);
608 
609 				/* if deleting port variables, do that */
610 				if (high.fromport != NOPORTPROTO)
611 				{
612 					(void)delval((INTBIG)high.fromport, VPORTPROTO, makename(high.fromvar->key));
613 				} else
614 				{
615 					/* if deleting text on invisible pin, delete pin too */
616 					if (ni->proto == gen_invispinprim) us_erasenodeinst(ni); else
617 						(void)delval((INTBIG)ni, VNODEINST, makename(high.fromvar->key));
618 				}
619 				endobjectchange((INTBIG)ni, VNODEINST);
620 			} else
621 			{
622 				ai = high.fromgeom->entryaddr.ai;
623 				startobjectchange((INTBIG)ai, VARCINST);
624 				(void)delval((INTBIG)ai, VARCINST, makename(high.fromvar->key));
625 				endobjectchange((INTBIG)ai, VARCINST);
626 			}
627 		} else if (high.fromport != NOPORTPROTO)
628 		{
629 			ni = high.fromgeom->entryaddr.ni;
630 			startobjectchange((INTBIG)ni, VNODEINST);
631 			us_undoportproto((NODEINST *)ni, high.fromport);
632 			endobjectchange((INTBIG)ni, VNODEINST);
633 		} else if (high.fromgeom->entryisnode)
634 			us_abortcommand(_("Cannot delete cell name"));
635 	}
636 
637 	/* look for option to re-connect arcs into an erased node */
638 	if (count > 0 && namesamen(par[0], x_("pass-through"), estrlen(par[0])) == 0)
639 	{
640 		for(i=0; list[i] != NOGEOM; i++)
641 		{
642 			if (!list[i]->entryisnode) continue;
643 			ni = list[i]->entryaddr.ni;
644 			j = us_erasepassthru(ni, TRUE, &ai);
645 			switch (j)
646 			{
647 				case 2:
648 					break;
649 				case -1:
650 					us_abortcommand(_("Arcs to node %s are of different type"),
651 						describenodeinst(ni));
652 					break;
653 				case -5:
654 					us_abortcommand(_("Cannot create connecting arc"));
655 					break;
656 				default:
657 					us_abortcommand(_("Must be 2 arcs on node %s (it has %ld)"),
658 						describenodeinst(ni), j);
659 					break;
660 			}
661 		}
662 		return;
663 	}
664 
665 	/* handle simple erasing */
666 	us_eraseobjectsinlist(np, list);
667 }
668 
us_find(INTBIG count,CHAR * par[])669 void us_find(INTBIG count, CHAR *par[])
670 {
671 	REGISTER INTBIG i, j, l, findport, findpoint, findexclusively, findangle,
672 		findwithin, findstill, findspecial, size, findeasy, findhard, total, type, addr,
673 		len, first, areasizex, areasizey, x, y, *newlist, extrainfo, findmore, findnobox;
674 	INTBIG xcur, ycur;
675 	REGISTER BOOLEAN waitforpush;
676 	XARRAY trans;
677 	REGISTER NODEINST *ni;
678 	REGISTER NODEPROTO *np, *onp;
679 	REGISTER LIBRARY *lib;
680 	REGISTER GEOM *geom, **glist;
681 	REGISTER ARCINST *ai;
682 	REGISTER PORTPROTO *pp;
683 	REGISTER NETWORK *net;
684 	REGISTER VARIABLE *var, *highvar;
685 	REGISTER CHAR *pt;
686 	CHAR **list;
687 	HIGHLIGHT newhigh, newhightext;
688 	REGISTER void *infstr;
689 
690 	if (count >= 1)
691 	{
692 		l = estrlen(pt = par[0]);
693 		if (namesamen(pt, x_("constraint-angle"), l) == 0 && l >= 12)
694 		{
695 			if (count >= 2)
696 			{
697 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_interactiveanglekey,
698 					atofr(par[1])*10/WHOLE, VINTEGER|VDONTSAVE);
699 			}
700 			var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_interactiveanglekey);
701 			if (var == NOVARIABLE) findangle = 0; else
702 				findangle = var->addr;
703 			if (findangle == 0)
704 			{
705 				ttyputverbose(M_("Interactive dragging done with no constraints"));
706 			} else
707 			{
708 				ttyputverbose(M_("Interactive dragging constrained to %ld degree angles"),
709 					findangle);
710 			}
711 			return;
712 		}
713 	}
714 
715 	/* make sure there is a cell being edited */
716 	np = us_needcell();
717 	if (np == NONODEPROTO) return;
718 	if ((np->cellview->viewstate&TEXTVIEW) != 0)
719 	{
720 		l = estrlen(pt = par[0]);
721 		if (namesamen(pt, x_("all"), l) == 0 && l >= 2)
722 		{
723 			/* special case: "find all" selects all text */
724 			i = us_totallines(el_curwindowpart);
725 			us_highlightline(el_curwindowpart, 0, i-1);
726 			return;
727 		}
728 		us_abortcommand(M_("There are no components to select in a text-only cell"));
729 		return;
730 	}
731 	if ((el_curwindowpart->state&WINDOWTYPE) == DISP3DWINDOW)
732 	{
733 		us_abortcommand(M_("Cannot select objects in a 3D window"));
734 		return;
735 	}
736 
737 	/* establish the default highlight environment */
738 	highvar = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
739 	if (highvar != NOVARIABLE)
740 		(void)us_makehighlight(((CHAR **)highvar->addr)[0], &newhigh); else
741 	{
742 		newhigh.status = 0;
743 		newhigh.fromgeom = NOGEOM;
744 		newhigh.fromport = NOPORTPROTO;
745 		newhigh.fromvar = NOVARIABLE;
746 		newhigh.fromvarnoeval = NOVARIABLE;
747 		newhigh.frompoint = 0;
748 	}
749 
750 	/* look for qualifiers */
751 	findport = findpoint = findexclusively = findwithin = findmore = 0;
752 	findstill = findspecial = findnobox = 0;
753 	extrainfo = 0;
754 	while (count > 0)
755 	{
756 		l = estrlen(pt = par[0]);
757 		if (namesamen(pt, x_("extra-info"), l) == 0 && l >= 3)
758 		{
759 			extrainfo = HIGHEXTRA;
760 			count--;   par++;   continue;
761 		}
762 		if (namesamen(pt, x_("more"), l) == 0 && l >= 1)
763 		{
764 			findmore++;
765 			count--;   par++;   continue;
766 		}
767 		if (namesamen(pt, x_("no-box"), l) == 0 && l >= 3)
768 		{
769 			findnobox++;
770 			count--;   par++;   continue;
771 		}
772 		if (namesamen(pt, x_("special"), l) == 0 && l >= 2)
773 		{
774 			findspecial++;
775 			count--;   par++;   continue;
776 		}
777 		if (namesamen(pt, x_("still"), l) == 0 && l >= 2)
778 		{
779 			findstill++;
780 			count--;   par++;   continue;
781 		}
782 		if (namesamen(pt, x_("exclusively"), l) == 0 && l >= 3)
783 		{
784 			findexclusively++;
785 			count--;   par++;   continue;
786 		}
787 		if (namesamen(pt, x_("within"), l) == 0 && l >= 1)
788 		{
789 			if (newhigh.status == 0)
790 			{
791 				us_abortcommand(M_("Find an object before working 'within' it"));
792 				return;
793 			}
794 			if ((newhigh.status&HIGHTYPE) != HIGHFROM || !newhigh.fromgeom->entryisnode)
795 			{
796 				us_abortcommand(M_("Must find a node before working 'within'"));
797 				return;
798 			}
799 
800 			findwithin++;
801 			count--;   par++;   continue;
802 		}
803 		if (namesamen(pt, x_("port"), l) == 0 && l >= 1)
804 		{
805 			findport = 1;
806 			count--;   par++;   continue;
807 		}
808 		if (namesamen(pt, x_("vertex"), l) == 0 && l >= 2)
809 		{
810 			findpoint = 1;
811 			count--;   par++;   continue;
812 		}
813 		break;
814 	}
815 
816 	if (count >= 1)
817 	{
818 		l = estrlen(pt = par[0]);
819 
820 		if (namesamen(pt, x_("all"), l) == 0 && l >= 2)
821 		{
822 			if (findpoint != 0 || findwithin != 0 || findmore != 0 ||
823 				findexclusively != 0 || findstill != 0 || findspecial != 0)
824 			{
825 				us_abortcommand(M_("Illegal options given to 'find all'"));
826 				return;
827 			}
828 			findeasy = findhard = 1;
829 			if (count >= 2)
830 			{
831 				l = estrlen(pt = par[1]);
832 				if (namesamen(pt, x_("easy"), l) == 0) findhard = 0; else
833 				if (namesamen(pt, x_("hard"), l) == 0) findeasy = 0; else
834 				{
835 					ttyputusage(x_("find all [easy|hard]"));
836 					return;
837 				}
838 			}
839 			total = 0;
840 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst) total++;
841 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst) total++;
842 			for(i=0; i<np->numvar; i++)
843 			{
844 				var = &np->firstvar[i];
845 				if ((var->type&VDISPLAY) != 0) total++;
846 			}
847 			if (total == 0) return;
848 			list = (CHAR **)emalloc((total * (sizeof (CHAR *))), el_tempcluster);
849 			if (list == 0) return;
850 
851 			newhigh.status = HIGHFROM | extrainfo;
852 			if (findnobox != 0) newhigh.status |= HIGHNOBOX;
853 			newhigh.cell = np;
854 			newhigh.frompoint = 0;
855 			newhigh.fromvar = NOVARIABLE;
856 			newhigh.fromvarnoeval = NOVARIABLE;
857 
858 			newhightext.status = HIGHTEXT;
859 			newhightext.cell = np;
860 			newhightext.fromport = NOPORTPROTO;
861 			newhightext.frompoint = 0;
862 			newhightext.fromvarnoeval = NOVARIABLE;
863 
864 			total = 0;
865 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
866 			{
867 				if (ni->proto->primindex == 0 && (us_useroptions&NOINSTANCESELECT) != 0)
868 				{
869 					/* cell instance that is hard to select */
870 					if (findhard == 0) continue;
871 				} else
872 				{
873 					/* regular node: see if it should be selected */
874 					if (findeasy == 0 && (ni->userbits&HARDSELECTN) == 0) continue;
875 					if (findhard == 0 && (ni->userbits&HARDSELECTN) != 0) continue;
876 				}
877 				if (ni->proto->primindex != 0 && (ni->proto->userbits&NINVISIBLE) != 0)
878 					continue;
879 
880 				/* if this is an invisible primitive with text, select the text */
881 				if (ni->proto == gen_invispinprim)
882 				{
883 					j = 0;
884 					for(i=0; i<ni->numvar; i++)
885 					{
886 						var = &ni->firstvar[i];
887 						if ((var->type&VDISPLAY) == 0) continue;
888 						newhightext.fromgeom = ni->geom;
889 						newhightext.fromvar = var;
890 						pt = us_makehighlightstring(&newhightext);
891 						(void)allocstring(&list[total], pt, el_tempcluster);
892 						total++;
893 						j = 1;
894 					}
895 					if (j != 0) continue;
896 				}
897 
898 				newhigh.fromgeom = ni->geom;
899 				newhigh.fromport = NOPORTPROTO;
900 				if (findport != 0)
901 				{
902 					pp = ni->proto->firstportproto;
903 					if (pp != NOPORTPROTO && pp->nextportproto == NOPORTPROTO)
904 						newhigh.fromport = pp;
905 				}
906 				pt = us_makehighlightstring(&newhigh);
907 				(void)allocstring(&list[total], pt, el_tempcluster);
908 				total++;
909 			}
910 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
911 			{
912 				if (findeasy == 0 && (ai->userbits&HARDSELECTA) == 0) continue;
913 				if (findhard == 0 && (ai->userbits&HARDSELECTA) != 0) continue;
914 				if ((ai->proto->userbits&AINVISIBLE) != 0) continue;
915 				newhigh.fromgeom = ai->geom;
916 				newhigh.fromport = NOPORTPROTO;
917 				pt = us_makehighlightstring(&newhigh);
918 				(void)allocstring(&list[total], pt, el_tempcluster);
919 				total++;
920 			}
921 			if (findeasy != 0)
922 			{
923 				for(i=0; i<np->numvar; i++)
924 				{
925 					var = &np->firstvar[i];
926 					if ((var->type&VDISPLAY) == 0) continue;
927 					newhightext.fromgeom = NOGEOM;
928 					newhightext.fromvar = var;
929 					pt = us_makehighlightstring(&newhightext);
930 					(void)allocstring(&list[total], pt, el_tempcluster);
931 					total++;
932 				}
933 			}
934 			if (total == 0) us_clearhighlightcount(); else
935 			{
936 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_highlightedkey, (INTBIG)list,
937 					VSTRING|VISARRAY|(total<<VLENGTHSH)|VDONTSAVE);
938 			}
939 			for(i=0; i<total; i++) efree(list[i]);
940 			efree((CHAR *)list);
941 			us_showallhighlight();
942 			if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO);
943 			return;
944 		}
945 
946 		if (namesamen(pt, x_("arc"), l) == 0 && l >= 3)
947 		{
948 			if (count <= 1)
949 			{
950 				ttyputusage(x_("find arc ARCNAME"));
951 				return;
952 			}
953 			if (findport != 0 || findpoint != 0 || findwithin != 0 || findspecial != 0)
954 			{
955 				us_abortcommand(M_("Illegal options given to 'find arc'"));
956 				return;
957 			}
958 			if (findexclusively != 0 && us_curarcproto == NOARCPROTO)
959 			{
960 				us_abortcommand(M_("Must select an arc for exclusive finding"));
961 				return;
962 			}
963 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
964 			{
965 				if (findexclusively != 0 && ai->proto != us_curarcproto) continue;
966 				var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
967 				if (var == NOVARIABLE) continue;
968 				if (namesame((CHAR *)var->addr, par[1]) == 0) break;
969 			}
970 			if (ai == NOARCINST)
971 			{
972 				us_abortcommand(_("Sorry, no %s arc named '%s' in this cell"),
973 					(findexclusively==0 ? x_("") : describearcproto(us_curarcproto)), par[1]);
974 				return;
975 			}
976 			newhigh.status = HIGHFROM;
977 			newhigh.fromgeom = ai->geom;
978 			newhigh.cell = np;
979 			us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
980 			return;
981 		}
982 
983 		if (namesamen(pt, x_("area-define"), l) == 0 && l >= 6)
984 		{
985 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
986 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
987 				findspecial != 0)
988 			{
989 				us_abortcommand(M_("Illegal options given to 'find area-define'"));
990 				return;
991 			}
992 			if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
993 			{
994 				/* nonsensical in outline-edit mode */
995 				return;
996 			}
997 
998 			us_clearhighlightcount();
999 			waitforpush = FALSE;
1000 			if (count >= 2)
1001 			{
1002 				l = estrlen(par[1]);
1003 				if (namesamen(par[1], x_("wait"), l) == 0 && l >= 1)
1004 					waitforpush = TRUE;
1005 			}
1006 			trackcursor(waitforpush, us_ignoreup, us_finddbegin, us_stretchdown, us_stoponchar,
1007 				us_invertdragup, TRACKDRAGGING);
1008 			if (el_pleasestop != 0) return;
1009 			np = getcurcell();
1010 			if (np == NONODEPROTO) return;
1011 			us_finddterm(&newhigh.stalx, &newhigh.staly);
1012 			if (us_demandxy(&xcur, &ycur)) return;
1013 			if (xcur >= newhigh.stalx) newhigh.stahx = xcur; else
1014 			{
1015 				newhigh.stahx = newhigh.stalx;   newhigh.stalx = xcur;
1016 			}
1017 			if (ycur >= newhigh.staly) newhigh.stahy = ycur; else
1018 			{
1019 				newhigh.stahy = newhigh.staly;   newhigh.staly = ycur;
1020 			}
1021 			newhigh.status = HIGHBBOX;
1022 			newhigh.cell = np;
1023 			us_addhighlight(&newhigh);
1024 			return;
1025 		}
1026 
1027 		if (namesamen(pt, x_("area-move"), l) == 0 && l >= 6)
1028 		{
1029 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1030 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
1031 				findspecial != 0)
1032 			{
1033 				us_abortcommand(M_("Illegal options given to 'find area-move'"));
1034 				return;
1035 			}
1036 			if (us_demandxy(&xcur, &ycur)) return;
1037 			gridalign(&xcur, &ycur, 1, np);
1038 
1039 			/* set the highlight */
1040 			if ((newhigh.status&HIGHTYPE) == HIGHBBOX)
1041 			{
1042 				areasizex = newhigh.stahx - newhigh.stalx;
1043 				areasizey = newhigh.stahy - newhigh.staly;
1044 			} else
1045 			{
1046 				areasizex = (el_curwindowpart->screenhx-el_curwindowpart->screenlx) / 5;
1047 				areasizey = (el_curwindowpart->screenhy-el_curwindowpart->screenly) / 5;
1048 			}
1049 
1050 			us_clearhighlightcount();
1051 
1052 			/* adjust the cursor position if selecting interactively */
1053 			if ((us_tool->toolstate&INTERACTIVE) != 0)
1054 			{
1055 				us_findinit(areasizex, areasizey);
1056 				trackcursor(FALSE, us_ignoreup, us_findmbegin, us_dragdown, us_stoponchar,
1057 					us_dragup, TRACKDRAGGING);
1058 				if (el_pleasestop != 0) return;
1059 				if (us_demandxy(&xcur, &ycur)) return;
1060 				gridalign(&xcur, &ycur, 1, np);
1061 			}
1062 			newhigh.status = HIGHBBOX;
1063 			newhigh.cell = np;
1064 			newhigh.stalx = xcur;   newhigh.stahx = xcur + areasizex;
1065 			newhigh.staly = ycur;   newhigh.stahy = ycur + areasizey;
1066 			us_addhighlight(&newhigh);
1067 			return;
1068 		}
1069 
1070 		if (namesamen(pt, x_("area-size"), l) == 0 && l >= 6)
1071 		{
1072 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1073 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findnobox != 0 ||
1074 				findspecial != 0)
1075 			{
1076 				us_abortcommand(M_("Illegal options given to 'find area-size'"));
1077 				return;
1078 			}
1079 			if (us_demandxy(&xcur, &ycur)) return;
1080 			gridalign(&xcur, &ycur, 1, np);
1081 
1082 			if (newhigh.status != HIGHBBOX)
1083 			{
1084 				us_abortcommand(M_("Use 'find area-move' first, then this"));
1085 				return;
1086 			}
1087 			if (np != newhigh.cell)
1088 			{
1089 				us_abortcommand(M_("Not in same cell as highlight area"));
1090 				return;
1091 			}
1092 
1093 			us_clearhighlightcount();
1094 
1095 			/* adjust the cursor position if selecting interactively */
1096 			if ((us_tool->toolstate&INTERACTIVE) != 0)
1097 			{
1098 				us_findinit(newhigh.stalx, newhigh.staly);
1099 				trackcursor(FALSE, us_ignoreup, us_findsbegin, us_stretchdown,
1100 					us_stoponchar, us_invertdragup, TRACKDRAGGING);
1101 				if (el_pleasestop != 0) return;
1102 				if (us_demandxy(&xcur, &ycur)) return;
1103 				gridalign(&xcur, &ycur, 1, np);
1104 			}
1105 			if (xcur >= newhigh.stalx) newhigh.stahx = xcur; else
1106 			{
1107 				newhigh.stahx = newhigh.stalx;   newhigh.stalx = xcur;
1108 			}
1109 			if (ycur >= newhigh.staly) newhigh.stahy = ycur; else
1110 			{
1111 				newhigh.stahy = newhigh.staly;   newhigh.staly = ycur;
1112 			}
1113 			us_addhighlight(&newhigh);
1114 			return;
1115 		}
1116 
1117 		if (namesamen(pt, x_("clear"), l) == 0 && l >= 2)
1118 		{
1119 			us_clearhighlightcount();
1120 			return;
1121 		}
1122 
1123 		if (namesamen(pt, x_("comp-interactive"), l) == 0 && l >= 3)
1124 		{
1125 			if (findpoint != 0 || findexclusively != 0 || extrainfo != 0)
1126 			{
1127 				us_abortcommand(M_("Illegal options given to 'find comp-interactive'"));
1128 				return;
1129 			}
1130 			if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
1131 			{
1132 				/* nonsensical in outline-edit mode */
1133 				return;
1134 			}
1135 			var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_interactiveanglekey);
1136 			if (var == NOVARIABLE) findangle = 0; else
1137 				findangle = var->addr;
1138 			us_findiinit(findport, extrainfo, findangle, 1-findmore, findstill,
1139 				findnobox, findspecial);
1140 			trackcursor(FALSE, us_ignoreup, us_findcibegin, us_findidown, us_stoponchar,
1141 				us_findiup, TRACKDRAGGING);
1142 			return;
1143 		}
1144 
1145 		if (namesamen(pt, x_("deselect-arcs"), l) == 0 && l >= 2)
1146 		{
1147 			var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
1148 			if (var == NOVARIABLE) return;
1149 			len = getlength(var);
1150 			list = (CHAR **)emalloc(len * (sizeof (CHAR *)), el_tempcluster);
1151 			if (list == 0) return;
1152 			j = 0;
1153 			for(i=0; i<len; i++)
1154 			{
1155 				if (us_makehighlight(((CHAR **)var->addr)[i], &newhigh)) break;
1156 				if ((newhigh.status&HIGHTYPE) == HIGHFROM &&
1157 					!newhigh.fromgeom->entryisnode) continue;
1158 				(void)allocstring(&list[j], ((CHAR **)var->addr)[i], el_tempcluster);
1159 				j++;
1160 			}
1161 			if (j == 0)
1162 			{
1163 				(void)delvalkey((INTBIG)us_tool, VTOOL, us_highlightedkey);
1164 			} else
1165 			{
1166 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_highlightedkey, (INTBIG)list,
1167 					VSTRING|VISARRAY|(j<<VLENGTHSH)|VDONTSAVE);
1168 			}
1169 			for(i=0; i<j; i++)
1170 				efree((CHAR *)list[i]);
1171 			efree((CHAR *)list);
1172 			return;
1173 		}
1174 
1175 		if (namesamen(pt, x_("down-stack"), l) == 0 && l >= 2)
1176 		{
1177 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1178 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
1179 			{
1180 				us_abortcommand(M_("Illegal options given to 'find down-stack'"));
1181 				return;
1182 			}
1183 			us_pushhighlight();
1184 			ttyputverbose(M_("Pushed"));
1185 			return;
1186 		}
1187 
1188 		if (namesamen(pt, x_("dragging-selects"), l) == 0 && l >= 2)
1189 		{
1190 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1191 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
1192 			{
1193 				us_abortcommand(M_("Illegal options given to 'find dragging-selects'"));
1194 				return;
1195 			}
1196 			if (count <= 1)
1197 			{
1198 				ttyputusage(x_("find dragging-selects WHAT"));
1199 				return;
1200 			}
1201 			len = estrlen(pt = par[1]);
1202 			if (namesamen(pt, x_("any-touching"), len) == 0)
1203 			{
1204 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_optionflagskey,
1205 					us_useroptions & ~MUSTENCLOSEALL, VINTEGER);
1206 				ttyputverbose(M_("Dragging selects anything touching the area"));
1207 				return;
1208 			}
1209 			if (namesamen(pt, x_("only-enclosed"), len) == 0)
1210 			{
1211 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_optionflagskey,
1212 					us_useroptions | MUSTENCLOSEALL, VINTEGER);
1213 				ttyputverbose(M_("Dragging selects only objects inside the area"));
1214 				return;
1215 			}
1216 			ttyputbadusage(x_("find dragging-selects"));
1217 			return;
1218 		}
1219 
1220 		if (namesamen(pt, x_("export"), l) == 0 && l >= 3)
1221 		{
1222 			if (count <= 1)
1223 			{
1224 				ttyputusage(x_("find export PORTNAME"));
1225 				return;
1226 			}
1227 			if (findport != 0 || findpoint != 0 || findexclusively != 0 || findwithin != 0 ||
1228 				findspecial != 0)
1229 			{
1230 				us_abortcommand(M_("'find export' cannot accept other control"));
1231 				return;
1232 			}
1233 			for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1234 				if (namesame(par[1], pp->protoname) == 0) break;
1235 			if (pp == NOPORTPROTO)
1236 			{
1237 				us_abortcommand(_("Sorry, no export named '%s' in this cell"), par[1]);
1238 				return;
1239 			}
1240 			newhigh.status = HIGHFROM;
1241 			newhigh.fromgeom = pp->subnodeinst->geom;
1242 			newhigh.fromport = pp->subportproto;
1243 			newhigh.cell = np;
1244 			us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
1245 			return;
1246 		}
1247 
1248 		if (namesamen(pt, x_("interactive"), l) == 0)
1249 		{
1250 			if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
1251 			{
1252 				/* do outline select and move if in that mode */
1253 				findwithin = 1;
1254 				findpoint = 1;
1255 			}
1256 			if (findexclusively != 0)
1257 			{
1258 				us_abortcommand(M_("Illegal options given to 'find interactively'"));
1259 				return;
1260 			}
1261 
1262 			/* special case: "find within vertex interactive" for polygon-editing */
1263 			if (findpoint != 0 && findwithin != 0)
1264 			{
1265 				if (newhigh.fromgeom == NOGEOM)
1266 				{
1267 					us_abortcommand(M_("Cannot edit this object"));
1268 					return;
1269 				}
1270 				ni = newhigh.fromgeom->entryaddr.ni;
1271 				us_pointinit(ni, 0);
1272 				trackcursor(FALSE, us_ignoreup, us_findpointbegin, us_movepdown,
1273 					us_stoponchar, us_dragup, TRACKDRAGGING);
1274 				if (el_pleasestop != 0) return;
1275 
1276 				highvar = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
1277 				if (highvar == NOVARIABLE) return;
1278 				(void)us_makehighlight(((CHAR **)highvar->addr)[0], &newhigh);
1279 				if (us_demandxy(&xcur, &ycur)) return;
1280 				gridalign(&xcur, &ycur, 1, np);
1281 				var = gettrace(ni);
1282 				if (var == NOVARIABLE) return;
1283 				size = getlength(var) / 2;
1284 				newlist = (INTBIG *)emalloc((size*2*SIZEOFINTBIG), el_tempcluster);
1285 				if (newlist == 0) return;
1286 				makerot(ni, trans);
1287 				x = (ni->highx + ni->lowx) / 2;
1288 				y = (ni->highy + ni->lowy) / 2;
1289 				for(i=0; i<size; i++)
1290 				{
1291 					if (i+1 == newhigh.frompoint)
1292 					{
1293 						newlist[i*2] = xcur;
1294 						newlist[i*2+1] = ycur;
1295 					} else xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y,
1296 						&newlist[i*2], &newlist[i*2+1], trans);
1297 				}
1298 
1299 				/* now re-draw this trace */
1300 				us_pushhighlight();
1301 				us_clearhighlightcount();
1302 				us_settrace(ni, newlist, size);
1303 				us_pophighlight(FALSE);
1304 				efree((CHAR *)newlist);
1305 				return;
1306 			}
1307 
1308 			/* traditional interactive selection */
1309 			var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_interactiveanglekey);
1310 			if (var == NOVARIABLE) findangle = 0; else
1311 				findangle = var->addr;
1312 			us_findiinit(findport, extrainfo, findangle, 1-findmore, findstill,
1313 				findnobox, findspecial);
1314 			trackcursor(FALSE, us_ignoreup, us_findibegin, us_findidown, us_stoponchar,
1315 				us_findiup, TRACKDRAGGING);
1316 			return;
1317 		}
1318 
1319 		if (namesamen(pt, x_("just-objects"), l) == 0)
1320 		{
1321 			/* must be a single "area select" */
1322 			var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
1323 			if (var == NOVARIABLE) return;
1324 			len = getlength(var);
1325 			if (len != 1) return;
1326 			(void)us_makehighlight(((CHAR **)var->addr)[0], &newhigh);
1327 			if ((newhigh.status&HIGHTYPE) != HIGHBBOX) return;
1328 
1329 			/* remove it and select everything in that area */
1330 			np = newhigh.cell;
1331 			us_clearhighlightcount();
1332 			total = us_selectarea(np, newhigh.stalx, newhigh.stahx, newhigh.staly,
1333 				newhigh.stahy, 0, 0, 0, &list);
1334 			if (total > 0)
1335 				(void)setvalkey((INTBIG)us_tool, VTOOL, us_highlightedkey, (INTBIG)list,
1336 					VSTRING|VISARRAY|(total<<VLENGTHSH)|VDONTSAVE);
1337 			return;
1338 		}
1339 
1340 		if (namesamen(pt, x_("name"), l) == 0 && l >= 2)
1341 		{
1342 			if (count <= 1)
1343 			{
1344 				ttyputusage(x_("find name HIGHLIGHTNAME"));
1345 				return;
1346 			}
1347 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1348 				findwithin != 0 || findmore != 0 || findspecial != 0)
1349 			{
1350 				us_abortcommand(M_("Illegal options given to 'find name'"));
1351 				return;
1352 			}
1353 			infstr = initinfstr();
1354 			addstringtoinfstr(infstr, x_("USER_highlight_"));
1355 			addstringtoinfstr(infstr, par[1]);
1356 			var = getval((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, returninfstr(infstr));
1357 			if (var == NOVARIABLE)
1358 			{
1359 				us_abortcommand(_("Cannot find saved highlight '%s'"), par[1]);
1360 				return;
1361 			}
1362 
1363 			(void)setvalkey((INTBIG)us_tool, VTOOL, us_highlightedkey, var->addr, var->type|VDONTSAVE);
1364 			if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO);
1365 			return;
1366 		}
1367 
1368 		if (namesamen(pt, x_("node"), l) == 0 && l >= 3)
1369 		{
1370 			if (count <= 1)
1371 			{
1372 				ttyputusage(x_("find node NODENAME"));
1373 				return;
1374 			}
1375 			if (findport != 0 || findwithin != 0 || findspecial != 0)
1376 			{
1377 				us_abortcommand(M_("Illegal options given to 'find node'"));
1378 				return;
1379 			}
1380 			if (findexclusively != 0 && us_curnodeproto == NONODEPROTO)
1381 			{
1382 				us_abortcommand(M_("Must select a node for exclusive finding"));
1383 				return;
1384 			}
1385 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1386 			{
1387 				if (findexclusively != 0 && ni->proto != us_curnodeproto) continue;
1388 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
1389 				if (var == NOVARIABLE) continue;
1390 				if (namesame((CHAR *)var->addr, par[1]) == 0) break;
1391 			}
1392 			if (ni == NONODEINST)
1393 			{
1394 				us_abortcommand(_("Sorry, no %s node named '%s' in this cell"),
1395 					(findexclusively==0 ? x_("") : describenodeproto(us_curnodeproto)), par[1]);
1396 				return;
1397 			}
1398 			newhigh.status = HIGHFROM;
1399 			newhigh.fromgeom = ni->geom;
1400 			newhigh.fromport = NOPORTPROTO;
1401 			newhigh.cell = np;
1402 			us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
1403 			return;
1404 		}
1405 
1406 		if (namesamen(pt, x_("nonmanhattan"), l) == 0 && l >= 3)
1407 		{
1408 			i = 0;
1409 			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1410 				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1411 					onp->temp1 = 0;
1412 			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1413 			{
1414 				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1415 				{
1416 					for(ai = onp->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1417 					{
1418 						if (ai->proto->tech == gen_tech || ai->proto->tech == art_tech ||
1419 							ai->proto->tech == sch_tech)
1420 								continue;
1421 						var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius_key);
1422 						if (var != NOVARIABLE || (ai->end[0].xpos != ai->end[1].xpos &&
1423 							ai->end[0].ypos != ai->end[1].ypos)) onp->temp1++;
1424 					}
1425 					for(ni = onp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1426 					{
1427 						if ((ni->rotation % 900) != 0) onp->temp1++;
1428 					}
1429 				}
1430 			}
1431 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1432 			{
1433 				if (ai->proto->tech == gen_tech || ai->proto->tech == art_tech)
1434 					continue;
1435 				var = getvalkey((INTBIG)ai, VARCINST, VINTEGER, el_arc_radius_key);
1436 				if (var == NOVARIABLE && (ai->end[0].xpos == ai->end[1].xpos ||
1437 					ai->end[0].ypos == ai->end[1].ypos)) continue;
1438 				if (i == 0) us_clearhighlightcount();
1439 				newhigh.status = HIGHFROM;
1440 				newhigh.cell = np;
1441 				newhigh.fromgeom = ai->geom;
1442 				newhigh.fromport = NOPORTPROTO;
1443 				newhigh.frompoint = 0;
1444 				us_addhighlight(&newhigh);
1445 				i++;
1446 			}
1447 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1448 			{
1449 				if ((ni->rotation % 900) == 0) continue;
1450 				if (i == 0) us_clearhighlightcount();
1451 				newhigh.status = HIGHFROM;
1452 				newhigh.cell = np;
1453 				newhigh.fromgeom = ni->geom;
1454 				newhigh.fromport = NOPORTPROTO;
1455 				newhigh.frompoint = 0;
1456 				us_addhighlight(&newhigh);
1457 				i++;
1458 			}
1459 			if (i == 0) ttyputmsg(_("No nonmanhattan objects in this cell")); else
1460 				ttyputmsg(_("%ld objects are not manhattan in this cell"), i);
1461 			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1462 			{
1463 				if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1464 				i = 0;
1465 				for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1466 					if (onp != np) i += onp->temp1;
1467 				if (i == 0) continue;
1468 				if (lib == el_curlib)
1469 				{
1470 					l = 0;
1471 					infstr = initinfstr();
1472 					for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1473 					{
1474 						if (onp == np || onp->temp1 == 0) continue;
1475 						if (l != 0) addtoinfstr(infstr, ' ');
1476 						addstringtoinfstr(infstr, describenodeproto(onp));
1477 						l++;
1478 					}
1479 					if (l == 1)
1480 					{
1481 						ttyputmsg(_("Found nonmanhattan geometry in cell %s"), returninfstr(infstr));
1482 					} else
1483 					{
1484 						ttyputmsg(_("Found nonmanhattan geometry in these cells: %s"),
1485 							returninfstr(infstr));
1486 					}
1487 				} else
1488 				{
1489 					ttyputmsg(_("Found nonmanhattan geometry in library %s"), lib->libname);
1490 				}
1491 			}
1492 			return;
1493 		}
1494 
1495 		if (namesamen(pt, x_("object"), l) == 0)
1496 		{
1497 			if (count < 2)
1498 			{
1499 				ttyputusage(x_("find object (TYPE ADDRESS | TYPEADDRESS)"));
1500 				return;
1501 			}
1502 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1503 				findwithin != 0 || findspecial != 0)
1504 			{
1505 				us_abortcommand(M_("Illegal options given to 'find object'"));
1506 				return;
1507 			}
1508 
1509 			/* determine type and address to highlight */
1510 			if (count == 3)
1511 			{
1512 				type = us_variabletypevalue(par[1]);
1513 				addr = myatoi(par[2]);
1514 			} else
1515 			{
1516 				type = VUNKNOWN;
1517 				if (namesamen(par[1], x_("node"), 4) == 0)
1518 				{
1519 					type = VNODEINST;
1520 					addr = myatoi(&par[1][4]);
1521 				}
1522 				if (namesamen(par[1], x_("arc"), 3) == 0)
1523 				{
1524 					type = VARCINST;
1525 					addr = myatoi(&par[1][3]);
1526 				}
1527 				if (namesamen(par[1], x_("port"), 4) == 0)
1528 				{
1529 					type = VPORTPROTO;
1530 					addr = myatoi(&par[1][4]);
1531 				}
1532 				if (namesamen(par[1], x_("network"), 7) == 0)
1533 				{
1534 					type = VNETWORK;
1535 					addr = myatoi(&par[1][7]);
1536 				}
1537 			}
1538 			if (type == VUNKNOWN)
1539 			{
1540 				us_abortcommand(_("Unknown object type in 'find object' command"));
1541 				return;
1542 			}
1543 			switch (type)
1544 			{
1545 				case VNODEINST:
1546 					ni = (NODEINST *)addr;
1547 					if (ni == 0 || ni == NONODEINST) return;
1548 					if (ni->parent != np)
1549 					{
1550 						us_abortcommand(_("Cannot find node %ld in this cell"), addr);
1551 						return;
1552 					}
1553 					newhigh.status = HIGHFROM;
1554 					newhigh.fromgeom = ni->geom;
1555 					newhigh.fromport = NOPORTPROTO;
1556 					newhigh.cell = np;
1557 					us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
1558 					break;
1559 				case VARCINST:
1560 					ai = (ARCINST *)addr;
1561 					if (ai == 0 || ai == NOARCINST) return;
1562 					if (ai->parent != np)
1563 					{
1564 						us_abortcommand(_("Cannot find arc %ld in this cell"), addr);
1565 						return;
1566 					}
1567 					newhigh.status = HIGHFROM;
1568 					newhigh.fromgeom = ai->geom;
1569 					newhigh.fromport = NOPORTPROTO;
1570 					newhigh.cell = np;
1571 					us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
1572 					break;
1573 				case VNETWORK:
1574 					net = (NETWORK *)addr;
1575 					if (net == 0 || net == NONETWORK) return;
1576 					if (net->parent != np)
1577 					{
1578 						us_abortcommand(_("Cannot find network %ld in this cell"), addr);
1579 						return;
1580 					}
1581 					for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1582 					{
1583 						if (ai->network != net) continue;
1584 						newhigh.status = HIGHFROM;
1585 						newhigh.fromgeom = ai->geom;
1586 						newhigh.fromport = NOPORTPROTO;
1587 						newhigh.cell = np;
1588 						us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
1589 					}
1590 					for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1591 					{
1592 						if (pp->network != net) continue;
1593 						newhigh.status = HIGHFROM;
1594 						newhigh.fromgeom = pp->subnodeinst->geom;
1595 						newhigh.fromport = pp->subportproto;
1596 						newhigh.cell = np;
1597 						us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
1598 					}
1599 					break;
1600 				case VPORTPROTO:
1601 					pp = (PORTPROTO *)addr;
1602 					if (pp == 0 || pp == NOPORTPROTO) return;
1603 					if (pp->parent != np)
1604 					{
1605 						us_abortcommand(_("Cannot find port %ld in this cell"), addr);
1606 						return;
1607 					}
1608 					newhigh.status = HIGHFROM;
1609 					newhigh.fromgeom = pp->subnodeinst->geom;
1610 					newhigh.fromport = pp->subportproto;
1611 					newhigh.cell = np;
1612 					us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
1613 					break;
1614 				default:
1615 					us_abortcommand(_("Cannot highlight objects of type %s"),
1616 						us_variabletypename(type));
1617 					return;
1618 			}
1619 			return;
1620 		}
1621 
1622 		if (namesamen(pt, x_("save"), l) == 0 && l >= 2)
1623 		{
1624 			if (count <= 1)
1625 			{
1626 				ttyputusage(x_("find save HIGHLIGHTNAME"));
1627 				return;
1628 			}
1629 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1630 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
1631 			{
1632 				us_abortcommand(M_("Illegal options given to 'find save'"));
1633 				return;
1634 			}
1635 			var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
1636 			if (var == NOVARIABLE)
1637 			{
1638 				us_abortcommand(_("Highlight something before saving highlight"));
1639 				return;
1640 			}
1641 			infstr = initinfstr();
1642 			addstringtoinfstr(infstr, x_("USER_highlight_"));
1643 			addstringtoinfstr(infstr, par[1]);
1644 			(void)setval((INTBIG)us_tool, VTOOL, returninfstr(infstr), var->addr, var->type|VDONTSAVE);
1645 			ttyputverbose(M_("%s saved"), par[1]);
1646 			return;
1647 		}
1648 
1649 		if (namesamen(pt, x_("set-easy-selection"), l) == 0 && l >= 5)
1650 		{
1651 			glist = us_gethighlighted(WANTARCINST|WANTNODEINST, 0, 0);
1652 			if (glist[0] == NOGEOM)
1653 			{
1654 				us_abortcommand(_("Select something before making it easy-to-select"));
1655 				return;
1656 			}
1657 			for(i=0; glist[i] != NOGEOM; i++)
1658 			{
1659 				geom = glist[i];
1660 				if (!geom->entryisnode)
1661 				{
1662 					ai = geom->entryaddr.ai;
1663 					ai->userbits &= ~HARDSELECTA;
1664 				} else
1665 				{
1666 					ni = geom->entryaddr.ni;
1667 					ni->userbits &= ~HARDSELECTN;
1668 				}
1669 			}
1670 			return;
1671 		}
1672 
1673 		if (namesamen(pt, x_("set-hard-selection"), l) == 0 && l >= 5)
1674 		{
1675 			glist = us_gethighlighted(WANTARCINST|WANTNODEINST, 0, 0);
1676 			if (glist[0] == NOGEOM)
1677 			{
1678 				us_abortcommand(_("Select something before making it easy-to-select"));
1679 				return;
1680 			}
1681 			for(i=0; glist[i] != NOGEOM; i++)
1682 			{
1683 				geom = glist[i];
1684 				if (!geom->entryisnode)
1685 				{
1686 					ai = geom->entryaddr.ai;
1687 					ai->userbits |= HARDSELECTA;
1688 				} else
1689 				{
1690 					ni = geom->entryaddr.ni;
1691 					ni->userbits |= HARDSELECTN;
1692 				}
1693 			}
1694 			return;
1695 		}
1696 
1697 		if (namesamen(pt, x_("similar"), l) == 0 && l >= 2)
1698 		{
1699 			if (findpoint != 0 || findwithin != 0 || findmore != 0 ||
1700 				findexclusively != 0 || findstill != 0 || findspecial != 0)
1701 			{
1702 				us_abortcommand(M_("Illegal options given to 'find similar'"));
1703 				return;
1704 			}
1705 
1706 			glist = us_gethighlighted(WANTNODEINST|WANTARCINST, 0, 0);
1707 			if (glist[0] == NOGEOM)
1708 			{
1709 				us_abortcommand(_("Must select objects before selecting all like them"));
1710 				return;
1711 			}
1712 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1713 				ni->temp1 = 0;
1714 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1715 				ai->temp1 = 0;
1716 			for(i=0; glist[i] != NOGEOM; i++)
1717 			{
1718 				if (glist[i]->entryisnode)
1719 				{
1720 					for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1721 						if (ni->proto == glist[i]->entryaddr.ni->proto) ni->temp1 = 1;
1722 				} else
1723 				{
1724 					for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1725 						if (ai->proto == glist[i]->entryaddr.ai->proto) ai->temp1 = 1;
1726 				}
1727 			}
1728 			infstr = initinfstr();
1729 			first = 0;
1730 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1731 			{
1732 				if (ni->temp1 == 0) continue;
1733 				if (first != 0) addtoinfstr(infstr, '\n');
1734 				first++;
1735 				formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1736 					describenodeproto(np), (INTBIG)ni->geom);
1737 			}
1738 			for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1739 			{
1740 				if (ai->temp1 == 0) continue;
1741 				if (first != 0) addtoinfstr(infstr, '\n');
1742 				first++;
1743 				formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1744 					describenodeproto(np), (INTBIG)ai->geom);
1745 			}
1746 			us_setmultiplehighlight(returninfstr(infstr), FALSE);
1747 			return;
1748 		}
1749 
1750 		if (namesamen(pt, x_("snap-mode"), l) == 0 && l >= 2)
1751 		{
1752 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1753 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
1754 			{
1755 				us_abortcommand(M_("Illegal options given to 'find snap-mode'"));
1756 				return;
1757 			}
1758 
1759 			if (count < 2)
1760 			{
1761 				switch (us_state&SNAPMODE)
1762 				{
1763 					case SNAPMODENONE:     ttyputmsg(M_("Snapping mode: none"));              break;
1764 					case SNAPMODECENTER:   ttyputmsg(M_("Snapping mode: center"));            break;
1765 					case SNAPMODEMIDPOINT: ttyputmsg(M_("Snapping mode: midpoint"));          break;
1766 					case SNAPMODEENDPOINT: ttyputmsg(M_("Snapping mode: end point"));         break;
1767 					case SNAPMODETANGENT:  ttyputmsg(M_("Snapping mode: tangent"));           break;
1768 					case SNAPMODEPERP:     ttyputmsg(M_("Snapping mode: perpendicular"));     break;
1769 					case SNAPMODEQUAD:     ttyputmsg(M_("Snapping mode: quadrant"));          break;
1770 					case SNAPMODEINTER:    ttyputmsg(M_("Snapping mode: any intersection"));  break;
1771 				}
1772 				return;
1773 			}
1774 			l = estrlen(pt = par[1]);
1775 			if (namesamen(pt, x_("none"), l) == 0)
1776 			{
1777 				us_state = (us_state & ~SNAPMODE) | SNAPMODENONE;
1778 				ttyputverbose(M_("Snapping mode: none"));
1779 				return;
1780 			}
1781 			if (namesamen(pt, x_("center"), l) == 0)
1782 			{
1783 				us_state = (us_state & ~SNAPMODE) | SNAPMODECENTER;
1784 				ttyputverbose(M_("Snapping mode: center"));
1785 				return;
1786 			}
1787 			if (namesamen(pt, x_("midpoint"), l) == 0)
1788 			{
1789 				us_state = (us_state & ~SNAPMODE) | SNAPMODEMIDPOINT;
1790 				ttyputverbose(M_("Snapping mode: midpoint"));
1791 				return;
1792 			}
1793 			if (namesamen(pt, x_("endpoint"), l) == 0)
1794 			{
1795 				us_state = (us_state & ~SNAPMODE) | SNAPMODEENDPOINT;
1796 				ttyputverbose(M_("Snapping mode: endpoint"));
1797 				return;
1798 			}
1799 			if (namesamen(pt, x_("tangent"), l) == 0)
1800 			{
1801 				us_state = (us_state & ~SNAPMODE) | SNAPMODETANGENT;
1802 				ttyputverbose(M_("Snapping mode: tangent"));
1803 				return;
1804 			}
1805 			if (namesamen(pt, x_("perpendicular"), l) == 0)
1806 			{
1807 				us_state = (us_state & ~SNAPMODE) | SNAPMODEPERP;
1808 				ttyputverbose(M_("Snapping mode: perpendicular"));
1809 				return;
1810 			}
1811 			if (namesamen(pt, x_("quadrant"), l) == 0)
1812 			{
1813 				us_state = (us_state & ~SNAPMODE) | SNAPMODEQUAD;
1814 				ttyputverbose(M_("Snapping mode: quadrant"));
1815 				return;
1816 			}
1817 			if (namesamen(pt, x_("intersection"), l) == 0)
1818 			{
1819 				us_state = (us_state & ~SNAPMODE) | SNAPMODEINTER;
1820 				ttyputverbose(M_("Snapping mode: any intersection"));
1821 				return;
1822 			}
1823 			us_abortcommand(M_("Unknown snapping mode: %s"), pt);
1824 			return;
1825 		}
1826 
1827 		if (namesamen(pt, x_("up-stack"), l) == 0 && l >= 1)
1828 		{
1829 			if (findport != 0 || findpoint != 0 || findexclusively != 0 ||
1830 				extrainfo != 0 || findwithin != 0 || findmore != 0 || findspecial != 0)
1831 			{
1832 				us_abortcommand(M_("Illegal options given to 'find up-stack'"));
1833 				return;
1834 			}
1835 			us_pophighlight(FALSE);
1836 			return;
1837 		}
1838 
1839 		if (namesamen(pt, x_("variable"), l) == 0 && l >= 2)
1840 		{
1841 			if (count <= 1)
1842 			{
1843 				ttyputusage(x_("find variable VARNAME"));
1844 				return;
1845 			}
1846 			if (findport != 0 || findpoint != 0 || findexclusively != 0 || findwithin != 0 ||
1847 				findspecial != 0)
1848 			{
1849 				us_abortcommand(M_("'find variable' cannot accept other control"));
1850 				return;
1851 			}
1852 			ni = (NODEINST *)us_getobject(VNODEINST, TRUE);
1853 			if (ni == NONODEINST) return;
1854 			for(i=0; i<ni->numvar; i++)
1855 			{
1856 				var = &ni->firstvar[i];
1857 				if ((var->type&VDISPLAY) == 0) continue;
1858 				if (namesame(par[1], makename(var->key)) == 0) break;
1859 			}
1860 			if (i >= ni->numvar)
1861 			{
1862 				us_abortcommand(_("Sorry, no variable named '%s' on the current node"),
1863 					par[1]);
1864 				return;
1865 			}
1866 			newhigh.status = HIGHTEXT;
1867 			newhigh.fromgeom = ni->geom;
1868 			newhigh.fromport = NOPORTPROTO;
1869 			newhigh.fromvar = var;
1870 			newhigh.cell = np;
1871 			us_setfind(&newhigh, 0, extrainfo, findmore, findnobox);
1872 			return;
1873 		}
1874 		ttyputbadusage(x_("find"));
1875 		return;
1876 	}
1877 
1878 	/* get the cursor co-ordinates */
1879 	if (us_demandxy(&xcur, &ycur)) return;
1880 
1881 	/* find the closest object to the cursor */
1882 	if (findwithin == 0)
1883 		us_findobject(xcur, ycur, el_curwindowpart, &newhigh, findexclusively, 0, findport, 0, findspecial);
1884 	if (newhigh.status == 0) return;
1885 	if (findport == 0) newhigh.fromport = NOPORTPROTO;
1886 	newhigh.cell = np;
1887 	us_setfind(&newhigh, findpoint, extrainfo, findmore, findnobox);
1888 }
1889 
us_getproto(INTBIG count,CHAR * par[])1890 void us_getproto(INTBIG count, CHAR *par[])
1891 {
1892 	REGISTER NODEPROTO *np, *onp;
1893 	REGISTER ARCPROTO *ap, *lat;
1894 	REGISTER INTBIG cellgroupcount, cellcount, doarc, i, l, firstinpass;
1895 	BOOLEAN butstate;
1896 	REGISTER CHAR *pp;
1897 	HIGHLIGHT high;
1898 	extern COMCOMP us_getproto1p;
1899 	REGISTER VARIABLE *highvar;
1900 	GEOM *fromgeom, *togeom;
1901 	PORTPROTO *fromport, *toport;
1902 	REGISTER USERCOM *uc, *ucsub;
1903 	POPUPMENU *pm, *cpm, *pmsub;
1904 	REGISTER POPUPMENUITEM *mi, *miret, *misub;
1905 	extern COMCOMP us_userp;
1906 	REGISTER void *infstr;
1907 
1908 	if (count == 0)
1909 	{
1910 		count = ttygetparam(M_("Getproto option: "), &us_getproto1p, MAXPARS, par);
1911 		if (count == 0)
1912 		{
1913 			us_abortedmsg();
1914 			return;
1915 		}
1916 	}
1917 	l = estrlen(pp = par[0]);
1918 
1919 	if (namesamen(pp, x_("instance"), l) == 0)
1920 	{
1921 		/* show a popup menu with instances */
1922 
1923 		/* number each cellgroup */
1924 		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1925 			np->temp1 = 0;
1926 		cellgroupcount = 0;
1927 		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1928 		{
1929 			if (np->temp1 != 0) continue;
1930 			cellgroupcount++;
1931 			FOR_CELLGROUP(onp, np)
1932 				onp->temp1 = cellgroupcount;
1933 		}
1934 		pm = (POPUPMENU *)emalloc(sizeof(POPUPMENU), us_tool->cluster);
1935 		if (pm == 0) return;
1936 		mi = (POPUPMENUITEM *)emalloc(cellgroupcount * sizeof(POPUPMENUITEM), us_tool->cluster);
1937 		if (mi == 0) return;
1938 		pm->name = x_("x");
1939 		infstr = initinfstr();
1940 		if (cellgroupcount != 0) formatinfstr(infstr, _("Cells in %s"), el_curlib->libname); else
1941 			formatinfstr(infstr, _("No cells in %s"), el_curlib->libname);
1942 		(void)allocstring(&pm->header, returninfstr(infstr), us_tool->cluster);
1943 		pm->list = mi;
1944 		pm->total = cellgroupcount;
1945 
1946 		/* fill the menu */
1947 		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1948 			np->temp1 = 0;
1949 		cellgroupcount = 0;
1950 		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1951 		{
1952 			if (np->temp1 != 0) continue;
1953 			cellcount = 0;
1954 			FOR_CELLGROUP(onp, np)
1955 			{
1956 				onp->temp1 = 1;
1957 				cellcount++;
1958 			}
1959 
1960 			uc = us_allocusercom();
1961 			mi[cellgroupcount].response = uc;
1962 			mi[cellgroupcount].response->active = -1;
1963 			mi[cellgroupcount].value = 0;
1964 
1965 			if (cellcount == 1)
1966 			{
1967 				/* only one cell of this cell: create it directly */
1968 				uc->active = parse(x_("getproto"), &us_userp, TRUE);
1969 				(void)allocstring(&uc->comname, x_("getproto"), us_tool->cluster);
1970 				uc->count = 2;
1971 				(void)allocstring(&uc->word[0], x_("node"), us_tool->cluster);
1972 				(void)allocstring(&uc->word[1], describenodeproto(np), us_tool->cluster);
1973 				mi[cellgroupcount].attribute = uc->word[1];
1974 			} else
1975 			{
1976 				/* multiple cells of this cell: make a submenu */
1977 				mi[cellgroupcount].attribute = np->protoname;
1978 				mi[cellgroupcount].value = x_(">>");
1979 				mi[cellgroupcount].maxlen = -1;
1980 				pmsub = (POPUPMENU *)emalloc(sizeof(POPUPMENU), us_tool->cluster);
1981 				if (pmsub == 0) return;
1982 				misub = (POPUPMENUITEM *)emalloc(cellcount * sizeof(POPUPMENUITEM), us_tool->cluster);
1983 				if (misub == 0) return;
1984 				pmsub->name = x_("x");
1985 				pmsub->header = 0;
1986 				pmsub->list = misub;
1987 				pmsub->total = cellcount;
1988 				cellcount = 0;
1989 				for(i=0; i<2; i++)
1990 				{
1991 					firstinpass = cellcount;
1992 					FOR_CELLGROUP(onp, np)
1993 					{
1994 						/* see if this cell should be included on this pass */
1995 						if (i == 0)
1996 						{
1997 							/* first pass: only include cells that match the current view */
1998 							if (!us_cellfromtech(onp, el_curtech)) continue;
1999 						} else
2000 						{
2001 							/* first pass: only include cells that don't match the current view */
2002 							if (us_cellfromtech(onp, el_curtech)) continue;
2003 						}
2004 
2005 						ucsub = us_allocusercom();
2006 						misub[cellcount].response = ucsub;
2007 						misub[cellcount].response->active = -1;
2008 						(void)allocstring(&misub[cellcount].attribute, describenodeproto(onp),
2009 							us_tool->cluster);
2010 						misub[cellcount].value = 0;
2011 
2012 						/* only one cell of this cell: create it directly */
2013 						ucsub->active = parse(x_("getproto"), &us_userp, TRUE);
2014 						(void)allocstring(&ucsub->comname, x_("getproto"), us_tool->cluster);
2015 						ucsub->count = 2;
2016 						(void)allocstring(&ucsub->word[0], x_("node"), us_tool->cluster);
2017 						(void)allocstring(&ucsub->word[1], describenodeproto(onp),
2018 							us_tool->cluster);
2019 						cellcount++;
2020 					}
2021 					esort(&misub[firstinpass], cellcount-firstinpass,
2022 						sizeof (POPUPMENUITEM), us_sortpopupmenuascending);
2023 				}
2024 				uc->menu = pmsub;
2025 			}
2026 			cellgroupcount++;
2027 		}
2028 
2029 		/* invoke the popup menu */
2030 		esort(mi, cellgroupcount, sizeof (POPUPMENUITEM), us_sortpopupmenuascending);
2031 		butstate = TRUE;
2032 		cpm = pm;
2033 		miret = us_popupmenu(&cpm, &butstate, TRUE, -1, -1, 4);
2034 		if (miret == 0)
2035 		{
2036 			us_abortcommand(_("Sorry, popup menus are not available"));
2037 		} else
2038 		{
2039 			if (miret != NOPOPUPMENUITEM)
2040 				us_execute(miret->response, FALSE, FALSE, FALSE);
2041 		}
2042 		for(i=0; i<cellgroupcount; i++)
2043 		{
2044 			uc = mi[i].response;
2045 			if (uc->menu != NOPOPUPMENU)
2046 			{
2047 				for(l=0; l<uc->menu->total; l++)
2048 				{
2049 					efree((CHAR *)uc->menu->list[l].attribute);
2050 					us_freeusercom(uc->menu->list[l].response);
2051 				}
2052 				efree((CHAR *)uc->menu->list);
2053 				efree((CHAR *)uc->menu);
2054 			}
2055 			us_freeusercom(uc);
2056 		}
2057 		efree((CHAR *)mi);
2058 		efree((CHAR *)pm->header);
2059 		efree((CHAR *)pm);
2060 		return;
2061 	}
2062 
2063 	if (namesamen(pp, x_("node"), l) == 0 && l >= 2 && count > 1)
2064 	{
2065 		np = getnodeproto(par[1]);
2066 		if (np == NONODEPROTO)
2067 		{
2068 			us_abortcommand(_("Cannot find node '%s'"), par[1]);
2069 			return;
2070 		}
2071 		us_setnodeproto(np);
2072 		return;
2073 	}
2074 	if (namesamen(pp, x_("arc"), l) == 0 && l >= 1 && count > 1)
2075 	{
2076 		ap = getarcproto(par[1]);
2077 		if (ap == NOARCPROTO) us_abortcommand(_("Cannot find arc '%s'"), par[1]); else
2078 			us_setarcproto(ap, TRUE);
2079 		return;
2080 	}
2081 
2082 	if (namesamen(pp, x_("this-proto"), l) == 0 && l >= 1)
2083 	{
2084 		np = us_needcell();
2085 		if (np == NONODEPROTO) return;
2086 		us_setnodeproto(np);
2087 		return;
2088 	}
2089 
2090 	/* decide whether arcs are the default */
2091 	doarc = 0;
2092 	if (!us_gettwoobjects(&fromgeom, &fromport, &togeom, &toport)) doarc = 1; else
2093 	{
2094 		highvar = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
2095 		if (highvar != NOVARIABLE && getlength(highvar) == 1)
2096 		{
2097 			(void)us_makehighlight(((CHAR **)highvar->addr)[0], &high);
2098 			if ((high.status&HIGHFROM) != 0 && !high.fromgeom->entryisnode) doarc++;
2099 		}
2100 	}
2101 
2102 	if (namesamen(pp, x_("next-proto"), l) == 0 && l >= 2)
2103 	{
2104 		if (doarc)
2105 		{
2106 			/* advance to the next arcproto */
2107 			ap = us_curarcproto->nextarcproto;
2108 			if (ap == NOARCPROTO) ap = el_curtech->firstarcproto;
2109 			us_setarcproto(ap, TRUE);
2110 		} else
2111 		{
2112 			/* advance to the next nodeproto */
2113 			np = us_curnodeproto;
2114 			if (np->primindex == 0) np = el_curtech->firstnodeproto; else
2115 			{
2116 				/* advance to next after "np" */
2117 				np = np->nextnodeproto;
2118 				if (np == NONODEPROTO) np = el_curtech->firstnodeproto;
2119 			}
2120 			us_setnodeproto(np);
2121 		}
2122 		return;
2123 	}
2124 
2125 	if (namesamen(pp, x_("prev-proto"), l) == 0 && l >= 1)
2126 	{
2127 		if (doarc)
2128 		{
2129 			/* backup to the previous arcproto */
2130 			for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
2131 				if (ap->nextarcproto == us_curarcproto) break;
2132 			if (ap == NOARCPROTO)
2133 				for(lat = el_curtech->firstarcproto; lat != NOARCPROTO; lat = lat->nextarcproto)
2134 					ap = lat;
2135 			us_setarcproto(ap, TRUE);
2136 		} else
2137 		{
2138 			/* backup to the previous nodeproto */
2139 			np = us_curnodeproto;
2140 			if (np->primindex == 0) np = el_curtech->firstnodeproto; else
2141 			{
2142 				/* back up to previous of "np" */
2143 				np = np->prevnodeproto;
2144 				if (np == NONODEPROTO)
2145 					for(np = el_curtech->firstnodeproto; np->nextnodeproto != NONODEPROTO;
2146 						np = np->nextnodeproto) ;
2147 			}
2148 			us_setnodeproto(np);
2149 		}
2150 		return;
2151 	}
2152 
2153 	/* must be a prototype name */
2154 	if (doarc != 0)
2155 	{
2156 		ap = getarcproto(pp);
2157 		if (ap != NOARCPROTO)
2158 		{
2159 			us_setarcproto(ap, TRUE);
2160 			return;
2161 		}
2162 	}
2163 
2164 	np = getnodeproto(pp);
2165 	if (np == NONODEPROTO)
2166 	{
2167 		if (doarc != 0) us_abortcommand(_("Cannot find node or arc '%s'"), pp); else
2168 			us_abortcommand(_("Cannot find node '%s'"), pp);
2169 		return;
2170 	}
2171 	us_setnodeproto(np);
2172 }
2173 
us_grid(INTBIG count,CHAR * par[])2174 void us_grid(INTBIG count, CHAR *par[])
2175 {
2176 	REGISTER INTBIG i, j;
2177 	REGISTER INTBIG l;
2178 	REGISTER CHAR *pp;
2179 
2180 	/* no arguments: toggle the grid state */
2181 	if (count == 0)
2182 	{
2183 		if (us_needwindow()) return;
2184 		if ((el_curwindowpart->state&WINDOWTYPE) != DISPWINDOW &&
2185 		    (el_curwindowpart->state&WINDOWTYPE) != WAVEFORMWINDOW)
2186 		{
2187 			us_abortcommand(_("Cannot show grid in this type of window"));
2188 			return;
2189 		}
2190 
2191 		/* save highlight */
2192 		us_pushhighlight();
2193 		us_clearhighlightcount();
2194 
2195 		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
2196 		us_gridset(el_curwindowpart, ~el_curwindowpart->state);
2197 		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
2198 
2199 		/* restore highlighting */
2200 		us_pophighlight(FALSE);
2201 		return;
2202 	}
2203 
2204 	l = estrlen(pp = par[0]);
2205 	if (namesamen(pp, x_("alignment"), l) == 0 && l >= 1)
2206 	{
2207 		if (count >= 2)
2208 		{
2209 			i = atofr(par[1]);
2210 			if (i < 0)
2211 			{
2212 				us_abortcommand(_("Alignment must be positive"));
2213 				return;
2214 			}
2215 			(void)setvalkey((INTBIG)us_tool, VTOOL, us_alignment_ratio_key, i, VINTEGER);
2216 		}
2217 		ttyputverbose(M_("Cursor alignment is %s lambda"), frtoa(us_alignment_ratio));
2218 		return;
2219 	}
2220 
2221 	if (namesamen(pp, x_("edges"), l) == 0 && l >= 1)
2222 	{
2223 		if (count >= 2)
2224 		{
2225 			i = atofr(par[1]);
2226 			if (i < 0)
2227 			{
2228 				us_abortcommand(_("Alignment must be positive"));
2229 				return;
2230 			}
2231 			(void)setvalkey((INTBIG)us_tool, VTOOL, us_alignment_edge_ratio_key, i, VINTEGER);
2232 		}
2233 		if (us_edgealignment_ratio == 0) ttyputverbose(M_("No edge alignment done")); else
2234 			ttyputverbose(M_("Edge alignment is %s lambda"), frtoa(us_edgealignment_ratio));
2235 		return;
2236 	}
2237 
2238 	if (namesamen(pp, x_("size"), l) == 0 && l >= 1)
2239 	{
2240 		if (count < 2)
2241 		{
2242 			ttyputusage(x_("grid size X [Y]"));
2243 			return;
2244 		}
2245 		i = atola(par[1], 0);
2246 		if (i&1) i++;
2247 		if (count >= 3)
2248 		{
2249 			j = atola(par[2], 0);
2250 			if (j&1) j++;
2251 		} else j = i;
2252 		if (i <= 0 || j <= 0)
2253 		{
2254 			us_abortcommand(_("Invalid grid spacing"));
2255 			return;
2256 		}
2257 
2258 		if (us_needwindow()) return;
2259 
2260 		/* save highlight */
2261 		us_pushhighlight();
2262 		us_clearhighlightcount();
2263 		startobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
2264 
2265 		/* turn grid off if on */
2266 		if ((el_curwindowpart->state&GRIDON) != 0)
2267 			(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("state"),
2268 				el_curwindowpart->state & ~GRIDON, VINTEGER);
2269 
2270 		/* adjust grid */
2271 		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("gridx"), i, VINTEGER);
2272 		(void)setval((INTBIG)el_curwindowpart, VWINDOWPART, x_("gridy"), j, VINTEGER);
2273 
2274 		/* show new grid */
2275 		us_gridset(el_curwindowpart, GRIDON);
2276 
2277 		/* restore highlighting */
2278 		endobjectchange((INTBIG)el_curwindowpart, VWINDOWPART);
2279 		us_pophighlight(FALSE);
2280 		return;
2281 	}
2282 	ttyputbadusage(x_("grid"));
2283 }
2284 
2285 #define NEWSFILE   x_("newsfile")			/* file with news */
2286 
2287 #define NEWSDATE   x_(".electricnews")		/* file with date of last news */
2288 
us_help(INTBIG count,CHAR * par[])2289 void us_help(INTBIG count, CHAR *par[])
2290 {
2291 	REGISTER CHAR *pp;
2292 	REGISTER INTBIG len, lastquiet, nday, nmonth, nyear, on, filestatus;
2293 	INTBIG day, month, year, hour, minute, second;
2294 	FILE *in;
2295 	CHAR line[256], *filename, *hd, *dummy;
2296 	time_t clock;
2297 	REGISTER void *infstr;
2298 
2299 	if (count > 0) len = estrlen(pp = par[0]);
2300 
2301 	/* show the user manual */
2302 	if (count >= 1 && namesamen(pp, x_("manual"), len) == 0)
2303 	{
2304 #ifdef DOCDIR
2305 		estrcpy(line, DOCDIR);
2306 		estrcat(line, x_("index.html"));
2307 		filestatus = fileexistence(line);
2308 		if (filestatus == 1 || filestatus == 3)
2309 		{
2310 			if (browsefile(line))
2311 			{
2312 				us_abortcommand(_("Cannot bring up the user's manual on this system"));
2313 			}
2314 			return;
2315 		}
2316 #endif
2317 		estrcpy(line, el_libdir);
2318 		len = estrlen(line);
2319 		if (line[len-1] == DIRSEP) line[len-1] = 0;
2320 		len = estrlen(line);
2321 		if (namesame(&line[len-3], x_("lib")) == 0) line[len-3] = 0;
2322 		estrcat(line, x_("html"));
2323 		estrcat(line, DIRSEPSTR);
2324 		estrcat(line, x_("manual"));
2325 		estrcat(line, DIRSEPSTR);
2326 		estrcat(line, x_("index.html"));
2327 		filestatus = fileexistence(line);
2328 		if (filestatus != 1 && filestatus != 3)
2329 		{
2330 			us_abortcommand(_("Sorry, cannot locate the user's manual"));
2331 			return;
2332 		}
2333 
2334 		if (browsefile(line))
2335 		{
2336 			us_abortcommand(_("Cannot bring up the user's manual on this system"));
2337 		}
2338 		return;
2339 	}
2340 
2341 	/* print news */
2342 	if (count >= 1 && namesamen(pp, x_("news"), len) == 0)
2343 	{
2344 		/* determine last date of news reading */
2345 		infstr = initinfstr();
2346 		hd = hashomedir();
2347 		if (hd == 0) addstringtoinfstr(infstr, el_libdir); else
2348 			addstringtoinfstr(infstr, hd);
2349 		addstringtoinfstr(infstr, NEWSDATE);
2350 		pp = truepath(returninfstr(infstr));
2351 		in = xopen(pp, el_filetypetext, 0, &dummy);
2352 		if (in == 0) year = month = day = 0; else
2353 		{
2354 			fclose(in);
2355 			clock = filedate(pp);
2356 			parsetime(clock, &year, &month, &day, &hour, &minute, &second);
2357 			month++;
2358 		}
2359 
2360 		/* get the news file */
2361 		infstr = initinfstr();
2362 		addstringtoinfstr(infstr, el_libdir);
2363 		addstringtoinfstr(infstr, NEWSFILE);
2364 		pp = truepath(returninfstr(infstr));
2365 		in = xopen(pp, us_filetypenews, x_(""), &filename);
2366 		if (in == NULL)
2367 		{
2368 			ttyputerr(_("Sorry, cannot find the news file: %s"), pp);
2369 			return;
2370 		}
2371 
2372 		/* read the file */
2373 		on = 0;
2374 
2375 		/* enable messages (if they were off) */
2376 		lastquiet = ttyquiet(0);
2377 		for(;;)
2378 		{
2379 			if (xfgets(line, MAXLINE, in)) break;
2380 			if (on != 0)
2381 			{
2382 				ttyputmsg(x_("%s"), line);
2383 				continue;
2384 			}
2385 			if (line[0] == ' ' || line[0] == 0) continue;
2386 
2387 			/* line with date, see if it is current */
2388 			pp = line;
2389 			nmonth = eatoi(pp);
2390 			while (*pp != '/' && *pp != 0) pp++;
2391 			if (*pp == '/') pp++;
2392 			nday = eatoi(pp);
2393 			while (*pp != '/' && *pp != 0) pp++;
2394 			if (*pp == '/') pp++;
2395 			nyear = eatoi(pp);
2396 			if (nyear < year) continue; else if (nyear > year) on = 1;
2397 			if (nmonth < month) continue; else if (nmonth > month) on = 1;
2398 			if (nday >= day) on = 1;
2399 			if (on != 0) ttyputmsg(x_("%s"), line);
2400 		}
2401 		xclose(in);
2402 
2403 		/* restore message output state */
2404 		(void)ttyquiet(lastquiet);
2405 
2406 		if (on == 0) ttyputmsg(_("No news"));
2407 
2408 		/* now mark the current date */
2409 		infstr = initinfstr();
2410 		hd = hashomedir();
2411 		if (hd == 0) addstringtoinfstr(infstr, el_libdir); else
2412 			addstringtoinfstr(infstr, hd);
2413 		addstringtoinfstr(infstr, NEWSDATE);
2414 		xclose(xcreate(truepath(returninfstr(infstr)), us_filetypenews, 0, 0));
2415 		return;
2416 	}
2417 
2418 	/* illustrate commands */
2419 	if (count >= 1 && namesamen(pp, x_("illustrate"), len) == 0)
2420 	{
2421 		us_illustratecommandset();
2422 		return;
2423 	}
2424 
2425 	/* dump pulldown menus */
2426 	if (count >= 1 && namesamen(pp, x_("pulldowns"), len) == 0)
2427 	{
2428 		us_dumppulldownmenus();
2429 		return;
2430 	}
2431 
2432 	/* general dialog-based help on command-line */
2433 	(void)us_helpdlog(x_("CL"));
2434 }
2435 
us_if(INTBIG count,CHAR * par[])2436 void us_if(INTBIG count, CHAR *par[])
2437 {
2438 	REGISTER INTBIG term1, term1type, term2, term2type;
2439 	REGISTER INTBIG relation;
2440 	REGISTER USERCOM *com;
2441 
2442 	/* make sure the proper number of parameters is given */
2443 	if (count < 4)
2444 	{
2445 		ttyputusage(x_("if TERM1 RELATION TERM2 COMMAND"));
2446 		return;
2447 	}
2448 
2449 	/* get term 1 */
2450 	if (isanumber(par[0]))
2451 	{
2452 		term1 = myatoi(par[0]);
2453 		term1type = VINTEGER;
2454 	} else
2455 	{
2456 		term1 = (INTBIG)par[0];
2457 		term1type = VSTRING;
2458 	}
2459 
2460 	/* get term 2 */
2461 	if (isanumber(par[2]))
2462 	{
2463 		term2 = myatoi(par[2]);
2464 		term2type = VINTEGER;
2465 	} else
2466 	{
2467 		term2 = (INTBIG)par[2];
2468 		term2type = VSTRING;
2469 	}
2470 
2471 	/* make sure the two terms are comparable */
2472 	if (term1type != term2type)
2473 	{
2474 		if (term1 == VINTEGER)
2475 		{
2476 			term1 = (INTBIG)par[0];
2477 			term1type = VSTRING;
2478 		} else
2479 		{
2480 			term2 = (INTBIG)par[1];
2481 			term2type = VSTRING;
2482 		}
2483 	}
2484 
2485 	/* determine the relation being tested */
2486 	relation = -1;
2487 	if (estrcmp(par[1], x_("==")) == 0) relation = 0;
2488 	if (estrcmp(par[1], x_("!=")) == 0) relation = 1;
2489 	if (estrcmp(par[1], x_("<"))  == 0) relation = 2;
2490 	if (estrcmp(par[1], x_("<=")) == 0) relation = 3;
2491 	if (estrcmp(par[1], x_(">"))  == 0) relation = 4;
2492 	if (estrcmp(par[1], x_(">=")) == 0) relation = 5;
2493 	if (relation < 0)
2494 	{
2495 		us_abortcommand(_("Unknown relation: %s"), par[1]);
2496 		return;
2497 	}
2498 
2499 	/* make sure that qualitative comparison is done on numbers */
2500 	if (relation > 1 && term1type != VINTEGER)
2501 	{
2502 		us_abortcommand(_("Inequality comparisons must be done on numbers"));
2503 		return;
2504 	}
2505 
2506 	/* see if the command should be executed */
2507 	switch (relation)
2508 	{
2509 		case 0:		/* == */
2510 			if (term1type == VINTEGER)
2511 			{
2512 				if (term1 != term2) return;
2513 			} else
2514 			{
2515 				if (namesame((CHAR *)term1, (CHAR *)term2) != 0) return;
2516 			}
2517 			break;
2518 		case 1:		/* != */
2519 			if (term1type == VINTEGER)
2520 			{
2521 				if (term1 == term2) return;
2522 			} else
2523 			{
2524 				if (namesame((CHAR *)term1, (CHAR *)term2) == 0) return;
2525 			}
2526 			break;
2527 		case 2:		/* < */
2528 			if (term1 >= term2) return;
2529 			break;
2530 		case 3:		/* <= */
2531 			if (term1 > term2) return;
2532 			break;
2533 		case 4:		/* > */
2534 			if (term1 <= term2) return;
2535 			break;
2536 		case 5:		/* >= */
2537 			if (term1 < term2) return;
2538 			break;
2539 	}
2540 
2541 	/* condition is true: create the command to execute */
2542 	com = us_buildcommand(count-3, &par[3]);
2543 	if (com == NOUSERCOM)
2544 	{
2545 		us_abortcommand(_("Condition true but there is no command to execute"));
2546 		return;
2547 	}
2548 	us_execute(com, FALSE, FALSE, FALSE);
2549 	us_freeusercom(com);
2550 }
2551 
us_interpret(INTBIG count,CHAR * par[])2552 void us_interpret(INTBIG count, CHAR *par[])
2553 {
2554 	REGISTER CHAR *pp;
2555 	REGISTER INTBIG language, len;
2556 	REGISTER VARIABLE *var;
2557 	REGISTER BOOLEAN fromfile;
2558 
2559 	language = VLISP;
2560 	fromfile = FALSE;
2561 	if (namesamen(par[0], x_("file"), estrlen(par[0])) == 0)
2562 	{
2563 		count--;
2564 		par++;
2565 		fromfile = TRUE;
2566 	}
2567 
2568 	if (count > 0)
2569 	{
2570 		len = estrlen(pp = par[0]);
2571 		if (namesamen(pp, x_("lisp"), len) == 0)
2572 		{
2573 			language = VLISP;
2574 			count--;
2575 			par++;
2576 		} else if (namesamen(pp, x_("tcl"), len) == 0)
2577 		{
2578 			language = VTCL;
2579 			count--;
2580 			par++;
2581 		} else if (namesamen(pp, x_("java"), len) == 0)
2582 		{
2583 			language = VJAVA;
2584 			count--;
2585 			par++;
2586 		}
2587 	}
2588 
2589 	/* handle "file" option */
2590 	if (fromfile)
2591 	{
2592 		if (count <= 0)
2593 		{
2594 			ttyputusage(x_("interpret file LANGUAGE FILENAME"));
2595 			return;
2596 		}
2597 		if (loadcode(par[0], language))
2598 			ttyputerr(_("Error loading code"));
2599 		return;
2600 	}
2601 
2602 	/* with no parameters, simply drop into the interpreter loop */
2603 	if (count == 0)
2604 	{
2605 		/* "languageconverse" returns false if it wants to continue conversation */
2606 		if (!languageconverse(language)) us_state |= LANGLOOP; else
2607 			ttyputmsg(_("Back to Electric"));
2608 		return;
2609 	}
2610 
2611 	if (count > 1)
2612 	{
2613 		us_abortcommand(_("Please provide only one parameter to be interpreted"));
2614 		return;
2615 	}
2616 
2617 	var = doquerry(par[0], language, VSTRING);
2618 	if (var == NOVARIABLE) ttyputmsg(x_("%s"), par[0]); else
2619 	{
2620 		ttyputmsg(x_("%s => %s"), par[0], describesimplevariable(var));
2621 	}
2622 }
2623 
us_iterate(INTBIG count,CHAR * par[])2624 void us_iterate(INTBIG count, CHAR *par[])
2625 {
2626 	REGISTER INTBIG i, j, l, times;
2627 	REGISTER BOOLEAN repeatcommand;
2628 	REGISTER USERCOM *uc;
2629 	REGISTER INTBIG len, total;
2630 	INTBIG cindex, objaddr, objtype;
2631 	VARIABLE *var, fvar;
2632 	CHAR *qual, parnames[30];
2633 	BOOLEAN comvar;
2634 	REGISTER USERCOM *com;
2635 	REGISTER void *infstr;
2636 
2637 	/* see if this is the "repeat last command" form */
2638 	repeatcommand = FALSE;
2639 	if (count == 0)
2640 	{
2641 		repeatcommand = TRUE;
2642 		times = 1;
2643 	} else
2644 	{
2645 		l = estrlen(par[0]);
2646 		if (namesamen(par[0], x_("remembered"), l) == 0 && l >= 1)
2647 		{
2648 			if (us_lastcommandcount == 0)
2649 			{
2650 				us_abortcommand(_("No previous command to repeat"));
2651 				return;
2652 			}
2653 			/* repeat the command issued by the "remember" statement */
2654 			uc = us_buildcommand(us_lastcommandcount, us_lastcommandpar);
2655 			if (uc == NOUSERCOM) return;
2656 			us_execute(uc, FALSE, FALSE, FALSE);
2657 			us_freeusercom(uc);
2658 			return;
2659 		}
2660 		if (isanumber(par[0]))
2661 		{
2662 			repeatcommand = TRUE;
2663 			times = eatoi(par[0]);
2664 		}
2665 	}
2666 
2667 	if (repeatcommand)
2668 	{
2669 		/* make sure there was a valid previous command */
2670 		if (us_lastcom == NOUSERCOM || us_lastcom->active < 0)
2671 		{
2672 			us_abortcommand(_("No last command to repeat"));
2673 			return;
2674 		}
2675 
2676 		/* copy last command into new one */
2677 		uc = us_allocusercom();
2678 		if (uc == NOUSERCOM)
2679 		{
2680 			ttyputnomemory();
2681 			return;
2682 		}
2683 		for(i=0; i<us_lastcom->count; i++)
2684 			if (allocstring(&uc->word[i], us_lastcom->word[i], us_tool->cluster))
2685 		{
2686 			ttyputnomemory();
2687 			return;
2688 		}
2689 		uc->active = us_lastcom->active;
2690 		uc->count = us_lastcom->count;
2691 		(void)allocstring(&uc->comname, us_lastcom->comname, us_tool->cluster);
2692 		uc->menu = us_lastcom->menu;
2693 
2694 		/* execute this command */
2695 		for(j=0; j<times; j++) us_execute(uc, TRUE, FALSE, TRUE);
2696 		us_freeusercom(uc);
2697 	} else
2698 	{
2699 		/* implement the iterate over array-variable form */
2700 		if (count < 2)
2701 		{
2702 			ttyputusage(x_("iterate ARRAY-VARIABLE MACRO"));
2703 			return;
2704 		}
2705 		if (us_getvar(par[0], &objaddr, &objtype, &qual, &comvar, &cindex))
2706 		{
2707 			us_abortcommand(_("Incorrect iterator variable name: %s"), par[0]);
2708 			return;
2709 		}
2710 		if (*qual != 0)
2711 		{
2712 			var = getval(objaddr, objtype, -1, qual);
2713 			if (var == NOVARIABLE)
2714 			{
2715 				us_abortcommand(_("Cannot find iterator variable: %s"), par[0]);
2716 				return;
2717 			}
2718 		} else
2719 		{
2720 			fvar.addr = objaddr;
2721 			fvar.type = objtype;
2722 			var = &fvar;
2723 		}
2724 		len = getlength(var);
2725 		if (len < 0) len = 1;
2726 		if (us_expandaddrtypearray(&us_iteratelimit, &us_iterateaddr,
2727 			&us_iteratetype, len)) return;
2728 		if ((var->type&VISARRAY) == 0)
2729 		{
2730 			us_iterateaddr[0] = var->addr;
2731 			us_iteratetype[0] = var->type;
2732 			total = 1;
2733 		} else
2734 		{
2735 			if ((var->type&VTYPE) == VGENERAL)
2736 			{
2737 				for(i=0; i<len; i += 2)
2738 				{
2739 					us_iterateaddr[i/2] = ((INTBIG *)var->addr)[i];
2740 					us_iteratetype[i/2] = ((INTBIG *)var->addr)[i+1];
2741 				}
2742 				total = len / 2;
2743 			} else
2744 			{
2745 				for(i=0; i<len; i++)
2746 				{
2747 					us_iterateaddr[i] = ((INTBIG *)var->addr)[i];
2748 					us_iteratetype[i] = var->type;
2749 				}
2750 				total = len;
2751 			}
2752 		}
2753 
2754 		/* now iterate with this value */
2755 		for(i=0; i<total; i++)
2756 		{
2757 			infstr = initinfstr();
2758 			addstringtoinfstr(infstr, par[1]);
2759 			(void)esnprintf(parnames, 30, x_(" %s 0%lo"), us_variabletypename(us_iteratetype[i]),
2760 				us_iterateaddr[i]);
2761 			addstringtoinfstr(infstr, parnames);
2762 			com = us_makecommand(returninfstr(infstr));
2763 			if (com->active < 0) { us_freeusercom(com);   break; }
2764 			us_execute(com, FALSE, FALSE, FALSE);
2765 			us_freeusercom(com);
2766 			if (stopping(STOPREASONITERATE)) break;
2767 		}
2768 	}
2769 }
2770 
us_killcell(INTBIG count,CHAR * par[])2771 void us_killcell(INTBIG count, CHAR *par[])
2772 {
2773 	REGISTER NODEPROTO *np, *onp;
2774 	REGISTER NODEINST *ni;
2775 	REGISTER LIBRARY *lib;
2776 	extern COMCOMP us_showdp;
2777 
2778 	if (count == 0)
2779 	{
2780 		count = ttygetparam(M_("Cell name: "), &us_showdp, MAXPARS, par);
2781 		if (count == 0)
2782 		{
2783 			us_abortedmsg();
2784 			return;
2785 		}
2786 	}
2787 
2788 	np = getnodeproto(par[0]);
2789 	if (np == NONODEPROTO)
2790 	{
2791 		us_abortcommand(_("No cell called %s"), par[0]);
2792 		return;
2793 	}
2794 	if (np->primindex != 0)
2795 	{
2796 		us_abortcommand(_("Can only kill cells"));
2797 		return;
2798 	}
2799 
2800 	/* disallow killing if lock is on */
2801 	if (us_cantedit(np, NONODEINST, TRUE)) return;
2802 
2803 	/* if there are still instances of the cell, mention them */
2804 	if (np->firstinst != NONODEINST)
2805 	{
2806 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2807 			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
2808 				onp->temp1 = 0;
2809 		for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
2810 			ni->parent->temp1++;
2811 		ttyputerr(_("Erase all of the following instances of this cell first:"));
2812 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
2813 			for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
2814 				if (onp->temp1 != 0)
2815 					ttyputmsg(_("  %ld instance(s) in cell %s"), onp->temp1, describenodeproto(onp));
2816 		return;
2817 	}
2818 
2819 	/* kill the cell */
2820 	us_dokillcell(np);
2821 	ttyputmsg(_("Cell %s:%s deleted"), np->lib->libname, nldescribenodeproto(np));
2822 }
2823