1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: usrgraph.c
6  * User interface tool: structure graphing module
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 "tecgen.h"
36 #include "tecart.h"
37 
38 /****************************** LIBRARY GRAPHING *****************************/
39 
40 #define NONODEDESCR ((NODEDESCR *)-1)
41 
42 typedef struct Inodedescr
43 {
44 	INTBIG             x, y;
45 	INTBIG             yoff;
46 	NODEINST          *pin;
47 	struct Inodedescr *main;
48 } NODEDESCR;
49 
50 static NODEPROTO *us_graphmainview(NODEPROTO *np);
51 
us_graphcells(NODEPROTO * top)52 void us_graphcells(NODEPROTO *top)
53 {
54 	REGISTER NODEPROTO *np, *sub, *graphnp, *truenp, *truesubnp;
55 	REGISTER NODEINST *ni, *nibot, *toppin;
56 	REGISTER ARCINST *ai;
57 	REGISTER LIBRARY *lib;
58 	REGISTER INTBIG more, maxdepth, color;
59 	REGISTER INTBIG *xval, *yoff, i, x, y, xe, ye, clock, maxwidth, xsc, lambda, xscale, yscale, yoffset;
60 	REGISTER PORTPROTO *pinpp;
61 	REGISTER VARIABLE *var;
62 	REGISTER NODEDESCR *nd, *ndsub;
63 	CHAR *newname;
64 	float spread;
65 	REGISTER void *infstr;
66 
67 	pinpp = gen_invispinprim->firstportproto;
68 
69 	/* create the graph cell */
70 	graphnp = newnodeproto(x_("CellStructure"), el_curlib);
71 	if (graphnp == NONODEPROTO) return;
72 	if (graphnp->prevversion != NONODEPROTO)
73 		ttyputverbose(M_("Creating new version of cell: CellStructure")); else
74 			ttyputverbose(M_("Creating cell: CellStructure"));
75 
76 	/* clear flags on all of the cells */
77 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
78 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
79 			np->temp1 = -1;
80 
81 	/* find all top-level cells */
82 	if (top != NONODEPROTO) top->temp1 = 0; else
83 	{
84 		for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
85 		{
86 			if (np->firstinst == NONODEINST) np->temp1 = 0;
87 		}
88 	}
89 
90 	/* now place all cells at their proper depth */
91 	maxdepth = 0;
92 	more = 1;
93 	while (more != 0)
94 	{
95 		more = 0;
96 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
97 			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
98 		{
99 			if (np->temp1 == -1) continue;
100 			for(ni = np->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
101 			{
102 				sub = ni->proto;
103 				if (sub->primindex != 0) continue;
104 
105 				/* ignore recursive references (showing icon in contents) */
106 				if (isiconof(sub, np)) continue;
107 				if (sub->temp1 <= np->temp1)
108 				{
109 					sub->temp1 = np->temp1 + 1;
110 					if (sub->temp1 > maxdepth) maxdepth = sub->temp1;
111 					more++;
112 				}
113 				truenp = contentsview(ni->proto);
114 				if (truenp == NONODEPROTO) continue;
115 				if (truenp->temp1 <= np->temp1)
116 				{
117 					truenp->temp1 = np->temp1 + 1;
118 					if (truenp->temp1 > maxdepth) maxdepth = truenp->temp1;
119 					more++;
120 				}
121 			}
122 		}
123 
124 		/* add in any cells referenced from other libraries */
125 		if (more == 0 && top == NONODEPROTO)
126 		{
127 			for(np = el_curlib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
128 			{
129 				if (np->temp1 >= 0) continue;
130 				np->temp1 = 0;
131 				more++;
132 			}
133 		}
134 	}
135 
136 	/* now assign X coordinates to each cell */
137 	maxdepth++;
138 	maxwidth = 0;
139 	xval = emalloc((SIZEOFINTBIG * maxdepth), el_tempcluster);
140 	if (xval == 0) return;
141 	yoff = emalloc((SIZEOFINTBIG * maxdepth), el_tempcluster);
142 	if (yoff == 0) return;
143 	for(i=0; i<maxdepth; i++) xval[i] = yoff[i] = 0;
144 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
145 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
146 	{
147 		/* ignore icon cells from the graph (merge with contents) */
148 		if (np->temp1 == -1) continue;
149 
150 		/* ignore associated cells for now */
151 		truenp = us_graphmainview(np);
152 		if (truenp != NONODEPROTO &&
153 			(np->firstnodeinst == NONODEINST || np->cellview == el_iconview ||
154 				np->cellview == el_skeletonview))
155 		{
156 			np->temp1 = -1;
157 			continue;
158 		}
159 
160 		nd = (NODEDESCR *)emalloc(sizeof (NODEDESCR), us_tool->cluster);
161 		nd->pin = NONODEINST;
162 		nd->main = NONODEDESCR;
163 
164 		nd->x = xval[np->temp1];
165 		xval[np->temp1] += estrlen(describenodeproto(np));
166 		if (xval[np->temp1] > maxwidth) maxwidth = xval[np->temp1];
167 		nd->y = np->temp1;
168 		nd->yoff = 0;
169 		np->temp1 = (INTBIG)nd;
170 	}
171 
172 	/* now center each row */
173 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
174 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
175 	{
176 		if (np->temp1 == -1) continue;
177 		nd = (NODEDESCR *)np->temp1;
178 		if (xval[nd->y] < maxwidth)
179 		{
180 			spread = (float)maxwidth / (float)xval[nd->y];
181 			nd->x = roundfloat(nd->x * spread);
182 		}
183 	}
184 
185 	/* generate accurate X/Y coordinates */
186 	lambda = el_curlib->lambda[art_tech->techindex];
187 	xscale = lambda * 2 / 3;
188 	yscale = lambda * 20;
189 	yoffset = lambda/2;
190 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
191 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
192 	{
193 		if (np->temp1 == -1) continue;
194 		nd = (NODEDESCR *)np->temp1;
195 		x = nd->x;   y = nd->y;
196 		x = x * xscale;
197 		y = -y * yscale + ((yoff[nd->y]++)%2) * yoffset;
198 		nd->x = x;   nd->y = y;
199 	}
200 
201 	/* make unattached cells sit with their contents view */
202 	if (top == NONODEPROTO)
203 	{
204 		for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
205 			for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
206 		{
207 			if (np->temp1 != -1) continue;
208 			if (np->firstnodeinst != NONODEINST && np->cellview != el_iconview &&
209 					np->cellview != el_skeletonview) continue;
210 			truenp = us_graphmainview(np);
211 			if (truenp == NONODEPROTO) continue;
212 			if (truenp->temp1 == -1) continue;
213 
214 			nd = (NODEDESCR *)truenp->temp1;
215 			ndsub = (NODEDESCR *)emalloc(sizeof (NODEDESCR), us_tool->cluster);
216 			ndsub->pin = NONODEINST;
217 			ndsub->main = nd;
218 			nd->yoff += yoffset*2;
219 			ndsub->x = nd->x;   ndsub->y = nd->y + nd->yoff;
220 			np->temp1 = (INTBIG)ndsub;
221 		}
222 	}
223 
224 	/* write the header message */
225 	xsc = maxwidth * xscale / 2;
226 	ni = newnodeinst(gen_invispinprim, xsc, xsc, yscale, yscale, 0, 0, graphnp);
227 	if (ni == NONODEINST) return;
228 	endobjectchange((INTBIG)ni, VNODEINST);
229 	infstr = initinfstr();
230 	if (top != NONODEPROTO)
231 	{
232 		formatinfstr(infstr, _("Structure below cell %s"), describenodeproto(top));
233 	} else
234 	{
235 		formatinfstr(infstr, _("Structure of library %s"), el_curlib->libname);
236 	}
237 	allocstring(&newname, returninfstr(infstr), el_tempcluster);
238 	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)newname, VSTRING|VDISPLAY);
239 	efree(newname);
240 	if (var != NOVARIABLE)
241 		TDSETSIZE(var->textdescript, TXTSETQLAMBDA(24));
242 
243 	/* place the components */
244 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
245 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
246 	{
247 		if (np == graphnp) continue;
248 		if (np->temp1 == -1) continue;
249 		nd = (NODEDESCR *)np->temp1;
250 
251 		x = nd->x;   y = nd->y;
252 		ni = newnodeinst(gen_invispinprim, x, x, y, y, 0, 0, graphnp);
253 		if (ni == NONODEINST) return;
254 		endobjectchange((INTBIG)ni, VNODEINST);
255 		nd->pin = ni;
256 
257 		/* write the cell name in the node */
258 		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)describenodeproto(np),
259 			VSTRING|VDISPLAY);
260 		if (var != NOVARIABLE)
261 			TDSETSIZE(var->textdescript, TXTSETQLAMBDA(4));
262 	}
263 
264 	/* attach related components with rigid arcs */
265 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
266 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
267 	{
268 		if (np == graphnp) continue;
269 		if (np->temp1 == -1) continue;
270 		nd = (NODEDESCR *)np->temp1;
271 		if (nd->main == NONODEDESCR) continue;
272 
273 		ai = newarcinst(art_solidarc, 0, FIXED, nd->pin, pinpp, nd->x, nd->y,
274 			nd->main->pin, pinpp, nd->main->x, nd->main->y, graphnp);
275 		if (ai == NOARCINST) return;
276 		endobjectchange((INTBIG)ai, VARCINST);
277 
278 		/* set an invisible color on the arc */
279 		(void)setvalkey((INTBIG)ai, VARCINST, art_colorkey, 0, VINTEGER);
280 	}
281 
282 	/* build wires between the hierarchical levels */
283 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
284 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
285 			np->temp2 = 0;
286 	clock = 0;
287 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
288 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
289 	{
290 		if (np == graphnp) continue;
291 
292 		/* always use the contents cell, not the icon */
293 		truenp = contentsview(np);
294 		if (truenp == NONODEPROTO) truenp = np;
295 		if (truenp->temp1 == -1) continue;
296 
297 		nd = (NODEDESCR *)truenp->temp1;
298 		toppin = NONODEINST;
299 		clock++;
300 		for(ni = truenp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
301 		{
302 			sub = ni->proto;
303 			if (sub->primindex != 0) continue;
304 
305 			/* ignore recursive references (showing icon in contents) */
306 			if (isiconof(sub, truenp)) continue;
307 
308 			truesubnp = contentsview(sub);
309 			if (truesubnp == NONODEPROTO) truesubnp = sub;
310 
311 			if (truesubnp->temp2 == clock) continue;
312 			truesubnp->temp2 = clock;
313 
314 			/* draw a line from cell "truenp" to cell "truesubnp" */
315 			x = nd->x;
316 			y = nd->y;
317 
318 			if (truesubnp->temp1 == -1) continue;
319 			ndsub = (NODEDESCR *)truesubnp->temp1;
320 			xe = ndsub->x;
321 			ye = ndsub->y;
322 			toppin = nd->pin;
323 			nibot = ndsub->pin;
324 			ai = newarcinst(art_solidarc, defaultarcwidth(art_solidarc), 0, toppin,
325 				pinpp, x, y, nibot, pinpp, xe, ye, graphnp);
326 			if (ai == NOARCINST) return;
327 			endobjectchange((INTBIG)ai, VARCINST);
328 
329 			/* set an appropriate color on the arc (red for jumps of more than 1 level of depth) */
330 			color = BLUE;
331 			if (nd->y - ndsub->y > yscale+yoffset+yoffset) color = RED;
332 			(void)setvalkey((INTBIG)ai, VARCINST, art_colorkey, color, VINTEGER);
333 		}
334 	}
335 
336 	/* free space */
337 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
338 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
339 	{
340 		if (np->temp1 == -1) continue;
341 		efree((CHAR *)np->temp1);
342 	}
343 	efree((CHAR *)xval);
344 	efree((CHAR *)yoff);
345 }
346 
347 /*
348  * routine to find the main cell that "np" is associated with in the graph.  This code is
349  * essentially the same as "contentscell()" except that any original type is allowed.
350  * Returns NONODEPROTO if the cell is not associated.
351  */
us_graphmainview(NODEPROTO * np)352 NODEPROTO *us_graphmainview(NODEPROTO *np)
353 {
354 	REGISTER NODEPROTO *rnp;
355 
356 	/* primitives have no contents view */
357 	if (np == NONODEPROTO) return(NONODEPROTO);
358 	if (np->primindex != 0) return(NONODEPROTO);
359 
360 	/* first check to see if there is a schematics link */
361 	FOR_CELLGROUP(rnp, np)
362 	{
363 		if (rnp->cellview == el_schematicview) return(rnp);
364 		if ((rnp->cellview->viewstate&MULTIPAGEVIEW) != 0) return(rnp);
365 	}
366 
367 	/* now check to see if there is any layout link */
368 	FOR_CELLGROUP(rnp, np)
369 		if (rnp->cellview == el_layoutview) return(rnp);
370 
371 	/* finally check to see if there is any "unknown" link */
372 	FOR_CELLGROUP(rnp, np)
373 		if (rnp->cellview == el_unknownview) return(rnp);
374 
375 	/* no contents found */
376 	return(NONODEPROTO);
377 }
378 
379 /****************************** COMMAND GRAPHING ******************************/
380 
381 #define MAXILLUSTRATEDEPTH  100		/* maximum depth of command graph */
382 #define FORCEDDEPTH           7		/* required depth of command graph */
383 #define MAXILLUSTRATEWIDTH  200		/* maximum depth of command graph */
384 #define XCOMSCALE          1000		/* horizontal distance between words */
385 #define YCOMSCALE       (-10000)	/* vertical distance between words */
386 #define YCOMOFFSET         1000		/* vertical offset between words */
387 
388 #define NOCOMILL ((COMILL *)-1)
389 
390 typedef struct Icomill
391 {
392 	CHAR *name;
393 	INTBIG  x, y;
394 	INTBIG  realx;
395 	INTBIG  children;
396 	INTBIG  depth;
397 	struct Icomill *parent;
398 	struct Icomill *nextcomill;
399 	NODEINST *real;
400 } COMILL;
401 
402 static COMILL *us_comilllist[MAXILLUSTRATEWIDTH];
403 static COMILL *us_allcomill;
404 
405 static INTBIG us_maxillustratedepth, us_maxcommandentries;
406 static INTBIG us_illustrateXpos[MAXILLUSTRATEDEPTH];		/* current build-out X position */
407 static COMCOMP *us_illustrateparam[MAXILLUSTRATEDEPTH];	/* current parameter type */
408 
409 /* prototypes for local routines */
410 static COMILL *us_illustratecommand(CHAR*, INTBIG, COMILL*);
411 
us_illustratecommandset(void)412 void us_illustratecommandset(void)
413 {
414 	REGISTER INTBIG i;
415 	REGISTER INTBIG j;
416 	CHAR *newmessage[1];
417 	REGISTER NODEPROTO *graphnp;
418 	REGISTER NODEINST *ni;
419 	REGISTER COMILL *ci, *nextci;
420 	INTBIG xs, ys, xe, ye;
421 	REGISTER ARCINST *ai;
422 	REGISTER VARIABLE *var;
423 
424 	/* create the graph cell */
425 	graphnp = newnodeproto(x_("CommandStructure"), el_curlib);
426 	if (graphnp == NONODEPROTO) return;
427 	if (graphnp->prevversion != NONODEPROTO)
428 		ttyputmsg(_("Creating new version of cell: CommandStructure")); else
429 			ttyputmsg(_("Creating cell: CommandStructure"));
430 
431 	us_maxillustratedepth = 0;
432 	us_maxcommandentries = 0;
433 	for(i = 0; i < MAXILLUSTRATEDEPTH; i++) us_illustrateXpos[i] = 0;
434 	for(i = 0; i < MAXILLUSTRATEWIDTH; i++) us_comilllist[i] = NOCOMILL;
435 	us_allcomill = NOCOMILL;
436 
437 	/* build the command graph */
438 	for(i = 0; us_lcommand[i].name != 0; i++)
439 	{
440 		for(j=0; j<us_lcommand[i].params; j++)
441 			us_illustrateparam[j] = us_lcommand[i].par[j];
442 		us_illustrateparam[us_lcommand[i].params] = NOCOMCOMP;
443 		us_comilllist[i] = us_illustratecommand(us_lcommand[i].name, 0, NOCOMILL);
444 	}
445 
446 	ttyputmsg(_("%ld entries in command graph"), us_maxcommandentries);
447 
448 	/* count the breadth information */
449 	j = 0;
450 	for(i = 0; i < us_maxillustratedepth; i++)
451 		if (us_illustrateXpos[i] > j) j = us_illustrateXpos[i];
452 
453 	/* write the header message */
454 	ni = newnodeinst(gen_invispinprim, 0, j, -YCOMSCALE,
455 		gen_invispinprim->highx-gen_invispinprim->lowx-YCOMSCALE, 0, 0, graphnp);
456 	if (ni == NONODEINST) return;
457 	endobjectchange((INTBIG)ni, VNODEINST);
458 	var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)_("Structure of commands"),
459 		VSTRING|VDISPLAY);
460 	if (var != NOVARIABLE)
461 		TDSETSIZE(var->textdescript, TXTSETPOINTS(20));
462 
463 	/* make the name spacing uniform */
464 	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
465 		ci->realx = ci->children = 0;
466 	for(i = us_maxillustratedepth-1; i > 0; i--)
467 	{
468 		for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
469 		{
470 			if (ci->depth != i) continue;
471 			ci->parent->realx += ci->x;
472 			ci->parent->children++;
473 		}
474 		for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
475 			if (ci->depth == i-1 && ci->children != 0)
476 				ci->x = ci->realx / ci->children;
477 	}
478 
479 	/* now place the names */
480 	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
481 	{
482 		if (ci->name == 0) continue;
483 		ni = newnodeinst(gen_invispinprim, ci->x,
484 			ci->x+gen_invispinprim->highx-gen_invispinprim->lowx, ci->y,
485 				ci->y+gen_invispinprim->highy-gen_invispinprim->lowy, 0, 0, graphnp);
486 		if (ni == NONODEINST) return;
487 		endobjectchange((INTBIG)ni, VNODEINST);
488 		ci->real = ni;
489 
490 		/* set the node name, color, font */
491 		var = setvalkey((INTBIG)ni, VNODEINST, art_messagekey, (INTBIG)ci->name, VSTRING|VDISPLAY);
492 		if (var != NOVARIABLE)
493 			defaulttextsize(2, var->textdescript);
494 		(void)setvalkey((INTBIG)ni, VNODEINST, art_colorkey, RED, VINTEGER);
495 	}
496 
497 	/* connect the names with arcs */
498 	for(ci = us_allcomill; ci != NOCOMILL; ci = ci->nextcomill)
499 	{
500 		if (ci->name == 0) continue;
501 		if (ci->parent == NOCOMILL) continue;
502 		portposition(ci->real, ci->real->proto->firstportproto, &xs, &ys);
503 		portposition(ci->parent->real, ci->parent->real->proto->firstportproto, &xe, &ye);
504 		ai = newarcinst(gen_universalarc, defaultarcwidth(gen_universalarc), 0, ci->real,
505 			ci->real->proto->firstportproto, xs, ys, ci->parent->real,
506 				ci->parent->real->proto->firstportproto, xe, ye, graphnp);
507 		if (ai == NOARCINST) break;
508 		endobjectchange((INTBIG)ai, VARCINST);
509 	}
510 
511 	/* delete it all */
512 	for(ci = us_allcomill; ci != NOCOMILL; ci = nextci)
513 	{
514 		nextci = ci->nextcomill;
515 		efree((CHAR *)ci);
516 	}
517 
518 	/* have the cell displayed on the screen */
519 	newmessage[0] = x_("CommandStructure");
520 	us_editcell(1, newmessage);
521 }
522 
us_illustratecommand(CHAR * name,INTBIG depth,COMILL * thisci)523 COMILL *us_illustratecommand(CHAR *name, INTBIG depth, COMILL *thisci)
524 {
525 	static COMCOMP us_recursioncomcomp = {
526 		NOKEYWORD, NOTOPLIST, NONEXTLIST, NOPARAMS, 0, x_(""), x_("")};
527 	REGISTER COMCOMP *cc;
528 	REGISTER COMILL *ci, *subci;
529 	REGISTER INTBIG i, j, k;
530 	REGISTER CHAR *arg;
531 
532 	/* put this name at the appropriate depth */
533 	if (depth >= MAXILLUSTRATEDEPTH) return(NOCOMILL);
534 	if (depth > us_maxillustratedepth) us_maxillustratedepth = depth;
535 
536 	ci = (COMILL *)emalloc(sizeof (COMILL), el_tempcluster);
537 	if (ci == 0) return(NOCOMILL);
538 	ci->name = name;
539 	ci->depth = depth;
540 	ci->parent = thisci;
541 	ci->nextcomill = us_allcomill;
542 	us_allcomill = ci;
543 	ci->x = us_illustrateXpos[depth];
544 	ci->y = ci->depth * YCOMSCALE + ((us_illustrateXpos[depth]/XCOMSCALE)%5-2) * YCOMOFFSET;
545 	us_illustrateXpos[depth] += XCOMSCALE;
546 	if (name != 0) us_maxcommandentries++;
547 
548 	/* if there is nothing below this command, return now */
549 	if (name == 0 || us_illustrateparam[depth] == NOCOMCOMP)
550 	{
551 		/* force extension of the tree to a specified depth */
552 		if (depth < FORCEDDEPTH)
553 			(void)us_illustratecommand((CHAR *)0, depth+1, ci);
554 		return(ci);
555 	}
556 
557 	cc = us_illustrateparam[depth];
558 	if (cc->ifmatch == NOKEYWORD)
559 	{
560 		if (cc->toplist == topoffile) arg = x_("FILE"); else
561 		if (cc->toplist == topoflibfile) arg = x_("LIBRARY"); else
562 		if (cc->toplist == topoftechs) arg = x_("TECH"); else
563 		if (cc->toplist == topoflibs) arg = x_("LIB"); else
564 		if (cc->toplist == topoftools) arg = x_("TOOL"); else
565 		if (cc->toplist == topofviews) arg = x_("VIEW"); else
566 		if (cc->toplist == topofnets) arg = x_("NET"); else
567 		if (cc->toplist == topofarcs) arg = x_("ARC"); else
568 		if (cc->toplist == topofcells) arg = x_("CELL"); else
569 		if (cc->toplist == us_topofcommands) arg = x_("COM"); else
570 		if (cc->toplist == us_topofmacros) arg = x_("MACRO"); else
571 		if (cc->toplist == us_topofpopupmenu) arg = x_("POPUP"); else
572 		if (cc->toplist == us_topofports) arg = x_("PORT"); else
573 		if (cc->toplist == us_topofcports) arg = x_("CELLPORT"); else
574 		if (cc->toplist == us_topofexpports) arg = x_("EXPORT"); else
575 		if (cc->toplist == us_topofwindows) arg = x_("WINDOWPART"); else
576 		if (cc->toplist == us_topoflayers) arg = x_("LAYER"); else
577 		if (cc->toplist == us_topofhighlight) arg = x_("HIGH"); else
578 		if (cc->toplist == us_topofarcnodes) arg = x_("ARC/NODE"); else
579 		if (cc->toplist == us_topofnodes) arg = x_("NODE"); else
580 		if (cc->toplist == us_topofcells) arg = x_("CELL"); else
581 		if (cc->toplist == us_topofprims) arg = x_("PRIM"); else
582 		if (cc->toplist == us_topofconstraints) arg = x_("CONSTR"); else
583 		if (cc->toplist == us_topofmbuttons) arg = x_("BUTTON"); else
584 		if (cc->toplist == us_topofedteclay) arg = x_("LAYER"); else
585 		if (cc->toplist == us_topofedtecarc) arg = x_("ARC"); else
586 		if (cc->toplist == us_topofedtecnode) arg = x_("NODE"); else
587 		if (cc->toplist == us_topofallthings) arg = x_("ANY"); else
588 		if (cc->toplist == us_topofvars) arg = x_("VAR"); else
589 		if (cc == &us_recursioncomcomp) arg = x_("***"); else arg = x_("ARG");
590 		subci = us_illustratecommand(arg, depth+1, ci);
591 		if (subci == NOCOMILL) return(NOCOMILL);
592 		return(ci);
593 	}
594 
595 	for(i=0; cc->ifmatch[i].name != 0; i++)
596 	{
597 		/* spread open the list and insert these options */
598 		k = cc->ifmatch[i].params;
599 		for(j = MAXILLUSTRATEDEPTH-k-2; j >= depth; j--)
600 			us_illustrateparam[j+k+1] = us_illustrateparam[j+1];
601 		for(j = 0; j < k; j++)
602 		{
603 			us_illustrateparam[depth+j+1] = cc->ifmatch[i].par[j];
604 			if (us_illustrateparam[depth+j+1] != us_illustrateparam[depth]) continue;
605 			us_illustrateparam[depth+j+1] = &us_recursioncomcomp;
606 		}
607 
608 		subci = us_illustratecommand(cc->ifmatch[i].name, depth+1, ci);
609 		if (subci == NOCOMILL) return(NOCOMILL);
610 
611 		/* remove the inserted options */
612 		for(j=depth+1; j<MAXILLUSTRATEDEPTH-k; j++)
613 			us_illustrateparam[j] = us_illustrateparam[j+k];
614 	}
615 	return(ci);
616 }
617 
618 /****************************** PULLDOWN MENU DUMPING ******************************/
619 
620 /* prototypes for local routines */
621 static void us_dumppulldownmenu(FILE *io, POPUPMENU *pm, CHAR *name, CHAR *prefix);
622 
623 /*
624  * Routine to dump the pulldown menus to an indented text file.
625  */
us_dumppulldownmenus(void)626 void us_dumppulldownmenus(void)
627 {
628 	FILE *io;
629 	CHAR *truename;
630 	REGISTER INTBIG i;
631 	REGISTER POPUPMENU *pm;
632 
633 	io = xcreate(x_("pulldowns.txt"), el_filetypetext, M_("Menu dump file"), &truename);
634 	if (io == NULL) return;
635 
636 	xprintf(io, M_("Pulldown menus in Electric as of %s\n"),
637 		timetostring(getcurrenttime()));
638 	for(i=0; i<us_pulldownmenucount; i++)
639 	{
640 		pm = us_pulldowns[i];
641 		xprintf(io, x_("\n"));
642 		us_dumppulldownmenu(io, pm, pm->header, x_(""));
643 	}
644 
645 	xclose(io);
646 	ttyputmsg(M_("Pulldown menus dumped to %s"), truename);
647 }
648 
us_dumppulldownmenu(FILE * io,POPUPMENU * pm,CHAR * name,CHAR * prefix)649 void us_dumppulldownmenu(FILE *io, POPUPMENU *pm, CHAR *name, CHAR *prefix)
650 {
651 	REGISTER POPUPMENUITEM *mi;
652 	REGISTER USERCOM *uc;
653 	REGISTER INTBIG i, j, k;
654 	CHAR comname[300], subprefix[50];
655 
656 	for(k=j=0; name[k] != 0; k++)
657 		if (name[k] != '&') comname[j++] = name[k];
658 	comname[j] = 0;
659 
660 	xprintf(io, x_("%s%s:\n"), prefix, comname);
661 	for(i=0; i<pm->total; i++)
662 	{
663 		mi = &pm->list[i];
664 		uc = mi->response;
665 		if (uc->active < 0)
666 		{
667 			xprintf(io, x_("%s    ----------\n"), prefix);
668 			continue;
669 		}
670 
671 		for(k=j=0; mi->attribute[k] != 0; k++)
672 		{
673 			if (mi->attribute[k] == '&') continue;
674 			if (mi->attribute[k] == '/') break;
675 			comname[j++] = mi->attribute[k];
676 		}
677 		comname[j] = 0;
678 
679 		if (uc->menu != NOPOPUPMENU)
680 		{
681 			estrcpy(subprefix, prefix);
682 			estrcat(subprefix, x_("    "));
683 			us_dumppulldownmenu(io, uc->menu, comname, subprefix);
684 			continue;
685 		}
686 		xprintf(io, x_("%s    %s\n"), prefix, comname);
687 	}
688 }
689