1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrstatus.c
6  * User interface tool: status display routines
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 "drc.h"
36 #include "network.h"
37 #include "efunction.h"
38 #include "edialogs.h"
39 #include "tecgen.h"
40 #include "tecschem.h"
41 #include "usrdiacom.h"
42 
43 static INTBIG *us_layer_letter_data = 0;	/* cache for "us_layerletters" */
44 static BOOLEAN us_shadowarcdetails = FALSE;	/* true if the arc status field has details */
45 
46 /* working memory for "us_uniqueportname()" */
47 static CHAR *us_uniqueretstr = NOSTRING;
48 
49 /* working memory for "us_shadowarcproto()" */
50 static CHAR *us_lastnetworkname;
51 static INTBIG us_lastnetworknametotal = 0;
52 
53 /* structure for "usrbits" information parsing */
54 struct explanation
55 {
56 	INTBIG bit;			/* bit mask in word */
57 	INTBIG shift;		/* shift of bit mask (-1 if a flag) */
58 	CHAR  *name;		/* name of the field */
59 };
60 
61 /* prototypes for local routines */
62 static void us_addobjects(NODEPROTO*);
63 static BOOLEAN us_wildmatch(CHAR*, CHAR*);
64 static void us_explaintool(INTBIG, struct explanation[]);
65 static void us_drawarcmenu(ARCPROTO *ap);
66 static BOOLEAN us_isuniquename(CHAR *name, NODEPROTO *cell, INTBIG type, void *exclude);
67 
68 static STATUSFIELD *us_clearstatuslines[3] = {0, 0, 0};
69 
70 /*
71  * Routine to free all memory associated with this module.
72  */
us_freestatusmemory(void)73 void us_freestatusmemory(void)
74 {
75 	REGISTER INTBIG i, lines;
76 
77 	if (us_layer_letter_data != 0) efree((CHAR *)us_layer_letter_data);
78 	if (us_statusalign != 0) ttyfreestatusfield(us_statusalign);
79 	if (us_statusangle != 0) ttyfreestatusfield(us_statusangle);
80 	if (us_statusarc != 0) ttyfreestatusfield(us_statusarc);
81 	if (us_statuscell != 0) ttyfreestatusfield(us_statuscell);
82 	if (us_statuscellsize != 0) ttyfreestatusfield(us_statuscellsize);
83 	if (us_statusselectcount != 0) ttyfreestatusfield(us_statusselectcount);
84 	if (us_statusgridsize != 0) ttyfreestatusfield(us_statusgridsize);
85 	if (us_statuslambda != 0) ttyfreestatusfield(us_statuslambda);
86 	if (us_statusnode != 0) ttyfreestatusfield(us_statusnode);
87 	if (us_statustechnology != 0) ttyfreestatusfield(us_statustechnology);
88 	if (us_statusxpos != 0) ttyfreestatusfield(us_statusxpos);
89 	if (us_statusypos != 0) ttyfreestatusfield(us_statusypos);
90 	if (us_statusproject != 0) ttyfreestatusfield(us_statusproject);
91 	if (us_statusroot != 0) ttyfreestatusfield(us_statusroot);
92 	if (us_statuspart != 0) ttyfreestatusfield(us_statuspart);
93 	if (us_statuspackage != 0) ttyfreestatusfield(us_statuspackage);
94 	if (us_statusselection != 0) ttyfreestatusfield(us_statusselection);
95 
96 	lines = ttynumstatuslines();
97 	for(i=0; i<lines; i++) ttyfreestatusfield(us_clearstatuslines[i]);
98 
99 	if (us_uniqueretstr != NOSTRING) efree(us_uniqueretstr);
100 	if (us_lastnetworknametotal > 0) efree((CHAR *)us_lastnetworkname);
101 }
102 
103 /******************** STATUS DISPLAY MANIPULATION ********************/
104 
105 /* routine to determine the state of the status fields */
us_initstatus(void)106 void us_initstatus(void)
107 {
108 	REGISTER INTBIG lines, i;
109 	static BOOLEAN first = TRUE;
110 
111 	if (first)
112 	{
113 		first = FALSE;
114 		lines = ttynumstatuslines();
115 		for(i=0; i<lines; i++) us_clearstatuslines[i] = ttydeclarestatusfield(i+1, 0, 100, x_(""));
116 		us_statuscell = us_statusnode = us_statusarc = 0;
117 		us_statuslambda = us_statustechnology = us_statusxpos = us_statusypos = 0;
118 		us_statusangle = us_statusalign = us_statuscellsize = us_statusgridsize = 0;
119 		us_statusproject = us_statusroot = us_statuspart = us_statuspackage = 0;
120 		us_statusselection = us_statusselectcount = 0;
121 		us_statuscell        = ttydeclarestatusfield(0,  0,   0, x_(""));
122 		us_statusnode        = ttydeclarestatusfield(1,  0,  20, _("NODE: "));
123 		us_statusarc         = ttydeclarestatusfield(1, 20,  40, _("ARC: "));
124 		us_statusselectcount = ttydeclarestatusfield(1, 40,  52, _("SELECTED: "));
125 		us_statuscellsize    = ttydeclarestatusfield(1, 52,  69, _("SIZE: "));
126 		us_statuslambda      = ttydeclarestatusfield(1, 69,  82, _("LAMBDA: "));
127 		us_statustechnology  = ttydeclarestatusfield(1, 82, 100, _("TECHNOLOGY: "));
128 		us_statusxpos        = ttydeclarestatusfield(1, 69,  82, _("X: "));
129 		us_statusypos        = ttydeclarestatusfield(1, 82, 100, _("Y: "));
130 		if (lines > 1)
131 		{
132 			us_statusangle    = ttydeclarestatusfield(2,  0,  25, _("ANGLE: "));
133 			us_statusalign    = ttydeclarestatusfield(2, 25,  50, _("ALIGN: "));
134 			us_statusgridsize = ttydeclarestatusfield(2, 75, 100, _("GRID: "));
135 		}
136 	}
137 }
138 
139 /*
140  * Routine to create a field in the status display.  "line" is the status line number (line 0
141  * is the window header).  "startper" and "endper" are the starting and ending percentages
142  * of the line to use (ignored for the window header).  "label" is the prefix to use.
143  * Returns an object to use with subsequent calls to "ttysetstatusfield" (zero on error).
144  */
ttydeclarestatusfield(INTBIG line,INTBIG startper,INTBIG endper,CHAR * label)145 STATUSFIELD *ttydeclarestatusfield(INTBIG line, INTBIG startper, INTBIG endper, CHAR *label)
146 {
147 	STATUSFIELD *sf;
148 
149 	sf = (STATUSFIELD *)emalloc((sizeof (STATUSFIELD)), us_tool->cluster);
150 	if (sf == 0) return(0);
151 	if (line < 0 || line > ttynumstatuslines()) return(0);
152 	if (line > 0 && (startper < 0 || endper > 100 || startper >= endper)) return(0);
153 	sf->line = line;
154 	sf->startper = startper;
155 	sf->endper = endper;
156 	(void)allocstring(&sf->label, label, us_tool->cluster);
157 #ifdef USEQT
158 	ttysetstatusfield(NOWINDOWFRAME, sf, x_(""), FALSE);
159 #endif
160 	return(sf);
161 }
162 
163 /* write the current grid alignment from "us_alignment_ratio" */
us_setalignment(WINDOWFRAME * frame)164 void us_setalignment(WINDOWFRAME *frame)
165 {
166 	CHAR valstring[60];
167 
168 	(void)estrcpy(valstring, frtoa(us_alignment_ratio));
169 	if (us_edgealignment_ratio != 0)
170 	{
171 		(void)estrcat(valstring, x_("/"));
172 		(void)estrcat(valstring, frtoa(us_edgealignment_ratio));
173 	}
174 	ttysetstatusfield(frame, us_statusalign, valstring, FALSE);
175 }
176 
177 /* write the current lambda value from the current library/technology in microns */
us_setlambda(WINDOWFRAME * frame)178 void us_setlambda(WINDOWFRAME *frame)
179 {
180 	REGISTER INTBIG len;
181 	float lambdainmicrons;
182 	CHAR hold[50];
183 
184 	/* show nothing in X/Y mode */
185 	if ((us_tool->toolstate&SHOWXY) != 0) return;
186 
187 	lambdainmicrons = scaletodispunit(el_curlib->lambda[el_curtech->techindex], DISPUNITMIC);
188 	(void)esnprintf(hold, 50, x_("%g"), lambdainmicrons);
189 	for(;;)
190 	{
191 		len = estrlen(hold)-1;
192 		if (hold[len] != '0') break;
193 		hold[len] = 0;
194 	}
195 	if (hold[len] == '.') hold[len] = 0;
196 	(void)estrcat(hold, x_("u"));
197 	ttysetstatusfield(NOWINDOWFRAME, us_statuslambda, hold, FALSE);
198 }
199 
200 /* write the name of the current technology from "el_curtech->techname" */
us_settechname(WINDOWFRAME * frame)201 void us_settechname(WINDOWFRAME *frame)
202 {
203 	/* show nothing in XY mode */
204 	if ((us_tool->toolstate&SHOWXY) != 0) return;
205 
206 	ttysetstatusfield(NOWINDOWFRAME, us_statustechnology, el_curtech->techname, TRUE);
207 }
208 
209 /* write the cursor coordinates */
us_setcursorpos(WINDOWFRAME * frame,INTBIG x,INTBIG y)210 void us_setcursorpos(WINDOWFRAME *frame, INTBIG x, INTBIG y)
211 {
212 	static INTBIG lastx=0, lasty=0;
213 
214 	/* show nothing in non XY mode */
215 	if ((us_tool->toolstate&SHOWXY) == 0) return;
216 
217 	if (lastx != x)
218 	{
219 		ttysetstatusfield(frame, us_statusxpos, latoa(x, 0), FALSE);
220 		lastx = x;
221 	}
222 
223 	if (lasty != y)
224 	{
225 		ttysetstatusfield(frame, us_statusypos, latoa(y, 0), FALSE);
226 		lasty = y;
227 	}
228 }
229 
230 /* set the current nodeproto */
us_setnodeproto(NODEPROTO * np)231 void us_setnodeproto(NODEPROTO *np)
232 {
233 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_node_key, (INTBIG)np, VNODEPROTO|VDONTSAVE);
234 }
235 
236 /*
237  * set the current arcproto to "ap".  If "always" is false, do this only if sensible.
238  */
us_setarcproto(ARCPROTO * ap,BOOLEAN always)239 void us_setarcproto(ARCPROTO *ap, BOOLEAN always)
240 {
241 	/* don't do it if setting universal arc when not in generic technology */
242 	if (!always && ap != NOARCPROTO && ap->tech != el_curtech)
243 	{
244 		if (ap == gen_universalarc) return;
245 	}
246 	(void)setvalkey((INTBIG)us_tool, VTOOL, us_current_arc_key, (INTBIG)ap, VARCPROTO|VDONTSAVE);
247 }
248 
249 /* shadow changes to the current nodeproto */
us_shadownodeproto(WINDOWFRAME * frame,NODEPROTO * np)250 void us_shadownodeproto(WINDOWFRAME *frame, NODEPROTO *np)
251 {
252 	REGISTER CHAR *pt;
253 	HIGHLIGHT high;
254 	REGISTER VARIABLE *var;
255 	REGISTER INTBIG len;
256 	REGISTER NODEINST *ni;
257 	REGISTER void *infstr;
258 
259 	/* code cannot be called by multiple procesors: uses globals */
260 	NOT_REENTRANT;
261 
262 	us_curnodeproto = np;
263 
264 	if (np == NONODEPROTO) pt = x_(""); else
265 		pt = describenodeproto(np);
266 
267 	/* see if the selection further qualifies the node description */
268 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
269 	if (var != NOVARIABLE)
270 	{
271 		len = getlength(var);
272 		if (len == 1)
273 		{
274 			if (!us_makehighlight(((CHAR **)var->addr)[0], &high))
275 			{
276 				if ((high.status&HIGHTEXT) != 0)
277 				{
278 					infstr = initinfstr();
279 					var = high.fromvar;
280 					if (var == NOVARIABLE)
281 					{
282 						if (high.fromport == NOPORTPROTO)
283 						{
284 							formatinfstr(infstr, _("%s (cell name)"),
285 								describenodeinst(high.fromgeom->entryaddr.ni));
286 						} else
287 						{
288 							formatinfstr(infstr, _("%s (export)"), high.fromport->protoname);
289 						}
290 					} else
291 					{
292 						if (high.fromgeom == NOGEOM)
293 						{
294 							formatinfstr(infstr, _("%s on cell"),
295 								us_trueattributename(var));
296 						} else if (high.fromport != NOPORTPROTO)
297 						{
298 							formatinfstr(infstr, _("%s on export"),
299 								us_trueattributename(var));
300 						} else
301 						{
302 							if (high.fromgeom->entryisnode)
303 							{
304 								ni = high.fromgeom->entryaddr.ni;
305 								if (ni->proto == gen_invispinprim)
306 								{
307 									addstringtoinfstr(infstr, us_invisiblepintextname(var));
308 								} else
309 								{
310 									formatinfstr(infstr, _("%s on node"), us_trueattributename(var));
311 								}
312 							} else
313 							{
314 								formatinfstr(infstr, _("%s on arc"), us_trueattributename(var));
315 							}
316 						}
317 					}
318 					pt = returninfstr(infstr);
319 				} else if ((high.status&HIGHFROM) != 0 && high.fromgeom->entryisnode)
320 				{
321 					ni = high.fromgeom->entryaddr.ni;
322 					if (ni->proto == np)
323 					{
324 						/* one node selected that is the same as the current proto: qualify proto */
325 						infstr = initinfstr();
326 						addstringtoinfstr(infstr, us_describenodeinsttype(np, ni, ni->userbits&NTECHBITS));
327 						var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
328 						if (var != NOVARIABLE)
329 							formatinfstr(infstr, x_(" [%s]"), (CHAR *)var->addr);
330 						pt = returninfstr(infstr);
331 					}
332 				}
333 			}
334 		}
335 	}
336 	ttysetstatusfield(frame, us_statusnode, pt, TRUE);
337 
338 	/* if a node was described with details, clear them */
339 	if (us_shadowarcdetails) us_shadowarcproto(frame, us_curarcproto);
340 
341 	/* highlight the menu entry */
342 	us_showcurrentnodeproto();
343 }
344 
345 /*
346  * routine to draw highlighting around the menu entry with the current
347  * node prototype
348  */
us_showcurrentnodeproto(void)349 void us_showcurrentnodeproto(void)
350 {
351 	REGISTER CHAR *str;
352 	REGISTER INTBIG x, y;
353 	REGISTER VARIABLE *var;
354 	REGISTER NODEPROTO *np;
355 	COMMANDBINDING commandbinding;
356 
357 	if (us_curnodeproto == NONODEPROTO) return;
358 	if ((us_tool->toolstate&MENUON) == 0) return;
359 	if ((us_tool->toolstate&ONESHOTMENU) != 0) return;
360 	if (us_menuhnx >= 0) us_highlightmenu(us_menuhnx, us_menuhny, el_colmenbor);
361 	us_menuhnx = -1;
362 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
363 	if (var != NOVARIABLE) for(x=0; x<us_menux; x++) for(y=0; y<us_menuy; y++)
364 	{
365 		if (us_menupos <= 1) str = ((CHAR **)var->addr)[y * us_menux + x]; else
366 			str = ((CHAR **)var->addr)[x * us_menuy + y];
367 		us_parsebinding(str, &commandbinding);
368 		np = commandbinding.nodeglyph;
369 		us_freebindingparse(&commandbinding);
370 		if (np == us_curnodeproto)
371 		{
372 			us_menuhnx = x;   us_menuhny = y;
373 			us_highlightmenu(us_menuhnx, us_menuhny, el_colhmenbor);
374 			break;
375 		}
376 	}
377 }
378 
379 /* shadow changes to the current arcproto */
us_shadowarcproto(WINDOWFRAME * frame,ARCPROTO * ap)380 void us_shadowarcproto(WINDOWFRAME *frame, ARCPROTO *ap)
381 {
382 	REGISTER ARCPROTO *oldap;
383 	REGISTER NETWORK *net, **netlist;
384 	REGISTER CHAR *pt;
385 	REGISTER INTBIG len;
386 	REGISTER void *infstr;
387 
388 	/* first show the arc/network in the status bar */
389 	netlist = net_gethighlightednets(FALSE);
390 	net = netlist[0];
391 	if (net == NONETWORK || (net->globalnet < 0 && net->namecount <= 0))
392 	{
393 		pt = x_("");
394 		us_shadowarcdetails = FALSE;
395 	} else
396 	{
397 		pt = describenetwork(net);
398 		us_shadowarcdetails = TRUE;
399 	}
400 	if (ap != us_curarcproto || us_lastnetworkname == 0 || namesame(us_lastnetworkname, pt) != 0)
401 	{
402 		len = estrlen(pt) + 1;
403 		if (len > us_lastnetworknametotal)
404 		{
405 			if (us_lastnetworknametotal > 0) efree((CHAR *)us_lastnetworkname);
406 			us_lastnetworknametotal = 0;
407 			us_lastnetworkname = (CHAR *)emalloc(len * SIZEOFCHAR, us_tool->cluster);
408 			if (us_lastnetworkname == 0) return;
409 			us_lastnetworknametotal = len;
410 		}
411 		estrcpy(us_lastnetworkname, pt);
412 		pt = describearcproto(ap);
413 		if (*us_lastnetworkname != 0)
414 		{
415 			infstr = initinfstr();
416 			formatinfstr(infstr, x_("%s [%s]"), pt, us_lastnetworkname);
417 			pt = returninfstr(infstr);
418 		}
419 
420 		ttysetstatusfield(frame, us_statusarc, pt, TRUE);
421 	}
422 
423 	/* next handle menu highlighting */
424 	if (us_curarcproto != ap)
425 	{
426 		/* undraw the menu entry with the last arc proto in it */
427 		oldap = us_curarcproto;
428 		us_curarcproto = ap;
429 
430 		/* adjust the component menu entries */
431 		if (oldap != NOARCPROTO) us_drawarcmenu(oldap);
432 		if (us_curarcproto != NOARCPROTO) us_drawarcmenu(us_curarcproto);
433 		us_showcurrentarcproto();
434 	}
435 }
436 
437 /*
438  * Routine to find arcproto "ap" in the component menu and redraw that entry.
439  */
us_drawarcmenu(ARCPROTO * ap)440 void us_drawarcmenu(ARCPROTO *ap)
441 {
442 	REGISTER CHAR *str;
443 	REGISTER INTBIG x, y;
444 	REGISTER VARIABLE *var;
445 	REGISTER ARCPROTO *pap;
446 	COMMANDBINDING commandbinding;
447 
448 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
449 	if (var == NOVARIABLE) return;
450 	for(x=0; x<us_menux; x++)
451 		for(y=0; y<us_menuy; y++)
452 	{
453 		if (us_menupos <= 1) str = ((CHAR **)var->addr)[y * us_menux + x]; else
454 			str = ((CHAR **)var->addr)[x * us_menuy + y];
455 		us_parsebinding(str, &commandbinding);
456 		pap = commandbinding.arcglyph;
457 		us_freebindingparse(&commandbinding);
458 		if (pap == ap)
459 		{
460 			us_drawmenuentry(x, y, str);
461 			return;
462 		}
463 	}
464 }
465 
466 /*
467  * routine to draw highlighting around the menu entry with the current
468  * arc prototype
469  */
us_showcurrentarcproto(void)470 void us_showcurrentarcproto(void)
471 {
472 	REGISTER CHAR *str;
473 	REGISTER INTBIG x, y;
474 	REGISTER VARIABLE *var;
475 	REGISTER ARCPROTO *ap;
476 	COMMANDBINDING commandbinding;
477 
478 	/* highlight the menu entry */
479 	if (us_curarcproto == NOARCPROTO) return;
480 	if ((us_tool->toolstate&MENUON) == 0) return;
481 	if ((us_tool->toolstate&ONESHOTMENU) != 0) return;
482 	if (us_menuhax >= 0) us_highlightmenu(us_menuhax, us_menuhay, el_colmenbor);
483 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_binding_menu_key);
484 	if (var != NOVARIABLE) for(x=0; x<us_menux; x++) for(y=0; y<us_menuy; y++)
485 	{
486 		if (us_menupos <= 1) str = ((CHAR **)var->addr)[y * us_menux + x]; else
487 			str = ((CHAR **)var->addr)[x * us_menuy + y];
488 		us_parsebinding(str, &commandbinding);
489 		ap = commandbinding.arcglyph;
490 		us_freebindingparse(&commandbinding);
491 		if (ap == us_curarcproto)
492 		{
493 			us_menuhax = x;   us_menuhay = y;
494 			us_highlightmenu(us_menuhax, us_menuhay, el_colhmenbor);
495 			break;
496 		}
497 	}
498 }
499 
500 /* write the default placement angle from "tool:user.USER_placement_angle" */
us_setnodeangle(WINDOWFRAME * frame)501 void us_setnodeangle(WINDOWFRAME *frame)
502 {
503 	CHAR hold[5];
504 	REGISTER VARIABLE *var;
505 	REGISTER INTBIG pangle;
506 
507 	var = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER, us_placement_angle_key);
508 	if (var == NOVARIABLE) pangle = 0; else pangle = var->addr;
509 	(void)estrcpy(hold, frtoa(pangle%3600*WHOLE/10));
510 	if (pangle >= 3600) (void)estrcat(hold, x_("T"));
511 	ttysetstatusfield(frame, us_statusangle, hold, FALSE);
512 }
513 
514 /* write the name of the cell in window "w" */
us_setcellname(WINDOWPART * w)515 void us_setcellname(WINDOWPART *w)
516 {
517 	REGISTER EDITOR *ed;
518 	REGISTER void *infstr;
519 
520 	/* do not show name of explorer window when it is split */
521 	if ((w->state&WINDOWTYPE) == EXPLORERWINDOW &&
522 		estrcmp(w->location, x_("entire")) != 0) return;
523 
524 	/* if this is part of a frame and the current window is in another part, don't write name */
525 	if (el_curwindowpart != NOWINDOWPART && w != el_curwindowpart &&
526 		w->frame == el_curwindowpart->frame) return;
527 
528 	/* write the cell name */
529 	infstr = initinfstr();
530 	switch (w->state&WINDOWTYPE)
531 	{
532 		case TEXTWINDOW:
533 		case POPTEXTWINDOW:
534 			if (w->curnodeproto == NONODEPROTO)
535 			{
536 				ed = w->editor;
537 				if (ed != NOEDITOR)
538 				{
539 					addstringtoinfstr(infstr, ed->header);
540 					break;
541 				}
542 			}
543 			/* FALLTHROUGH */
544 		case DISPWINDOW:
545 		case DISP3DWINDOW:
546 			if (w->curnodeproto != NONODEPROTO)
547 			{
548 				formatinfstr(infstr, x_("%s:%s"), w->curnodeproto->lib->libname,
549 					nldescribenodeproto(w->curnodeproto));
550 				if (w->curnodeproto->lib != el_curlib)
551 					formatinfstr(infstr, _(" - Current library: %s"), el_curlib->libname);
552 			}
553 			break;
554 		case EXPLORERWINDOW:
555 			addstringtoinfstr(infstr, _("Cell Explorer"));
556 			break;
557 		case WAVEFORMWINDOW:
558 			if (w->curnodeproto != NONODEPROTO)
559 			{
560 				formatinfstr(infstr, _("Waveform of %s"), describenodeproto(w->curnodeproto));
561 			} else
562 			{
563 				addstringtoinfstr(infstr, _("Waveform"));
564 			}
565 			break;
566 	}
567 
568 	ttysetstatusfield(w->frame, us_statuscell, returninfstr(infstr), TRUE);
569 }
570 
571 /*
572  * Routine to update the number of selected objects.
573  */
us_setselectioncount(void)574 void us_setselectioncount(void)
575 {
576 	CHAR valstring[50];
577 	REGISTER VARIABLE *var;
578 	REGISTER WINDOWPART *w;
579 
580 	valstring[0] = 0;
581 	var = getvalkey((INTBIG)us_tool, VTOOL, VSTRING|VISARRAY, us_highlightedkey);
582 	if (var != NOVARIABLE)
583 		esnprintf(valstring, 50, x_("%ld"), getlength(var));
584 
585 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
586 	{
587 		if ((w->state&WINDOWTYPE) == DISPWINDOW)
588 			ttysetstatusfield(w->frame, us_statusselectcount, valstring, FALSE); else
589 				ttysetstatusfield(w->frame, us_statusselectcount, x_(""), FALSE);
590 	}
591 }
592 
593 /*
594  * write the size of the cell in window "w" from the size of "w->curnodeproto"
595  */
us_setcellsize(WINDOWPART * w)596 void us_setcellsize(WINDOWPART *w)
597 {
598 	REGISTER NODEPROTO *np;
599 	CHAR valstring[50];
600 	REGISTER INTBIG lines, lambda;
601 
602 	np = w->curnodeproto;
603 	valstring[0] = 0;
604 	if (np != NONODEPROTO)
605 	{
606 		switch (w->state&WINDOWTYPE)
607 		{
608 			case TEXTWINDOW:
609 			case POPTEXTWINDOW:
610 				lines = us_totallines(w);
611 				esnprintf(valstring, 50, _("%ld lines"), lines);
612 				break;
613 			case DISPWINDOW:
614 			case DISP3DWINDOW:
615 				lambda = lambdaofcell(np);
616 				(void)estrcpy(valstring, latoa(np->highx - np->lowx, lambda));
617 				(void)estrcat(valstring, x_("x"));
618 				(void)estrcat(valstring, latoa(np->highy - np->lowy, lambda));
619 				break;
620 		}
621 	}
622 	ttysetstatusfield(w->frame, us_statuscellsize, valstring, FALSE);
623 }
624 
625 /* write the grid size of window "w" from "w->gridx" and "w->gridy" */
us_setgridsize(WINDOWPART * w)626 void us_setgridsize(WINDOWPART *w)
627 {
628 	REGISTER NODEPROTO *np;
629 	CHAR valstring[50];
630 
631 	np = w->curnodeproto;
632 	valstring[0] = 0;
633 	if (np != NONODEPROTO)
634 	{
635 		switch (w->state&WINDOWTYPE)
636 		{
637 			case DISPWINDOW:
638 			case DISP3DWINDOW:
639 				(void)estrcpy(valstring, frtoa(w->gridx));
640 				(void)estrcat(valstring, x_("x"));
641 				(void)estrcat(valstring, frtoa(w->gridy));
642 				break;
643 		}
644 	}
645 	ttysetstatusfield(NOWINDOWFRAME, us_statusgridsize, valstring, FALSE);
646 }
647 
648 /* routine to re-write the status information (on all frames if frame is 0) */
us_redostatus(WINDOWFRAME * frame)649 void us_redostatus(WINDOWFRAME *frame)
650 {
651 	REGISTER WINDOWPART *w;
652 	REGISTER ARCPROTO *ap;
653 	REGISTER NODEPROTO *np;
654 	REGISTER INTBIG lines, i;
655 
656 	/* clear the status bar */
657 	lines = ttynumstatuslines();
658 	for(i=0; i<lines; i++) ttysetstatusfield(NOWINDOWFRAME, us_clearstatuslines[i], x_(""), FALSE);
659 
660 	/* node and arc */
661 	ap = us_curarcproto;   np = us_curnodeproto;
662 	us_curarcproto = (ARCPROTO *)0;   us_curnodeproto = (NODEPROTO *)0;
663 	us_shadownodeproto(NOWINDOWFRAME, np);
664 	us_shadowarcproto(NOWINDOWFRAME, ap);
665 
666 	/* write the window information on the window lines */
667 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
668 	{
669 		us_setcellname(w);
670 		if (graphicshas(CANSTATUSPERFRAME) || w->frame == frame)
671 			us_setcellsize(w);
672 		us_setgridsize(w);
673 	}
674 	if (el_curwindowpart != NOWINDOWPART)
675 		us_setcellsize(el_curwindowpart);
676 
677 	/* other fields */
678 	us_setnodeangle(frame);
679 	us_setalignment(frame);
680 	us_setlambda(frame);
681 	us_settechname(frame);
682 	us_setselectioncount();
683 }
684 
685 /******************** OUTPUT PREPARATION ********************/
686 
687 /*
688  * routine to list macros by the package of origin
689  */
us_printmacros(void)690 void us_printmacros(void)
691 {
692 	INTBIG len, coms, keyboard, gotany, i, j, count;
693 	CHAR line[150], *build[MAXPARS];
694 	REGISTER CHAR *name, *comname, *inter;
695 	REGISTER MACROPACK *pack, *thispack;
696 	REGISTER VARIABLE *var;
697 
698 	/* count the number of macros in each package */
699 	for(pack=us_macropacktop; pack!=NOMACROPACK; pack=pack->nextmacropack)
700 		pack->total = 0;
701 	keyboard = gotany = 0;
702 	for(i=0; i<us_tool->numvar; i++)
703 	{
704 		if (namesamen(makename(us_tool->firstvar[i].key), x_("USER_macro_"), 11) == 0)
705 		{
706 			var = &us_tool->firstvar[i];
707 			if (getlength(var) <= 3) continue;
708 			count = us_parsecommand(((CHAR **)var->addr)[0], build);
709 			for(j=0; j<count; j++)
710 				if (namesamen(build[j], x_("macropack="), 10) == 0)
711 			{
712 				pack = us_getmacropack(&build[j][10]);
713 				if (pack != NOMACROPACK) pack->total++;
714 				break;
715 			}
716 			if (j >= count) keyboard++;
717 			gotany++;
718 		}
719 	}
720 	if (gotany == 0)
721 	{
722 		ttyputmsg(M_("No macros defined"));
723 		return;
724 	}
725 
726 	ttyputmsg(M_("---------- Macros by Package ----------"));
727 
728 	/* now print the macros by package */
729 	for(pack = us_macropacktop; ; pack = pack->nextmacropack)
730 	{
731 		if (pack == NOMACROPACK)
732 		{
733 			if (keyboard == 0) break;
734 			name = M_("Keyboard-defined");
735 		} else
736 		{
737 			if (pack->total == 0) continue;
738 			name = pack->packname;
739 		}
740 		(void)estrcpy(line, name);
741 		(void)estrcat(line, M_(" macros:"));
742 		len = estrlen(line);
743 		inter = x_("");
744 		for(i=0; i<us_tool->numvar; i++)
745 			if (namesamen(makename(us_tool->firstvar[i].key), x_("USER_macro_"), 11) == 0)
746 		{
747 			var = &us_tool->firstvar[i];
748 			if (getlength(var) <= 3) continue;
749 			count = us_parsecommand(((CHAR **)var->addr)[0], build);
750 			thispack = NOMACROPACK;
751 			for(j=0; j<count; j++)
752 				if (namesamen(build[j], x_("macropack="), 10) == 0)
753 			{
754 				thispack = us_getmacropack(&build[j][10]);
755 				break;
756 			}
757 			if (thispack != pack) continue;
758 			(void)estrcat(line, inter);  len += estrlen(inter);
759 			inter = x_(",");
760 			comname = &makename(var->key)[11];
761 			coms = estrlen(comname) + 2;
762 			if (len + coms >= MESSAGESWIDTH)
763 			{
764 				ttyputmsg(line);  (void)estrcpy(line, x_("  "));  len = 2;
765 			}
766 			(void)estrcat(line, x_(" "));
767 			(void)estrcat(line, comname);  len += coms;
768 
769 		}
770 		if (len > 2) ttyputmsg(line);
771 		if (pack == NOMACROPACK) break;
772 	}
773 }
774 
775 /*
776  * print macro "mac".  Show every command in the macro.
777  */
us_printmacro(VARIABLE * mac)778 void us_printmacro(VARIABLE *mac)
779 {
780 	REGISTER INTBIG j, len;
781 
782 	if (mac == NOVARIABLE) return;
783 	len = getlength(mac);
784 	if (len <= 3)
785 	{
786 		ttyputmsg(M_("Macro '%s' is not defined"), &makename(mac->key)[11]);
787 		return;
788 	}
789 
790 	ttyputmsg(M_("Macro '%s' has %ld %s"), &makename(mac->key)[11], len-3,
791 		makeplural(M_("command"), len-3));
792 	for(j=3; j<len; j++) ttyputmsg(x_("   %s"), ((CHAR **)mac->addr)[j]);
793 }
794 
795 static struct
796 {
797 	CHAR *typestring;
798 	INTBIG typecode;
799 } us_vartypename[] =
800 {
801 	{x_("Unknown"),      VUNKNOWN},
802 	{x_("Integer"),      VINTEGER},
803 	{x_("Address"),      VADDRESS},
804 	{x_("Character"),    VCHAR},
805 	{x_("String"),       VSTRING},
806 	{x_("Float"),        VFLOAT},
807 	{x_("Double"),       VDOUBLE},
808 	{x_("Nodeinst"),     VNODEINST},
809 	{x_("Nodeproto"),    VNODEPROTO},
810 	{x_("Portarcinst"),  VPORTARCINST},
811 	{x_("Portexpinst"),  VPORTEXPINST},
812 	{x_("Portproto"),    VPORTPROTO},
813 	{x_("Arcinst"),      VARCINST},
814 	{x_("Arcproto"),     VARCPROTO},
815 	{x_("Geometry"),     VGEOM},
816 	{x_("Library"),      VLIBRARY},
817 	{x_("Technology"),   VTECHNOLOGY},
818 	{x_("Tool"),         VTOOL},
819 	{x_("R-tree"),       VRTNODE},
820 	{x_("Fixed-Point"),  VFRACT},
821 	{x_("Network"),      VNETWORK},
822 	{x_("View"),         VVIEW},
823 	{x_("WindowPart"),   VWINDOWPART},
824 	{x_("Graphics"),     VGRAPHICS},
825 	{x_("Short"),        VSHORT},
826 	{x_("Boolean"),      VBOOLEAN},
827 	{x_("Constraint"),   VCONSTRAINT},
828 	{x_("General"),      VGENERAL},
829 	{x_("Window-Frame"), VWINDOWFRAME},
830 	{x_("Polygon"),      VPOLYGON},
831 	{NULL, 0}
832 };
833 
us_variabletypename(INTBIG type)834 CHAR *us_variabletypename(INTBIG type)
835 {
836 	INTBIG i;
837 
838 	for(i=0; us_vartypename[i].typestring != 0; i++)
839 		if (us_vartypename[i].typecode == (type&VTYPE))
840 			return(us_vartypename[i].typestring);
841 	return(x_("unknown"));
842 }
843 
us_variabletypevalue(CHAR * name)844 INTBIG us_variabletypevalue(CHAR *name)
845 {
846 	INTBIG i;
847 
848 	for(i=0; us_vartypename[i].typestring != 0; i++)
849 		if (namesame(name, us_vartypename[i].typestring) == 0)
850 			return(us_vartypename[i].typecode);
851 	return(0);
852 }
853 
854 /*
855  * routine to describe variable "var".  If "index" is nonnegative then describe
856  * entry "index" of an array variable.  The description is returned.
857  */
us_variableattributes(VARIABLE * var,INTBIG aindex)858 CHAR *us_variableattributes(VARIABLE *var, INTBIG aindex)
859 {
860 	CHAR *arr, line[30];
861 	REGISTER INTBIG len;
862 	REGISTER void *infstr;
863 
864 	if ((var->type & VISARRAY) == 0 || aindex >= 0) arr = x_(""); else
865 	{
866 		len = getlength(var);
867 		if (len == 0) arr = x_(" array[]"); else
868 		{
869 			if ((var->type&VTYPE) == VGENERAL) len /= 2;
870 			(void)esnprintf(line, 30, x_(" array[%ld]"), len);
871 			arr = line;
872 		}
873 	}
874 	infstr = initinfstr();
875 	if ((var->type&VDONTSAVE) != 0) addstringtoinfstr(infstr, _("Temporary "));
876 	if ((var->type&VDISPLAY) != 0)
877 	{
878 		addstringtoinfstr(infstr, _("Displayable("));
879 		addstringtoinfstr(infstr, us_describetextdescript(var->textdescript));
880 		addstringtoinfstr(infstr, x_(") "));
881 	}
882 	if ((var->type&(VCODE1|VCODE2)) != 0)
883 	{
884 		switch (var->type&(VCODE1|VCODE2))
885 		{
886 			case VLISP: addstringtoinfstr(infstr, x_("LISP "));  break;
887 			case VTCL:  addstringtoinfstr(infstr, x_("TCL "));   break;
888 			case VJAVA: addstringtoinfstr(infstr, x_("Java "));  break;
889 		}
890 	}
891 	addstringtoinfstr(infstr, us_variabletypename(var->type));
892 	addstringtoinfstr(infstr, arr);
893 	return(returninfstr(infstr));
894 }
895 
896 /* routine to recursively count and print the nodes in cell "cell" */
us_describecontents(NODEPROTO * cell)897 void us_describecontents(NODEPROTO *cell)
898 {
899 	REGISTER INTBIG i;
900 	REGISTER NODEPROTO *np;
901 	REGISTER LIBRARY *lib, *printlib;
902 	REGISTER TECHNOLOGY *curtech, *printtech, *tech;
903 	REGISTER NODEPROTO **sortedcells;
904 
905 	/* first zero the count of each nodeproto */
906 	for(tech = el_technologies; tech != NOTECHNOLOGY; tech = tech->nexttechnology)
907 		for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
908 			np->temp1 = 0;
909 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
910 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
911 			np->temp1 = 0;
912 
913 	/* now look at every object recursively in this cell */
914 	us_addobjects(cell);
915 
916 	/* print the totals */
917 	ttyputmsg(_("Contents of cell %s:"), describenodeproto(cell));
918 	printtech = NOTECHNOLOGY;
919 	for(curtech = el_technologies; curtech != NOTECHNOLOGY; curtech = curtech->nexttechnology)
920 	{
921 		for(np = curtech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
922 			if (np->temp1 != 0)
923 		{
924 			if (curtech != printtech)
925 			{
926 				ttyputmsg(_("%s technology:"), curtech->techname);
927 				printtech = curtech;
928 			}
929 			ttyputmsg(_("%6d %s %s"), np->temp1, describenodeproto(np),
930 				makeplural(_("node"), np->temp1));
931 		}
932 	}
933 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
934 	{
935 		sortedcells = us_sortlib(lib, (CHAR *)0);
936 		if (sortedcells == 0) ttyputnomemory(); else
937 		{
938 			printlib = NOLIBRARY;
939 			for(i = 0; sortedcells[i] != NONODEPROTO; i++)
940 			{
941 				np = sortedcells[i];
942 				if (np->temp1 == 0) continue;
943 				if (lib != printlib)
944 				{
945 					ttyputmsg(_("%s library:"), lib->libname);
946 					printlib = lib;
947 				}
948 				ttyputmsg(_("%6d %s %s"), np->temp1, nldescribenodeproto(np),
949 					makeplural(_("node"), np->temp1));
950 			}
951 			efree((CHAR *)sortedcells);
952 		}
953 	}
954 }
955 
956 /*
957  * routine to recursively examine cell "np" and update the number of
958  * instantiated primitive nodeprotos in the "temp1" field of the nodeprotos.
959  */
us_addobjects(NODEPROTO * np)960 void us_addobjects(NODEPROTO *np)
961 {
962 	REGISTER NODEINST *ni;
963 	REGISTER NODEPROTO *cnp;
964 
965 	for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
966 	{
967 		ni->proto->temp1++;
968 		if (ni->proto->primindex != 0) continue;
969 
970 		/* ignore recursive references (showing icon in contents) */
971 		if (isiconof(ni->proto, np)) continue;
972 		cnp = contentsview(ni->proto);
973 		if (cnp == NONODEPROTO) cnp = ni->proto;
974 		us_addobjects(cnp);
975 	}
976 }
977 
978 static struct
979 {
980 	NODEPROTO **prim;
981 	INTBIG      bits;
982 	CHAR       *statusmessage;
983 } us_specialstatus[] =
984 {
985 	{&sch_transistorprim, TRANNMOS,     x_("nMOS")},
986 	{&sch_transistorprim, TRANDMOS,     x_("dMOS")},
987 	{&sch_transistorprim, TRANPMOS,     x_("pMOS")},
988 	{&sch_transistorprim, TRANNPN,      x_("NPN")},
989 	{&sch_transistorprim, TRANPNP,      x_("PNP")},
990 	{&sch_transistorprim, TRANNJFET,    x_("NJFET")},
991 	{&sch_transistorprim, TRANPJFET,    x_("PJFET")},
992 	{&sch_transistorprim, TRANDMES,     x_("DMES")},
993 	{&sch_transistorprim, TRANEMES,     x_("EMES")},
994 	{&sch_transistor4prim,TRANNMOS,     x_("nMOS")},
995 	{&sch_transistor4prim,TRANDMOS,     x_("dMOS")},
996 	{&sch_transistor4prim,TRANPMOS,     x_("pMOS")},
997 	{&sch_transistor4prim,TRANNPN,      x_("NPN")},
998 	{&sch_transistor4prim,TRANPNP,      x_("PNP")},
999 	{&sch_transistor4prim,TRANNJFET,    x_("NJFET")},
1000 	{&sch_transistor4prim,TRANPJFET,    x_("PJFET")},
1001 	{&sch_transistor4prim,TRANDMES,     x_("DMES")},
1002 	{&sch_transistor4prim,TRANEMES,     x_("EMES")},
1003 	{&sch_twoportprim,    TWOPVCCS,     x_("VCCS")},
1004 	{&sch_twoportprim,    TWOPCCVS,     x_("CCVS")},
1005 	{&sch_twoportprim,    TWOPVCVS,     x_("VCVS")},
1006 	{&sch_twoportprim,    TWOPCCCS,     x_("CCCS")},
1007 	{&sch_twoportprim,    TWOPTLINE,    x_("Transmission")},
1008 	{&sch_diodeprim,      DIODEZENER,   x_("Zener")},
1009 	{&sch_capacitorprim,  CAPACELEC,    x_("Electrolytic")},
1010 	{&sch_ffprim,         FFTYPERS,     x_("RS")},
1011 	{&sch_ffprim,         FFTYPEJK,     x_("JK")},
1012 	{&sch_ffprim,         FFTYPED,      x_("D")},
1013 	{&sch_ffprim,         FFTYPET,      x_("T")},
1014 	{0, 0, 0}
1015 };
1016 
1017 /*
1018  * Routine to describe the node in the commandbinding structure "commandbinding".
1019  */
us_describemenunode(COMMANDBINDING * commandbinding)1020 CHAR *us_describemenunode(COMMANDBINDING *commandbinding)
1021 {
1022 	REGISTER INTBIG bits;
1023 
1024 	if (commandbinding->menumessage == 0) bits = 0; else
1025 		bits = myatoi(commandbinding->menumessage);
1026 	return(us_describenodeinsttype(commandbinding->nodeglyph, NONODEINST, bits));
1027 }
1028 
1029 /*
1030  * Routine to return a description of node "np" that is qualified with bits "bits".
1031  * It may also be qualified by having an instance "ni" (but it may be NONODEINST).
1032  */
us_describenodeinsttype(NODEPROTO * np,NODEINST * ni,INTBIG bits)1033 CHAR *us_describenodeinsttype(NODEPROTO *np, NODEINST *ni, INTBIG bits)
1034 {
1035 	REGISTER INTBIG i;
1036 	REGISTER CHAR *pt;
1037 	REGISTER void *infstr;
1038 
1039 	infstr = initinfstr();
1040 	addstringtoinfstr(infstr, describenodeproto(np));
1041 	for(i=0; us_specialstatus[i].prim != 0; i++)
1042 		if (us_specialstatus[i].bits == bits &&
1043 			*us_specialstatus[i].prim == np) break;
1044 	if (us_specialstatus[i].prim != 0)
1045 	{
1046 		formatinfstr(infstr, x_(" (%s)"), us_specialstatus[i].statusmessage);
1047 	} else
1048 	{
1049 		/* add information if from technology editor */
1050 		if (ni != NONODEINST)
1051 		{
1052 			pt = us_teceddescribenode(ni);
1053 			if (pt != 0) formatinfstr(infstr, x_(" (%s)"), pt);
1054 		}
1055 	}
1056 	return(returninfstr(infstr));
1057 }
1058 
1059 /*
1060  * routine to sort the cells in library "lib" by cell name and return an
1061  * array of NODEPROTO pointers (terminating with NONODEPROTO).  If "matchspec"
1062  * is nonzero, it is a string that forces wildcard matching of cell names.
1063  */
us_sortlib(LIBRARY * lib,CHAR * matchspec)1064 NODEPROTO **us_sortlib(LIBRARY *lib, CHAR *matchspec)
1065 {
1066 	REGISTER NODEPROTO **sortindex, *np;
1067 	REGISTER INTBIG total, i;
1068 	REGISTER CHAR *pt;
1069 
1070 	/* remove library name if it is part of spec */
1071 	if (matchspec != 0)
1072 	{
1073 		for(pt = matchspec; *pt != 0; pt++) if (*pt == ':') break;
1074 		if (*pt == ':') matchspec = pt+1;
1075 	}
1076 
1077 	/* count the number of cells in the library */
1078 	total = 0;
1079 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1080 		if (us_wildmatch(nldescribenodeproto(np), matchspec)) total++;
1081 
1082 	/* allocate array for sorting entries */
1083 	sortindex = (NODEPROTO **)emalloc(((total+1) * (sizeof (NODEPROTO *))), el_tempcluster);
1084 	if (sortindex == 0) return(0);
1085 	for(np = lib->firstnodeproto, i = 0; np != NONODEPROTO; np = np->nextnodeproto)
1086 		if (us_wildmatch(nldescribenodeproto(np), matchspec))
1087 			sortindex[i++] = np;
1088 	sortindex[i] = NONODEPROTO;
1089 
1090 	/* sort the cell names */
1091 	esort(sortindex, total, sizeof (NODEPROTO *), sort_cellnameascending);
1092 	return(sortindex);
1093 }
1094 
1095 /*
1096  * routine to match a name in "name" with a wildcard specification in "spec".
1097  * If "spec" is zero, all names are valid.  Returns true if the name matches.
1098  */
us_wildmatch(CHAR * name,CHAR * spec)1099 BOOLEAN us_wildmatch(CHAR *name, CHAR *spec)
1100 {
1101 	REGISTER CHAR *pt, *pt2;
1102 	REGISTER CHAR c1, c2;
1103 
1104 	if (spec == 0) return(TRUE);
1105 
1106 	pt = name;
1107 	pt2 = spec;
1108 	for(;;)
1109 	{
1110 		if (*pt == 0 && *pt2 == 0) break;
1111 		if (*pt != 0 && *pt2 == '?')
1112 		{
1113 			pt++;
1114 			pt2++;
1115 			continue;
1116 		}
1117 		if (*pt2 == '*')
1118 		{
1119 			for(;;)
1120 			{
1121 				if (us_wildmatch(pt, &pt2[1]) != 0) return(TRUE);
1122 				if (*pt == 0) break;
1123 				pt++;
1124 			}
1125 			return(FALSE);
1126 		}
1127 		if (isupper(*pt)) c1 = tolower(*pt); else c1 = *pt;
1128 		if (isupper(*pt2)) c2 = tolower(*pt2); else c2 = *pt2;
1129 		if (c1 != c2) return(FALSE);
1130 		pt++;
1131 		pt2++;
1132 	}
1133 	return(TRUE);
1134 }
1135 
1136 /* routine to print the tool information about nodeinst "ni" */
us_printnodetoolinfo(NODEINST * ni)1137 void us_printnodetoolinfo(NODEINST *ni)
1138 {
1139 	static struct explanation explain[] =
1140 	{
1141 		{DEADN,       -1,      N_("dead")},
1142 		{NEXPAND,     -1,      N_("expand")},
1143 		{WIPED,       -1,      N_("wiped")},
1144 		{NSHORT,      -1,      N_("short")},
1145 		{0, -1, NULL}
1146 	};
1147 	us_explaintool(ni->userbits, explain);
1148 }
1149 
1150 /* routine to print the tool information about arcinst "ai" */
us_printarctoolinfo(ARCINST * ai)1151 void us_printarctoolinfo(ARCINST *ai)
1152 {
1153 	static struct explanation explain[] =
1154 	{
1155 		{FIXED,         -1,                 N_("rigid")},
1156 		{CANTSLIDE,     -1,                 N_("rigid-in-ports")},
1157 		{FIXANG,        -1,                 N_("fixed-angle")},
1158 		{DEADA,         -1,                 N_("dead")},
1159 		{AANGLE,        AANGLESH,           N_("angle")},
1160 		{ASHORT,        -1,                 N_("short")},
1161 		{AFUNCTION,     APBUS<<AFUNCTIONSH, N_("bus")},
1162 		{NOEXTEND,      -1,                 N_("ends-shortened")},
1163 		{ISNEGATED,     -1,                 N_("negated")},
1164 		{ISDIRECTIONAL, -1,                 N_("directional")},
1165 		{NOTEND0,       -1,                 N_("skip-tail")},
1166 		{NOTEND1,       -1,                 N_("skip-head")},
1167 		{REVERSEEND,    -1,                 N_("direction-reversed")},
1168 		{0, -1, NULL}
1169 	};
1170 	us_explaintool(ai->userbits, explain);
1171 }
1172 
1173 /* helper routine for "us_printnodetoolinfo" and "us_printarctoolinfo" */
us_explaintool(INTBIG bits,struct explanation explain[])1174 void us_explaintool(INTBIG bits, struct explanation explain[])
1175 {
1176 	REGISTER INTBIG j, k;
1177 	CHAR partial[40];
1178 	REGISTER void *infstr;
1179 
1180 	/* print the tool bit information */
1181 	infstr = initinfstr();
1182 	(void)esnprintf(partial, 40, _("User state = %ld"), bits);
1183 	addstringtoinfstr(infstr, partial);
1184 	k = 0;
1185 	for(j=0; explain[j].bit != 0; j++)
1186 	{
1187 		if (explain[j].shift != -1)
1188 		{
1189 			if (k == 0) addtoinfstr(infstr, '('); else
1190 				addstringtoinfstr(infstr, x_(", "));
1191 			addstringtoinfstr(infstr, TRANSLATE(explain[j].name));
1192 			addtoinfstr(infstr, '=');
1193 			(void)esnprintf(partial, 40, x_("%ld"),(bits&explain[j].bit)>>explain[j].shift);
1194 			addstringtoinfstr(infstr, partial);
1195 			k++;
1196 			continue;
1197 		}
1198 		if ((bits & explain[j].bit) == 0) continue;
1199 		if (k == 0) addtoinfstr(infstr, '('); else addstringtoinfstr(infstr, x_(", "));
1200 		addstringtoinfstr(infstr, TRANSLATE(explain[j].name));
1201 		k++;
1202 	}
1203 	if (k != 0) addtoinfstr(infstr, ')');
1204 	ttyputmsg(x_("%s"), returninfstr(infstr));
1205 }
1206 
1207 /*
1208  * routine to print information about port prototype "pp" on nodeinst "ni".
1209  * If the port prototype's "temp1" is nonzero, this port has already been
1210  * mentioned and should not be done again.
1211  */
us_chatportproto(NODEINST * ni,PORTPROTO * pp)1212 void us_chatportproto(NODEINST *ni, PORTPROTO *pp)
1213 {
1214 	REGISTER PORTARCINST *pi;
1215 	REGISTER PORTEXPINST *pe;
1216 	REGISTER ARCINST *ai;
1217 	REGISTER INTBIG i, lambda;
1218 	INTBIG lx, hx, ly, hy;
1219 	REGISTER CHAR *activity;
1220 	CHAR locx[40], locy[40];
1221 	static POLYGON *poly = NOPOLYGON;
1222 	REGISTER void *infstr;
1223 
1224 	/* get polygon */
1225 	(void)needstaticpolygon(&poly, 4, us_tool->cluster);
1226 
1227 	/* if this port has already been discussed, quit now */
1228 	if (pp->temp1 != 0) return;
1229 	pp->temp1++;
1230 
1231 	/* talk about the port prototype */
1232 	lambda = lambdaofnode(ni);
1233 	infstr = initinfstr();
1234 	activity = describeportbits(pp->userbits);
1235 	if (*activity != 0)
1236 	{
1237 		formatinfstr(infstr, _("%s port %s connects to "), activity, pp->protoname);
1238 	} else
1239 	{
1240 		formatinfstr(infstr, _("Port %s connects to "), pp->protoname);
1241 	}
1242 	for(i=0; pp->connects[i] != NOARCPROTO; i++)
1243 	{
1244 		if (i != 0) addstringtoinfstr(infstr, x_(", "));
1245 		addstringtoinfstr(infstr, pp->connects[i]->protoname);
1246 	}
1247 	ttyputmsg(x_("%s"), returninfstr(infstr));
1248 
1249 	if (pp->subnodeinst == NONODEINST)
1250 	{
1251 		shapeportpoly(ni, pp, poly, FALSE);
1252 
1253 		/* see if the port is a single point */
1254 		for(i=1; i<poly->count; i++)
1255 			if (poly->xv[i] != poly->xv[i-1] || poly->yv[i] != poly->yv[i-1]) break;
1256 		if (i >= poly->count)
1257 		{
1258 			ttyputmsg(_("  Located at (%s, %s)"), latoa(poly->xv[0], lambda), latoa(poly->yv[0], lambda));
1259 		} else if (isbox(poly, &lx,&hx, &ly,&hy))
1260 		{
1261 			if (lx == hx) (void)esnprintf(locx, 40, _("at %s"), latoa(lx, lambda)); else
1262 				(void)esnprintf(locx, 40, _("from %s to %s"), latoa(lx, lambda), latoa(hx, lambda));
1263 			if (ly == hy) (void)esnprintf(locy, 40, _("at %s"), latoa(ly, lambda)); else
1264 				(void)esnprintf(locy, 40, _("from %s to %s"), latoa(ly, lambda), latoa(hy, lambda));
1265 			ttyputmsg(_("  Located %s in X and %s in Y"), locx, locy);
1266 		} else
1267 		{
1268 			infstr = initinfstr();
1269 			for(i=0; i<poly->count; i++)
1270 				formatinfstr(infstr, x_(" (%s, %s)"), latoa(poly->xv[i], lambda), latoa(poly->yv[i], lambda));
1271 			ttyputmsg(_("  Located inside polygon%s"), returninfstr(infstr));
1272 		}
1273 	} else ttyputmsg(_("  Located at subnode %s subport %s"),
1274 		describenodeinst(pp->subnodeinst), pp->subportproto->protoname);
1275 
1276 	/* talk about any arcs on this prototype */
1277 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1278 	{
1279 		if (pi->proto != pp) continue;
1280 		ai = pi->conarcinst;
1281 		if (ai->end[0].portarcinst == pi) i = 0; else i = 1;
1282 		ttyputmsg(_("  Connected at (%s,%s) to %s"), latoa(ai->end[i].xpos, lambda),
1283 			latoa(ai->end[i].ypos, lambda), describearcinst(ai));
1284 	}
1285 
1286 	/* talk about any exports of this prototype */
1287 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1288 		if (pe->proto == pp)
1289 	{
1290 		infstr = initinfstr();
1291 		formatinfstr(infstr, _("  Available as %s export '%s' (label %s"),
1292 			describeportbits(pe->exportproto->userbits), pe->exportproto->protoname,
1293 				us_describetextdescript(pe->exportproto->textdescript));
1294 		if ((pe->exportproto->userbits&PORTDRAWN) != 0) addstringtoinfstr(infstr, _(",always-drawn"));
1295 		if ((pe->exportproto->userbits&BODYONLY) != 0) addstringtoinfstr(infstr, _(",only-on-body"));
1296 		addstringtoinfstr(infstr, x_(") "));
1297 		ttyputmsg(x_("%s"), returninfstr(infstr));
1298 	}
1299 }
1300 
1301 /*
1302  * routine to construct a string that describes the text descriptor field
1303  * in "descript".  The string has the form "POSITION+(DX,DY),SIZE" if there
1304  * is an offset, or "POSITION,SIZE" if there is not.  The string is returned.
1305  */
us_describetextdescript(UINTBIG * descript)1306 CHAR *us_describetextdescript(UINTBIG *descript)
1307 {
1308 	REGISTER INTBIG xdist, ydist;
1309 	CHAR offset[50];
1310 	REGISTER void *infstr;
1311 
1312 	infstr = initinfstr();
1313 	addstringtoinfstr(infstr, us_describestyle(TDGETPOS(descript)));
1314 	if (TDGETXOFF(descript) != 0 || TDGETYOFF(descript) != 0)
1315 	{
1316 		xdist = TDGETXOFF(descript);
1317 		ydist = TDGETYOFF(descript);
1318 		(void)esnprintf(offset, 50, x_("+(%s,%s)"), frtoa(xdist*WHOLE/4), frtoa(ydist*WHOLE/4));
1319 		addstringtoinfstr(infstr, offset);
1320 	}
1321 	addtoinfstr(infstr, ',');
1322 	addstringtoinfstr(infstr, us_describefont(TDGETSIZE(descript)));
1323 	return(returninfstr(infstr));
1324 }
1325 
1326 /*
1327  * routine to return a string describing text style "style".
1328  */
us_describestyle(INTBIG style)1329 CHAR *us_describestyle(INTBIG style)
1330 {
1331 	switch (style)
1332 	{
1333 		case VTPOSCENT:      return(_("centered"));
1334 		case VTPOSBOXED:     return(_("boxed"));
1335 		case VTPOSUP:        return(_("up"));
1336 		case VTPOSDOWN:      return(_("down"));
1337 		case VTPOSLEFT:      return(_("left"));
1338 		case VTPOSRIGHT:     return(_("right"));
1339 		case VTPOSUPLEFT:    return(_("up-left"));
1340 		case VTPOSUPRIGHT:   return(_("up-right"));
1341 		case VTPOSDOWNLEFT:  return(_("down-left"));
1342 		case VTPOSDOWNRIGHT: return(_("down-right"));
1343 	}
1344 	return(x_("?"));
1345 }
1346 
1347 /*
1348  * routine to return a string describing text font "font".
1349  */
us_describefont(INTBIG font)1350 CHAR *us_describefont(INTBIG font)
1351 {
1352 	static CHAR description[60];
1353 	if (TXTGETPOINTS(font) != 0)
1354 	{
1355 		esnprintf(description, 60, _("%ld-points"), TXTGETPOINTS(font));
1356 		return(description);
1357 	}
1358 	if (TXTGETQLAMBDA(font) != 0)
1359 	{
1360 		esnprintf(description, 60, _("%s-lambda"), frtoa(TXTGETQLAMBDA(font) * WHOLE / 4));
1361 		return(description);
1362 	}
1363 	return(x_("?"));
1364 }
1365 
1366 /*
1367  * Routine to return the type of nonlayout text that is in variable "var".
1368  */
us_invisiblepintextname(VARIABLE * var)1369 CHAR *us_invisiblepintextname(VARIABLE *var)
1370 {
1371 	if (namesame(makename(var->key), x_("VERILOG_code")) == 0)
1372 		return(_("Verilog code"));
1373 	if (namesame(makename(var->key), x_("VERILOG_declaration")) == 0)
1374 		return(_("Verilog declaration"));
1375 	if (namesame(makename(var->key), x_("SIM_spice_card")) == 0)
1376 		return(_("SPICE card"));
1377 	return(_("Nonlayout Text"));
1378 }
1379 
1380 typedef struct
1381 {
1382 	CHAR *origvarname;
1383 	CHAR *uservarname;
1384 } USERVARIABLES;
1385 
1386 USERVARIABLES us_uservariables[] =
1387 {
1388 	{x_("ARC_name"),                 N_("Arc Name")},
1389 	{x_("ARC_radius"),               N_("Arc Radius")},
1390 	{x_("ART_color"),                N_("Color")},
1391 	{x_("ART_degrees"),              N_("Number of Degrees")},
1392 	{x_("ART_message"),              N_("Text")},
1393 	{x_("NET_ncc_match"),            N_("NCC equivalence")},
1394 	{x_("NET_ncc_forcedassociation"),N_("NCC association")},
1395 	{x_("NODE_name"),                N_("Node Name")},
1396 	{x_("SCHEM_capacitance"),        N_("Capacitance")},
1397 	{x_("SCHEM_diode"),              N_("Diode Size")},
1398 	{x_("SCHEM_inductance"),         N_("Inductance")},
1399 	{x_("SCHEM_resistance"),         N_("Resistance")},
1400 	{x_("SIM_fall_delay"),           N_("Fall Delay")},
1401 	{x_("SIM_fasthenry_group_name"), N_("FastHenry Group")},
1402 	{x_("SIM_rise_delay"),           N_("Rise Delay")},
1403 	{x_("SIM_spice_model"),          N_("SPICE model")},
1404 	{x_("transistor_width"),         N_("Transistor Width")},
1405 	{x_("SCHEM_global_name"),        N_("Global Signal Name")},
1406 	{0, 0}
1407 };
1408 
1409 /*
1410  * Routine to see if variable name "name" is a known variable.  If
1411  * so, returns a better name for it.  If not, returns zero.
1412  */
us_bettervariablename(CHAR * name)1413 CHAR *us_bettervariablename(CHAR *name)
1414 {
1415 	REGISTER INTBIG i;
1416 
1417 	/* handle standard variable names */
1418 	for(i=0; us_uservariables[i].origvarname != 0; i++)
1419 	{
1420 		if (namesame(name, us_uservariables[i].origvarname) == 0)
1421 			return(TRANSLATE(us_uservariables[i].uservarname));
1422 	}
1423 	return(0);
1424 }
1425 
1426 /*
1427  * Routine to see if variable "var" should have a units qualifier after it.
1428  * If so, returns the type.  If not, returns zero.
1429  */
us_variableunits(VARIABLE * var)1430 CHAR *us_variableunits(VARIABLE *var)
1431 {
1432 	REGISTER INTBIG units;
1433 
1434 	/* specialized fields that adjust with units */
1435 	units = TDGETUNITS(var->textdescript);
1436 	switch (units)
1437 	{
1438 		case VTUNITSRES:		/* resistance */
1439 			return(TRANSLATE(us_resistancenames[(us_electricalunits&INTERNALRESUNITS) >> INTERNALRESUNITSSH]));
1440 		case VTUNITSCAP:		/* capacitance */
1441 			return(TRANSLATE(us_capacitancenames[(us_electricalunits&INTERNALCAPUNITS) >> INTERNALCAPUNITSSH]));
1442 		case VTUNITSIND:		/* inductance */
1443 			return(TRANSLATE(us_inductancenames[(us_electricalunits&INTERNALINDUNITS) >> INTERNALINDUNITSSH]));
1444 		case VTUNITSCUR:		/* current */
1445 			return(TRANSLATE(us_currentnames[(us_electricalunits&INTERNALCURUNITS) >> INTERNALCURUNITSSH]));
1446 		case VTUNITSVOLT:		/* voltage */
1447 			return(TRANSLATE(us_voltagenames[(us_electricalunits&INTERNALVOLTUNITS) >> INTERNALVOLTUNITSSH]));
1448 		case VTUNITSTIME:		/* time */
1449 			return(TRANSLATE(us_timenames[(us_electricalunits&INTERNALTIMEUNITS) >> INTERNALTIMEUNITSSH]));
1450 	}
1451 	return(0);
1452 }
1453 
1454 /*
1455  * Routine to return a user-friendly name of variable "var".
1456  */
us_trueattributename(VARIABLE * var)1457 CHAR *us_trueattributename(VARIABLE *var)
1458 {
1459 	REGISTER CHAR *pt, *bettername, *unitname;
1460 	REGISTER void *infstr;
1461 
1462 	infstr = initinfstr();
1463 	pt = makename(var->key);
1464 	if (namesamen(pt, x_("ATTR_"), 5) == 0)
1465 	{
1466 		if (TDGETISPARAM(var->textdescript) != 0)
1467 			formatinfstr(infstr, _("Parameter '%s'"), &pt[5]); else
1468 				formatinfstr(infstr, _("Attribute '%s'"), &pt[5]);
1469 	} else
1470 	{
1471 		bettername = us_bettervariablename(pt);
1472 		if (bettername != 0) addstringtoinfstr(infstr, bettername); else
1473 			formatinfstr(infstr, _("Variable '%s'"), pt);
1474 	}
1475 	unitname = us_variableunits(var);
1476 	if (unitname != 0) formatinfstr(infstr, x_(" (%s)"), unitname);
1477 	return(returninfstr(infstr));
1478 }
1479 
1480 /*
1481  * routine to print a description of color map entry "ind"
1482  */
us_printcolorvalue(INTBIG ind)1483 void us_printcolorvalue(INTBIG ind)
1484 {
1485 	GRAPHICS *desc;
1486 	CHAR line[40];
1487 	REGISTER INTBIG i, j, high;
1488 	REGISTER VARIABLE *varred, *vargreen, *varblue;
1489 	REGISTER void *infstr;
1490 
1491 	/* get color arrays */
1492 	varred = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_red_key);
1493 	vargreen = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_green_key);
1494 	varblue = getvalkey((INTBIG)us_tool, VTOOL, VINTEGER|VISARRAY, us_colormap_blue_key);
1495 	if (varred == NOVARIABLE || vargreen == NOVARIABLE || varblue == NOVARIABLE)
1496 	{
1497 		ttyputerr(_("Cannot get current color map"));
1498 		return;
1499 	}
1500 
1501 	infstr = initinfstr();
1502 	if ((ind&(LAYERH|LAYERG)) == 0)
1503 	{
1504 		(void)esnprintf(line, 40, _("Entry %ld"), ind);
1505 		addstringtoinfstr(infstr, line);
1506 		j = 0;
1507 		high = el_curtech->layercount;
1508 		for(i=0; i<high; i++)
1509 		{
1510 			desc = el_curtech->layers[i];
1511 			if (desc->bits == LAYERO && desc->col != ind) continue;
1512 			if (desc->bits != LAYERO && ((desc->col&ind) == 0 || (ind&LAYEROE) != 0)) continue;
1513 			if (j == 0) addstringtoinfstr(infstr, x_(" (")); else
1514 				addtoinfstr(infstr, ',');
1515 			j++;
1516 			addstringtoinfstr(infstr, layername(el_curtech, i));
1517 		}
1518 		if (j != 0) addtoinfstr(infstr, ')');
1519 		addstringtoinfstr(infstr, _(" is"));
1520 	} else
1521 	{
1522 		if ((ind&LAYERG) != 0) addstringtoinfstr(infstr, _("Grid entry is")); else
1523 			if ((ind&LAYERH) != 0) addstringtoinfstr(infstr, _("Highlight entry is"));
1524 	}
1525 	(void)esnprintf(line, 40, _(" color (%ld,%ld,%ld)"), ((INTBIG *)varred->addr)[ind],
1526 		((INTBIG *)vargreen->addr)[ind], ((INTBIG *)varblue->addr)[ind]);
1527 	addstringtoinfstr(infstr, line);
1528 	ttyputmsg(x_("%s"), returninfstr(infstr));
1529 }
1530 
1531 /*
1532  * routine to make a line of text that describes cell "np".  The text
1533  * includes the cell name, version, creation, and revision dates.  The
1534  * amount of space to leave for the cell name is "maxlen" characters
1535  * (which is negative if dumping to a file).
1536  */
us_makecellline(NODEPROTO * np,INTBIG maxlen)1537 CHAR *us_makecellline(NODEPROTO *np, INTBIG maxlen)
1538 {
1539 	CHAR line[40];
1540 	REGISTER INTBIG l, i, gooddrc, len, total, lambda;
1541 	INTBIG year, month, mday, hour, minute, second;
1542 	REGISTER CHAR *pt;
1543 	REGISTER VARIABLE *var;
1544 	REGISTER NODEINST *ni;
1545 	UINTBIG lastgooddate;
1546 	REGISTER void *infstr;
1547 
1548 	infstr = initinfstr();
1549 	pt = describenodeproto(np);
1550 	addstringtoinfstr(infstr, pt);
1551 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1552 	{
1553 		l = estrlen(pt);
1554 		for(i=l; i<maxlen; i++) addtoinfstr(infstr, ' ');
1555 	}
1556 
1557 	/* add the version number */
1558 	(void)esnprintf(line, 40, x_("%5ld"), np->version);
1559 	addstringtoinfstr(infstr, line);
1560 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1561 		addstringtoinfstr(infstr, x_("   "));
1562 
1563 	/* add the creation date */
1564 	if (np->creationdate == 0)
1565 	{
1566 		if (maxlen < 0) addstringtoinfstr(infstr, _("UNRECORDED")); else
1567 			addstringtoinfstr(infstr, _("     UNRECORDED     "));
1568 	} else
1569 	{
1570 		if (maxlen < 0)
1571 		{
1572 			parsetime((time_t)np->creationdate, &year, &month, &mday, &hour, &minute, &second);
1573 			esnprintf(line, 40, x_("%4ld-%02ld-%02ld %02ld:%02ld:%02ld"), year, month, mday, hour,
1574 				minute, second);
1575 			addstringtoinfstr(infstr, line);
1576 		} else
1577 		{
1578 			pt = timetostring(np->creationdate);
1579 			if (pt == NULL)
1580 			{
1581 				addstringtoinfstr(infstr, _("     UNAVAILABLE     "));
1582 			} else
1583 			{
1584 				for(i=4; i<=9; i++) addtoinfstr(infstr, pt[i]);
1585 				for(i=19; i<=23; i++) addtoinfstr(infstr, pt[i]);
1586 				for(i=10; i<=18; i++) addtoinfstr(infstr, pt[i]);
1587 			}
1588 		}
1589 	}
1590 
1591 	/* add the revision date */
1592 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1593 		addstringtoinfstr(infstr, x_("  "));
1594 	if (np->revisiondate == 0)
1595 	{
1596 		if (maxlen < 0) addstringtoinfstr(infstr, _("UNRECORDED")); else
1597 			addstringtoinfstr(infstr, _("     UNRECORDED     "));
1598 	} else
1599 	{
1600 		if (maxlen < 0)
1601 		{
1602 			parsetime((time_t)np->revisiondate, &year, &month, &mday, &hour, &minute, &second);
1603 			esnprintf(line, 40, x_("%4ld-%02ld-%02ld %02ld:%02ld:%02ld"), year, month, mday, hour,
1604 				minute, second);
1605 			addstringtoinfstr(infstr, line);
1606 		} else
1607 		{
1608 			pt = timetostring(np->revisiondate);
1609 			if (pt == NULL)
1610 			{
1611 				addstringtoinfstr(infstr, _("     UNAVAILABLE     "));
1612 			} else
1613 			{
1614 				for(i=4; i<=9; i++) addtoinfstr(infstr, pt[i]);
1615 				for(i=19; i<=23; i++) addtoinfstr(infstr, pt[i]);
1616 				for(i=10; i<=18; i++) addtoinfstr(infstr, pt[i]);
1617 			}
1618 		}
1619 	}
1620 
1621 	/* add the size */
1622 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1623 		addstringtoinfstr(infstr, x_("  "));
1624 	if ((np->cellview->viewstate&TEXTVIEW) != 0)
1625 	{
1626 		var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING|VISARRAY, el_cell_message_key);
1627 		if (var == NOVARIABLE) len = 0; else
1628 			len = getlength(var);
1629 		if (maxlen < 0) esnprintf(line, 40, _("%ld lines"), len); else
1630 			esnprintf(line, 40, _("%8ld lines   "), len);
1631 		addstringtoinfstr(infstr, line);
1632 	} else
1633 	{
1634 		lambda = lambdaofcell(np);
1635 		estrcpy(line, latoa(np->highx-np->lowx, lambda));
1636 		if (maxlen >= 0)
1637 		{
1638 			for(i = estrlen(line); i<8; i++) addtoinfstr(infstr, ' ');
1639 		}
1640 		addstringtoinfstr(infstr, line);
1641 		addtoinfstr(infstr, 'x');
1642 		estrcpy(line, latoa(np->highy-np->lowy, lambda));
1643 		addstringtoinfstr(infstr, line);
1644 		if (maxlen >= 0)
1645 		{
1646 			for(i = estrlen(line); i<8; i++) addtoinfstr(infstr, ' ');
1647 		}
1648 	}
1649 	if (maxlen < 0) addtoinfstr(infstr, '\t');
1650 
1651 	/* count the number of instances */
1652 	total = 0;
1653 	for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst) total++;
1654 	if (maxlen < 0) esnprintf(line, 40, x_("%ld"), total); else
1655 		esnprintf(line, 40, x_("%4ld"), total);
1656 	addstringtoinfstr(infstr, line);
1657 
1658 	/* show other factors about the cell */
1659 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1660 		addstringtoinfstr(infstr, x_("   "));
1661 	if ((np->userbits&NPLOCKED) != 0) addstringtoinfstr(infstr, x_("L")); else
1662 		addstringtoinfstr(infstr, x_(" "));
1663 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1664 		addstringtoinfstr(infstr, x_(" "));
1665 	if ((np->userbits&NPILOCKED) != 0) addstringtoinfstr(infstr, x_("I")); else
1666 		addstringtoinfstr(infstr, x_(" "));
1667 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1668 		addstringtoinfstr(infstr, x_(" "));
1669 	if ((np->userbits&INCELLLIBRARY) != 0) addstringtoinfstr(infstr, x_("C")); else
1670 		addstringtoinfstr(infstr, x_(" "));
1671 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1672 		addstringtoinfstr(infstr, x_(" "));
1673 	gooddrc = 0;
1674 	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER, dr_lastgooddrckey);
1675 	if (var != NOVARIABLE)
1676 	{
1677 		lastgooddate = (UINTBIG)var->addr;
1678 		if (np->revisiondate <= lastgooddate) gooddrc = 1;
1679 	}
1680 	if (gooddrc != 0) addstringtoinfstr(infstr, x_("D")); else
1681 		addstringtoinfstr(infstr, x_(" "));
1682 	if (maxlen < 0) addtoinfstr(infstr, '\t'); else
1683 		addstringtoinfstr(infstr, x_(" "));
1684 	if (net_ncchasmatch(np) != 0) addstringtoinfstr(infstr, x_("N")); else
1685 		addstringtoinfstr(infstr, x_(" "));
1686 	return(returninfstr(infstr));
1687 }
1688 
1689 /******************** KEYBOARD SUPPORT ********************/
1690 
1691 /*
1692  * routine to check to see if library "lib" has changed and warn the
1693  * user prior to destroying changes to that library.  If "lib" is NOLIBRARY, all
1694  * libraries are checked.  "action" is the type of action to be performed:
1695  * 0=quit, 1=close library, 2=replace library
1696  * If the operation can be cancelled, "cancancel" is true.  Returns true
1697  * if the operation is to be aborted.
1698  */
us_preventloss(LIBRARY * lib,INTBIG action,BOOLEAN cancancel)1699 BOOLEAN us_preventloss(LIBRARY *lib, INTBIG action, BOOLEAN cancancel)
1700 {
1701 	REGISTER INTBIG result, count;
1702 	CHAR *par[10];
1703 	REGISTER LIBRARY *l;
1704 	extern COMCOMP us_yesnop;
1705 	REGISTER void *infstr;
1706 
1707 	for(l = el_curlib; l != NOLIBRARY; l = l->nextlibrary)
1708 	{
1709 		if ((l->userbits&HIDDENLIBRARY) != 0) continue;
1710 		if (lib != NOLIBRARY && lib != l) continue;
1711 		if ((l->userbits&(LIBCHANGEDMAJOR|LIBCHANGEDMINOR)) == 0) continue;
1712 
1713 		infstr = initinfstr();
1714 		if ((l->userbits&LIBCHANGEDMAJOR) != 0)
1715 			formatinfstr(infstr, _("Library %s has changed significantly.  "), l->libname); else
1716 				formatinfstr(infstr, _("Library %s has changed insignificantly.  "), l->libname);
1717 		switch (action)
1718 		{
1719 			case 0: addstringtoinfstr(infstr, _("Save before quitting?"));   break;
1720 			case 1: addstringtoinfstr(infstr, _("Save before closing?"));    break;
1721 			case 2: addstringtoinfstr(infstr, _("Save before replacing?"));  break;
1722 		}
1723 		if (cancancel)
1724 		{
1725 			result = us_quitdlog(returninfstr(infstr), (action==0 ? 1 : 0));
1726 			if (result == 0)
1727 			{
1728 				ttyputverbose(M_("Keep working"));
1729 				return(TRUE);
1730 			}
1731 			if (result == 1) continue;
1732 			if (result == 2) break;
1733 			if (result == 3)
1734 			{
1735 				/* save the library */
1736 				makeoptionstemporary(l);
1737 				(void)asktool(io_tool, x_("write"), (INTBIG)l, (INTBIG)x_("binary"));
1738 			}
1739 		} else
1740 		{
1741 			count = ttygetparam(returninfstr(infstr), &us_yesnop, MAXPARS, par);
1742 			if (count > 0 && namesamen(par[0], x_("yes"), estrlen(par[0])) == 0)
1743 			{
1744 				/* save the library */
1745 				makeoptionstemporary(l);
1746 				(void)asktool(io_tool, x_("write"), (INTBIG)l, (INTBIG)x_("binary"));
1747 			}
1748 		}
1749 	}
1750 
1751 	/* also check for option changes on quit */
1752 	if (us_optionschanged && action == 0)
1753 	{
1754 		if (optionshavechanged())
1755 		{
1756 			if (us_saveoptdlog(TRUE))
1757 			{
1758 				ttyputverbose(M_("Keep working"));
1759 				return(TRUE);
1760 			}
1761 		}
1762 	}
1763 	return(FALSE);
1764 }
1765 
1766 /*
1767  * Routine to save all options by creating a dummy library and saving it (without
1768  * making options temporary first).
1769  */
us_saveoptions(void)1770 void us_saveoptions(void)
1771 {
1772 	REGISTER INTBIG retval;
1773 	REGISTER LIBRARY *lib;
1774 	REGISTER CHAR *libname, *libfile;
1775 
1776 	libname = us_tempoptionslibraryname();
1777 	libfile = optionsfilepath();
1778 	lib = newlibrary(libname, libfile);
1779 	if (lib == NOLIBRARY)
1780 	{
1781 		ttyputerr(_("Cannot create options library %s"), libfile);
1782 		return;
1783 	}
1784 	lib->userbits |= READFROMDISK | HIDDENLIBRARY;
1785 	retval = asktool(io_tool, x_("write"), (INTBIG)lib, (INTBIG)x_("nobackupbinary"));
1786 	if (retval != 0) ttyputerr(_("Could not save options library")); else
1787 		ttyputmsg(_("Options have been saved to %s"), lib->libfile);
1788 	killlibrary(lib);
1789 	us_optionschanged = FALSE;
1790 
1791 	/* recache the options so that the system knows what has changed */
1792 	cacheoptionbitvalues();
1793 }
1794 
1795 /*
1796  * Routine to construct a temporary library name that doesn't already exist.
1797  */
us_tempoptionslibraryname(void)1798 CHAR *us_tempoptionslibraryname(void)
1799 {
1800 	static CHAR libname[256];
1801 	REGISTER LIBRARY *lib;
1802 	REGISTER INTBIG i;
1803 
1804 	for(i=1; ; i++)
1805 	{
1806 		esnprintf(libname, 256, x_("options%ld"), i);
1807 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1808 			if (namesame(libname, lib->libname) == 0) break;
1809 		if (lib == NOLIBRARY) break;
1810 	}
1811 	return(libname);
1812 }
1813 
1814 /*
1815  * Routine to ensure that everything gets saved.  Returns TRUE if something got saved.
1816  */
us_saveeverything(void)1817 BOOLEAN us_saveeverything(void)
1818 {
1819 	REGISTER INTBIG total, i, l, retval;
1820 	REGISTER LIBRARY *lib, *deplib, **libsave;
1821 	REGISTER NODEPROTO *np;
1822 	REGISTER NODEINST *ni;
1823 	REGISTER BOOLEAN gotsaved;
1824 
1825 	/* see which libraries need to be saved */
1826 	gotsaved = FALSE;
1827 	total = 0;
1828 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1829 	{
1830 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1831 		if ((lib->userbits&(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) == 0) continue;
1832 		total++;
1833 	}
1834 	if (total == 0) return(gotsaved);
1835 	libsave = (LIBRARY **)emalloc(total * (sizeof (LIBRARY *)), el_tempcluster);
1836 	if (libsave == 0) return(gotsaved);
1837 
1838 	/* make a list of the libraries to save */
1839 	total = 0;
1840 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1841 	{
1842 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1843 		if ((lib->userbits&(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) == 0) continue;
1844 		libsave[total++] = lib;
1845 	}
1846 
1847 	/* be sure the most referenced libraries are saved first */
1848 	for(i=0; i<total; i++) libsave[i]->temp1 = i;
1849 	for(i=0; i<total; i++)
1850 	{
1851 		lib = libsave[i];
1852 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1853 		{
1854 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
1855 			{
1856 				if (ni->proto->primindex != 0) continue;
1857 				deplib = ni->proto->lib;
1858 				if (deplib == lib) continue;
1859 				if (deplib->temp1 <= lib->temp1) continue;
1860 				l = deplib->temp1;   deplib->temp1 = lib->temp1;   lib->temp1 = l;
1861 			}
1862 		}
1863 	}
1864 	esort(libsave, total, sizeof (LIBRARY *), us_librarytemp1ascending);
1865 
1866 	/* now save every library that needs it */
1867 	for(i=0; i<total; i++)
1868 	{
1869 		lib = libsave[i];
1870 
1871 		/* save the library in binary format */
1872 		makeoptionstemporary(lib);
1873 		retval = asktool(io_tool, x_("write"), (INTBIG)lib, (INTBIG)x_("binary"));
1874 		restoreoptionstate(lib);
1875 		if (retval != 0) continue;
1876 		gotsaved = TRUE;
1877 	}
1878 
1879 	/* clean up */
1880 	efree((CHAR *)libsave);
1881 
1882 	/* save options if any have changed */
1883 	if (us_optionschanged && gotsaved && us_logrecord != NULL)
1884 	{
1885 		if (optionshavechanged())
1886 			(void)us_saveoptdlog(FALSE);
1887 	}
1888 	return(gotsaved);
1889 }
1890 
1891 /* Prompt: Session Logging */
1892 static DIALOGITEM us_seslogdialogitems[] =
1893 {
1894  /*  1 */ {0, {204,100,228,268}, BUTTON, N_("Save All Information")},
1895  /*  2 */ {0, {240,100,264,268}, BUTTON, N_("Disable Session Logging")},
1896  /*  3 */ {0, {4,4,20,284}, MESSAGE, N_("Warning: not all information has been saved.")},
1897  /*  4 */ {0, {36,4,52,356}, MESSAGE, N_("Unless all libraries and options are saved together")},
1898  /*  5 */ {0, {56,4,72,356}, MESSAGE, N_("It is not possible to reconstruct the session after a crash.")},
1899  /*  6 */ {0, {88,4,104,356}, MESSAGE, N_("The following information has not been saved:")},
1900  /*  7 */ {0, {108,4,192,356}, SCROLL, x_("")}
1901 };
1902 static DIALOG us_seslogdialog = {{75,75,348,441}, N_("Session Logging Warning"), 0, 7, us_seslogdialogitems, 0, 0};
1903 
1904 #define DSEL_SAVEALL    1		/* save all information (button) */
1905 #define DSEL_DISLOG     2		/* disable session logging (button) */
1906 #define DSEL_UNSAVED    7		/* what is unsaved (scroll) */
1907 
us_continuesessionlogging(void)1908 void us_continuesessionlogging(void)
1909 {
1910 	REGISTER BOOLEAN unsaved;
1911 	REGISTER void *dia, *infstr;
1912 	REGISTER INTBIG itemHit;
1913 	REGISTER LIBRARY *lib;
1914 
1915 	/* if session logging is already off, never mind */
1916 	if (us_logrecord == NULL) return;
1917 
1918 	/* see if there are any unsaved libraries */
1919 	unsaved = FALSE;
1920 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1921 	{
1922 		if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1923 		if ((lib->userbits&(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) != 0)
1924 			unsaved = TRUE;
1925 	}
1926 	if (us_optionschanged && optionshavechanged())
1927 		unsaved = TRUE;
1928 
1929 	/* if something is unsaved, prompt the user */
1930 	if (unsaved)
1931 	{
1932 		dia = DiaInitDialog(&us_seslogdialog);
1933 		if (dia == 0) return;
1934 		DiaInitTextDialog(dia, DSEL_UNSAVED, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1, 0);
1935 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1936 		{
1937 			if ((lib->userbits&HIDDENLIBRARY) != 0) continue;
1938 			if ((lib->userbits&(LIBCHANGEDMAJOR | LIBCHANGEDMINOR)) == 0) continue;
1939 			infstr = initinfstr();
1940 			formatinfstr(infstr, x_("Library %s"), lib->libname);
1941 			DiaStuffLine(dia, DSEL_UNSAVED, returninfstr(infstr));
1942 		}
1943 		if (us_optionschanged && optionshavechanged())
1944 			DiaStuffLine(dia, DSEL_UNSAVED, _("Options"));
1945 		DiaSelectLine(dia, DSEL_UNSAVED, -1);
1946 		for(;;)
1947 		{
1948 			itemHit = DiaNextHit(dia);
1949 			if (itemHit == DSEL_SAVEALL || itemHit == DSEL_DISLOG) break;
1950 		}
1951 		DiaDoneDialog(dia);
1952 		if (itemHit == DSEL_DISLOG)
1953 		{
1954 			/* disable session logging */
1955 			logfinishrecord();
1956 			return;
1957 		}
1958 
1959 		/* save everything */
1960 		(void)us_saveeverything();
1961 	}
1962 
1963 	/* erase everything in the clipboard */
1964 	us_clearclipboard();
1965 
1966 	/* close all modeless dialogs */
1967 	DiaCloseAllModeless();
1968 
1969 	/* truncate the session log */
1970 	logfinishrecord();
1971 	logstartrecord();
1972 }
1973 
1974 /*
1975  * routine to determine the color map entry that corresponds to the string
1976  * "pp".  If the string is a number then return that number.  If the string
1977  * contains layer letters from the current technology, return that index.
1978  * The value of "purpose" determines how to interpret the layer letters: 0
1979  * means that the letters indicate entries in the color map and 1 means that
1980  * only one letter is allowed and its layer number should be returned.  If no
1981  * entry can be determined, the routine issues an error and returns -1.
1982  */
us_getcolormapentry(CHAR * pp,BOOLEAN layerletter)1983 INTBIG us_getcolormapentry(CHAR *pp, BOOLEAN layerletter)
1984 {
1985 	CHAR *ch, *pt1;
1986 	REGISTER INTBIG color, i, high;
1987 	GRAPHICS *desc;
1988 
1989 	/* first see if the string is all digits and return that value if so */
1990 	if (isanumber(pp)) return(myatoi(pp));
1991 
1992 	/* for layer conversion, the job is simple */
1993 	high = el_curtech->layercount;
1994 	if (layerletter)
1995 	{
1996 		if (pp[0] == 0 || pp[1] != 0)
1997 		{
1998 			us_abortcommand(_("Can only give one layer letter"));
1999 			return(-1);
2000 		}
2001 		for(i=0; i<high; i++)
2002 		{
2003 			for(ch = us_layerletters(el_curtech, i); *ch != 0; ch++)
2004 				if (*ch == pp[0]) return(i);
2005 		}
2006 		us_abortcommand(_("Letter '%s' is not a valid layer"), pp);
2007 		return(-1);
2008 	}
2009 
2010 	/* accumulate the desired color */
2011 	color = 0;
2012 	for(pt1 = pp; *pt1 != 0; pt1++)
2013 	{
2014 		/* find the layer that corresponds to letter "*pt1" */
2015 		for(i=0; i<high; i++)
2016 		{
2017 			/* see if this layer has the right letter */
2018 			for(ch = us_layerletters(el_curtech, i); *ch != 0; ch++)
2019 				if (*ch == *pt1) break;
2020 			if (*ch == 0) continue;
2021 
2022 			/* get the color characteristics of this layer */
2023 			desc = el_curtech->layers[i];
2024 			if ((desc->bits & color) != 0)
2025 			{
2026 				us_abortcommand(_("No single color for the letters '%s'"), pp);
2027 				return(-1);
2028 			}
2029 			color |= desc->col;
2030 			break;
2031 		}
2032 		if (i == high)
2033 		{
2034 			us_abortcommand(_("Letter '%c' is not a valid layer"), *pt1);
2035 			return(-1);
2036 		}
2037 	}
2038 	return(color);
2039 }
2040 
2041 /*
2042  * routine to return a unique port prototype name in cell "cell" given that
2043  * a new prototype wants to be named "name".  The routine allocates space
2044  * for the string that is returned so this must be freed when done.
2045  */
us_uniqueportname(CHAR * name,NODEPROTO * cell)2046 CHAR *us_uniqueportname(CHAR *name, NODEPROTO *cell)
2047 {
2048 	CHAR *str;
2049 
2050 	str = us_uniqueobjectname(name, cell, VPORTPROTO, 0);
2051 
2052 	if (us_uniqueretstr != NOSTRING) efree(us_uniqueretstr);
2053 	(void)allocstring(&us_uniqueretstr, str, us_tool->cluster);
2054 	return(us_uniqueretstr);
2055 }
2056 
2057 /*
2058  * Routine to determine whether the name "name" is unique in cell "cell"
2059  * (given that it is on objects of type "type").  Does not consider object
2060  * "exclude" (if nonzero).
2061  */
us_isuniquename(CHAR * name,NODEPROTO * cell,INTBIG type,void * exclude)2062 BOOLEAN us_isuniquename(CHAR *name, NODEPROTO *cell, INTBIG type, void *exclude)
2063 {
2064 	REGISTER PORTPROTO *pp;
2065 	REGISTER ARCINST *ai;
2066 	REGISTER NODEINST *ni;
2067 	REGISTER VARIABLE *var;
2068 
2069 	switch (type)
2070 	{
2071 		case VPORTPROTO:
2072 			pp = getportproto(cell, name);
2073 			if (pp == NOPORTPROTO) break;
2074 			if (exclude != 0 && (PORTPROTO *)exclude == pp) break;
2075 			return(FALSE);
2076 		case VNODEINST:
2077 			for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2078 			{
2079 				if (exclude != 0 && (NODEINST *)exclude == ni) continue;
2080 				var = getvalkey((INTBIG)ni, VNODEINST, VSTRING, el_node_name_key);
2081 				if (var == NOVARIABLE) continue;
2082 				if (namesame(name, (CHAR *)var->addr) == 0) return(FALSE);
2083 			}
2084 			break;
2085 		case VARCINST:
2086 			for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
2087 			{
2088 				if (exclude != 0 && (ARCINST *)exclude == ai) continue;
2089 				var = getvalkey((INTBIG)ai, VARCINST, VSTRING, el_arc_name_key);
2090 				if (var == NOVARIABLE) continue;
2091 				if (namesame(name, (CHAR *)var->addr) == 0) return(FALSE);
2092 			}
2093 			break;
2094 	}
2095 	return(TRUE);
2096 }
2097 
2098 /*
2099  * routine to return a unique object name in cell "cell" starting with the
2100  * name "name".
2101  */
us_uniqueobjectname(CHAR * name,NODEPROTO * cell,INTBIG type,void * exclude)2102 CHAR *us_uniqueobjectname(CHAR *name, NODEPROTO *cell, INTBIG type, void *exclude)
2103 {
2104 	CHAR *newname, separatestring[2];
2105 	REGISTER INTBIG nextindex, i, possiblestart, possibleend, startindex, endindex, spacing,
2106 		endpos, startpos;
2107 	BOOLEAN foundnumbers;
2108 	REGISTER void *infstr;
2109 
2110 	/* first see if the name is unique */
2111 	if (us_isuniquename(name, cell, type, exclude)) return(name);
2112 
2113 	/* now see if the name ends in "]" */
2114 	i = estrlen(name);
2115 	if (name[i-1] == ']')
2116 	{
2117 		/* see if the array contents can be incremented */
2118 		possiblestart = -1;
2119 		endpos = i-1;
2120 		for(;;)
2121 		{
2122 			/* find the range of characters in square brackets */
2123 			for(startpos = endpos; startpos >= 0; startpos--)
2124 				if (name[startpos] == '[') break;
2125 			if (name[startpos] != '[') break;
2126 
2127 			/* see if there is a comma in the bracketed expression */
2128 			for(i=startpos+1; i<endpos; i++)
2129 				if (name[i] == ',') break;
2130 			if (name[i] == ',')
2131 			{
2132 				/* this bracketed expression cannot be incremented: move on */
2133 				if (startpos > 0 && name[startpos-1] == ']')
2134 				{
2135 					endpos = startpos-1;
2136 					continue;
2137 				}
2138 				break;
2139 			}
2140 
2141 			/* see if there is a colon in the bracketed expression */
2142 			for(i=startpos+1; i<endpos; i++)
2143 				if (name[i] == ':') break;
2144 			if (name[i] == ':')
2145 			{
2146 				/* colon: make sure there are two numbers */
2147 				name[i] = 0;
2148 				name[endpos] = 0;
2149 				foundnumbers = isanumber(&name[startpos+1]) && isanumber(&name[i+1]);
2150 				name[i] = ':';
2151 				name[endpos] = ']';
2152 				if (foundnumbers)
2153 				{
2154 					startindex = atoi(&name[startpos+1]);
2155 					endindex = atoi(&name[i+1]);
2156 					spacing = abs(endindex - startindex) + 1;
2157 					for(nextindex = 1; ; nextindex++)
2158 					{
2159 						infstr = initinfstr();
2160 						for(i=0; i<startpos; i++) addtoinfstr(infstr, name[i]);
2161 						formatinfstr(infstr, x_("[%ld:%ld"), startindex+spacing*nextindex,
2162 							endindex+spacing*nextindex);
2163 						addstringtoinfstr(infstr, &name[endpos]);
2164 						newname = returninfstr(infstr);
2165 						if (us_isuniquename(newname, cell, type, 0)) break;
2166 					}
2167 					return(newname);
2168 				}
2169 
2170 				/* this bracketed expression cannot be incremented: move on */
2171 				if (startpos > 0 && name[startpos-1] == ']')
2172 				{
2173 					endpos = startpos-1;
2174 					continue;
2175 				}
2176 				break;
2177 			}
2178 
2179 			/* see if this bracketed expression is a pure number */
2180 			name[endpos] = 0;
2181 			foundnumbers = isanumber(&name[startpos+1]);
2182 			name[endpos] = ']';
2183 			if (foundnumbers)
2184 			{
2185 				nextindex = myatoi(&name[startpos+1]) + 1;
2186 				for(; ; nextindex++)
2187 				{
2188 					infstr = initinfstr();
2189 					for(i=0; i<startpos; i++) addtoinfstr(infstr, name[i]);
2190 					formatinfstr(infstr, x_("[%ld"), nextindex);
2191 					addstringtoinfstr(infstr, &name[endpos]);
2192 					newname = returninfstr(infstr);
2193 					if (us_isuniquename(newname, cell, type, 0)) break;
2194 				}
2195 				return(newname);
2196 			}
2197 
2198 			/* remember the first index that could be incremented in a pinch */
2199 			if (possiblestart < 0)
2200 			{
2201 				possiblestart = startpos;
2202 				possibleend = endpos;
2203 			}
2204 
2205 			/* this bracketed expression cannot be incremented: move on */
2206 			if (startpos > 0 && name[startpos-1] == ']')
2207 			{
2208 				endpos = startpos-1;
2209 				continue;
2210 			}
2211 			break;
2212 		}
2213 
2214 		/* if there was a possible place to increment, do it */
2215 		if (possiblestart >= 0)
2216 		{
2217 			/* nothing simple, but this one can be incremented */
2218 			for(i=possibleend-1; i>possiblestart; i--)
2219 				if (!isdigit(name[i])) break;
2220 			nextindex = myatoi(&name[i+1]) + 1;
2221 			startpos = i+1;
2222 			if (name[startpos-1] == us_separatechar) startpos--;
2223 			for(; ; nextindex++)
2224 			{
2225 				infstr = initinfstr();
2226 				for(i=0; i<startpos; i++) addtoinfstr(infstr, name[i]);
2227 				formatinfstr(infstr, x_("%c%ld"), us_separatechar, nextindex);
2228 				addstringtoinfstr(infstr, &name[possibleend]);
2229 				newname = returninfstr(infstr);
2230 				if (us_isuniquename(newname, cell, type, 0)) break;
2231 			}
2232 			return(newname);
2233 		}
2234 	}
2235 
2236 	/* array contents cannot be incremented: increment base name */
2237 	for(startpos=0; name[startpos] != 0; startpos++)
2238 		if (name[startpos] == '[') break;
2239 	endpos = startpos;
2240 
2241 	/* if there is a numeric part at the end, increment that */
2242 	separatestring[0] = (CHAR)us_separatechar;
2243 	separatestring[1] = 0;
2244 	while (startpos > 0 && isdigit(name[startpos-1])) startpos--;
2245 	if (startpos >= endpos)
2246 	{
2247 		nextindex = 1;
2248 		if (startpos > 0 && name[startpos-1] == us_separatechar) startpos--;
2249 	} else
2250 	{
2251 		nextindex = myatoi(&name[startpos]) + 1;
2252 		separatestring[0] = 0;
2253 	}
2254 
2255 	for(; ; nextindex++)
2256 	{
2257 		infstr = initinfstr();
2258 		for(i=0; i<startpos; i++) addtoinfstr(infstr, name[i]);
2259 		formatinfstr(infstr, x_("%s%ld"), separatestring, nextindex);
2260 		addstringtoinfstr(infstr, &name[endpos]);
2261 		newname = returninfstr(infstr);
2262 		if (us_isuniquename(newname, cell, type, 0)) break;
2263 	}
2264 	return(newname);
2265 }
2266 
2267 /*
2268  * routine to initialize the database variable "USER_layer_letters".  This
2269  * is called once at initialization and again whenever the array is changed.
2270  */
us_initlayerletters(void)2271 void us_initlayerletters(void)
2272 {
2273 	REGISTER VARIABLE *var;
2274 	REGISTER TECHNOLOGY *t;
2275 
2276 	if (us_layer_letter_data != 0) efree((CHAR *)us_layer_letter_data);
2277 	us_layer_letter_data = emalloc((el_maxtech * SIZEOFINTBIG), us_tool->cluster);
2278 	if (us_layer_letter_data == 0) return;
2279 	for(t = el_technologies; t != NOTECHNOLOGY; t = t->nexttechnology)
2280 	{
2281 		var = getvalkey((INTBIG)t, VTECHNOLOGY, VSTRING|VISARRAY, us_layer_letters_key);
2282 		us_layer_letter_data[t->techindex] = (var == NOVARIABLE ? 0 : var->addr);
2283 	}
2284 }
2285 
2286 /*
2287  * routine to return a string of unique letters describing layer "layer"
2288  * in technology "tech".  The letters for all layers of a given technology
2289  * must not intersect.  This routine accesses the "USER_layer_letters"
2290  * variable on the technology objects.
2291  */
us_layerletters(TECHNOLOGY * tech,INTBIG layer)2292 CHAR *us_layerletters(TECHNOLOGY *tech, INTBIG layer)
2293 {
2294 	REGISTER INTBIG addr;
2295 
2296 	if (us_layer_letter_data == 0)
2297 	{
2298 		us_initlayerletters();
2299 		if (us_layer_letter_data == 0) return(x_(""));
2300 	}
2301 
2302 	addr = us_layer_letter_data[tech->techindex];
2303 	if (addr == 0) return(x_(""));
2304 	return(((CHAR **)addr)[layer]);
2305 }
2306 
2307 /*
2308  * routine to change an tool state.  The name of the tool is in "pt" (if "pt" is
2309  * null then an tool name is prompted).  The state of the tool is set to "state"
2310  * (0 for off, 1 for permanently off, 2 for on).
2311  */
us_settool(INTBIG count,CHAR * par[],INTBIG state)2312 void us_settool(INTBIG count, CHAR *par[], INTBIG state)
2313 {
2314 	REGISTER INTBIG i;
2315 	BOOLEAN toolstate;
2316 	REGISTER CHAR *pt;
2317 	extern COMCOMP us_onofftoolp;
2318 	REGISTER TOOL *tool;
2319 
2320 	if (count == 0)
2321 	{
2322 		count = ttygetparam(M_("Which tool: "), &us_onofftoolp, MAXPARS, par);
2323 		if (count == 0)
2324 		{
2325 			us_abortcommand(M_("Specify an tool to control"));
2326 			return;
2327 		}
2328 	}
2329 	pt = par[0];
2330 
2331 	for(i=0; i<el_maxtools; i++)
2332 		if (namesame(el_tools[i].toolname, pt) == 0) break;
2333 	if (i >= el_maxtools)
2334 	{
2335 		us_abortcommand(_("No tool called %s"), pt);
2336 		return;
2337 	}
2338 	tool = &el_tools[i];
2339 
2340 	if (tool == us_tool && state <= 1)
2341 	{
2342 		us_abortcommand(M_("No! I won't go!"));
2343 		return;
2344 	}
2345 	if ((tool->toolstate&TOOLON) == 0 && state <= 1)
2346 	{
2347 		ttyputverbose(M_("%s already off"), pt);
2348 		return;
2349 	}
2350 	if ((tool->toolstate&TOOLON) != 0 && state > 1)
2351 	{
2352 		ttyputverbose(M_("%s already on"), pt);
2353 		return;
2354 	}
2355 
2356 	if (state <= 1)
2357 	{
2358 		ttyputverbose(M_("%s turned off"), tool->toolname);
2359 		if (state != 0) toolstate = TRUE; else
2360 			toolstate = FALSE;
2361 		toolturnoff(tool, toolstate);
2362 	} else
2363 	{
2364 		if ((tool->toolstate&TOOLINCREMENTAL) == 0)
2365 			ttyputverbose(M_("%s turned on"), tool->toolname); else
2366 				ttyputmsg(_("%s turned on, catching up on changes"), tool->toolname);
2367 		toolturnon(tool);
2368 	}
2369 }
2370