1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrcomln.c
6  * User interface tool: command handler for L through N
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 "egraphics.h"
34 #include "usr.h"
35 #include "usrtrack.h"
36 #include "efunction.h"
37 #include "edialogs.h"
38 #include "conlay.h"
39 #include "tecart.h"
40 #include "tecgen.h"
41 #include "tecschem.h"
42 #if SIMTOOL
43 #  include "sim.h"
44 #endif
45 
us_lambda(INTBIG count,CHAR * par[])46 void us_lambda(INTBIG count, CHAR *par[])
47 {
48 	REGISTER CHAR *pp;
49 	REGISTER INTBIG lam, oldlam, newunit, how;
50 	REGISTER INTBIG l;
51 	REGISTER WINDOWPART *w;
52 	REGISTER NODEPROTO *np;
53 	REGISTER LIBRARY *lib;
54 	REGISTER TECHNOLOGY *tech;
55 	TECHNOLOGY *techarray[1];
56 	INTBIG newlam[1];
57 	extern COMCOMP us_lambdachp, us_noyesp;
58 
59 	if (count > 0)
60 	{
61 		l = estrlen(pp = par[0]);
62 
63 		/* handle display unit setting */
64 		if (namesamen(pp, x_("display-units"), l) == 0)
65 		{
66 			if (count >= 2)
67 			{
68 				l = estrlen(pp = par[1]);
69 				if (namesamen(pp, x_("lambda"), l) == 0) newunit = DISPUNITLAMBDA; else
70 				if (namesamen(pp, x_("inch"), l) == 0) newunit = DISPUNITINCH; else
71 				if (namesamen(pp, x_("centimeter"), l) == 0 && l >= 7) newunit = DISPUNITCM; else
72 				if (namesamen(pp, x_("millimeter"), l) == 0 && l >= 4) newunit = DISPUNITMM; else
73 				if (namesamen(pp, x_("mil"), l) == 0 && l >= 3) newunit = DISPUNITMIL; else
74 				if (namesamen(pp, x_("micron"), l) == 0 && l >= 3) newunit = DISPUNITMIC; else
75 				if (namesamen(pp, x_("centimicron"), l) == 0 && l >= 7) newunit = DISPUNITCMIC; else
76 				if (namesamen(pp, x_("nanometer"), l) == 0) newunit = DISPUNITNM; else
77 				{
78 					us_abortcommand(_("Unknown display unit: %s"), pp);
79 					return;
80 				}
81 				setvalkey((INTBIG)us_tool, VTOOL, us_displayunitskey, newunit, VINTEGER);
82 			}
83 			switch (el_units&DISPLAYUNITS)
84 			{
85 				case DISPUNITLAMBDA:
86 					ttyputverbose(M_("Distance expressed in lambda (currently %ld %ss)"),
87 						el_curlib->lambda[el_curtech->techindex], unitsname(el_units));
88 					break;
89 				case DISPUNITINCH:
90 					ttyputverbose(M_("Distance expressed in inches"));
91 					break;
92 				case DISPUNITCM:
93 					ttyputverbose(M_("Distance expressed in centimeters"));
94 					break;
95 				case DISPUNITMM:
96 					ttyputverbose(M_("Distance expressed in millimeters"));
97 					break;
98 				case DISPUNITMIL:
99 					ttyputverbose(M_("Distance expressed in mils"));
100 					break;
101 				case DISPUNITMIC:
102 					ttyputverbose(M_("Distance expressed in microns"));
103 					break;
104 				case DISPUNITCMIC:
105 					ttyputverbose(M_("Distance expressed in centimicrons"));
106 					break;
107 				case DISPUNITNM:
108 					ttyputverbose(M_("Distance expressed in nanometers"));
109 					break;
110 			}
111 			return;
112 		}
113 
114 		/* handle internal unit setting */
115 		if (namesamen(pp, x_("internal-units"), l) == 0)
116 		{
117 			if (count >= 2)
118 			{
119 				l = estrlen(pp = par[1]);
120 				if (namesamen(pp, x_("half-decimicron"), l) == 0 && l >= 6)
121 				{
122 					changeinternalunits(NOLIBRARY, el_units, INTUNITHDMIC);
123 				} else if (namesamen(pp, x_("half-nanometer"), l) == 0 && l >= 6)
124 				{
125 					changeinternalunits(NOLIBRARY, el_units, INTUNITHNM);
126 				} else
127 				{
128 					us_abortcommand(_("Unknown internal unit: %s"), pp);
129 					return;
130 				}
131 			}
132 			ttyputverbose(M_("Smallest internal unit is %s"), unitsname(el_units));
133 			return;
134 		}
135 
136 		if (namesamen(pp, x_("change-tech"), l) == 0 && l >= 8) how = 0; else
137 			if (namesamen(pp, x_("change-lib"), l) == 0 && l >= 8) how = 1; else
138 				if (namesamen(pp, x_("change-all-libs"), l) == 0 && l >= 8) how = 2; else
139 		{
140 			ttyputusage(x_("lambda change-tech|change-lib|change-all-libs VALUE"));
141 			return;
142 		}
143 
144 		if (count <= 1)
145 		{
146 			count = ttygetparam(M_("Lambda value: "), &us_lambdachp, MAXPARS-1, &par[1]) + 1;
147 			if (count == 1)
148 			{
149 				us_abortedmsg();
150 				return;
151 			}
152 		}
153 
154 		lam = eatoi(par[1]);
155 		if (lam <= 0)
156 		{
157 			us_abortcommand(_("Lambda value must be positive"));
158 			return;
159 		}
160 		if (lam / 4 * 4 != lam)
161 		{
162 			count = ttygetparam(_("Instability may result if lambda is not divisible by four.  Continue? [n]"),
163 				&us_noyesp, MAXPARS, par);
164 			if (count <= 0 || namesamen(par[0], x_("yes"), estrlen(par[0])) != 0)
165 			{
166 				ttyputverbose(M_("No change made"));
167 				return;
168 			}
169 		}
170 
171 		/* save highlighting */
172 		us_pushhighlight();
173 		us_clearhighlightcount();
174 
175 		oldlam = el_curlib->lambda[el_curtech->techindex];
176 		techarray[0] = el_curtech;
177 		newlam[0] = lam;
178 		changelambda(1, techarray, newlam, el_curlib, how);
179 		us_setlambda(NOWINDOWFRAME);
180 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
181 		{
182 			if (w->curnodeproto == NONODEPROTO) continue;
183 			if (w->curnodeproto->tech != el_curtech) continue;
184 			if (how == 2 || (how == 1 && w->curnodeproto->lib == el_curlib))
185 			{
186 				w->screenlx = muldiv(w->screenlx, lam, oldlam);
187 				w->screenhx = muldiv(w->screenhx, lam, oldlam);
188 				w->screenly = muldiv(w->screenly, lam, oldlam);
189 				w->screenhy = muldiv(w->screenhy, lam, oldlam);
190 				computewindowscale(w);
191 			} else us_redisplay(w);
192 		}
193 
194 		/* restore highlighting */
195 		us_pophighlight(FALSE);
196 	}
197 
198 	/* determine which technologies to report */
199 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology) tech->temp1 = 0;
200 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
201 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
202 			np->tech->temp1++;
203 	el_curtech->temp1++;
204 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
205 	{
206 		if (tech->temp1 == 0) continue;
207 		ttyputverbose(M_("Lambda size for %s is %s"), tech->techname,
208 			latoa(el_curlib->lambda[tech->techindex], 0));
209 	}
210 }
211 
us_library(INTBIG count,CHAR * par[])212 void us_library(INTBIG count, CHAR *par[])
213 {
214 	REGISTER LIBRARY *lib, *olib, *lastlib, *mergelib, *oldlib;
215 	REGISTER INTBIG i, l, makecurrent, multifile, found, total, stripopts,
216 		retval, arg, newstate;
217 	REGISTER CHAR *pp, *pt, *style, *libf, *oldlibfile, save;
218 	REGISTER NODEPROTO *np;
219 	REGISTER NODEINST *ni;
220 	REGISTER BOOLEAN first, doquiet;
221 	REGISTER VARIABLE *var;
222 	REGISTER EDITOR *ed;
223 	REGISTER WINDOWPART *w, *curw;
224 	INTBIG lx, hx, ly, hy;
225 	INTBIG dummy;
226 	CHAR *newpar[5], *libn, *extn;
227 	extern COMCOMP us_libraryp, us_yesnop;
228 	REGISTER void *infstr;
229 
230 	if (count == 0)
231 	{
232 		count = ttygetparam(M_("Library option: "), &us_libraryp, MAXPARS, par);
233 		if (count == 0)
234 		{
235 			us_abortedmsg();
236 			return;
237 		}
238 	}
239 	l = estrlen(pp = par[0]);
240 
241 	if (namesamen(pp, x_("default-path"), l) == 0 && l >= 9)
242 	{
243 		if (count < 2)
244 		{
245 			ttyputusage(x_("library default-path PATHNAME"));
246 			return;
247 		}
248 		setlibdir(par[1]);
249 		ttyputverbose(M_("Default library path is now '%s'"), el_libdir);
250 		return;
251 	}
252 
253 	if (namesamen(pp, x_("touch"), l) == 0 && l >= 1)
254 	{
255 		/* mark all libraries as "changed" */
256 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
257 		{
258 			if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
259 			lib->userbits |= LIBCHANGEDMAJOR | LIBCHANGEDMINOR;
260 		}
261 		ttyputmsg(_("All libraries now need to be saved"));
262 		return;
263 	}
264 
265 	if (namesamen(pp, x_("purge"), l) == 0 && l >= 1)
266 	{
267 		/* delete all cells that are not the most recent and are unused */
268 		found = 1;
269 		total = 0;
270 		while (found != 0)
271 		{
272 			found = 0;
273 			for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
274 			{
275 				if (np->newestversion == np) continue;
276 				if (np->firstinst != NONODEINST) continue;
277 				ttyputmsg(_("Deleting cell %s"), describenodeproto(np));
278 				us_dokillcell(np);
279 				found++;
280 				total++;
281 				break;
282 			}
283 		}
284 		if (total == 0) ttyputmsg(_("No unused old cell versions to delete")); else
285 			ttyputmsg(_("Deleted %ld cells"), total);
286 		return;
287 	}
288 
289 	if (namesamen(pp, x_("kill"), l) == 0 && l >= 1)
290 	{
291 		if (count <= 1) lib = el_curlib; else
292 		{
293 			lib = getlibrary(par[1]);
294 			if (lib == NOLIBRARY)
295 			{
296 				us_abortcommand(_("No library called %s"), par[1]);
297 				return;
298 			}
299 		}
300 
301 		/* make sure that this isn't the last library */
302 		i = 0;
303 		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
304 		{
305 			if ((olib->userbits&HIDDENLIBRARY) != 0) continue;
306 			if (olib == lib) continue;
307 			i++;
308 		}
309 		if (i == 0)
310 		{
311 			us_abortcommand(_("Cannot kill last library"));
312 			return;
313 		}
314 
315 		/* check for usage by other libraries */
316 		first = TRUE;
317 		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
318 			olib->temp1 = 0;
319 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
320 		{
321 			for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
322 				ni->parent->lib->temp1 = 1;
323 		}
324 		for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
325 		{
326 			if (olib == lib) continue;
327 			if (olib->temp1 == 0) continue;
328 			if (first)
329 			{
330 				first = FALSE;
331 				ttyputerr(_("Cannot delete library %s because:"), lib->libname);
332 			}
333 			infstr = initinfstr();
334 			formatinfstr(infstr, _("   These cells (in library %s) use it:"),
335 				olib->libname);
336 			for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
337 			{
338 				for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
339 				{
340 					if (ni->proto->primindex != 0) continue;
341 					if (ni->proto->lib == lib) break;
342 				}
343 				if (ni == NONODEINST) continue;
344 				formatinfstr(infstr, x_(" %s"), nldescribenodeproto(np));
345 			}
346 			ttyputmsg(x_("%s"), returninfstr(infstr));
347 		}
348 		if (!first) return;
349 
350 		/* make sure it hasn't changed (unless "safe" option is given) */
351 		if (count <= 2 || namesamen(par[2], x_("safe"), estrlen(par[2])) != 0)
352 		{
353 			if (us_preventloss(lib, 1, TRUE)) return;
354 		}
355 
356 		/* switch to another library if killing current one */
357 		if (lib == el_curlib)
358 		{
359 			us_switchtolibrary(el_curlib->nextlibrary);
360 			ttyputverbose(M_("Current library is now %s"), el_curlib->libname);
361 		} else ttyputverbose(M_("Library %s killed"), lib->libname);
362 
363 		/* remove display of all cells in this library */
364 		for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
365 		{
366 			if (w->curnodeproto == NONODEPROTO) continue;
367 			if (w->curnodeproto->lib == lib) us_clearwindow(w);
368 		}
369 
370 		ttyputmsg(x_("Library %s closed"), lib->libname);
371 
372 		/* kill the library */
373 		killlibrary(lib);
374 
375 		/*
376 		 * the cell structure has changed
377 		 *
378 		 * this wouldn't be necessary if the "killlibrary()" routine did a
379 		 * broadcast.  Then it would be detected properly in "us_killobject"
380 		 */
381 		us_redoexplorerwindow();
382 		return;
383 	}
384 
385 	if (namesamen(pp, x_("read"), l) == 0 && l >= 1)
386 	{
387 		if (count < 2)
388 		{
389 			ttyputusage(x_("library read FILENAME [options]"));
390 			return;
391 		}
392 
393 		/* get style of input */
394 		libf = par[1];
395 		makecurrent = 0;
396 		style = x_("binary");
397 		extn = x_(".elib");
398 		mergelib = NOLIBRARY;
399 		doquiet = FALSE;
400 		while (count >= 3)
401 		{
402 			l = estrlen(pp = par[2]);
403 			if (namesamen(pp, x_("binary"), l) == 0 && l >= 1) { style = x_("binary"); extn = x_(".elib"); doquiet = TRUE; } else
404 			if (namesamen(pp, x_("cif"),    l) == 0 && l >= 1) { style = x_("cif");    extn = x_(".cif");  doquiet = TRUE; } else
405 			if (namesamen(pp, x_("def"),    l) == 0 && l >= 1) { style = x_("def");    extn = x_(".def");                  } else
406 			if (namesamen(pp, x_("dxf"),    l) == 0 && l >= 1) { style = x_("dxf");    extn = x_(".dxf");                  } else
407 			if (namesamen(pp, x_("edif"),   l) == 0 && l >= 1) { style = x_("edif");   extn = x_(".edif");                 } else
408 			if (namesamen(pp, x_("gds"),    l) == 0 && l >= 1) { style = x_("gds");    extn = x_(".gds");  doquiet = TRUE; } else
409 			if (namesamen(pp, x_("lef"),    l) == 0 && l >= 1) { style = x_("lef");    extn = x_(".lef");                  } else
410 			if (namesamen(pp, x_("sdf"),    l) == 0 && l >= 2) { style = x_("sdf");    extn = x_(".sdf");  doquiet = TRUE; } else
411 			if (namesamen(pp, x_("sue"),    l) == 0 && l >= 2) { style = x_("sue");    extn = x_(".sue");  doquiet = TRUE; } else
412 			if (namesamen(pp, x_("text"),   l) == 0 && l >= 1) { style = x_("text");   extn = x_(".txt");  doquiet = TRUE; } else
413 			if (namesamen(pp, x_("vhdl"),   l) == 0 && l >= 1) { style = x_("vhdl");   extn = x_(".vhdl"); doquiet = TRUE; } else
414 			if (namesamen(pp, x_("make-current"), l) == 0 && l >= 2) makecurrent++; else
415 			if (namesamen(pp, x_("merge"), l) == 0 && l >= 2)
416 			{
417 				if (count < 4)
418 				{
419 					ttyputusage(x_("library read FILENAME merge LIBRARYNAME"));
420 					return;
421 				}
422 				mergelib = getlibrary(par[3]);
423 				if (mergelib == NOLIBRARY)
424 				{
425 					us_abortcommand(_("Cannot find merge library %s"), par[3]);
426 					return;
427 				}
428 				count--;
429 				par++;
430 			} else
431 			{
432 				us_abortcommand(_("Read options: binary|cif|def|dxf|edif|gds|lef|sdf|sue|text|vhdl|make-current|(merge LIB)"));
433 				return;
434 			}
435 			count--;
436 			par++;
437 		}
438 
439 		/* turn off highlighting */
440 		us_clearhighlightcount();
441 
442 		/* see if multiple files were selected */
443 		multifile = 0;
444 		for(pt = libf; *pt != 0; pt++) if (*pt == NONFILECH) multifile++;
445 
446 		/* loop through all files */
447 		retval = 0;
448 		for(i=0; ; i++)
449 		{
450 			/* get the next file into "libf" */
451 			for(pt = libf; *pt != 0 && *pt != NONFILECH; pt++) ;
452 			save = *pt;
453 			*pt = 0;
454 
455 			/* get file name and remove extension from library name */
456 			(void)allocstring(&libn, skippath(libf), el_tempcluster);
457 			l = estrlen(libn) - estrlen(extn);
458 			if (namesame(&libn[l], extn) == 0)
459 			{
460 				libn[l] = 0;
461 
462 				/* remove extension from library file if not binary */
463 				if (estrcmp(extn, x_(".elib")) != 0) libf[estrlen(libf) - estrlen(extn)] = 0;
464 			}
465 
466 			oldlib = NOLIBRARY;
467 			if (multifile != 0)
468 			{
469 				/* place the new library file name onto the current library */
470 				lib = el_curlib;
471 				oldlibfile = lib->libfile;
472 				lib->libfile = libf;
473 			} else if (mergelib != NOLIBRARY)
474 			{
475 				/* place the new library file name onto the merge library */
476 				lib = mergelib;
477 				oldlibfile = lib->libfile;
478 				lib->libfile = libf;
479 			} else
480 			{
481 				/* reading a new library (or replacing an old one) */
482 				oldlib = getlibrary(libn);
483 				if (oldlib != NOLIBRARY)
484 				{
485 					/* not a new library: make sure the old library is saved */
486 					if (us_preventloss(oldlib, 2, TRUE) != 0)
487 					{
488 						efree(libn);
489 						return;
490 					}
491 
492 					/* replacing library: clear windows */
493 					for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
494 					{
495 						if (w->curnodeproto != NONODEPROTO &&
496 							w->curnodeproto->lib == oldlib)
497 						{
498 							w->curnodeproto = NONODEPROTO;
499 							us_redisplaynow(w, TRUE);
500 						}
501 					}
502 
503 					/* rename this library, delete it later */
504 					oldlib->libname[0] = 0;
505 				}
506 
507 				/* create the library */
508 				lib = newlibrary(libn, libf);
509 				if (lib == NOLIBRARY)
510 				{
511 					us_abortcommand(_("Cannot create library %s"), libn);
512 					efree(libn);
513 					return;
514 				}
515 			}
516 
517 			/* read the library */
518 			if (multifile == 0) arg = 0; else
519 			{
520 				arg = 2;
521 				if (i == 0) arg = 1; else
522 					if (i == multifile) arg = 3;
523 			}
524 			if (mergelib == NOLIBRARY && doquiet) changesquiet(TRUE);
525 			if (asktool(io_tool, x_("read"), (INTBIG)lib, (INTBIG)style, arg) != 0)
526 				retval = 1;
527 			if (mergelib == NOLIBRARY && doquiet) changesquiet(FALSE);
528 			if (multifile != 0 || mergelib != NOLIBRARY) lib->libfile = oldlibfile;
529 
530 			/* delete the former library if this replaced it */
531 			if (oldlib != NOLIBRARY)
532 			{
533 				us_replacelibraryreferences(oldlib, lib);
534 				if (oldlib == el_curlib) selectlibrary(lib, TRUE);
535 				killlibrary(oldlib);
536 			}
537 
538 			/* advance to the next file in the list */
539 			*pt = (CHAR)save;
540 			if (save == 0) break;
541 			libf = pt + 1;
542 			if (retval != 0) break;
543 		}
544 
545 		if (retval == 0)
546 		{
547 			if (makecurrent != 0 || lib == el_curlib)
548 			{
549 #if SIMTOOL
550 				sim_window_stopsimulation();
551 #endif
552 
553 				selectlibrary(lib, TRUE);
554 				us_setlambda(NOWINDOWFRAME);
555 				if (us_curnodeproto == NONODEPROTO || us_curnodeproto->primindex == 0)
556 				{
557 					nextchangequiet();
558 					(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_node_key,
559 						(INTBIG)el_curtech->firstnodeproto, VNODEPROTO|VDONTSAVE);
560 					us_shadownodeproto(NOWINDOWFRAME, el_curtech->firstnodeproto);
561 				}
562 				np = el_curlib->curnodeproto;
563 				if (el_curwindowpart != NOWINDOWPART &&
564 					el_curwindowpart->curnodeproto == NONODEPROTO &&
565 						(el_curwindowpart->state&WINDOWTYPE) != EXPLORERWINDOW)
566 							curw = el_curwindowpart; else
567 				{
568 					for(curw = el_topwindowpart; curw != NOWINDOWPART; curw = curw->nextwindowpart)
569 						if (curw->curnodeproto == NONODEPROTO &&
570 							(curw->state&WINDOWTYPE) != EXPLORERWINDOW) break;
571 				}
572 				if (np != NONODEPROTO)
573 				{
574 					if (curw == NOWINDOWPART)
575 					{
576 						curw = us_wantnewwindow(0);
577 						if (curw == NOWINDOWPART)
578 						{
579 							us_abortcommand(_("Cannot create new window"));
580 							return;
581 						}
582 					} else
583 					{
584 						if (curw->termhandler != 0)
585 							(*curw->termhandler)(curw);
586 						if ((curw->state&WINDOWTYPE) == POPTEXTWINDOW)
587 							screenrestorebox(curw->editor->savedbox, -1);
588 						if ((curw->state&WINDOWTYPE) == TEXTWINDOW ||
589 							(curw->state&WINDOWTYPE) == POPTEXTWINDOW)
590 								us_freeeditor(curw->editor);
591 					}
592 					if ((np->cellview->viewstate&TEXTVIEW) != 0)
593 					{
594 						/* text window: make an editor */
595 						if (us_makeeditor(curw, describenodeproto(np), &dummy, &dummy) == NOWINDOWPART)
596 						{
597 							efree(libn);
598 							return;
599 						}
600 
601 						/* get the text that belongs here */
602 						var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING|VISARRAY, el_cell_message_key);
603 						if (var == NOVARIABLE)
604 						{
605 							newpar[0] = x_("");
606 							var = setvalkey((INTBIG)np, VNODEPROTO, el_cell_message_key,
607 								(INTBIG)newpar, VSTRING|VISARRAY|(1<<VLENGTHSH));
608 							if (var == NOVARIABLE)
609 							{
610 								efree(libn);
611 								return;
612 							}
613 						}
614 
615 						/* setup for editing */
616 						curw->curnodeproto = np;
617 						ed = curw->editor;
618 						ed->editobjaddr = (CHAR *)np;
619 						ed->editobjtype = VNODEPROTO;
620 						ed->editobjqual = x_("FACET_message");
621 						ed->editobjvar = var;
622 
623 						/* load the text into the window */
624 						us_suspendgraphics(curw);
625 						l = getlength(var);
626 						for(i=0; i<l; i++)
627 						{
628 							pp = ((CHAR **)var->addr)[i];
629 							if (i == l-1 && *pp == 0) continue;
630 							us_addline(curw, i, pp);
631 						}
632 						us_resumegraphics(curw);
633 						curw->changehandler = us_textcellchanges;
634 						us_setcellname(curw);
635 					} else
636 					{
637 						/* adjust the new window to account for borders in the old one */
638 						if ((curw->state&WINDOWTYPE) != DISPWINDOW)
639 						{
640 							curw->usehx -= DISPLAYSLIDERSIZE;
641 							curw->usely += DISPLAYSLIDERSIZE;
642 						}
643 						if ((curw->state&WINDOWTYPE) == WAVEFORMWINDOW)
644 						{
645 							curw->uselx -= DISPLAYSLIDERSIZE;
646 							curw->usely -= DISPLAYSLIDERSIZE;
647 						}
648 						if ((curw->state&WINDOWMODE) != 0)
649 						{
650 							curw->uselx -= WINDOWMODEBORDERSIZE;
651 							curw->usehx += WINDOWMODEBORDERSIZE;
652 							curw->usely -= WINDOWMODEBORDERSIZE;
653 							curw->usehy += WINDOWMODEBORDERSIZE;
654 						}
655 						curw->curnodeproto = np;
656 						newstate = (curw->state & ~(WINDOWTYPE|WINDOWMODE)) | DISPWINDOW;
657 						if ((np->userbits&TECEDITCELL) != 0) newstate |= WINDOWTECEDMODE;
658 						us_setwindowmode(curw, curw->state, newstate);
659 						curw->editor = NOEDITOR;
660 						curw->buttonhandler = DEFAULTBUTTONHANDLER;
661 						curw->charhandler = DEFAULTCHARHANDLER;
662 						curw->changehandler = DEFAULTCHANGEHANDLER;
663 						curw->termhandler = DEFAULTTERMHANDLER;
664 						curw->redisphandler = DEFAULTREDISPHANDLER;
665 						us_fullview(np, &lx, &hx, &ly, &hy);
666 						us_squarescreen(curw, NOWINDOWPART, FALSE, &lx, &hx, &ly, &hy, 0);
667 						curw->screenlx = lx;
668 						curw->screenhx = hx;
669 						curw->screenly = ly;
670 						curw->screenhy = hy;
671 						computewindowscale(curw);
672 						us_setcellname(curw);
673 						us_setcellsize(curw);
674 						us_setgridsize(curw);
675 						if (curw->redisphandler != 0)
676 							(*curw->redisphandler)(curw);
677 						us_state |= CURCELLCHANGED;
678 					}
679 				}
680 
681 				/* redisplay the explorer window */
682 				us_redoexplorerwindow();
683 			}
684 		} else if (mergelib == NOLIBRARY)
685 		{
686 			/* library is incomplete: remove it from the list */
687 			if (el_curlib != lib)
688 			{
689 				/* this is not the current library: safe to delete */
690 				lastlib = NOLIBRARY;
691 				for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
692 				{
693 					if (olib == lib) break;
694 					lastlib = olib;
695 				}
696 				if (lastlib != NOLIBRARY) lastlib->nextlibrary = lib->nextlibrary;
697 			} else
698 			{
699 				/* current library is incomplete */
700 				if (el_curlib->nextlibrary == NOLIBRARY)
701 				{
702 					/* must create a new library: it is the only one */
703 					el_curlib = alloclibrary();
704 					(void)allocstring(&el_curlib->libname, lib->libname, el_curlib->cluster);
705 					(void)allocstring(&el_curlib->libfile, lib->libfile, el_curlib->cluster);
706 					el_curlib->nextlibrary = NOLIBRARY;
707 				} else el_curlib = el_curlib->nextlibrary;
708 
709 				us_clearhighlightcount();
710 				us_setlambda(NOWINDOWFRAME);
711 				if (us_curnodeproto == NONODEPROTO || us_curnodeproto->primindex == 0)
712 				{
713 					if ((us_state&NONPERSISTENTCURNODE) != 0) us_setnodeproto(NONODEPROTO); else
714 						us_setnodeproto(el_curtech->firstnodeproto);
715 				}
716 				if (el_curlib->curnodeproto != NONODEPROTO)
717 				{
718 					newpar[0] = describenodeproto(el_curlib->curnodeproto);
719 					us_editcell(1, newpar);
720 				}
721 			}
722 		}
723 		us_endbatch();
724 		efree(libn);
725 		noundoallowed();
726 		return;
727 	}
728 
729 	if (namesamen(pp, x_("use"), l) == 0 && l >= 1)
730 	{
731 		if (count <= 1)
732 		{
733 			ttyputusage(x_("library use LIBNAME"));
734 			return;
735 		}
736 
737 		/* find actual library name */
738 		libn = skippath(par[1]);
739 
740 		lib = getlibrary(libn);
741 		if (lib != NOLIBRARY)
742 		{
743 			if (el_curlib == lib) return;
744 			us_switchtolibrary(lib);
745 			return;
746 		}
747 
748 		/* create new library */
749 		lib = newlibrary(libn, par[1]);
750 		if (lib == NOLIBRARY)
751 		{
752 			us_abortcommand(_("Cannot create library %s"), libn);
753 			return;
754 		}
755 
756 		/* make this library the current one */
757 		us_switchtolibrary(lib);
758 		ttyputverbose(M_("New library: %s"), libn);
759 		return;
760 	}
761 
762 	if (namesamen(pp, x_("new"), l) == 0 && l >= 1)
763 	{
764 		if (count <= 1)
765 		{
766 			ttyputusage(x_("library new LIBNAME"));
767 			return;
768 		}
769 
770 		/* find actual library name */
771 		libn = skippath(par[1]);
772 
773 		lib = getlibrary(libn);
774 		if (lib != NOLIBRARY)
775 		{
776 			us_abortcommand(_("Already a library called '%s'"), libn);
777 			return;
778 		}
779 
780 		/* create new library */
781 		lib = newlibrary(libn, par[1]);
782 		if (lib == NOLIBRARY)
783 		{
784 			us_abortcommand(_("Cannot create library %s"), libn);
785 			return;
786 		}
787 
788 		ttyputverbose(M_("New library: %s"), libn);
789 		return;
790 	}
791 
792 	if (namesamen(pp, x_("save"), l) == 0 && l >= 2)
793 	{
794 		if (us_saveeverything())
795 		{
796 			/* manage reset of session log (if all information is saved) */
797 			us_continuesessionlogging();
798 		}
799 		return;
800 	}
801 
802 	if (namesamen(pp, x_("write"), l) == 0 && l >= 1)
803 	{
804 		if (count < 2)
805 		{
806 			ttyputusage(x_("library write LIBNAME [options]"));
807 			return;
808 		}
809 
810 		/* get style of output */
811 		style = x_("binary");
812 		if (count >= 3)
813 		{
814 			l = estrlen(pp = par[2]);
815 			if (l >= 1 && namesamen(pp, x_("binary"), l) == 0) style = x_("binary"); else
816 			if (l >= 1 && namesamen(pp, x_("cif"), l) == 0) style = x_("cif"); else
817 			if (l >= 1 && namesamen(pp, x_("dxf"), l) == 0) style = x_("dxf"); else
818 			if (l >= 2 && namesamen(pp, x_("eagle"), l) == 0) style = x_("eagle"); else
819 			if (l >= 2 && namesamen(pp, x_("ecad"), l) == 0) style = x_("ecad"); else
820 			if (l >= 2 && namesamen(pp, x_("edif"), l) == 0) style = x_("edif"); else
821 			if (l >= 1 && namesamen(pp, x_("gds"), l) == 0) style = x_("gds"); else
822 			if (l >= 1 && namesamen(pp, x_("hpgl"), l) == 0) style = x_("hpgl"); else
823 			if (l >= 2 && namesamen(pp, x_("lef"), l) == 0) style = x_("lef"); else
824 			if (l >= 1 && namesamen(pp, x_("l"), l) == 0) style = x_("l"); else
825 			if (l >= 2 && namesamen(pp, x_("pads"), l) == 0) style = x_("pads"); else
826 			if (l >= 2 && namesamen(pp, x_("postscript"), l) == 0) style = x_("postscript"); else
827 			if (l >= 2 && namesamen(pp, x_("printed-postscript"), l) == 0) style = x_("printed-postscript"); else
828 			if (l >= 1 && namesamen(pp, x_("quickdraw"), l) == 0) style = x_("quickdraw"); else
829 			if (l >= 1 && namesamen(pp, x_("skill"), l) == 0) style = x_("skill"); else
830 			if (l >= 1 && namesamen(pp, x_("text"), l) == 0) style = x_("text"); else
831 			{
832 				us_abortcommand(_("File formats: binary|cif|dxf|eagle|ecad|edif|gds|hpgl|l|lef|pads|postscript|printed-postscript|quickdraw|skill|text"));
833 				return;
834 			}
835 		}
836 
837 		/* get library to write */
838 		lib = getlibrary(par[1]);
839 		if (lib == NOLIBRARY)
840 		{
841 			us_abortcommand(_("No library called %s"), par[1]);
842 			return;
843 		}
844 
845 		/* do it */
846 		if (namesame(style, x_("binary")) == 0 || namesame(style, x_("text")) == 0) stripopts = 1; else
847 			stripopts = 0;
848 		if (stripopts != 0) makeoptionstemporary(lib);
849 		retval = asktool(io_tool, x_("write"), (INTBIG)lib, (INTBIG)style);
850 		if (stripopts != 0) restoreoptionstate(lib);
851 		if (retval != 0) return;
852 
853 		/* offer to save any referenced libraries */
854 		if (namesame(style, x_("binary")) == 0)
855 		{
856 			for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
857 				for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
858 					np->temp2 = 0;
859 			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
860 				for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
861 					if (ni->proto->primindex == 0)
862 						ni->proto->temp2 = 1;
863 			for(olib = el_curlib; olib != NOLIBRARY; olib = olib->nextlibrary)
864 			{
865 				if (olib == lib) continue;
866 				for(np = olib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
867 					if (np->temp2 != 0) break;
868 				if (np == NONODEPROTO) continue;
869 				if ((olib->userbits&(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) == 0) continue;
870 
871 				infstr = initinfstr();
872 				if ((olib->userbits&LIBCHANGEDMAJOR) == 0)
873 				{
874 					formatinfstr(infstr, _("Also save referenced library %s (which has changed insignificantly)?"),
875 						olib->libname);
876 				} else
877 				{
878 					formatinfstr(infstr, _("Also save referenced library %s (which has changed significantly)?"),
879 						olib->libname);
880 				}
881 				i = ttygetparam(returninfstr(infstr), &us_yesnop, 3, newpar);
882 				if (i == 1 && newpar[0][0] == 'n') continue;
883 				makeoptionstemporary(olib);
884 				(void)asktool(io_tool, x_("write"), (INTBIG)olib, (INTBIG)style);
885 				restoreoptionstate(olib);
886 			}
887 		}
888 
889 		/* rewind session recording if writing in permanent format */
890 		if (*style == 'b' || *style == 't')
891 		{
892 			/* manage reset of session log (if all information is saved) */
893 			us_continuesessionlogging();
894 		}
895 		return;
896 	}
897 
898 	ttyputbadusage(x_("library"));
899 }
900 
us_macbegin(INTBIG count,CHAR * par[])901 void us_macbegin(INTBIG count, CHAR *par[])
902 {
903 	REGISTER INTBIG i, l;
904 	REGISTER BOOLEAN verbose, execute;
905 	REGISTER INTBIG key;
906 	REGISTER CHAR *ch, *pp;
907 	CHAR *newmacro[3];
908 	extern COMCOMP us_macbeginnp;
909 	REGISTER VARIABLE *var;
910 	REGISTER void *infstr;
911 
912 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING, us_macrobuildingkey);
913 	if (var != NOVARIABLE)
914 	{
915 		us_abortcommand(_("Already defining a macro"));
916 		return;
917 	}
918 
919 	/* get macro name */
920 	if (count == 0)
921 	{
922 		count = ttygetparam(M_("Macro name: "), &us_macbeginnp, MAXPARS, par);
923 		if (count == 0)
924 		{
925 			us_abortedmsg();
926 			return;
927 		}
928 	}
929 	pp = par[0];
930 
931 	/* get switches */
932 	verbose = FALSE;   execute = TRUE;
933 	for(i=1; i<count; i++)
934 	{
935 		ch = par[i];
936 		l = estrlen(ch);
937 		if (namesamen(ch, x_("verbose"), l) == 0) verbose = TRUE; else
938 		if (namesamen(ch, x_("no-execute"), l) == 0) execute = FALSE; else
939 		{
940 			ttyputusage(x_("macbegin MACRONAME [verbose|no-execute]"));
941 			return;
942 		}
943 	}
944 
945 	/* make sure macro name isn't overloading existing command */
946 	for(i=0; us_lcommand[i].name != 0; i++)
947 		if (namesame(pp, us_lcommand[i].name) == 0)
948 	{
949 		us_abortcommand(_("There is a command with that name"));
950 		return;
951 	}
952 
953 	/* see if macro name already exists */
954 	if (us_getmacro(pp) == NOVARIABLE)
955 	{
956 		/* new macro name, check for validity */
957 		for(ch = pp; *ch != 0; ch++) if (*ch == ' ' || *ch == '\t')
958 		{
959 			us_abortcommand(_("Macro name must not have embedded spaces"));
960 			return;
961 		}
962 	}
963 
964 	/* create the macro variable */
965 	infstr = initinfstr();
966 	addstringtoinfstr(infstr, x_("USER_macro_"));
967 	addstringtoinfstr(infstr, pp);
968 	key = makekey(returninfstr(infstr));
969 
970 	/* build the first two lines of the macro */
971 	infstr = initinfstr();
972 	if (us_curmacropack != NOMACROPACK)
973 	{
974 		addstringtoinfstr(infstr, x_("macropack="));
975 		addstringtoinfstr(infstr, us_curmacropack->packname);
976 	}
977 	if (verbose) addstringtoinfstr(infstr, x_(" verbose"));
978 	if (!execute) addstringtoinfstr(infstr, x_(" noexecute"));
979 	newmacro[0] = returninfstr(infstr);
980 	newmacro[1] = x_("");		/* no parameters yet */
981 	newmacro[2] = x_("");		/* no parsing structures yet */
982 	(void)setvalkey((INTBIG)us_tool, VTOOL, key, (INTBIG)newmacro,
983 		VSTRING|VISARRAY|(3<<VLENGTHSH)|VDONTSAVE);
984 
985 	/* turn on macro remembering */
986 	nextchangequiet();
987 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_macrobuildingkey, (INTBIG)pp, VSTRING|VDONTSAVE);
988 	nextchangequiet();
989 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_macrorunningkey, (INTBIG)pp, VSTRING|VDONTSAVE);
990 
991 	ttyputmsg(_("Remembering %s..."), pp);
992 }
993 
us_macdone(INTBIG count,CHAR * par[])994 void us_macdone(INTBIG count, CHAR *par[])
995 {
996 	REGISTER VARIABLE *var;
997 
998 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING, us_macrorunningkey);
999 	if (var == NOVARIABLE)
1000 	{
1001 		us_abortcommand(_("Can only stop execution of a macro if one is running"));
1002 		return;
1003 	}
1004 	us_state |= MACROTERMINATED;
1005 }
1006 
us_macend(INTBIG count,CHAR * par[])1007 void us_macend(INTBIG count, CHAR *par[])
1008 {
1009 	REGISTER VARIABLE *var;
1010 
1011 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING, us_macrobuildingkey);
1012 	if (var == NOVARIABLE)
1013 	{
1014 		us_abortcommand(_("Not in a macro"));
1015 		return;
1016 	}
1017 	ttyputmsg(_("%s defined"), (CHAR *)var->addr);
1018 	nextchangequiet();
1019 	(void)delvalkey((INTBIG)us_tool, VTOOL, us_macrobuildingkey);
1020 	if (getvalkey((INTBIG)us_tool, VTOOL, VSTRING, us_macrorunningkey) != NOVARIABLE)
1021 	{
1022 		nextchangequiet();
1023 		(void)delvalkey((INTBIG)us_tool, VTOOL, us_macrorunningkey);
1024 	}
1025 }
1026 
us_menu(INTBIG count,CHAR * par[])1027 void us_menu(INTBIG count, CHAR *par[])
1028 {
1029 	REGISTER INTBIG l, s, i, total, changed, position, x, y, varkey,
1030 		j, len, arctot, pintot, comptot, puretot, fun, coms;
1031 	REGISTER BOOLEAN autoset, verbose;
1032 	BOOLEAN butstate;
1033 	REGISTER CHAR *pp, **temp;
1034 	CHAR number[10], *pt, *implicit[6];
1035 	POPUPMENU *pm, *cpm, *newpm;
1036 	REGISTER POPUPMENUITEM *mi, *miret;
1037 	NODEPROTO *np;
1038 	ARCPROTO *ap;
1039 	REGISTER USERCOM *uc;
1040 	REGISTER VARIABLE *var;
1041 	COMCOMP *comarray[MAXPARS];
1042 	COMMANDBINDING commandbinding;
1043 	extern COMCOMP us_menup;
1044 	REGISTER void *infstr;
1045 
1046 	/* with no arguments, toggle the menu state */
1047 	if (count == 0)
1048 	{
1049 		count = ttygetparam(_("Menu display option: "), &us_menup, MAXPARS, par);
1050 		if (count == 0)
1051 		{
1052 			us_abortedmsg();
1053 			return;
1054 		}
1055 	}
1056 
1057 	/* check for simple changes of the menu display */
1058 	l = estrlen(pp = par[0]);
1059 	if (namesamen(pp, x_("on"), l) == 0 && l >= 2)
1060 	{
1061 		if ((us_tool->toolstate&MENUON) != 0)
1062 		{
1063 			us_abortcommand(_("Menu is already displayed"));
1064 			return;
1065 		}
1066 
1067 		/* save highlighting */
1068 		us_pushhighlight();
1069 		us_clearhighlightcount();
1070 
1071 		startobjectchange((INTBIG)us_tool, VTOOL);
1072 		(void)setval((INTBIG)us_tool, VTOOL, x_("toolstate"), us_tool->toolstate|MENUON,
1073 			VINTEGER);
1074 		endobjectchange((INTBIG)us_tool, VTOOL);
1075 
1076 		/* restore highlighting */
1077 		us_pophighlight(FALSE);
1078 		return;
1079 	}
1080 
1081 	if (namesamen(pp, x_("off"), l) == 0 && l >= 2)
1082 	{
1083 		if ((us_tool->toolstate&MENUON) == 0)
1084 		{
1085 			us_abortcommand(_("Menu is not displayed"));
1086 			return;
1087 		}
1088 
1089 		/* save highlighting */
1090 		us_pushhighlight();
1091 		us_clearhighlightcount();
1092 
1093 		startobjectchange((INTBIG)us_tool, VTOOL);
1094 		(void)setval((INTBIG)us_tool, VTOOL, x_("toolstate"), us_tool->toolstate & ~MENUON, VINTEGER);
1095 		endobjectchange((INTBIG)us_tool, VTOOL);
1096 
1097 		/* restore highlighting */
1098 		us_pophighlight(FALSE);
1099 		return;
1100 	}
1101 
1102 	if (namesamen(pp, x_("dopopup"), l) == 0 && l >= 1)
1103 	{
1104 		if (count <= 1)
1105 		{
1106 			implicit[0] = x_("telltool");
1107 			implicit[1] = x_("user");
1108 			uc = us_buildcommand(2, implicit);
1109 		} else uc = us_buildcommand(count-1, &par[1]);
1110 		if (uc == NOUSERCOM)
1111 		{
1112 			us_abortcommand(_("Command '%s' not found"), par[1]);
1113 			return;
1114 		}
1115 
1116 		/* loop through the popup menus of this command */
1117 		for(;;)
1118 		{
1119 			/* convert to a string of command completion objects */
1120 			total = us_fillcomcomp(uc, comarray);
1121 			if (total <= uc->count) break;
1122 
1123 			/* count the number of options */
1124 			us_pathiskey = comarray[uc->count]->ifmatch;
1125 			pt = x_("");
1126 			(void)(*comarray[uc->count]->toplist)(&pt);
1127 			for(coms=0; (pp = (*comarray[uc->count]->nextcomcomp)()); coms++) ;
1128 			if ((comarray[uc->count]->interpret&INCLUDENOISE) != 0) coms++;
1129 
1130 			/* build the prompt */
1131 			infstr = initinfstr();
1132 			addstringtoinfstr(infstr, comarray[uc->count]->noise);
1133 
1134 			/* if there are no choices, prompt directly */
1135 			if (coms == 0)
1136 			{
1137 				addstringtoinfstr(infstr, x_(": "));
1138 				pp = ttygetline(returninfstr(infstr));
1139 				if (pp == 0) break;
1140 				(void)allocstring(&uc->word[uc->count], pp, us_tool->cluster);
1141 				uc->count++;
1142 				continue;
1143 			}
1144 
1145 			/* create the popup menu */
1146 			pm = (POPUPMENU *)emalloc(sizeof(POPUPMENU), el_tempcluster);
1147 			if (pm == 0) return;
1148 			pm->name = x_("noname");
1149 			(void)allocstring(&pm->header, returninfstr(infstr), el_tempcluster);
1150 			pm->total = coms;
1151 			mi = (POPUPMENUITEM *)emalloc((coms * (sizeof (POPUPMENUITEM))), el_tempcluster);
1152 			if (mi == 0) return;
1153 			pm->list = mi;
1154 
1155 			/* fill the popup menu */
1156 			pt = x_("");
1157 			(void)(*comarray[uc->count]->toplist)(&pt);
1158 			for(i=0; i<coms; i++)
1159 			{
1160 				mi[i].valueparse = NOCOMCOMP;
1161 				mi[i].response = NOUSERCOM;
1162 
1163 				/* if noise is includable, do it first */
1164 				if (i == 0 && (comarray[uc->count]->interpret&INCLUDENOISE) != 0)
1165 				{
1166 					mi[i].attribute = comarray[uc->count]->noise;
1167 					if (comarray[uc->count]->def != 0)
1168 					{
1169 						mi[i].maxlen = maxi(20, estrlen(comarray[uc->count]->def));
1170 						mi[i].value = (CHAR *)emalloc((mi[i].maxlen+1) * SIZEOFCHAR, el_tempcluster);
1171 						(void)estrcpy(mi[i].value, comarray[uc->count]->def);
1172 					} else
1173 					{
1174 						mi[i].maxlen = 20;
1175 						mi[i].value = (CHAR *)emalloc((mi[i].maxlen+1) * SIZEOFCHAR, el_tempcluster);
1176 						(void)estrcpy(mi[i].value, x_("*"));
1177 					}
1178 					continue;
1179 				}
1180 
1181 				/* normal entry */
1182 				mi[i].attribute = (*comarray[uc->count]->nextcomcomp)();
1183 				mi[i].value = 0;
1184 				mi[i].maxlen = -1;
1185 
1186 				/* see what would come next with this keyword */
1187 				(void)allocstring(&uc->word[uc->count], mi[i].attribute, el_tempcluster);
1188 				uc->count++;
1189 				total = us_fillcomcomp(uc, comarray);
1190 				uc->count--;
1191 				efree(uc->word[uc->count]);
1192 				if (total > uc->count+1)
1193 				{
1194 					if ((comarray[uc->count+1]->interpret&INPUTOPT) != 0)
1195 					{
1196 						mi[i].valueparse = comarray[uc->count+1];
1197 						if (comarray[uc->count+1]->def != 0)
1198 						{
1199 							mi[i].maxlen = maxi(20, estrlen(comarray[uc->count+1]->def));
1200 							mi[i].value = (CHAR *)emalloc((mi[i].maxlen+1) * SIZEOFCHAR, el_tempcluster);
1201 							(void)estrcpy(mi[i].value, comarray[uc->count+1]->def);
1202 						} else
1203 						{
1204 							mi[i].maxlen = 20;
1205 							mi[i].value = (CHAR *)emalloc((mi[i].maxlen+1) * SIZEOFCHAR, el_tempcluster);
1206 							(void)estrcpy(mi[i].value, x_("*"));
1207 						}
1208 					}
1209 				}
1210 			}
1211 
1212 			/* invoke the popup menu */
1213 			butstate = TRUE;
1214 			cpm = pm;
1215 			miret = us_popupmenu(&cpm, &butstate, TRUE, -1, -1, 0);
1216 			if (miret == 0) us_abortcommand(_("Sorry, this display cannot support popup menus"));
1217 			if (miret == NOPOPUPMENUITEM || miret == 0)
1218 			{
1219 				efree((CHAR *)mi);
1220 				efree((CHAR *)pm->header);
1221 				efree((CHAR *)pm);
1222 				break;
1223 			}
1224 
1225 			/* see if multiple answers were given */
1226 			changed = 0;
1227 			for(i=0; i<pm->total; i++) if (mi[i].changed) changed++;
1228 			if (changed > 1 || (changed == 1 && miret->changed == 0))
1229 			{
1230 				if ((comarray[uc->count]->interpret&MULTIOPT) == 0)
1231 				{
1232 					us_abortcommand(_("Multiple commands cannot be selected"));
1233 					return;
1234 				}
1235 
1236 				/* tack on all of the answers */
1237 				for(i=0; i<pm->total; i++) if (mi[i].changed)
1238 				{
1239 					if (uc->count >= MAXPARS-1) break;
1240 					(void)allocstring(&uc->word[uc->count++], mi[i].attribute, us_tool->cluster);
1241 					(void)allocstring(&uc->word[uc->count++], mi[i].value, us_tool->cluster);
1242 				}
1243 				if (!miret->changed && uc->count < MAXPARS)
1244 					(void)allocstring(&uc->word[uc->count++], miret->attribute, us_tool->cluster);
1245 
1246 				for(i=0; i<pm->total; i++)
1247 					if (mi[i].maxlen >= 0) efree(mi[i].value);
1248 				efree((CHAR *)mi);
1249 				efree((CHAR *)pm);
1250 				break;
1251 			}
1252 
1253 			/* if noise was mentioned, copy the value */
1254 			if (miret == mi && miret->changed &&
1255 				(comarray[uc->count]->interpret&INCLUDENOISE) != 0)
1256 			{
1257 				(void)allocstring(&uc->word[uc->count++], miret->value, us_tool->cluster);
1258 			} else
1259 			{
1260 				(void)allocstring(&uc->word[uc->count++], miret->attribute, us_tool->cluster);
1261 				if (miret->changed)
1262 					(void)allocstring(&uc->word[uc->count++], miret->value, us_tool->cluster);
1263 			}
1264 			for(i=0; i<pm->total; i++)
1265 				if (mi[i].maxlen >= 0) efree(mi[i].value);
1266 			efree((CHAR *)mi);
1267 			efree((CHAR *)pm);
1268 		}
1269 
1270 		/* issue the completed command */
1271 		if ((us_tool->toolstate&ECHOBIND) != 0) verbose = TRUE; else
1272 			verbose = FALSE;
1273 		us_execute(uc, verbose, FALSE, TRUE);
1274 		us_freeusercom(uc);
1275 		return;
1276 	}
1277 
1278 	if (namesamen(pp, x_("popup"), l) == 0 && l >= 1)
1279 	{
1280 		if (count <= 2)
1281 		{
1282 			ttyputusage(x_("menu popup NAME OPTIONS"));
1283 			return;
1284 		}
1285 
1286 		/* check out the name of the pop-up menu */
1287 		pm = us_getpopupmenu(par[1]);
1288 
1289 		/* determine what to do with the menu */
1290 		l = estrlen(pp = par[2]);
1291 		if (namesamen(pp, x_("header"), l) == 0 && l >= 1)
1292 		{
1293 			/* set header: ensure it is specified */
1294 			if (count <= 3)
1295 			{
1296 				ttyputusage(x_("menu popup header MESSAGE"));
1297 				return;
1298 			}
1299 			if (pm == NOPOPUPMENU)
1300 			{
1301 				us_abortcommand(_("No popup menu called %s"), par[1]);
1302 				return;
1303 			}
1304 			infstr = initinfstr();
1305 			addstringtoinfstr(infstr, x_("USER_binding_popup_"));
1306 			addstringtoinfstr(infstr, par[1]);
1307 			varkey = makekey(returninfstr(infstr));
1308 			var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, varkey);
1309 			if (var == NOVARIABLE)
1310 			{
1311 				us_abortcommand(_("Cannot find the menu"));
1312 				return;
1313 			}
1314 			(void)setindkey((INTBIG)us_tool, VTOOL, varkey, 0, (INTBIG)par[3]);
1315 			return;
1316 		}
1317 		if (namesamen(pp, x_("size"), l) == 0 && l >= 1)
1318 		{
1319 			/* set size: ensure it is specified */
1320 			if (count <= 3)
1321 			{
1322 				ttyputusage(x_("menu popup NAME size SIZE"));
1323 				return;
1324 			}
1325 			s = myatoi(par[3]);
1326 			if (s <= 0)
1327 			{
1328 				us_abortcommand(_("Popup menu size must be positive"));
1329 				return;
1330 			}
1331 
1332 			/* set size of menu */
1333 			if (pm == NOPOPUPMENU)
1334 			{
1335 				/* create the variable with the popup menu */
1336 				temp = (CHAR **)emalloc(((s+1) * (sizeof (CHAR *))), el_tempcluster);
1337 				(void)allocstring(&temp[0], x_("MENU!"), el_tempcluster);
1338 				for(i=0; i<s; i++)
1339 				{
1340 					infstr = initinfstr();
1341 					addstringtoinfstr(infstr, x_("inputpopup="));
1342 					addstringtoinfstr(infstr, par[1]);
1343 					addstringtoinfstr(infstr, x_(" command=bind set popup "));
1344 					addstringtoinfstr(infstr, par[1]);
1345 					(void)esnprintf(number, 10, x_(" %ld"), i);
1346 					addstringtoinfstr(infstr, number);
1347 					(void)allocstring(&temp[i+1], returninfstr(infstr), el_tempcluster);
1348 				}
1349 				infstr = initinfstr();
1350 				addstringtoinfstr(infstr, x_("USER_binding_popup_"));
1351 				addstringtoinfstr(infstr, par[1]);
1352 				varkey = makekey(returninfstr(infstr));
1353 				(void)setvalkey((INTBIG)us_tool, VTOOL, varkey, (INTBIG)temp,
1354 					VSTRING|VISARRAY|VDONTSAVE|((s+1)<<VLENGTHSH));
1355 				for(i=0; i<=s; i++) efree(temp[i]);
1356 				efree((CHAR *)temp);
1357 			} else
1358 			{
1359 				/* get the former popup menu */
1360 				infstr = initinfstr();
1361 				addstringtoinfstr(infstr, x_("USER_binding_popup_"));
1362 				addstringtoinfstr(infstr, par[1]);
1363 				varkey = makekey(returninfstr(infstr));
1364 				var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, varkey);
1365 				if (var == NOVARIABLE)
1366 				{
1367 					us_abortcommand(_("Cannot find popup menu %s"), par[1]);
1368 					return;
1369 				}
1370 				len = getlength(var);
1371 
1372 				/* create the new popup menu */
1373 				temp = (CHAR **)emalloc(((s+1) * (sizeof (CHAR *))), el_tempcluster);
1374 				if (temp == 0) return;
1375 				(void)allocstring(&temp[0], ((CHAR **)var->addr)[0], el_tempcluster);
1376 
1377 				/* copy the existing menu entries */
1378 				for(i=1; i<mini((s+1), len); i++)
1379 				{
1380 					us_parsebinding(((CHAR **)var->addr)[i], &commandbinding);
1381 					infstr = initinfstr();
1382 					if (commandbinding.inputpopup) addstringtoinfstr(infstr, x_("input"));
1383 					addstringtoinfstr(infstr, x_("popup="));
1384 					addstringtoinfstr(infstr, par[1]);
1385 					if (commandbinding.menumessage != 0)
1386 					{
1387 						addstringtoinfstr(infstr, x_(" message=\""));
1388 						addstringtoinfstr(infstr, commandbinding.menumessage);
1389 						addtoinfstr(infstr, '"');
1390 					}
1391 					addstringtoinfstr(infstr, x_(" command="));
1392 					addstringtoinfstr(infstr, commandbinding.command);
1393 					(void)allocstring(&temp[i], returninfstr(infstr), el_tempcluster);
1394 					us_freebindingparse(&commandbinding);
1395 				}
1396 
1397 				for(j=i; j<s+1; j++)
1398 				{
1399 					infstr = initinfstr();
1400 					addstringtoinfstr(infstr, x_("inputpopup="));
1401 					addstringtoinfstr(infstr, par[1]);
1402 					addstringtoinfstr(infstr, x_(" command=bind set popup "));
1403 					addstringtoinfstr(infstr, par[1]);
1404 					(void)esnprintf(number, 10, x_(" %ld"), j);
1405 					addstringtoinfstr(infstr, number);
1406 					(void)allocstring(&temp[j], returninfstr(infstr), el_tempcluster);
1407 				}
1408 				(void)setvalkey((INTBIG)us_tool, VTOOL, varkey, (INTBIG)temp,
1409 					VSTRING|VISARRAY|VDONTSAVE|((s+1)<<VLENGTHSH));
1410 				for(i=0; i<=s; i++) efree(temp[i]);
1411 				efree((CHAR *)temp);
1412 				newpm = us_getpopupmenu(par[1]);
1413 				for(i=0; i<us_pulldownmenucount; i++)
1414 					us_recursivelyreplacemenu(us_pulldowns[i], pm, newpm);
1415 		}
1416 			return;
1417 		}
1418 		ttyputbadusage(x_("menu popup"));
1419 		return;
1420 	}
1421 
1422 	if (namesamen(pp, x_("setmenubar"), l) == 0 && l >= 2)
1423 	{
1424 		/* free former menubar information */
1425 		if (us_pulldownmenucount != 0)
1426 		{
1427 			efree((CHAR *)us_pulldowns);
1428 			efree((CHAR *)us_pulldownmenupos);
1429 		}
1430 		us_pulldownmenucount = 0;
1431 
1432 		/* allocate new menubar information */
1433 		if (count > 1)
1434 		{
1435 			us_pulldowns = (POPUPMENU **)emalloc((count-1) * (sizeof (POPUPMENU *)), us_tool->cluster);
1436 			us_pulldownmenupos = (INTBIG *)emalloc((count-1) * SIZEOFINTBIG, us_tool->cluster);
1437 			if (us_pulldowns == 0 || us_pulldownmenupos == 0) return;
1438 			us_pulldownmenucount = count-1;
1439 		}
1440 
1441 		/* load the menubar information */
1442 		for(i=1; i<count; i++)
1443 		{
1444 			/* check out the name of the pop-up menu */
1445 			pm = us_getpopupmenu(par[i]);
1446 			if (pm == NOPOPUPMENU)
1447 			{
1448 				ttyputbadusage(x_("menu setmenubar"));
1449 				return;
1450 			}
1451 			us_pulldowns[i-1] = pm;
1452 			us_validatemenu(pm);
1453 		}
1454 
1455 		/* always use native menu handling */
1456 		nativemenuload(count-1, &par[1]);
1457 		return;
1458 	}
1459 
1460 	/* see if new fixed menu location has been specified */
1461 	position = us_menupos;
1462 	x = us_menux;   y = us_menuy;
1463 	if (namesamen(pp, x_("top"), l) == 0 && l >= 1)
1464 	{
1465 		position = 0;
1466 		count--;   par++;
1467 	} else if (namesamen(pp, x_("bottom"), l) == 0 && l >= 1)
1468 	{
1469 		position = 1;
1470 		count--;   par++;
1471 	} else if (namesamen(pp, x_("left"), l) == 0 && l >= 1)
1472 	{
1473 		position = 2;
1474 		count--;   par++;
1475 	} else if (namesamen(pp, x_("right"), l) == 0 && l >= 1)
1476 	{
1477 		position = 3;
1478 		count--;   par++;
1479 	}
1480 
1481 	/* check for new fixed menu size */
1482 	autoset = FALSE;
1483 	if (count > 0)
1484 	{
1485 		if (namesamen(par[0], x_("size"), estrlen(par[0])) != 0)
1486 		{
1487 			ttyputbadusage(x_("menu"));
1488 			return;
1489 		}
1490 		if (count == 2 && namesamen(par[1], x_("auto"), estrlen(par[1])) == 0)
1491 		{
1492 			autoset = TRUE;
1493 			arctot = pintot = comptot = puretot = 0;
1494 			for(ap = el_curtech->firstarcproto; ap != NOARCPROTO; ap = ap->nextarcproto)
1495 			{
1496 				if ((ap->userbits&ANOTUSED) != 0) continue;
1497 				np = getpinproto(ap);
1498 				if (np != NONODEPROTO && (np->userbits & NNOTUSED) != 0) continue;
1499 				arctot++;
1500 			}
1501 			for(np = el_curtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1502 			{
1503 				if ((np->userbits & NNOTUSED) != 0) continue;
1504 				fun = (np->userbits&NFUNCTION) >> NFUNCTIONSH;
1505 				if (fun == NPPIN) pintot++; else
1506 					if (fun == NPNODE) puretot++; else
1507 						comptot++;
1508 			}
1509 			if (pintot + comptot == 0) pintot = puretot;
1510 			x = arctot + pintot + comptot + 1;   y = 1;
1511 			if (x > 40)
1512 			{
1513 				x = (x+2) / 3;
1514 				y = 3;
1515 			} else if (x > 20)
1516 			{
1517 				x = (x+1) / 2;
1518 				y = 2;
1519 			}
1520 			if (position > 1) { i = x;   x = y;   y = i; }
1521 		} else if (count == 3)
1522 		{
1523 			x = eatoi(par[1]);
1524 			y = eatoi(par[2]);
1525 			if (x <= 0 || y <= 0)
1526 			{
1527 				us_abortcommand(_("Menu sizes must be positive numbers"));
1528 				return;
1529 			}
1530 		} else
1531 		{
1532 			ttyputusage(x_("menu size (ROWS COLUMNS) | auto"));
1533 			return;
1534 		}
1535 	}
1536 
1537 	/* set the fixed menu up */
1538 	if (x == us_menux && y == us_menuy && position == us_menupos &&
1539 		(us_tool->toolstate&MENUON) != 0 && !autoset)
1540 	{
1541 		ttyputverbose(M_("Menu has not changed"));
1542 		return;
1543 	}
1544 
1545 	if (position != us_menupos) resetpaletteparameters();
1546 	startobjectchange((INTBIG)us_tool, VTOOL);
1547 	if (x != us_menux || y != us_menuy || position != us_menupos || autoset)
1548 		us_setmenusize(x, y, position, autoset);
1549 	if ((us_tool->toolstate&MENUON) == 0)
1550 		(void)setval((INTBIG)us_tool, VTOOL, x_("toolstate"), us_tool->toolstate|MENUON, VINTEGER);
1551 	endobjectchange((INTBIG)us_tool, VTOOL);
1552 }
1553 
us_mirror(INTBIG count,CHAR * par[])1554 void us_mirror(INTBIG count, CHAR *par[])
1555 {
1556 	REGISTER NODEINST *ni, *theni, *subni, **nilist, **newnilist;
1557 	REGISTER INTBIG amt, gamt, i;
1558 	REGISTER INTBIG nicount, lx, hx, ly, hy, dist, bestdist, aicount, newnicount;
1559 	REGISTER CHAR *pt;
1560 	REGISTER VARIABLE *var, *tempvar;
1561 	REGISTER ARCINST *ai, **ailist, **newailist;
1562 	REGISTER NODEPROTO *np;
1563 	REGISTER PORTPROTO *thepp, *pp;
1564 	REGISTER GEOM **list, *geom;
1565 	REGISTER HIGHLIGHT *high;
1566 	XARRAY transtz, rot, transfz, t1, t2;
1567 	INTBIG gx, gy, cx, cy, x, y, thex, they, tempcenter[2];
1568 	extern COMCOMP us_mirrorp;
1569 
1570 	list = us_gethighlighted(WANTARCINST|WANTNODEINST, 0, 0);
1571 	if (list[0] == NOGEOM)
1572 	{
1573 		us_abortcommand(_("Must select objects to mirror"));
1574 		return;
1575 	}
1576 	np = geomparent(list[0]);
1577 
1578 	/* figure out which nodes get mirrored */
1579 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1580 		ni->temp1 = 0;
1581 	for(i=0; list[i] != NOGEOM; i++)
1582 	{
1583 		geom = list[i];
1584 		if (geom->entryisnode)
1585 		{
1586 			ni = geom->entryaddr.ni;
1587 			if (us_cantedit(np, ni, TRUE)) continue;
1588 			ni->temp1 = 1;
1589 		} else
1590 		{
1591 			if (us_cantedit(np, NONODEINST, TRUE)) return;
1592 			ai = geom->entryaddr.ai;
1593 			ai->end[0].nodeinst->temp1 = 1;
1594 			ai->end[1].nodeinst->temp1 = 1;
1595 		}
1596 	}
1597 
1598 	/* find the one node that is to be mirrored */
1599 	nicount = 0;
1600 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1601 	{
1602 		if (ni->temp1 == 0) continue;
1603 		if (nicount == 0)
1604 		{
1605 			lx = ni->lowx;   hx = ni->highx;
1606 			ly = ni->lowy;   hy = ni->highy;
1607 		} else
1608 		{
1609 			if (ni->lowx < lx) lx = ni->lowx;
1610 			if (ni->highx > hx) hx = ni->highx;
1611 			if (ni->lowy < ly) ly = ni->lowy;
1612 			if (ni->highy > hy) hy = ni->highy;
1613 		}
1614 		theni = ni;
1615 		nicount++;
1616 	}
1617 	if (nicount > 1)
1618 	{
1619 		/* multiple nodes: find the center one */
1620 		theni = NONODEINST;
1621 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1622 		{
1623 			if (ni->temp1 == 0) continue;
1624 			dist = computedistance((lx+hx)/2, (ly+hy)/2, (ni->lowx+ni->highx)/2,
1625 				(ni->lowy+ni->highy)/2);
1626 
1627 			/* LINTED "bestdist" used in proper order */
1628 			if (theni == NONODEINST || dist < bestdist)
1629 			{
1630 				theni = ni;
1631 				bestdist = dist;
1632 			}
1633 		}
1634 	}
1635 
1636 	if (count == 0)
1637 	{
1638 		count = ttygetparam(M_("Horizontally or vertically? "), &us_mirrorp, MAXPARS, par);
1639 		if (count == 0)
1640 		{
1641 			us_abortedmsg();
1642 			return;
1643 		}
1644 	}
1645 	pt = par[0];
1646 	if (*pt != 'h' && *pt != 'v')
1647 	{
1648 		ttyputusage(x_("mirror horizontal|vertical"));
1649 		return;
1650 	}
1651 
1652 	/* determine amount of rotation to effect the mirror */
1653 	if (*pt == 'h')
1654 	{
1655 		if (theni->transpose == 0) amt = 2700; else amt = 900;
1656 	} else
1657 	{
1658 		if (theni->transpose == 0) amt = 900; else amt = 2700;
1659 	}
1660 
1661 	/* determine translation when mirroring about grab or trace point */
1662 	if (count >= 2) i = estrlen(par[1]);
1663 	tempvar = NOVARIABLE;
1664 	if (count >= 2 && namesamen(par[1], x_("sensibly"), i) == 0)
1665 	{
1666 		if (nicount == 1)
1667 		{
1668 			if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
1669 			{
1670 				par[1] = x_("about-trace-point");
1671 			} else
1672 			{
1673 				var = getvalkey((INTBIG)theni->proto, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
1674 				if (var != NOVARIABLE)
1675 				{
1676 					par[1] = x_("about-grab-point");
1677 				} else
1678 				{
1679 					/* special cases: schematic primitives should mirror sensibly */
1680 					if (theni->proto->primindex != 0 && theni->proto->tech == sch_tech)
1681 					{
1682 						if (theni->proto == sch_transistorprim ||
1683 							theni->proto == sch_transistor4prim)
1684 						{
1685 							tempcenter[0] = 0;
1686 							tempcenter[1] = -(theni->proto->highy - theni->proto->lowy) / 2;
1687 							tempvar = setvalkey((INTBIG)theni->proto, VNODEPROTO, el_prototype_center_key,
1688 								(INTBIG)tempcenter, VINTEGER|VISARRAY|(2<<VLENGTHSH));
1689 							par[1] = x_("about-grab-point");
1690 						} else if (theni->proto == sch_gndprim)
1691 						{
1692 							tempcenter[0] = 0;
1693 							tempcenter[1] = (theni->proto->highy - theni->proto->lowy) / 2;
1694 							tempvar = setvalkey((INTBIG)theni->proto, VNODEPROTO, el_prototype_center_key,
1695 								(INTBIG)tempcenter, VINTEGER|VISARRAY|(2<<VLENGTHSH));
1696 							par[1] = x_("about-grab-point");
1697 						}
1698 					}
1699 				}
1700 			}
1701 		}
1702 	}
1703 	if (count >= 2 && namesamen(par[1], x_("about-grab-point"), i) == 0 && i >= 7)
1704 	{
1705 		if (nicount != 1)
1706 		{
1707 			us_abortcommand(_("Must select 1 node to mirror about its grab point"));
1708 			return;
1709 		}
1710 
1711 		/* find the grab point */
1712 		corneroffset(theni, theni->proto, theni->rotation, theni->transpose, &gx, &gy, FALSE);
1713 		gx += theni->lowx;   gy += theni->lowy;
1714 
1715 		/* build transformation for this operation */
1716 		transid(transtz);   transtz[2][0] = -gx;   transtz[2][1] = -gy;
1717 		if (*pt == 'h') gamt = 2700; else gamt = 900;
1718 		makeangle(gamt, 1, rot);
1719 		transid(transfz);   transfz[2][0] = gx;   transfz[2][1] = gy;
1720 		transmult(transtz, rot, t1);
1721 		transmult(t1, transfz, t2);
1722 
1723 		/* find out where true center moves */
1724 		cx = (theni->lowx+theni->highx)/2;   cy = (theni->lowy+theni->highy)/2;
1725 		xform(cx, cy, &gx, &gy, t2);
1726 		gx -= cx;   gy -= cy;
1727 		if (tempvar != NOVARIABLE)
1728 			(void)delvalkey((INTBIG)theni->proto, VNODEPROTO, el_prototype_center_key);
1729 	} else if (count >= 2 && namesamen(par[1], x_("about-trace-point"), i) == 0 && i >= 7)
1730 	{
1731 		if (nicount != 1)
1732 		{
1733 			us_abortcommand(_("Must select 1 node to mirror about its outline point"));
1734 			return;
1735 		}
1736 
1737 		/* get the trace information */
1738 		var = gettrace(theni);
1739 		if (var == NOVARIABLE)
1740 		{
1741 			us_abortcommand(_("Highlighted node must have outline information"));
1742 			return;
1743 		}
1744 
1745 		/* find the pivot point */
1746 		high = us_getonehighlight();
1747 		i = high->frompoint;   if (i != 0) i--;
1748 		makerot(theni, t1);
1749 		gx = (theni->highx + theni->lowx) / 2;
1750 		gy = (theni->highy + theni->lowy) / 2;
1751 		xform(((INTBIG *)var->addr)[i*2]+gx, ((INTBIG *)var->addr)[i*2+1]+gy, &gx, &gy, t1);
1752 
1753 		/* build transformation for this operation */
1754 		transid(transtz);   transtz[2][0] = -gx;   transtz[2][1] = -gy;
1755 		if (*pt == 'h') gamt = 2700; else gamt = 900;
1756 		makeangle(gamt, 1, rot);
1757 		transid(transfz);   transfz[2][0] = gx;   transfz[2][1] = gy;
1758 		transmult(transtz, rot, t1);
1759 		transmult(t1, transfz, t2);
1760 
1761 		/* find out where true center moves */
1762 		cx = (theni->lowx+theni->highx)/2;   cy = (theni->lowy+theni->highy)/2;
1763 		xform(cx, cy, &gx, &gy, t2);
1764 		gx -= cx;   gy -= cy;
1765 	} else gx = gy = 0;
1766 
1767 	/* now make sure that it is all connected */
1768 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1769 		ai->userbits &= ~ARCFLAGBIT;
1770 	aicount = newnicount = 0;
1771 	for(;;)
1772 	{
1773 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1774 			ni->userbits &= ~NODEFLAGBIT;
1775 		us_nettravel(theni, NODEFLAGBIT);
1776 		for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1777 		{
1778 			if (ni->temp1 == 0) continue;
1779 			if ((ni->userbits&NODEFLAGBIT) == 0) break;
1780 		}
1781 		if (ni == NONODEINST) break;
1782 
1783 		/* this node is unconnected: connect it */
1784 		thepp = theni->proto->firstportproto;
1785 		if (thepp == NOPORTPROTO)
1786 		{
1787 			/* no port on the cell: create one */
1788 			subni = newnodeinst(gen_univpinprim, theni->proto->lowx, theni->proto->highx,
1789 				theni->proto->lowy, theni->proto->highy, 0, 0, theni->proto);
1790 			if (subni == NONODEINST) break;
1791 			thepp = newportproto(theni->proto, subni, subni->proto->firstportproto, x_("temp"));
1792 
1793 			/* add to the list of temporary nodes */
1794 			newnilist = (NODEINST **)emalloc((newnicount+1) * (sizeof (NODEINST *)), el_tempcluster);
1795 			if (newnilist == 0) break;
1796 
1797 			/* LINTED "nilist" used in proper order */
1798 			for(i=0; i<newnicount; i++) newnilist[i] = nilist[i];
1799 			if (newnicount > 0) efree((CHAR *)nilist);
1800 			nilist = newnilist;
1801 			nilist[newnicount] = subni;
1802 			newnicount++;
1803 		}
1804 		pp = ni->proto->firstportproto;
1805 		portposition(theni, thepp, &thex, &they);
1806 		portposition(ni, pp, &x, &y);
1807 		ai = newarcinst(gen_invisiblearc, 0, 0, theni, thepp, thex, they,
1808 			ni, pp, x, y, np);
1809 		if (ai == NOARCINST) break;
1810 
1811 		/* add to the list of temporary arcs */
1812 		newailist = (ARCINST **)emalloc((aicount+1) * (sizeof (ARCINST *)), el_tempcluster);
1813 		if (newailist == 0) break;
1814 
1815 		/* LINTED "ailist" used in proper order */
1816 		for(i=0; i<aicount; i++) newailist[i] = ailist[i];
1817 		if (aicount > 0) efree((CHAR *)ailist);
1818 		ailist = newailist;
1819 		ailist[aicount] = ai;
1820 		aicount++;
1821 	}
1822 
1823 	/* save highlighting */
1824 	us_pushhighlight();
1825 	us_clearhighlightcount();
1826 
1827 	/* set arc constraints appropriately */
1828 	for(ai = np->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
1829 	{
1830 		if (ai->end[0].nodeinst->temp1 != 0 && ai->end[1].nodeinst->temp1 != 0)
1831 		{
1832 			/* arc connecting two mirrored nodes: make rigid */
1833 			(void)(*el_curconstraint->setobject)((INTBIG)ai, VARCINST, CHANGETYPETEMPRIGID, 0);
1834 		}
1835 	}
1836 
1837 	/* mirror the node */
1838 	startobjectchange((INTBIG)theni, VNODEINST);
1839 	modifynodeinst(theni, gx, gy, gx, gy, amt, 1-theni->transpose*2);
1840 	endobjectchange((INTBIG)theni, VNODEINST);
1841 
1842 	/* delete intermediate arcs used to constrain */
1843 	for(i=0; i<aicount; i++)
1844 		(void)killarcinst(ailist[i]);
1845 	if (aicount > 0) efree((CHAR *)ailist);
1846 
1847 	/* delete intermediate nodes used to constrain */
1848 	for(i=0; i<newnicount; i++)
1849 	{
1850 		(void)killportproto(nilist[i]->parent, nilist[i]->firstportexpinst->exportproto);
1851 		(void)killnodeinst(nilist[i]);
1852 	}
1853 	if (newnicount > 0) efree((CHAR *)nilist);
1854 
1855 	/* restore highlighting */
1856 	us_pophighlight(TRUE);
1857 
1858 	if (*pt == 'h') ttyputverbose(M_("Node mirrored horizontally")); else
1859 		ttyputverbose(M_("Node mirrored vertically"));
1860 }
1861 
1862 static DIALOGITEM us_movebydialogitems[] =
1863 {
1864  /*  1 */ {0, {64,96,88,160}, BUTTON, N_("OK")},
1865  /*  2 */ {0, {64,8,88,72}, BUTTON, N_("Cancel")},
1866  /*  3 */ {0, {8,48,24,142}, EDITTEXT, x_("")},
1867  /*  4 */ {0, {8,20,24,42}, MESSAGE, N_("dX:")},
1868  /*  5 */ {0, {32,48,48,142}, EDITTEXT, x_("")},
1869  /*  6 */ {0, {32,20,48,42}, MESSAGE, N_("dY:")}
1870 };
1871 static DIALOG us_movebydialog = {{50,75,147,244}, N_("Move By Amount"), 0, 6, us_movebydialogitems, 0, 0};
1872 
1873 /* special items for the "move by" dialog: */
1874 #define DMVB_XBY       3		/* X change (edit text) */
1875 #define DMVB_XBY_L     4		/* X change label (stat text) */
1876 #define DMVB_YBY       5		/* Y change (edit text) */
1877 #define DMVB_YBY_L     6		/* Y change label (stat text) */
1878 
us_move(INTBIG count,CHAR * par[])1879 void us_move(INTBIG count, CHAR *par[])
1880 {
1881 	REGISTER NODEINST *ni, **nodelist;
1882 	REGISTER NODEPROTO *np;
1883 	REGISTER ARCINST *ai;
1884 	REGISTER INTBIG ang;
1885 	static INTBIG movebyx = 0, movebyy = 0;
1886 	REGISTER INTBIG l, total, i, wid, len, itemHit, amt;
1887 	INTBIG numtexts, snapx, snapy, bestlx, bestly, lx, hx, ly, hy,
1888 		dx, dy, xcur, ycur, p1x, p1y, p2x, p2y;
1889 	REGISTER GEOM **list;
1890 	HIGHLIGHT thishigh, otherhigh;
1891 	BOOLEAN centeredprimitives;
1892 	REGISTER CHAR *pp;
1893 	CHAR **textlist;
1894 	static POLYGON *poly = NOPOLYGON;
1895 	REGISTER VARIABLE *var;
1896 	REGISTER void *dia;
1897 
1898 	/* get polygon */
1899 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
1900 
1901 	/* make sure there is a current cell */
1902 	np = us_needcell();
1903 	if (np == NONODEPROTO) return;
1904 
1905 	/* get the objects to be moved (mark nodes with nonzero "temp1") */
1906 	list = us_gethighlighted(WANTNODEINST | WANTARCINST, &numtexts, &textlist);
1907 
1908 	if (list[0] == NOGEOM && numtexts == 0)
1909 	{
1910 		us_abortcommand(_("First select objects to move"));
1911 		return;
1912 	}
1913 
1914 	/* make sure they are all in the same cell */
1915 	for(i=0; list[i] != NOGEOM; i++) if (np != geomparent(list[i]))
1916 	{
1917 		us_abortcommand(_("All moved objects must be in the same cell"));
1918 		return;
1919 	}
1920 
1921 	/* count the number of nodes */
1922 	for(total=0, ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1923 	{
1924 		if (ni->temp1 == 0) continue;
1925 		if (us_cantedit(np, ni, TRUE)) return;
1926 		total++;
1927 	}
1928 	if (total == 0 && numtexts == 0) return;
1929 
1930 	/* build a list that includes all nodes touching moved arcs */
1931 	if (total > 0)
1932 	{
1933 		nodelist = (NODEINST **)emalloc((total * (sizeof (NODEINST *))), el_tempcluster);
1934 		if (nodelist == 0) return;
1935 		for(i=0, ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1936 			if (ni->temp1 != 0) nodelist[i++] = ni;
1937 	}
1938 
1939 	if (count > 0)
1940 	{
1941 		l = estrlen(pp = par[0]);
1942 		if (namesamen(pp, x_("valign"), l) == 0 && l >= 1)
1943 		{
1944 			/* handle vertical alignment */
1945 			if (count < 2) ttyputusage(x_("move valign EDGE")); else
1946 			{
1947 				l = estrlen(pp = par[1]);
1948 				if (namesamen(pp, x_("top"), l) == 0 && l >= 1)
1949 				{
1950 					us_alignnodes(total, nodelist, FALSE, 0);
1951 				} else if (namesamen(pp, x_("bottom"), l) == 0 && l >= 1)
1952 				{
1953 					us_alignnodes(total, nodelist, FALSE, 1);
1954 				} else if (namesamen(pp, x_("center"), l) == 0 && l >= 1)
1955 				{
1956 					us_alignnodes(total, nodelist, FALSE, 2);
1957 				} else ttyputbadusage(x_("move valign"));
1958 			}
1959 			if (total > 0) efree((CHAR *)nodelist);
1960 			return;
1961 		}
1962 		if (namesamen(pp, x_("halign"), l) == 0 && l >= 1)
1963 		{
1964 			/* handle horizontal alignment */
1965 			if (count < 2) ttyputusage(x_("move halign EDGE")); else
1966 			{
1967 				l = estrlen(pp = par[1]);
1968 				if (namesamen(pp, x_("left"), l) == 0 && l >= 1)
1969 				{
1970 					us_alignnodes(total, nodelist, TRUE, 0);
1971 				} else if (namesamen(pp, x_("right"), l) == 0 && l >= 1)
1972 				{
1973 					us_alignnodes(total, nodelist, TRUE, 1);
1974 				} else if (namesamen(pp, x_("center"), l) == 0 && l >= 1)
1975 				{
1976 					us_alignnodes(total, nodelist, TRUE, 2);
1977 				} else ttyputbadusage(x_("move halign"));
1978 			}
1979 			if (total > 0) efree((CHAR *)nodelist);
1980 			return;
1981 		}
1982 	}
1983 
1984 	/* figure out lower-left corner of this collection of objects */
1985 	if (total == 0)
1986 	{
1987 		bestlx = (np->lowx + np->highx) / 2;
1988 		bestly = (np->lowy + np->highy) / 2;
1989 	} else
1990 	{
1991 		us_getlowleft(nodelist[0], &bestlx, &bestly);
1992 	}
1993 	for(i=1; i<total; i++)
1994 	{
1995 		us_getlowleft(nodelist[i], &lx, &ly);
1996 		if (lx < bestlx) bestlx = lx;
1997 		if (ly < bestly) bestly = ly;
1998 	}
1999 	for(i=0; list[i] != NOGEOM; i++) if (!list[i]->entryisnode)
2000 	{
2001 		ai = list[i]->entryaddr.ai;
2002 		wid = ai->width - arcwidthoffset(ai);
2003 		makearcpoly(ai->length, wid, ai, poly, FILLED);
2004 		getbbox(poly, &lx, &hx, &ly, &hy);
2005 		if (lx < bestlx) bestlx = lx;
2006 		if (ly < bestly) bestly = ly;
2007 	}
2008 
2009 	/* special case when moving one node: account for cell center */
2010 	if (total == 1 && list[1] == NOGEOM && numtexts == 0)
2011 	{
2012 		/* get standard corner offset */
2013 		ni = nodelist[0];
2014 		if ((us_useroptions&CENTEREDPRIMITIVES) != 0) centeredprimitives = TRUE; else
2015 			centeredprimitives = FALSE;
2016 		corneroffset(ni, ni->proto, ni->rotation, ni->transpose, &lx, &ly,
2017 			centeredprimitives);
2018 		bestlx = ni->lowx + lx;
2019 		bestly = ni->lowy + ly;
2020 	}
2021 
2022 	/* special case if there is exactly one snap point */
2023 	if (us_getonesnappoint(&snapx, &snapy))
2024 	{
2025 		bestlx = snapx;   bestly = snapy;
2026 	}
2027 
2028 	/* special case if two objects are selected with snap points */
2029 	if (count == 0)
2030 	{
2031 		var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
2032 		if (var != NOVARIABLE)
2033 		{
2034 			len = getlength(var);
2035 			if (len == 2)
2036 			{
2037 				if (!us_makehighlight(((CHAR **)var->addr)[0], &thishigh) &&
2038 					!us_makehighlight(((CHAR **)var->addr)[1], &otherhigh))
2039 				{
2040 					/* describe these two objects */
2041 					if ((thishigh.status&HIGHSNAP) != 0 && (otherhigh.status&HIGHSNAP) != 0)
2042 					{
2043 						/* move first to second */
2044 						us_getsnappoint(&thishigh, &p1x, &p1y);
2045 						us_getsnappoint(&otherhigh, &p2x, &p2y);
2046 						dx = p2x - p1x;
2047 						dy = p2y - p1y;
2048 						if (dx == 0 && dy == 0)
2049 						{
2050 							us_abortcommand(_("Points are already aligned"));
2051 							if (total > 0) efree((CHAR *)nodelist);
2052 							return;
2053 						}
2054 						ni = thishigh.fromgeom->entryaddr.ni;
2055 						us_pushhighlight();
2056 						us_clearhighlightcount();
2057 						startobjectchange((INTBIG)ni, VNODEINST);
2058 						modifynodeinst(ni, dx, dy, dx, dy, 0, 0);
2059 						endobjectchange((INTBIG)ni, VNODEINST);
2060 						us_pophighlight(TRUE);
2061 						if (total > 0) efree((CHAR *)nodelist);
2062 						return;
2063 					}
2064 				}
2065 			}
2066 		}
2067 	}
2068 
2069 	/* no arguments: simple motion */
2070 	if (count == 0)
2071 	{
2072 		if ((us_tool->toolstate&INTERACTIVE) != 0)
2073 		{
2074 			/* interactive motion: track cursor */
2075 			us_multidraginit(bestlx, bestly, list, nodelist, total, 0, TRUE);
2076 			trackcursor(FALSE, us_ignoreup, us_multidragbegin, us_multidragdown,
2077 				us_stoponchar, us_multidragup, TRACKDRAGGING);
2078 			if (el_pleasestop != 0)
2079 			{
2080 				if (total > 0) efree((CHAR *)nodelist);
2081 				return;
2082 			}
2083 		}
2084 
2085 		/* get co-ordinates of cursor */
2086 		if (us_demandxy(&xcur, &ycur))
2087 		{
2088 			if (total > 0) efree((CHAR *)nodelist);
2089 			return;
2090 		}
2091 		gridalign(&xcur, &ycur, 1, np);
2092 
2093 		/* make the move if it is valid */
2094 		if (xcur == bestlx && ycur == bestly) ttyputverbose(M_("Null motion")); else
2095 		{
2096 			us_pushhighlight();
2097 			us_clearhighlightcount();
2098 			us_manymove(list, nodelist, total, xcur-bestlx, ycur-bestly);
2099 			us_moveselectedtext(numtexts, textlist, list, xcur-bestlx, ycur-bestly);
2100 			us_pophighlight(TRUE);
2101 		}
2102 		if (total > 0) efree((CHAR *)nodelist);
2103 		return;
2104 	}
2105 
2106 	l = estrlen(pp = par[0]);
2107 
2108 	/* handle absolute motion option "move to X Y" */
2109 	if (namesamen(pp, x_("to"), l) == 0 && l >= 1)
2110 	{
2111 		/* get absolute position to place object */
2112 		if (count < 3)
2113 		{
2114 			ttyputusage(x_("move to X Y"));
2115 			if (total > 0) efree((CHAR *)nodelist);
2116 			return;
2117 		}
2118 		xcur = atola(par[1], 0);
2119 		ycur = atola(par[2], 0);
2120 
2121 		/* make the move if it is valid */
2122 		if (xcur == bestlx && ycur == bestly) ttyputverbose(M_("Null motion")); else
2123 		{
2124 			us_pushhighlight();
2125 			us_clearhighlightcount();
2126 			us_manymove(list, nodelist, total, xcur-bestlx, ycur-bestly);
2127 			us_moveselectedtext(numtexts, textlist, list, xcur-bestlx, ycur-bestly);
2128 			us_pophighlight(TRUE);
2129 		}
2130 		if (total > 0) efree((CHAR *)nodelist);
2131 		return;
2132 	}
2133 
2134 	/* handle relative motion option "move by X Y" */
2135 	if (namesamen(pp, x_("by"), l) == 0 && l >= 1)
2136 	{
2137 		/* get absolute position to place object */
2138 		if (count == 2 && namesamen(par[1], x_("dialog"), estrlen(par[1])) == 0)
2139 		{
2140 			/* get coordinates from dialog */
2141 			dia = DiaInitDialog(&us_movebydialog);
2142 			if (dia == 0)
2143 			{
2144 				if (total > 0) efree((CHAR *)nodelist);
2145 				return;
2146 			}
2147 			DiaSetText(dia, DMVB_XBY, frtoa(movebyx));
2148 			DiaSetText(dia, DMVB_YBY, frtoa(movebyy));
2149 			for(;;)
2150 			{
2151 				itemHit = DiaNextHit(dia);
2152 				if (itemHit == CANCEL || itemHit == OK) break;
2153 			}
2154 			movebyx = atofr(DiaGetText(dia, DMVB_XBY));
2155 			movebyy = atofr(DiaGetText(dia, DMVB_YBY));
2156 			xcur = atola(DiaGetText(dia, DMVB_XBY), 0);
2157 			ycur = atola(DiaGetText(dia, DMVB_YBY), 0);
2158 			DiaDoneDialog(dia);
2159 			if (itemHit == CANCEL)
2160 			{
2161 				if (total > 0) efree((CHAR *)nodelist);
2162 				return;
2163 			}
2164 		} else
2165 		{
2166 			/* get coordinates from command line */
2167 			if (count < 3)
2168 			{
2169 				ttyputusage(x_("move by X Y"));
2170 				if (total > 0) efree((CHAR *)nodelist);
2171 				return;
2172 			}
2173 			xcur = atola(par[1], 0);
2174 			ycur = atola(par[2], 0);
2175 		}
2176 
2177 		/* make the move if it is valid */
2178 		if (xcur == 0 && ycur == 0) ttyputverbose(M_("Null motion")); else
2179 		{
2180 			us_pushhighlight();
2181 			us_clearhighlightcount();
2182 			us_manymove(list, nodelist, total, xcur, ycur);
2183 			us_moveselectedtext(numtexts, textlist, list, xcur, ycur);
2184 			us_pophighlight(TRUE);
2185 		}
2186 		if (total > 0) efree((CHAR *)nodelist);
2187 		return;
2188 	}
2189 
2190 	/* handle motion to cursor along an angle: "move angle ANG" */
2191 	if (namesamen(pp, x_("angle"), l) == 0 && l >= 1)
2192 	{
2193 		/* get co-ordinates of cursor */
2194 		if (us_demandxy(&xcur, &ycur))
2195 		{
2196 			if (total > 0) efree((CHAR *)nodelist);
2197 			return;
2198 		}
2199 		gridalign(&xcur, &ycur, 1, np);
2200 
2201 		/* get angle to align along */
2202 		if (count < 2)
2203 		{
2204 			ttyputusage(x_("move angle ANGLE"));
2205 			if (total > 0) efree((CHAR *)nodelist);
2206 			return;
2207 		}
2208 		ang = atofr(par[1])*10/WHOLE;
2209 
2210 		/* adjust the cursor position if selecting interactively */
2211 		if ((us_tool->toolstate&INTERACTIVE) != 0)
2212 		{
2213 			us_multidraginit(bestlx, bestly, list, nodelist, total, ang, TRUE);
2214 			trackcursor(FALSE, us_ignoreup, us_multidragbegin, us_multidragdown,
2215 				us_stoponchar, us_multidragup, TRACKDRAGGING);
2216 			if (el_pleasestop != 0)
2217 			{
2218 				if (total > 0) efree((CHAR *)nodelist);
2219 				return;
2220 			}
2221 			if (us_demandxy(&xcur, &ycur))
2222 			{
2223 				if (total > 0) efree((CHAR *)nodelist);
2224 				return;
2225 			}
2226 			gridalign(&xcur, &ycur, 1, np);
2227 		}
2228 
2229 		/* compute sliding for this highlighting */
2230 		us_getslide(ang, bestlx, bestly, xcur, ycur, &dx, &dy);
2231 
2232 		/* make the move if it is nonnull */
2233 		if (dx == bestlx && dy == bestly) ttyputverbose(M_("Null motion")); else
2234 		{
2235 			us_pushhighlight();
2236 			us_clearhighlightcount();
2237 			us_manymove(list, nodelist, total, dx-bestlx, dy-bestly);
2238 			us_moveselectedtext(numtexts, textlist, list, xcur-bestlx, ycur-bestly);
2239 			us_pophighlight(TRUE);
2240 		}
2241 		if (total > 0) efree((CHAR *)nodelist);
2242 		return;
2243 	}
2244 
2245 	/* if direction is specified, get it */
2246 	dx = dy = 0;
2247 	if (namesamen(pp, x_("up"), l) == 0 && l >= 1)
2248 	{
2249 		/* in outline edit, this makes no sense */
2250 		if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
2251 		{
2252 			if (total > 0) efree((CHAR *)nodelist);
2253 			return;
2254 		}
2255 		if (count < 2) amt = el_curlib->lambda[el_curtech->techindex]; else
2256 		{
2257 			l = el_units;
2258 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITLAMBDA;
2259 			amt = atola(par[1], 0);
2260 			el_units = l;
2261 		}
2262 		dy += amt;
2263 	} else if (namesamen(pp, x_("down"), l) == 0 && l >= 1)
2264 	{
2265 		/* in outline edit, this makes no sense */
2266 		if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
2267 		{
2268 			if (total > 0) efree((CHAR *)nodelist);
2269 			return;
2270 		}
2271 		if (count < 2) amt = el_curlib->lambda[el_curtech->techindex]; else
2272 		{
2273 			l = el_units;
2274 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITLAMBDA;
2275 			amt = atola(par[1], 0);
2276 			el_units = l;
2277 		}
2278 		dy -= amt;
2279 	} else if (namesamen(pp, x_("left"), l) == 0 && l >= 1)
2280 	{
2281 		/* in outline edit, change selection to previous point */
2282 		if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
2283 		{
2284 			par[0] = x_("trace");
2285 			par[1] = x_("prev-point");
2286 			us_node(2, par);
2287 			if (total > 0) efree((CHAR *)nodelist);
2288 			return;
2289 		}
2290 		if (count < 2) amt = el_curlib->lambda[el_curtech->techindex]; else
2291 		{
2292 			l = el_units;
2293 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITLAMBDA;
2294 			amt = atola(par[1], 0);
2295 			el_units = l;
2296 		}
2297 		dx -= amt;
2298 	} else if (namesamen(pp, x_("right"), l) == 0 && l >= 1)
2299 	{
2300 		/* in outline edit, change selection to next point */
2301 		if ((el_curwindowpart->state&WINDOWOUTLINEEDMODE) != 0)
2302 		{
2303 			par[0] = x_("trace");
2304 			par[1] = x_("next-point");
2305 			us_node(2, par);
2306 			if (total > 0) efree((CHAR *)nodelist);
2307 			return;
2308 		}
2309 		if (count < 2) amt = el_curlib->lambda[el_curtech->techindex]; else
2310 		{
2311 			l = el_units;
2312 			el_units = (el_units & ~DISPLAYUNITS) | DISPUNITLAMBDA;
2313 			amt = atola(par[1], 0);
2314 			el_units = l;
2315 		}
2316 		dx += amt;
2317 	} else
2318 	{
2319 		ttyputbadusage(x_("move"));
2320 		if (total > 0) efree((CHAR *)nodelist);
2321 		return;
2322 	}
2323 
2324 	/* transform to space of this window */
2325 	if ((el_curwindowpart->state&INPLACEEDIT) != 0)
2326 	{
2327 		xform(0, 0, &snapx, &snapy, el_curwindowpart->outofcell);
2328 		snapx += dx;   snapy += dy;
2329 		xform(snapx, snapy, &dx, &dy, el_curwindowpart->intocell);
2330 	}
2331 
2332 	/* now move the object */
2333 	if (dx != 0 || dy != 0)
2334 	{
2335 		us_pushhighlight();
2336 		us_clearhighlightcount();
2337 		us_manymove(list, nodelist, total, dx, dy);
2338 		us_moveselectedtext(numtexts, textlist, list, dx, dy);
2339 		us_pophighlight(TRUE);
2340 	}
2341 	if (total > 0) efree((CHAR *)nodelist);
2342 }
2343 
us_node(INTBIG count,CHAR * par[])2344 void us_node(INTBIG count, CHAR *par[])
2345 {
2346 	REGISTER INTBIG amt, i, *newlist, x, y, curlamb, inside, outside, l, negated, size, nogood,
2347 		total, addpoint, deletepoint, whichpoint, p, movepoint, pointgiven, segs, degs,
2348 		nextpoint, prevpoint;
2349 	BOOLEAN waitfordown, stillok;
2350 	XARRAY trans;
2351 	INTBIG d[2], xcur, ycur, initlist[8];
2352 	REGISTER CHAR *pt, *ch;
2353 	REGISTER GEOM **list;
2354 	extern COMCOMP us_nodep;
2355 	REGISTER TECHNOLOGY *tech;
2356 	REGISTER NODEPROTO *np;
2357 	REGISTER NODEINST *ni, *store;
2358 	REGISTER HIGHLIGHT *high;
2359 	HIGHLIGHT newhigh;
2360 	REGISTER VARIABLE *var;
2361 
2362 	/* disallow node modification if lock is on */
2363 	np = us_needcell();
2364 	if (np == NONODEPROTO) return;
2365 
2366 	if (count == 0)
2367 	{
2368 		count = ttygetparam(M_("Node option: "), &us_nodep, MAXPARS, par);
2369 		if (count == 0)
2370 		{
2371 			us_abortedmsg();
2372 			return;
2373 		}
2374 	}
2375 	l = estrlen(pt = par[0]);
2376 
2377 	/* handle negation */
2378 	if (namesamen(pt, x_("not"), l) == 0 && l >= 2)
2379 	{
2380 		negated = 1;
2381 		count--;
2382 		par++;
2383 		l = estrlen(pt = par[0]);
2384 	} else negated = 0;
2385 
2386 	if (namesamen(pt, x_("expand"), l) == 0 && l >= 1)
2387 	{
2388 		if (count < 2) amt = MAXINTBIG; else amt = eatoi(par[1]);
2389 		list = us_gethighlighted(WANTNODEINST, 0, 0);
2390 		if (list[0] == NOGEOM)
2391 		{
2392 			us_abortcommand(_("Select nodes to be expanded"));
2393 			return;
2394 		}
2395 
2396 		/* save highlighting */
2397 		us_pushhighlight();
2398 		us_clearhighlightcount();
2399 
2400 		/* pre-compute */
2401 		for(i=0; list[i] != NOGEOM; i++)
2402 		{
2403 			ni = list[i]->entryaddr.ni;
2404 			if (negated)
2405 			{
2406 				if ((ni->userbits & NEXPAND) != 0)
2407 					(void)us_setunexpand(ni, amt);
2408 			}
2409 			startobjectchange((INTBIG)ni, VNODEINST);
2410 		}
2411 
2412 		/* do the change */
2413 		for(i=0; list[i] != NOGEOM; i++)
2414 		{
2415 			ni = list[i]->entryaddr.ni;
2416 			if (negated == 0) us_doexpand(ni, amt, 0); else
2417 				us_dounexpand(ni);
2418 		}
2419 
2420 		/* post-draw */
2421 		for(i=0; list[i] != NOGEOM; i++)
2422 		{
2423 			ni = list[i]->entryaddr.ni;
2424 			endobjectchange((INTBIG)ni, VNODEINST);
2425 		}
2426 
2427 		/* restore highlighting */
2428 		us_pophighlight(FALSE);
2429 		return;
2430 	}
2431 
2432 	if (namesamen(pt, x_("name"), l) == 0 && l >= 2)
2433 	{
2434 		ni = (NODEINST *)us_getobject(VNODEINST, FALSE);
2435 		if (ni == NONODEINST) return;
2436 
2437 		if (negated == 0)
2438 		{
2439 			if (count < 2)
2440 			{
2441 				ttyputusage(x_("node name NODENAME"));
2442 				return;
2443 			}
2444 			ch = par[1];
2445 
2446 			/* sensibility check for multiple nodes with the same name */
2447 			for(store = ni->parent->firstnodeinst; store != NONODEINST; store = store->nextnodeinst)
2448 			{
2449 				if (store == ni) continue;
2450 				var = getvalkey((INTBIG)store, VNODEINST, VSTRING, el_node_name_key);
2451 				if (var == NOVARIABLE) continue;
2452 				if (namesame(ch, (CHAR *)var->addr) == 0)
2453 				{
2454 					ttyputmsg(_("Warning: already a node in this cell called %s"), ch);
2455 					break;
2456 				}
2457 			}
2458 		}
2459 
2460 		/* save highlighting */
2461 		us_pushhighlight();
2462 		us_clearhighlightcount();
2463 
2464 		/* change the name of the nodeinst */
2465 		startobjectchange((INTBIG)ni, VNODEINST);
2466 		if (negated != 0) (void)delvalkey((INTBIG)ni, VNODEINST, el_node_name_key); else
2467 		{
2468 			var = setvalkey((INTBIG)ni, VNODEINST, el_node_name_key, (INTBIG)ch, VSTRING|VDISPLAY);
2469 			if (var != NOVARIABLE)
2470 			{
2471 				defaulttextsize(3, var->textdescript);
2472 
2473 				/* shift text down if on a cell instance */
2474 				if (ni->proto->primindex == 0)
2475 				{
2476 					us_setdescriptoffset(var->textdescript,
2477 						0, (ni->highy-ni->lowy) / el_curlib->lambda[el_curtech->techindex]);
2478 				}
2479 			}
2480 		}
2481 		endobjectchange((INTBIG)ni, VNODEINST);
2482 
2483 		/* restore highlighting */
2484 		us_pophighlight(FALSE);
2485 
2486 		/* report results and quit */
2487 		if (negated != 0) ttyputverbose(M_("Node name removed")); else
2488 			ttyputverbose(M_("Node name is '%s'"), ch);
2489 		return;
2490 	}
2491 
2492 	/* everything after this point is structural and must check for locks */
2493 	if (us_cantedit(np, NONODEINST, TRUE)) return;
2494 
2495 	if (namesamen(pt, x_("cover-implant"), l) == 0 && l >= 1)
2496 	{
2497 		us_coverimplant();
2498 		return;
2499 	}
2500 
2501 	if (namesamen(pt, x_("regrid-selected"), l) == 0 && l >= 1)
2502 	{
2503 		us_regridselected();
2504 		return;
2505 	}
2506 
2507 	if (namesamen(pt, x_("trace"), l) == 0 && l >= 1)
2508 	{
2509 		/* special case for converting text to layout */
2510 		if (count < 2)
2511 		{
2512 			ttyputusage(x_("node trace OPTIONS"));
2513 			return;
2514 		}
2515 		l = estrlen(pt = par[1]);
2516 		if (namesamen(pt, x_("init-points"), l) == 0 && l >= 1)
2517 		{
2518 			store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2519 			if (store == NONODEINST) return;
2520 			if ((store->proto->userbits&HOLDSTRACE) == 0)
2521 			{
2522 				us_abortcommand(_("Sorry, %s nodes cannot hold outline information"),
2523 					describenodeproto(store->proto));
2524 				return;
2525 			}
2526 			if (store->proto == art_openedpolygonprim || store->proto == art_openeddottedpolygonprim ||
2527 				store->proto == art_openeddashedpolygonprim || store->proto == art_openedthickerpolygonprim ||
2528 				store->proto == art_splineprim)
2529 			{
2530 				x = (store->lowx + store->highx) / 2;
2531 				y = (store->lowy + store->highy) / 2;
2532 				tech = store->parent->tech;
2533 				curlamb = store->parent->lib->lambda[tech->techindex];
2534 				initlist[0] = x - curlamb * 3;
2535 				initlist[1] = y - curlamb * 3;
2536 				initlist[2] = x - curlamb;
2537 				initlist[3] = y + curlamb * 3;
2538 				initlist[4] = x + curlamb;
2539 				initlist[5] = y - curlamb * 3;
2540 				initlist[6] = x + curlamb * 3;
2541 				initlist[7] = y + curlamb * 3;
2542 				us_pushhighlight();
2543 				us_clearhighlightcount();
2544 				us_settrace(store, initlist, 4);
2545 				us_pophighlight(FALSE);
2546 			} else if (store->proto == art_closedpolygonprim || store->proto == art_filledpolygonprim)
2547 			{
2548 				x = (store->lowx + store->highx) / 2;
2549 				y = (store->lowy + store->highy) / 2;
2550 				tech = store->parent->tech;
2551 				curlamb = store->parent->lib->lambda[tech->techindex];
2552 				initlist[0] = x;
2553 				initlist[1] = y - curlamb * 3;
2554 				initlist[2] = x - curlamb * 3;
2555 				initlist[3] = y;
2556 				initlist[4] = x;
2557 				initlist[5] = y + curlamb * 3;
2558 				initlist[6] = x + curlamb * 3;
2559 				initlist[7] = y - curlamb * 3;
2560 				us_pushhighlight();
2561 				us_clearhighlightcount();
2562 				us_settrace(store, initlist, 4);
2563 				us_pophighlight(FALSE);
2564 			}
2565 			return;
2566 		}
2567 		if (namesamen(pt, x_("place-text"), l) == 0 && l >= 2)
2568 		{
2569 			if (count < 10)
2570 			{
2571 				ttyputusage(x_("node trace place-text LAYER SIZE FONT ITALIC BOLD UNDERLINE SEPARATION MESSAGE"));
2572 				return;
2573 			}
2574 			us_layouttext(par[2], eatoi(par[3]), 1, eatoi(par[4]), eatoi(par[5]),
2575 				eatoi(par[6]), eatoi(par[7]), eatoi(par[8]), par[9]);
2576 			return;
2577 		}
2578 
2579 		/* special case for filleting */
2580 		if (namesamen(pt, x_("fillet"), l) == 0)
2581 		{
2582 			us_dofillet();
2583 			return;
2584 		}
2585 
2586 		/* special case for annulus construction */
2587 		if (namesamen(pt, x_("construct-annulus"), l) == 0)
2588 		{
2589 			if (count < 4)
2590 			{
2591 				ttyputusage(x_("node trace construct-annulus INSIDE OUTSIDE [RESOLUTION] [DEGREES]"));
2592 				return;
2593 			}
2594 			inside = atola(par[2], 0);
2595 			outside = atola(par[3], 0);
2596 			segs = 16;
2597 			if (count >= 5) segs = myatoi(par[4]);
2598 			if (segs < 4) segs = 4;
2599 			degs = 3600;
2600 			if (count >= 6) degs = atofr(par[5])*10/WHOLE;
2601 			if (degs <= 0) degs = 3600;
2602 			if (degs > 3600) degs = 3600;
2603 
2604 			/* make sure node can handle trace information */
2605 			store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2606 			if (store == NONODEINST) return;
2607 			if ((store->proto->userbits&HOLDSTRACE) == 0)
2608 			{
2609 				us_abortcommand(_("Sorry, %s nodes cannot hold outline information"),
2610 					describenodeproto(store->proto));
2611 				return;
2612 			}
2613 
2614 			/* allocate space for the trace */
2615 			newlist = emalloc(((segs+1)*4*SIZEOFINTBIG), el_tempcluster);
2616 			if (newlist == 0)
2617 			{
2618 				ttyputnomemory();
2619 				return;
2620 			}
2621 
2622 			l = 0;
2623 			if (inside > 0)
2624 			{
2625 				for(i=0; i<=segs; i++)
2626 				{
2627 					p = degs*i/segs;
2628 					x = mult(inside, cosine(p)) + (store->lowx+store->highx)/2;
2629 					y = mult(inside, sine(p)) + (store->lowy+store->highy)/2;
2630 					newlist[l++] = x;   newlist[l++] = y;
2631 				}
2632 			}
2633 			for(i=segs; i>=0; i--)
2634 			{
2635 				p = degs*i/segs;
2636 				x = mult(outside, cosine(p)) + (store->lowx+store->highx)/2;
2637 				y = mult(outside, sine(p)) + (store->lowy+store->highy)/2;
2638 				newlist[l++] = x;   newlist[l++] = y;
2639 			}
2640 
2641 			/* save highlighting, set trace, restore highlighting */
2642 			us_pushhighlight();
2643 			us_clearhighlightcount();
2644 			us_settrace(store, newlist, l/2);
2645 			us_pophighlight(FALSE);
2646 			efree((CHAR *)newlist);
2647 			return;
2648 		}
2649 
2650 		/* parse the options */
2651 		store = NONODEINST;
2652 		waitfordown = FALSE;
2653 		addpoint = deletepoint = movepoint = pointgiven = nextpoint = prevpoint = 0;
2654 		for(i=1; i<count; i++)
2655 		{
2656 			l = estrlen(pt = par[i]);
2657 
2658 			if (namesamen(pt, x_("store-trace"), l) == 0 && l >= 1)
2659 			{
2660 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2661 				if (store == NONODEINST) return;
2662 			} else if (namesamen(pt, x_("add-point"), l) == 0 && l >= 1)
2663 			{
2664 				addpoint++;
2665 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2666 				if (store == NONODEINST) return;
2667 				high = us_getonehighlight();
2668 				whichpoint = high->frompoint;
2669 				if (count > i+2 && isanumber(par[i+1]) && isanumber(par[i+2]))
2670 				{
2671 					xcur = atola(par[i+1], 0);
2672 					ycur = atola(par[i+2], 0);
2673 					i += 2;
2674 					pointgiven++;
2675 				}
2676 			} else if (namesamen(pt, x_("move-point"), l) == 0 && l >= 1)
2677 			{
2678 				movepoint++;
2679 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2680 				if (store == NONODEINST) return;
2681 				high = us_getonehighlight();
2682 				whichpoint = high->frompoint;
2683 				if (count > i+2 && isanumber(par[i+1]) && isanumber(par[i+2]))
2684 				{
2685 					xcur = atola(par[i+1], 0);
2686 					ycur = atola(par[i+2], 0);
2687 					i += 2;
2688 					pointgiven++;
2689 				}
2690 			} else if (namesamen(pt, x_("delete-point"), l) == 0 && l >= 1)
2691 			{
2692 				deletepoint++;
2693 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2694 				if (store == NONODEINST) return;
2695 				high = us_getonehighlight();
2696 				whichpoint = high->frompoint;
2697 			} else if (namesamen(pt, x_("next-point"), l) == 0 && l >= 1)
2698 			{
2699 				nextpoint++;
2700 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2701 				if (store == NONODEINST) return;
2702 				high = us_getonehighlight();
2703 				whichpoint = high->frompoint;
2704 			} else if (namesamen(pt, x_("prev-point"), l) == 0 && l >= 2)
2705 			{
2706 				prevpoint++;
2707 				store = (NODEINST *)us_getobject(VNODEINST, FALSE);
2708 				if (store == NONODEINST) return;
2709 				high = us_getonehighlight();
2710 				whichpoint = high->frompoint;
2711 			} else if (namesamen(pt, x_("wait-for-down"), l) == 0 && l >= 1)
2712 			{
2713 				waitfordown = TRUE;
2714 			} else
2715 			{
2716 				ttyputbadusage(x_("node trace"));
2717 				return;
2718 			}
2719 		}
2720 
2721 		if (addpoint + deletepoint + movepoint + nextpoint + prevpoint > 1)
2722 		{
2723 			us_abortcommand(_("Can only add OR delete OR move OR change point"));
2724 			return;
2725 		}
2726 
2727 		/* make sure node can handle trace information */
2728 		if (store != NONODEINST)
2729 		{
2730 			if ((store->proto->userbits&HOLDSTRACE) == 0)
2731 			{
2732 				us_abortcommand(_("Sorry, %s nodes cannot hold outline information"),
2733 					describenodeproto(store->proto));
2734 				return;
2735 			}
2736 		}
2737 
2738 		/* handle moving around the trace */
2739 		if (nextpoint != 0)
2740 		{
2741 			var = gettrace(store);
2742 			if (var == NOVARIABLE)
2743 			{
2744 				us_abortcommand(_("No points on this node"));
2745 				return;
2746 			}
2747 			size = getlength(var) / 2;
2748 			whichpoint++;
2749 			if (whichpoint >= size) whichpoint = 0;
2750 
2751 			us_clearhighlightcount();
2752 			newhigh.status = HIGHFROM;
2753 			newhigh.fromgeom = store->geom;
2754 			newhigh.fromport = NOPORTPROTO;
2755 			newhigh.frompoint = whichpoint;
2756 			newhigh.cell = store->parent;
2757 			us_addhighlight(&newhigh);
2758 			return;
2759 		}
2760 		if (prevpoint != 0)
2761 		{
2762 			var = gettrace(store);
2763 			if (var == NOVARIABLE)
2764 			{
2765 				us_abortcommand(_("No points on this node"));
2766 				return;
2767 			}
2768 			size = getlength(var) / 2;
2769 			whichpoint--;
2770 			if (whichpoint < 0) whichpoint = size-1;
2771 
2772 			us_clearhighlightcount();
2773 			newhigh.status = HIGHFROM;
2774 			newhigh.fromgeom = store->geom;
2775 			newhigh.fromport = NOPORTPROTO;
2776 			newhigh.frompoint = whichpoint;
2777 			newhigh.cell = store->parent;
2778 			us_addhighlight(&newhigh);
2779 			return;
2780 		}
2781 
2782 		/* handle freeform cursor traces */
2783 		if (addpoint == 0 && deletepoint == 0 && movepoint == 0)
2784 		{
2785 			/* just do tracking if no storage requested */
2786 			if (store == NONODEINST)
2787 			{
2788 				/* save highlighting */
2789 				us_pushhighlight();
2790 				us_clearhighlightcount();
2791 
2792 				trackcursor(waitfordown, us_ignoreup, us_tracebegin,
2793 					us_tracedown, us_stoponchar, us_traceup, TRACKDRAWING);
2794 
2795 				/* restore highlighting */
2796 				us_pophighlight(FALSE);
2797 				return;
2798 			}
2799 
2800 			/* clear highlighting */
2801 			us_clearhighlightcount();
2802 
2803 			/* get the trace */
2804 			trackcursor(waitfordown, us_ignoreup, us_tracebegin, us_tracedown,
2805 				us_stoponchar, us_traceup, TRACKDRAWING);
2806 			if (el_pleasestop != 0) return;
2807 
2808 			/* get the trace data in the "%T" variable */
2809 			var = getval((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_commandvarname('T'));
2810 			if (var == NOVARIABLE)
2811 			{
2812 				us_abortcommand(_("Invalid outline information"));
2813 				return;
2814 			}
2815 			size = getlength(var) / 2;
2816 
2817 			/* create a place to keep this data */
2818 			newlist = emalloc((size*2*SIZEOFINTBIG), el_tempcluster);
2819 			if (newlist == 0)
2820 			{
2821 				ttyputnomemory();
2822 				return;
2823 			}
2824 
2825 			/* copy the trace from "%T" to "newlist" */
2826 			curlamb = el_curlib->lambda[el_curtech->techindex];
2827 			el_curlib->lambda[el_curtech->techindex] = 4;
2828 			nogood = 0;
2829 			for(i=0; i<size; i++)
2830 			{
2831 				x = ((INTBIG *)var->addr)[i*2];
2832 				y = ((INTBIG *)var->addr)[i*2+1];
2833 				if (us_setxy(x, y)) nogood++;
2834 				(void)getxy(&xcur, &ycur);
2835 				newlist[i*2] = xcur;   newlist[i*2+1] = ycur;
2836 			}
2837 			el_curlib->lambda[el_curtech->techindex] = curlamb;
2838 
2839 			/* if data is valid, store it in the node */
2840 			if (nogood != 0) ttyputerr(_("Outline not inside window")); else
2841 			{
2842 				us_settrace(store, newlist, size);
2843 
2844 				/* highlighting the node */
2845 				newhigh.status = HIGHFROM;
2846 				newhigh.fromgeom = store->geom;
2847 				newhigh.fromport = NOPORTPROTO;
2848 				newhigh.frompoint = size+1;
2849 				newhigh.cell = store->parent;
2850 				us_addhighlight(&newhigh);
2851 			}
2852 			efree((CHAR *)newlist);
2853 			return;
2854 		}
2855 
2856 		/* add a point to a trace on this object */
2857 		if (addpoint != 0)
2858 		{
2859 			/* clear highlighting */
2860 			us_clearhighlightcount();
2861 
2862 			stillok = TRUE;
2863 			if (pointgiven == 0)
2864 			{
2865 				if (us_demandxy(&xcur, &ycur)) stillok = FALSE; else
2866 				{
2867 					gridalign(&xcur, &ycur, 1, np);
2868 
2869 					/* adjust the cursor position if selecting interactively */
2870 					if ((us_tool->toolstate&INTERACTIVE) != 0)
2871 					{
2872 						us_pointinit(store, whichpoint);
2873 						trackcursor(FALSE, us_ignoreup, us_pointbegin, us_addpdown,
2874 							us_stoponchar, us_dragup, TRACKDRAGGING);
2875 						if (el_pleasestop != 0) stillok = FALSE; else
2876 						{
2877 							if (us_demandxy(&xcur, &ycur)) stillok = FALSE; else
2878 								gridalign(&xcur, &ycur, 1, np);
2879 						}
2880 					}
2881 				}
2882 			}
2883 
2884 			if (stillok)
2885 			{
2886 				var = gettrace(store);
2887 				if (var == NOVARIABLE)
2888 				{
2889 					d[0] = xcur;   d[1] = ycur;
2890 					us_settrace(store, d, 1);
2891 					whichpoint = 1;
2892 				} else
2893 				{
2894 					size = getlength(var) / 2;
2895 					newlist = emalloc(((size+1)*2*SIZEOFINTBIG), el_tempcluster);
2896 					if (newlist == 0)
2897 					{
2898 						ttyputnomemory();
2899 						return;
2900 					}
2901 					p = 0;
2902 					makerot(store, trans);
2903 					x = (store->highx + store->lowx) / 2;
2904 					y = (store->highy + store->lowy) / 2;
2905 					if (whichpoint == 0) whichpoint++;
2906 					for(i=0; i<size; i++)
2907 					{
2908 						xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y, &newlist[p],
2909 							&newlist[p+1], trans);
2910 						p += 2;
2911 						if (i+1 == whichpoint)
2912 						{
2913 							newlist[p++] = xcur;
2914 							newlist[p++] = ycur;
2915 						}
2916 					}
2917 
2918 					/* now re-draw this trace */
2919 					us_settrace(store, newlist, size+1);
2920 					whichpoint++;
2921 					efree((CHAR *)newlist);
2922 				}
2923 			}
2924 
2925 			/* highlighting the node */
2926 			newhigh.status = HIGHFROM;
2927 			newhigh.fromgeom = store->geom;
2928 			newhigh.fromport = NOPORTPROTO;
2929 			newhigh.frompoint = whichpoint;
2930 			newhigh.cell = store->parent;
2931 			us_addhighlight(&newhigh);
2932 			return;
2933 		}
2934 
2935 		/* delete the current point */
2936 		if (deletepoint != 0)
2937 		{
2938 			var = gettrace(store);
2939 			if (var == NOVARIABLE)
2940 			{
2941 				us_abortcommand(_("No outline data to delete"));
2942 				return;
2943 			}
2944 			if (whichpoint == 0)
2945 			{
2946 				us_abortcommand(_("Highlight a point or line to delete"));
2947 				return;
2948 			}
2949 			size = getlength(var) / 2;
2950 			if (size <= 1)
2951 			{
2952 				us_abortcommand(_("Outline must retain at least one point"));
2953 				return;
2954 			}
2955 			newlist = emalloc(((size-1)*2*SIZEOFINTBIG), el_tempcluster);
2956 			if (newlist == 0)
2957 			{
2958 				ttyputnomemory();
2959 				return;
2960 			}
2961 			p = 0;
2962 			makerot(store, trans);
2963 			x = (store->highx + store->lowx) / 2;
2964 			y = (store->highy + store->lowy) / 2;
2965 			total = 0;
2966 			for(i=0; i<size; i++)
2967 			{
2968 				if (i+1 == whichpoint) { total++;   continue;}
2969 				xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y,
2970 					&newlist[p], &newlist[p+1], trans);
2971 				p += 2;
2972 			}
2973 
2974 			/* clear highlighting */
2975 			us_clearhighlightcount();
2976 
2977 			/* now re-draw this trace */
2978 			us_settrace(store, newlist, size-total);
2979 			whichpoint = maxi(whichpoint-total, 1);
2980 			efree((CHAR *)newlist);
2981 
2982 			/* highlighting the node */
2983 			newhigh.status = HIGHFROM;
2984 			newhigh.fromgeom = store->geom;
2985 			newhigh.fromport = NOPORTPROTO;
2986 			newhigh.frompoint = whichpoint;
2987 			newhigh.cell = store->parent;
2988 			us_addhighlight(&newhigh);
2989 			return;
2990 		}
2991 
2992 		/* move a point on a trace on this object */
2993 		if (movepoint != 0)
2994 		{
2995 			var = gettrace(store);
2996 			if (var == NOVARIABLE)
2997 			{
2998 				us_abortcommand(_("No outline data to move"));
2999 				return;
3000 			}
3001 			if (whichpoint == 0)
3002 			{
3003 				us_abortcommand(_("Highlight a point or line to move"));
3004 				return;
3005 			}
3006 
3007 			/* save highlighting */
3008 			us_pushhighlight();
3009 			us_clearhighlightcount();
3010 
3011 			stillok = TRUE;
3012 			if (pointgiven == 0)
3013 			{
3014 				if (us_demandxy(&xcur, &ycur)) stillok = FALSE; else
3015 				{
3016 					gridalign(&xcur, &ycur, 1, np);
3017 
3018 					/* adjust the cursor position if selecting interactively */
3019 					if ((us_tool->toolstate&INTERACTIVE) != 0)
3020 					{
3021 						us_pointinit(store, whichpoint);
3022 						trackcursor(FALSE, us_ignoreup, us_pointbegin, us_movepdown,
3023 							us_stopandpoponchar, us_dragup, TRACKDRAGGING);
3024 						if (el_pleasestop != 0) stillok = FALSE; else
3025 						{
3026 							if (us_demandxy(&xcur, &ycur)) stillok = FALSE; else
3027 								gridalign(&xcur, &ycur, 1, np);
3028 						}
3029 					}
3030 				}
3031 			}
3032 
3033 			if (stillok)
3034 			{
3035 				size = getlength(var) / 2;
3036 				newlist = emalloc((size*2*SIZEOFINTBIG), el_tempcluster);
3037 				if (newlist == 0)
3038 				{
3039 					ttyputnomemory();
3040 					us_pophighlight(FALSE);
3041 					return;
3042 				}
3043 				makerot(store, trans);
3044 				x = (store->highx + store->lowx) / 2;
3045 				y = (store->highy + store->lowy) / 2;
3046 				for(i=0; i<size; i++)
3047 				{
3048 					if (i+1 == whichpoint)
3049 					{
3050 						newlist[i*2] = xcur;
3051 						newlist[i*2+1] = ycur;
3052 					} else xform(((INTBIG *)var->addr)[i*2]+x, ((INTBIG *)var->addr)[i*2+1]+y,
3053 						&newlist[i*2], &newlist[i*2+1], trans);
3054 				}
3055 
3056 				/* now re-draw this trace */
3057 				us_settrace(store, newlist, size);
3058 				efree((CHAR *)newlist);
3059 			}
3060 
3061 			/* restore highlighting */
3062 			us_pophighlight(TRUE);
3063 			return;
3064 		}
3065 		return;
3066 	}
3067 
3068 	ttyputbadusage(x_("node"));
3069 }
3070