1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dbnoproto.c
6  * Database cell manipulation 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 "database.h"
34 #include "egraphics.h"
35 #include "network.h"
36 #include "usr.h"
37 
38 #define PRINTFRAMEQLAMBDA 60		/* size of text in frames (in quarter lambda units) */
39 
40 /* hierarchical traversal data structures */
41 typedef struct
42 {
43 	NODEINST **path;
44 	INTBIG    *index;
45 	INTBIG     depth;
46 	INTBIG     limit;
47 } HTRAVERSAL;
48 
49 static HTRAVERSAL db_hiertraverse = {0, 0, 0, 0};
50 
51 static BOOLEAN    db_hiertraversing = FALSE;
52 static INTBIG     db_hiertempclimb = 0;
53        UINTBIG    db_traversaltimestamp = 0;
54 static INTBIG     db_descent_path_key = 0;	/* variable key for "USER_descent_path" */
55 
56 /* prototypes for local routines */
57 static CHAR   *db_getframedesc(NODEPROTO*);
58 static INTBIG  db_copyxlibvars(INTBIG, INTBIG, INTBIG, INTBIG, NODEPROTO*, LIBRARY*);
59 static BOOLEAN db_portassociate(NODEINST*, NODEINST*, BOOLEAN);
60 static BOOLEAN db_doesntconnect(ARCPROTO*[], PORTPROTO*);
61 static void    db_changeallports(PORTPROTO*);
62 static BOOLEAN db_addtohierpath(NODEINST *ni, INTBIG index, BOOLEAN bottom);
63 static void    db_copyhierarchicaltraversal(HTRAVERSAL *src, HTRAVERSAL *dst);
64        void    db_correctcellgroups(NODEPROTO *cell);
65 
66 /*
67  * Routine to free all memory associated with this module.
68  */
db_freenoprotomemory(void)69 void db_freenoprotomemory(void)
70 {
71 	if (db_hiertraverse.limit > 0)
72 	{
73 		efree((CHAR *)db_hiertraverse.path);
74 		efree((CHAR *)db_hiertraverse.index);
75 	}
76 }
77 
78 /************************* NODEPROTOS *************************/
79 
80 /*
81  * routine to allocate a nodeproto from memory cluster "cluster" and return
82  * its address.  The routine returns NONODEPROTO if allocation fails.
83  */
allocnodeproto(CLUSTER * cluster)84 NODEPROTO *allocnodeproto(CLUSTER *cluster)
85 {
86 	REGISTER NODEPROTO *np;
87 
88 	if (cluster == NOCLUSTER)
89 		return((NODEPROTO *)db_error(DBNOMEM|DBALLOCNODEPROTO));
90 	np = (NODEPROTO *)emalloc((sizeof (NODEPROTO)), cluster);
91 	if (np == 0) return((NODEPROTO *)db_error(DBNOMEM|DBALLOCNODEPROTO));
92 	np->protoname = x_("ERROR!");
93 	np->changeaddr = (CHAR *)NOCHANGE;
94 	np->userbits = np->temp1 = np->temp2 = 0;
95 	np->numvar = 0;
96 	np->firstvar = NOVARIABLE;
97 	np->firstinst = NONODEINST;
98 	np->firstnodeinst = NONODEINST;
99 	np->firstarcinst = NOARCINST;
100 	np->firstportproto = NOPORTPROTO;
101 	np->cachedequivcell = NONODEPROTO;
102 	np->nextcellgrp = np;
103 	np->nextcont = NONODEPROTO;
104 	np->numportprotos = 0;
105 	np->portprotohashtablesize = 0;
106 	np->tech = NOTECHNOLOGY;
107 	np->lib = NOLIBRARY;
108 	np->nextnodeproto = NONODEPROTO;
109 	np->cellview = NOVIEW;
110 	np->version = 1;
111 	np->prevversion = NONODEPROTO;
112 	np->newestversion = NONODEPROTO;
113 	np->creationdate = 0;
114 	np->revisiondate = 0;
115 	np->firstnetwork = NONETWORK;
116 	np->netd = 0;
117 	np->globalnetcount = 0;
118 	np->rtree = NORTNODE;
119 	np->primindex = 0;
120 	np->lowx = np->highx = np->lowy = np->highy = 0;
121 	np->prevnodeproto = NONODEPROTO;
122 	np->adirty = 0;
123 	return(np);
124 }
125 
126 /*
127  * routine to return nodeproto "np" to the pool of free nodes
128  */
freenodeproto(NODEPROTO * np)129 void freenodeproto(NODEPROTO *np)
130 {
131 	REGISTER INTBIG i;
132 
133 	if (np == NONODEPROTO) return;
134 	if (np->globalnetcount > 0)
135 	{
136 		for(i=0; i<np->globalnetcount; i++)
137 			efree((CHAR *)np->globalnetnames[i]);
138 		efree((CHAR *)np->globalnetchar);
139 		efree((CHAR *)np->globalnetworks);
140 		efree((CHAR *)np->globalnetnames);
141 	}
142 	if (np->portprotohashtablesize > 0) efree((CHAR *)np->portprotohashtable);
143 	if (np->numvar != 0) db_freevars(&np->firstvar, &np->numvar);
144 	net_freenetprivate(np);
145 	efree((CHAR *)np);
146 }
147 
148 /*
149  * routine to create a new cell named "fname", in library
150  * "lib".  The address of the cell is returned (NONODEPROTO if an error occurs).
151  */
newnodeproto(CHAR * fname,LIBRARY * lib)152 NODEPROTO *newnodeproto(CHAR *fname, LIBRARY *lib)
153 {
154 	REGISTER NODEPROTO *np, *onp;
155 	REGISTER CHAR *ptr, *opt, *cpt, *nameend;
156 	REGISTER CHAR save;
157 	REGISTER INTBIG explicitversion;
158 	REGISTER VIEW *view, *v;
159 	REGISTER void *infstr;
160 
161 	/* copy the name to a temporary location so that it can be modified */
162 	infstr = initinfstr();
163 	addstringtoinfstr(infstr, fname);
164 	fname = returninfstr(infstr);
165 
166 	/* search for view specification */
167 	view = el_unknownview;
168 	nameend = 0;
169 	for(ptr = fname; *ptr != 0; ptr++) if (*ptr == '{') break;
170 	if (*ptr == '{')
171 	{
172 		nameend = ptr;
173 		for(opt = ptr+1; *opt != 0; opt++) if (*opt == '}') break;
174 		if (*opt == 0 || opt[1] != 0)
175 		{
176 			db_donextchangequietly = FALSE;
177 			return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
178 		}
179 		*opt = 0;
180 		for(v = el_views; v != NOVIEW; v = v->nextview)
181 			if (namesame(&ptr[1], v->sviewname) == 0) break;
182 		if (v == NOVIEW) for(v = el_views; v != NOVIEW; v = v->nextview)
183 			if (namesame(&ptr[1], v->viewname) == 0) break;
184 
185 		/* special case of schematic pages: create a view */
186 		if (v == NOVIEW && (ptr[1] == 'p' || ptr[1] == 'P') && isanumber(&ptr[2]))
187 		{
188 			/* create a new schematic page view */
189 			cpt = (CHAR *)emalloc((16+estrlen(&ptr[2])) * SIZEOFCHAR, el_tempcluster);
190 			if (cpt == 0)
191 			{
192 				db_donextchangequietly = FALSE;
193 				return(NONODEPROTO);
194 			}
195 			(void)estrcpy(cpt, x_("schematic-page-"));
196 			(void)estrcat(cpt, &ptr[2]);
197 			v = newview(cpt, &ptr[1]);
198 			efree(cpt);
199 		}
200 
201 		*opt = '}';
202 		if (v == NOVIEW)
203 		{
204 			db_donextchangequietly = FALSE;
205 			return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
206 		}
207 		view = v;
208 	}
209 
210 	/* search for version specification */
211 	explicitversion = 0;
212 	for(ptr = fname; *ptr != 0; ptr++) if (*ptr == ';') break;
213 	if (*ptr == ';')
214 	{
215 		if (nameend == 0 || ptr < nameend) nameend = ptr;
216 		explicitversion = eatoi(&ptr[1]);
217 		if (explicitversion <= 0)
218 		{
219 			db_donextchangequietly = FALSE;
220 			return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
221 		}
222 	}
223 	if (nameend == 0) nameend = ptr;
224 
225 	/* scan main cell name for illegal characters */
226 	if (*fname == 0)
227 	{
228 		db_donextchangequietly = FALSE;
229 		return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
230 	}
231 	for(opt = fname; opt != ptr; opt++)
232 		if (*opt <= ' ' || *opt == ':' || *opt >= 0177)
233 	{
234 		db_donextchangequietly = FALSE;
235 		return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
236 	}
237 
238 	/* create the cell */
239 	np = allocnodeproto(lib->cluster);
240 	if (np == NONODEPROTO)
241 	{
242 		db_donextchangequietly = FALSE;
243 		return(NONODEPROTO);
244 	}
245 
246 	/* initialize */
247 	save = *nameend;
248 	*nameend = 0;
249 	(void)allocstring(&np->protoname, fname, lib->cluster);
250 	*nameend = save;
251 	np->primindex = 0;
252 	np->lowx = np->highx = 0;
253 	np->lowy = np->highy = 0;
254 	np->firstinst = NONODEINST;
255 	np->firstnodeinst = NONODEINST;
256 	np->firstarcinst = NOARCINST;
257 	np->tech = NOTECHNOLOGY;
258 	np->lib = lib;
259 	np->firstportproto = NOPORTPROTO;
260 	np->adirty = 0;
261 	np->cellview = view;
262 	np->tech = whattech(np);
263 	np->creationdate = (UINTBIG)getcurrenttime();
264 	np->revisiondate = (UINTBIG)getcurrenttime();
265 	net_initnetprivate(np);
266 
267 	/* determine version number of this cell */
268 	if (explicitversion > 0)
269 	{
270 		np->version = explicitversion;
271 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
272 			if (onp->cellview == view && onp->version == explicitversion &&
273 				namesame(onp->protoname, np->protoname) == 0)
274 		{
275 			db_donextchangequietly = FALSE;
276 			return((NODEPROTO *)db_error(DBBADNAME|DBNEWNODEPROTO));
277 		}
278 	} else
279 	{
280 		np->version = 1;
281 		for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
282 			if (onp->cellview == view && namesame(onp->protoname, np->protoname) == 0 &&
283 				onp->version >= np->version) np->version = onp->version + 1;
284 	}
285 
286 	/* figure out which cellgroup to add this to */
287 	for(onp = lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
288 		if (namesame(onp->protoname, np->protoname) == 0 && onp->newestversion == onp) break;
289 	if (onp != NONODEPROTO) np->nextcellgrp = onp;
290 
291 	/* insert in the library and cell structures */
292 	db_insertnodeproto(np);
293 
294 	/* create initial R-tree data */
295 	if (geomstructure(np))
296 	{
297 		db_donextchangequietly = FALSE;
298 		return(NONODEPROTO);
299 	}
300 
301 	/* handle change control, constraint, and broadcast */
302 	if (!db_donextchangequietly && !db_dochangesquietly)
303 	{
304 		/* report the new cell */
305 		np->changeaddr = (CHAR *)db_change((INTBIG)np, NODEPROTONEW, 0, 0, 0, 0, 0, 0);
306 
307 		/* tell constraint system about new cell */
308 		(*el_curconstraint->newobject)((INTBIG)np, VNODEPROTO);
309 
310 		db_setchangecell(np);
311 	}
312 	db_donextchangequietly = FALSE;
313 
314 	/* tell which cell it is */
315 	return(np);
316 }
317 
318 /*
319  * routine to create a new primitive nodeproto with name "fname", default
320  * size "sx" by "sy", and primitive index "pi".  The nodeproto is placed in
321  * technology "tech".  The address of the primitive nodeproto is returned.
322  * Upon error, the value NONODEPROTO is returned.
323  */
db_newprimnodeproto(CHAR * fname,INTBIG sx,INTBIG sy,INTBIG pi,TECHNOLOGY * tech)324 NODEPROTO *db_newprimnodeproto(CHAR *fname, INTBIG sx, INTBIG sy, INTBIG pi,
325 	TECHNOLOGY *tech)
326 {
327 	REGISTER NODEPROTO *np, *ent;
328 	REGISTER CHAR *pp;
329 
330 	/* check for errors */
331 	for(np = tech->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
332 	{
333 		if (np->primindex == pi)
334 		{
335 			ttyputmsg(_("Index %ld of technology %s in use"), pi, tech->techname);
336 			return(NONODEPROTO);
337 		}
338 		if (namesame(fname, np->protoname) == 0)
339 		{
340 			ttyputmsg(_("Error: Two technologies with same name: %s and %s"), fname, tech->techname);
341 			return(NONODEPROTO);
342 		}
343 	}
344 	if (pi <= 0)
345 	{
346 		ttyputmsg(_("Error: Component index must be positive but %s:%s has %ld"),
347 			tech->techname, fname, pi);
348 		return(NONODEPROTO);
349 	}
350 	if (sx < 0 || sy < 0)
351 	{
352 		ttyputmsg(_("Error: Component size must be positive, but %s:%s is %ldx%ld"),
353 			tech->techname, fname, sx, sy);
354 		return(NONODEPROTO);
355 	}
356 	for(pp = fname; *pp != 0; pp++) if (*pp <= ' ' || *pp == ':' || *pp >= 0177)
357 	{
358 		ttyputmsg(_("Error: Primitive name must not contain ':' and others but '%s:%s' does"),
359 			tech->techname, fname);
360 		return(NONODEPROTO);
361 	}
362 
363 	/* create the nodeproto */
364 	np = allocnodeproto(tech->cluster);
365 	if (np == NONODEPROTO)
366 	{
367 		ttyputnomemory();
368 		return(NONODEPROTO);
369 	}
370 
371 	/* insert at the end of the list of primitives */
372 	if (tech->firstnodeproto == NONODEPROTO)
373 	{
374 		tech->firstnodeproto = np;   ent = NONODEPROTO;
375 	} else
376 	{
377 		ent = tech->firstnodeproto;
378 		while (ent->nextnodeproto != NONODEPROTO) ent = ent->nextnodeproto;
379 		ent->nextnodeproto = np;
380 	}
381 	np->prevnodeproto = ent;
382 	np->nextnodeproto = NONODEPROTO;
383 
384 	/* initialize name and size */
385 	if (allocstring(&np->protoname, fname, tech->cluster))
386 	{
387 		ttyputnomemory();
388 		return(NONODEPROTO);
389 	}
390 	np->primindex = pi;
391 	np->lowx = -sx/2;   np->highx = np->lowx + sx;
392 	np->lowy = -sy/2;   np->highy = np->lowy + sy;
393 	np->firstinst = NONODEINST;
394 	np->firstnodeinst = NONODEINST;
395 	np->firstarcinst = NOARCINST;
396 	np->tech = tech;
397 	np->firstportproto = NOPORTPROTO;
398 	np->adirty = 0;
399 
400 	/* tell which nodeproto it is */
401 	return(np);
402 }
403 
404 /*
405  * routine to kill nodeproto "np".  Returns true on error
406  */
killnodeproto(NODEPROTO * np)407 BOOLEAN killnodeproto(NODEPROTO *np)
408 {
409 	REGISTER NODEPROTO *lnt;
410 	REGISTER PORTPROTO *pp, *npt;
411 
412 	/* cannot delete primitive nodeprotos */
413 	if (np == NONODEPROTO)
414 	{
415 		db_donextchangequietly = FALSE;
416 		return(db_error(DBBADPROTO|DBKILLNODEPROTO)!=0);
417 	}
418 	if (np->primindex != 0)
419 	{
420 		db_donextchangequietly = FALSE;
421 		return(db_error(DBPRIMITIVE|DBKILLNODEPROTO)!=0);
422 	}
423 
424 	/* search the library for the nodeproto */
425 	for(lnt = np->lib->firstnodeproto; lnt != NONODEPROTO; lnt = lnt->nextnodeproto)
426 		if (np == lnt) break;
427 	if (lnt == NONODEPROTO)
428 	{
429 		db_donextchangequietly = FALSE;
430 		return(db_error(DBBADCELL|DBKILLNODEPROTO)!=0);
431 	}
432 
433 	/* cannot delete nodeproto with any instances */
434 	if (np->firstinst != NONODEINST)
435 	{
436 		db_donextchangequietly = FALSE;
437 		return(db_error(DBHASINSTANCES|DBKILLNODEPROTO)!=0);
438 	}
439 
440 	/* delete everything in the cell */
441 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = npt)
442 	{
443 		npt = pp->nextportproto;
444 		if (killportproto(np, pp)) ttyputerr(_("killportproto error"));
445 	}
446 	while (np->firstarcinst != NOARCINST) db_killarcinst(np->firstarcinst);
447 	while (np->firstnodeinst != NONODEINST) db_killnodeinst(np->firstnodeinst);
448 
449 	/* remove nodeproto from library */
450 	db_retractnodeproto(np);
451 
452 	/* handle change control, constraint, and broadcast */
453 	if (!db_donextchangequietly && !db_dochangesquietly)
454 	{
455 		/* remove any "changecell" modules for this cell */
456 		db_removechangecell(np);
457 
458 		/* record the change to the cell for broadcasting */
459 		np->changeaddr = (CHAR *)db_change((INTBIG)np, NODEPROTOKILL, 0, 0, 0, 0, 0, 0);
460 
461 		/* tell constraint system about killed cell */
462 		(*el_curconstraint->killobject)((INTBIG)np, VNODEPROTO);
463 	} else
464 	{
465 		/* delete the cell now: no change control */
466 		freenodeproto(np);
467 	}
468 	db_donextchangequietly = FALSE;
469 
470 	return(FALSE);
471 }
472 
473 /*
474  * routine to add nodeproto "np" to the list of cells in the library
475  */
db_insertnodeproto(NODEPROTO * np)476 void db_insertnodeproto(NODEPROTO *np)
477 {
478 	REGISTER NODEPROTO *ent, *onp, *cellgrpchain1, *cellgrpchain2, *snp;
479 	REGISTER LIBRARY *lib;
480 	REGISTER INTBIG i, j;
481 	REGISTER BOOLEAN rebuildhash;
482 
483 	/* see if this name/view already exists */
484 	lib = np->lib;
485 	rebuildhash = FALSE;
486 	snp = db_findnodeprotoname(np->protoname, np->cellview, lib);
487 	if (snp != NONODEPROTO)
488 	{
489 		/* this name/view does exist: see if we have a newer version */
490 		if (np->version > snp->version)
491 		{
492 			/* this new cell is the newest version */
493 			FOR_CELLGROUP(onp, snp)
494 				if (onp->nextcellgrp == snp) break;
495 			if (onp == NONODEPROTO)
496 			{
497 				ttyputmsg(M_("ERROR: Cannot replace newer version (v%ld) of cell %s"),
498 					np->version, np->protoname);
499 				return;
500 			}
501 
502 			/* replace snp with np in the cellgroup chain */
503 			onp->nextcellgrp = np;
504 			np->nextcellgrp = snp->nextcellgrp;
505 
506 			/* put old one in its own group */
507 			snp->nextcellgrp = snp;
508 			np->prevversion = snp;
509 			for(; snp != NONODEPROTO; snp = snp->prevversion)
510 				snp->newestversion = np;
511 			np->newestversion = np;
512 
513 			/* this version is newer: rebuild hash table */
514 			rebuildhash = TRUE;
515 		} else
516 		{
517 			/* this new cell is an old version: find place in list of versions */
518 			np->nextcellgrp = np;
519 			np->newestversion = snp;
520 			ent = NONODEPROTO;
521 			for(; snp != NONODEPROTO; snp = snp->prevversion)
522 			{
523 				if (np->version >= snp->version) break;
524 				ent = snp;
525 			}
526 			ent->prevversion = np;
527 			np->prevversion = snp;
528 		}
529 	} else
530 	{
531 		/* insert into a cellgroup if requested */
532 		if (np->nextcellgrp != np)
533 		{
534 			cellgrpchain1 = np->nextcellgrp;
535 			cellgrpchain2 = cellgrpchain1->nextcellgrp;
536 			if (cellgrpchain2 == cellgrpchain1)
537 			{
538 				/* cell group has only 2 in it now */
539 				cellgrpchain1->nextcellgrp = np;
540 			} else
541 			{
542 				/* insert into longer chain */
543 				cellgrpchain1->nextcellgrp = np;
544 				np->nextcellgrp = cellgrpchain2;
545 			}
546 		}
547 
548 		/* this name/view does not exist: add it to the hash table */
549 		np->newestversion = np;
550 		np->prevversion = NONODEPROTO;
551 		if (lib->numnodeprotos < lib->nodeprotohashtablesize/2)
552 		{
553 			i = db_namehash(np->protoname) + (INTBIG)np->cellview;
554 			i = abs(i) % lib->nodeprotohashtablesize;
555 			for(j=1; j<=lib->nodeprotohashtablesize; j += 2)
556 			{
557 				if (lib->nodeprotohashtable[i] == NONODEPROTO)
558 				{
559 					lib->nodeprotohashtable[i] = np;
560 					lib->nodeprotoviewhashtable[i] = np->cellview;
561 					break;
562 				}
563 				i += j;
564 				if (i >= lib->nodeprotohashtablesize) i -= lib->nodeprotohashtablesize;
565 			}
566 		} else
567 		{
568 			rebuildhash = TRUE;
569 		}
570 	}
571 
572 	/* add this cell to the list of cells in the library */
573 	np->prevnodeproto = lib->tailnodeproto;
574 	np->nextnodeproto = NONODEPROTO;
575 	if (lib->tailnodeproto == NONODEPROTO) lib->firstnodeproto = np; else
576 		lib->tailnodeproto->nextnodeproto = np;
577 	lib->tailnodeproto = np;
578 	lib->numnodeprotos++;
579 
580 	/* rebuild the hash table if requested */
581 	if (rebuildhash) db_buildnodeprotohashtable(lib);
582 
583 #if 0
584 	/* insert into a cellgroup if requested */
585 	if (np->nextcellgrp != np && np->newestversion == np)
586 	{
587 		cellgrpchain1 = np->nextcellgrp;
588 		cellgrpchain2 = cellgrpchain1->nextcellgrp;
589 		if (cellgrpchain2 == cellgrpchain1)
590 		{
591 			/* cell group has only 2 in it now */
592 			cellgrpchain1->nextcellgrp = np;
593 		} else
594 		{
595 			/* insert into longer chain */
596 			cellgrpchain1->nextcellgrp = np;
597 			np->nextcellgrp = cellgrpchain2;
598 		}
599 	}
600 #endif
601 
602 	/* mark a change to the database */
603 	db_changetimestamp++;
604 }
605 
606 /*
607  * routine to remove cell "np" from all lists in the library and cell
608  */
db_retractnodeproto(NODEPROTO * np)609 void db_retractnodeproto(NODEPROTO *np)
610 {
611 	REGISTER NODEPROTO *onp, *lastnp;
612 	REGISTER LIBRARY *lib;
613 
614 	/* remove cell from list in the library */
615 	lib = np->lib;
616 	if (np->prevnodeproto == NONODEPROTO)
617 		lib->firstnodeproto = np->nextnodeproto; else
618 			np->prevnodeproto->nextnodeproto = np->nextnodeproto;
619 	if (np->nextnodeproto == NONODEPROTO)
620 		lib->tailnodeproto = np->prevnodeproto; else
621 			np->nextnodeproto->prevnodeproto = np->prevnodeproto;
622 	lib->numnodeprotos--;
623 
624 	if (np->newestversion != np)
625 	{
626 		/* easy to handle removal of old version */
627 		lastnp = NONODEPROTO;
628 		for(onp = np->newestversion; onp != NONODEPROTO; onp = onp->prevversion)
629 		{
630 			if (onp == np) break;
631 			lastnp = onp;
632 		}
633 		if (lastnp != NONODEPROTO) lastnp->prevversion = np->prevversion;
634 			else ttyputerr(_("Unusual version link in database"));
635 	} else
636 	{
637 		/* bring any older versions to the front */
638 		for(onp = np->prevversion; onp != NONODEPROTO; onp = onp->prevversion)
639 			onp->newestversion = np->prevversion;
640 	}
641 
642 	/* clear cache of port associations in this cell */
643 	db_clearportcache(np);
644 
645 	/* remove cellgroup link */
646 	db_removecellfromgroup(np);
647 
648 	/* rebuild hash table of nodeproto names */
649 	db_buildnodeprotohashtable(lib);
650 
651 	/* mark a change to the database */
652 	db_changetimestamp++;
653 }
654 
655 /*
656  * Routine to remove cell "np" from its cellgroup.
657  */
db_removecellfromgroup(NODEPROTO * np)658 void db_removecellfromgroup(NODEPROTO *np)
659 {
660 	REGISTER NODEPROTO *onp, *enp;
661 	REGISTER INTBIG count, truelimit;
662 
663 	if (np->nextcellgrp != np)
664 	{
665 		/* find node that points to this */
666 		count = truelimit = 0;
667 		FOR_CELLGROUP(onp, np)
668 		{
669 			if (onp->nextcellgrp == np) break;
670 			count++;
671 			if (count > 1000)
672 			{
673 				if (truelimit == 0)
674 				{
675 					for(enp = np->lib->firstnodeproto; enp != NONODEPROTO; enp = enp->nextnodeproto)
676 						truelimit++;
677 					truelimit += 100;
678 				} else
679 				{
680 					if (count > truelimit) return;
681 				}
682 			}
683 		}
684 		if (onp != NONODEPROTO)
685 		{
686 			if (np->nextcellgrp == onp) onp->nextcellgrp = onp; else
687 				onp->nextcellgrp = np->nextcellgrp;
688 		}
689 	}
690 }
691 
692 /*
693  * Routine to rebuild the hash table of nodeproto names in library "lib".
694  */
db_buildnodeprotohashtable(LIBRARY * lib)695 void db_buildnodeprotohashtable(LIBRARY *lib)
696 {
697 	REGISTER INTBIG i, j, numnodeprotos, hashtablesize;
698 	REGISTER NODEPROTO *np;
699 
700 	/* free the former table */
701 	if (lib->nodeprotohashtablesize > 0)
702 	{
703 		lib->nodeprotohashtablesize = 0;
704 		efree((CHAR *)lib->nodeprotohashtable);
705 		efree((CHAR *)lib->nodeprotoviewhashtable);
706 	}
707 
708 	/* determine the size of the hash table */
709 	numnodeprotos = 0;
710 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto) numnodeprotos++;
711 	if (numnodeprotos != lib->numnodeprotos)
712 	{
713 		ttyputmsg(_("Warning: number of nodeprotos in library %s corrected"), lib->libname);
714 		lib->numnodeprotos = numnodeprotos;
715 	}
716 	hashtablesize = pickprime(numnodeprotos * 4);
717 
718 	/* create the hash table and clear it */
719 	lib->nodeprotohashtable = (NODEPROTO **)emalloc(hashtablesize * (sizeof (NODEPROTO *)), lib->cluster);
720 	if (lib->nodeprotohashtable == 0) return;
721 	lib->nodeprotoviewhashtable = (VIEW **)emalloc(hashtablesize * (sizeof (VIEW *)), lib->cluster);
722 	if (lib->nodeprotoviewhashtable == 0) return;
723 	for(i=0; i<hashtablesize; i++)
724 		lib->nodeprotohashtable[i] = NONODEPROTO;
725 	lib->nodeprotohashtablesize = hashtablesize;
726 
727 	/* insert all nodeprotos into the table */
728 	for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
729 	{
730 		if (np->newestversion != np) continue;
731 		i = db_namehash(np->protoname) + (INTBIG)np->cellview;
732 		i = abs(i) % hashtablesize;
733 		for(j=1; j<=hashtablesize; j += 2)
734 		{
735 			if (lib->nodeprotohashtable[i] == NONODEPROTO)
736 			{
737 				lib->nodeprotohashtable[i] = np;
738 				lib->nodeprotoviewhashtable[i] = np->cellview;
739 				break;
740 			}
741 			i += j;
742 			if (i >= hashtablesize) i -= hashtablesize;
743 		}
744 	}
745 }
746 
747 /*
748  * Routine to find the nodeproto named "name" with view "view" in library "lib".
749  * Returns NONODEPROTO if not found.
750  */
db_findnodeprotoname(CHAR * name,VIEW * view,LIBRARY * lib)751 NODEPROTO *db_findnodeprotoname(CHAR *name, VIEW *view, LIBRARY *lib)
752 {
753 	REGISTER NODEPROTO *np;
754 	REGISTER INTBIG i, j;
755 
756 	if (lib->nodeprotohashtablesize > 0)
757 	{
758 		i = db_namehash(name) + (INTBIG)view;
759 		i = abs(i) % lib->nodeprotohashtablesize;
760 		for(j=1; j<=lib->nodeprotohashtablesize; j += 2)
761 		{
762 			np = lib->nodeprotohashtable[i];
763 			if (np == NONODEPROTO) break;
764 			if (namesame(name, np->protoname) == 0 &&
765 				lib->nodeprotoviewhashtable[i] == view) return(np);
766 			i += j;
767 			if (i >= lib->nodeprotohashtablesize) i -= lib->nodeprotohashtablesize;
768 		}
769 	} else
770 	{
771 		for (np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
772 			if (namesame(name, np->protoname) == 0 && view == np->cellview) return(np);
773 	}
774 	return(NONODEPROTO);
775 }
776 
777 #define	HASH_SH		5
778 #define	HASH_MSK	((1 << HASH_SH) - 1)
779 
780 /*
781  * Routine to encode a cell or portproto name for the hash table.
782  */
db_namehash(CHAR * name)783 INTBIG db_namehash(CHAR *name)
784 {
785 	REGISTER INTBIG hash, shiftout, c;
786 
787 	hash = 0;
788 	while ((c = *name++ & 0377) != 0)
789 	{
790 		c = tolower(c);
791 		shiftout = (hash >> (31 - HASH_SH)) & HASH_MSK;
792 		hash = ((hash << HASH_SH) | shiftout) ^ c;
793 	}
794 	hash &= 0x7fffffff;
795 	return(hash);
796 }
797 
798 /*
799  * routine to return the current cell (the one in the current window).
800  * returns NONODEPROTO if there is none.
801  */
getcurcell(void)802 NODEPROTO *getcurcell(void)
803 {
804 	if (el_curwindowpart != NOWINDOWPART) return(el_curwindowpart->curnodeproto);
805 	return(NONODEPROTO);
806 }
807 
808 /******************** HIERARCHICAL TRAVERSER ********************/
809 
810 /*
811  * routine to return the current path to this location (cell "here") in the traversal.  The depth is put
812  * in "depth" and the list of NODEINSTs is put in "nilist".  The first entry in the array
813  * is the top of the hierarchy, and the last entry is the instance in whose definition
814  * the traversal currently resides.
815  */
gettraversalpath(NODEPROTO * here,WINDOWPART * win,NODEINST *** nilist,INTBIG ** indexlist,INTBIG * depth,INTBIG maxdepth)816 void gettraversalpath(NODEPROTO *here, WINDOWPART *win, NODEINST ***nilist, INTBIG **indexlist, INTBIG *depth, INTBIG maxdepth)
817 {
818 	REGISTER NODEPROTO *np, *cnp;
819 	REGISTER NODEINST *ni;
820 	INTBIG index;
821 	Q_UNUSED( maxdepth );
822 
823 	if (db_hiertraversing)
824 	{
825 		/* see if any additional hierarchy can be found at the top of the list */
826 		if (db_hiertraverse.depth > 0) np = db_hiertraverse.path[0]->parent; else
827 			np = here;
828 		if (np != NONODEPROTO)
829 		{
830 			for(;;)
831 			{
832 				if (np == NONODEPROTO) break;
833 				ni = descentparent(np, &index, win, 0);
834 				if (ni == NONODEINST) break;
835 				if (db_addtohierpath(ni, 0, FALSE)) break;
836 				np = ni->parent;
837 				cnp = contentsview(np);
838 				if (cnp != NONODEPROTO) np = cnp;
839 			}
840 		}
841 		*depth = db_hiertraverse.depth - db_hiertempclimb;
842 		*indexlist = db_hiertraverse.index;
843 		*nilist = db_hiertraverse.path;
844 	} else
845 	{
846 		/* not in an active traversal, see if one exists from edit sequences */
847 		np = here;
848 		for(;;)
849 		{
850 			if (np == NONODEPROTO) break;
851 			ni = descentparent(np, &index, win, 0);
852 			if (ni == NONODEINST) break;
853 			if (db_addtohierpath(ni, 0, FALSE)) break;
854 			np = ni->parent;
855 			cnp = contentsview(np);
856 			if (cnp != NONODEPROTO) np = cnp;
857 		}
858 		*depth = db_hiertraverse.depth - db_hiertempclimb;
859 		*indexlist = db_hiertraverse.index;
860 		*nilist = db_hiertraverse.path;
861 		db_hiertraverse.depth = 0;
862 	}
863 }
864 
865 /*
866  * internal routine to return the current path to this location in the traversal,
867  * ignoring implicit paths made by editing.  The depth is put in "depth" and the list
868  * of NODEINSTs is put in "nilist".  The first entry in the array is the top of the
869  * hierarchy, and the last entry is the instance in whose definition the traversal
870  * currently resides.
871  */
db_gettraversalpath(NODEPROTO * here,WINDOWPART * win,NODEINST *** nilist,INTBIG * depth)872 void db_gettraversalpath(NODEPROTO *here, WINDOWPART *win, NODEINST ***nilist, INTBIG *depth)
873 {
874 	Q_UNUSED( here );
875 	*nilist = db_hiertraverse.path;
876 	*depth = db_hiertraverse.depth - db_hiertempclimb;
877 }
878 
879 /*
880  * routine to begin the traversal of the hierarchy.  At each level of the
881  * traversal, the routine "downhierarchy" is called with the cell and the user data.
882  */
begintraversehierarchy(void)883 void begintraversehierarchy(void)
884 {
885 	db_hiertraversing = TRUE;
886 	db_hiertraverse.depth = 0;
887 	db_hiertempclimb = 0;
888 	db_traversaltimestamp++;
889 }
890 
891 /*
892  * routine to stop the traversal of the hierarchy.
893  */
endtraversehierarchy(void)894 void endtraversehierarchy(void)
895 {
896 	db_hiertraversing = FALSE;
897 }
898 
899 /*
900  * routine to traversal down the hierarchy, into nodeinst "ni", index "index" of
901  * the array (if the node is arrayed).
902  */
downhierarchy(NODEINST * ni,NODEPROTO * np,INTBIG index)903 void downhierarchy(NODEINST *ni, NODEPROTO *np, INTBIG index)
904 {
905 	Q_UNUSED( np );
906 	/* add this node to the traversal stack */
907 	if (db_addtohierpath(ni, index, TRUE)) return;
908 	db_traversaltimestamp++;
909 }
910 
911 /*
912  * Routine to report the levels of hierarchy current being "popped out".
913  */
getpopouthierarchy(void)914 INTBIG getpopouthierarchy(void)
915 {
916 	return(db_hiertempclimb);
917 }
918 
919 /*
920  * Temporarily climb "climb" levels out of the hierarchy stack.
921  */
popouthierarchy(INTBIG climb)922 void popouthierarchy(INTBIG climb)
923 {
924 	db_hiertempclimb = climb;
925 }
926 
db_addtohierpath(NODEINST * ni,INTBIG index,BOOLEAN bottom)927 BOOLEAN db_addtohierpath(NODEINST *ni, INTBIG index, BOOLEAN bottom)
928 {
929 	REGISTER INTBIG i, newlimit, *newindex;
930 	REGISTER NODEINST **nilist;
931 
932 	/* make sure there is room in the list of nodes traversed */
933 	if (db_hiertraverse.depth >= db_hiertraverse.limit)
934 	{
935 		newlimit = db_hiertraverse.limit+10;
936 		if (newlimit < db_hiertraverse.depth) newlimit = db_hiertraverse.depth;
937 		nilist = (NODEINST **)emalloc(newlimit * (sizeof (NODEINST *)), db_cluster);
938 		if (nilist == 0) return(TRUE);
939 		newindex = (INTBIG *)emalloc(newlimit * SIZEOFINTBIG, db_cluster);
940 		if (newindex == 0) return(TRUE);
941 		for(i=0; i<db_hiertraverse.depth; i++)
942 		{
943 			nilist[i] = db_hiertraverse.path[i];
944 			newindex[i] = db_hiertraverse.index[i];
945 		}
946 		if (db_hiertraverse.limit > 0)
947 		{
948 			efree((CHAR *)db_hiertraverse.path);
949 			efree((CHAR *)db_hiertraverse.index);
950 		}
951 		db_hiertraverse.path = nilist;
952 		db_hiertraverse.index = newindex;
953 		db_hiertraverse.limit = newlimit;
954 	}
955 	if (bottom)
956 	{
957 		db_hiertraverse.path[db_hiertraverse.depth] = ni;
958 		db_hiertraverse.index[db_hiertraverse.depth] = index;
959 	} else
960 	{
961 		for(i=db_hiertraverse.depth; i>0; i--)
962 		{
963 			db_hiertraverse.path[i] = db_hiertraverse.path[i-1];
964 			db_hiertraverse.index[i] = db_hiertraverse.index[i-1];
965 		}
966 		db_hiertraverse.path[0] = ni;
967 		db_hiertraverse.index[0] = index;
968 	}
969 	db_hiertraverse.depth++;
970 	return(FALSE);
971 }
972 
973 /*
974  * routine to back out of a hierarchy traversal
975  */
uphierarchy(void)976 void uphierarchy(void)
977 {
978 	if (db_hiertraverse.depth <= 0) return;
979 
980 	/* pop the node off the traversal stack */
981 	db_hiertraverse.depth--;
982 	db_traversaltimestamp++;
983 }
984 
985 /*
986  * Routine to determine the node instance that is the proper parent of cell "np"
987  * in window "win" (if "win" is NOWINDOWPART, ignore that factor).
988  * This is done by examining the variable "USER_descent_path" on the cell, which
989  * is created when the user goes "down the hierarchy" into this cell.  The routine
990  * not only extracts this information, but it verifies that the instance still
991  * exists.  Returns NONODEINST if there is no parent.
992  */
descentparent(NODEPROTO * np,INTBIG * index,WINDOWPART * win,INTBIG * viewinfo)993 NODEINST *descentparent(NODEPROTO *np, INTBIG *index, WINDOWPART *win, INTBIG *viewinfo)
994 {
995 	REGISTER VARIABLE *var;
996 	REGISTER INTBIG *curlist, i, j, len;
997 	REGISTER NODEINST *ni, *vni;
998 	REGISTER WINDOWPART *w;
999 	REGISTER NODEPROTO *inp;
1000 
1001 	/* find this level in the stack */
1002 	if (db_descent_path_key == 0)
1003 		db_descent_path_key = makekey(x_("USER_descent_path"));
1004 	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, db_descent_path_key);
1005 	if (var == NOVARIABLE) return(NONODEINST);
1006 	len = getlength(var);
1007 	curlist = (INTBIG *)var->addr;
1008 	for(i=0; i<len; i+=25)
1009 	{
1010 		ni = (NODEINST *)curlist[i];
1011 		w = (WINDOWPART *)curlist[i+1];
1012 		if (ni->parent == np) continue;
1013 		if (win != NOWINDOWPART && win != w) continue;
1014 
1015 		*index = curlist[i+2];
1016 		if (viewinfo != 0)
1017 		{
1018 			for(j=0; j<22; j++) viewinfo[j] = curlist[i+3+j];
1019 		}
1020 
1021 		/* validate this pointer: node must be a current instance of this cell */
1022 		for(vni = np->firstinst; vni != NONODEINST; vni = vni->nextinst)
1023 			if (vni == ni) return(ni);
1024 
1025 		/* might be an icon */
1026 		for(inp = iconview(np); inp != NONODEPROTO; inp = inp->prevversion)
1027 		{
1028 			for(vni = inp->firstinst; vni != NONODEINST; vni = vni->nextinst)
1029 				if (vni == ni) return(ni);
1030 		}
1031 	}
1032 	return(NONODEINST);
1033 }
1034 
sethierarchicalparent(NODEPROTO * np,NODEINST * parentni,WINDOWPART * win,INTBIG thisindex,INTBIG * viewinfo)1035 void sethierarchicalparent(NODEPROTO *np, NODEINST *parentni, WINDOWPART *win, INTBIG thisindex, INTBIG *viewinfo)
1036 {
1037 	REGISTER WINDOWPART *w, *ow;
1038 	REGISTER VARIABLE *var;
1039 	REGISTER NODEINST *ni;
1040 	REGISTER INTBIG i, j, k, len, *curlist, *newlist, index;
1041 	INTBIG oneentry[25];
1042 
1043 	if (db_descent_path_key == 0)
1044 		db_descent_path_key = makekey(x_("USER_descent_path"));
1045 	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, db_descent_path_key);
1046 	if (var == NOVARIABLE)
1047 	{
1048 		oneentry[0] = (INTBIG)parentni;
1049 		oneentry[1] = (INTBIG)win;
1050 		oneentry[2] = thisindex;
1051 		if (viewinfo == 0)
1052 		{
1053 			for(i=0; i<22; i++) oneentry[i+3] = 0;
1054 		} else
1055 		{
1056 			for(i=0; i<22; i++) oneentry[i+3] = viewinfo[i];
1057 		}
1058 		(void)setvalkey((INTBIG)np, VNODEPROTO, db_descent_path_key, (INTBIG)oneentry,
1059 			VINTEGER|VISARRAY|(25<<VLENGTHSH)|VDONTSAVE);
1060 	} else
1061 	{
1062 		/* validate the entries */
1063 		len = getlength(var);
1064 		newlist = (INTBIG *)emalloc((len+25) * SIZEOFINTBIG, el_tempcluster);
1065 		if (newlist == 0) return;
1066 		curlist = (INTBIG *)var->addr;
1067 		j = 0;
1068 		for(i=0; i<len; i += 25)
1069 		{
1070 			ni = (NODEINST *)curlist[i];
1071 			w = (WINDOWPART *)curlist[i+1];
1072 			index = curlist[i+2];
1073 
1074 			/* remove entries that point to the same parent instance */
1075 			if (ni == parentni) continue;
1076 
1077 			/* remove entries that point to the same window */
1078 			if (w == win) continue;
1079 
1080 			/* remove obsolete windows */
1081 			if (w != NOWINDOWPART)
1082 			{
1083 				for(ow = el_topwindowpart; ow != NOWINDOWPART; ow = ow->nextwindowpart)
1084 					if (ow == w) break;
1085 				if (ow == NOWINDOWPART) continue;
1086 			}
1087 
1088 			/* keep the existing entry */
1089 			newlist[j++] = (INTBIG)ni;
1090 			newlist[j++] = (INTBIG)w;
1091 			newlist[j++] = index;
1092 			for(k=0; k<22; k++) newlist[j++] = curlist[i+3+k];
1093 		}
1094 
1095 		/* add the new entry */
1096 		newlist[j++] = (INTBIG)parentni;
1097 		newlist[j++] = (INTBIG)win;
1098 		newlist[j++] = thisindex;
1099 		if (viewinfo == 0)
1100 		{
1101 			for(i=0; i<22; i++) newlist[j++] = 0;
1102 		} else
1103 		{
1104 			for(i=0; i<22; i++) newlist[j++] = viewinfo[i];
1105 		}
1106 		(void)setvalkey((INTBIG)np, VNODEPROTO, db_descent_path_key, (INTBIG)newlist,
1107 			VINTEGER|VISARRAY|(j<<VLENGTHSH)|VDONTSAVE);
1108 		efree((CHAR *)newlist);
1109 	}
1110 }
1111 
1112 /*
1113  * Routine to create a hierarchical traversal snapshot object and return it.
1114  * Returns zero on error.
1115  */
newhierarchicaltraversal(void)1116 void *newhierarchicaltraversal(void)
1117 {
1118 	REGISTER HTRAVERSAL *ht;
1119 
1120 	ht = (HTRAVERSAL *)emalloc(sizeof (HTRAVERSAL), db_cluster);
1121 	if (ht == 0) return(0);
1122 	ht->limit = 0;
1123 	return((void *)ht);
1124 }
1125 
1126 /*
1127  * Routine to take a snapshot of the current hierarchical traversal and
1128  * save it in "snapshot" (an object returned by "newhierarchicaltraversal()").
1129  */
gethierarchicaltraversal(void * snapshot)1130 void gethierarchicaltraversal(void *snapshot)
1131 {
1132 	REGISTER HTRAVERSAL *ht;
1133 
1134 	ht = (HTRAVERSAL *)snapshot;
1135 	db_copyhierarchicaltraversal(&db_hiertraverse, ht);
1136 }
1137 
1138 /*
1139  * Routine to set saved hierarchical traversal "snapshot" as the current hierarchical traversal.
1140  */
sethierarchicaltraversal(void * snapshot)1141 void sethierarchicaltraversal(void *snapshot)
1142 {
1143 	REGISTER HTRAVERSAL *ht;
1144 
1145 	ht = (HTRAVERSAL *)snapshot;
1146 	db_copyhierarchicaltraversal(ht, &db_hiertraverse);
1147 }
1148 
1149 /*
1150  * Routine to delete a saved hierarchy traversal snapshot.
1151  */
killhierarchicaltraversal(void * snapshot)1152 void killhierarchicaltraversal(void *snapshot)
1153 {
1154 	REGISTER HTRAVERSAL *ht;
1155 
1156 	ht = (HTRAVERSAL *)snapshot;
1157 	if (ht->limit > 0)
1158 	{
1159 		efree((CHAR *)ht->path);
1160 		efree((CHAR *)ht->index);
1161 	}
1162 	efree((CHAR *)ht);
1163 }
1164 
1165 /*
1166  * Internal routine to copy hierarchical traversal stacks.
1167  */
db_copyhierarchicaltraversal(HTRAVERSAL * src,HTRAVERSAL * dst)1168 void db_copyhierarchicaltraversal(HTRAVERSAL *src, HTRAVERSAL *dst)
1169 {
1170 	REGISTER INTBIG i, *newindex;
1171 	REGISTER NODEINST **newpath;
1172 
1173 	if (src->depth > dst->limit)
1174 	{
1175 		newpath = (NODEINST **)emalloc(src->depth * (sizeof (NODEINST *)), db_cluster);
1176 		if (newpath == 0) return;
1177 		newindex = (INTBIG *)emalloc(src->depth * SIZEOFINTBIG, db_cluster);
1178 		if (newindex == 0) return;
1179 
1180 		if (dst->limit > 0)
1181 		{
1182 			efree((CHAR *)dst->path);
1183 			efree((CHAR *)dst->index);
1184 		}
1185 		dst->path = newpath;
1186 		dst->index = newindex;
1187 		dst->limit = src->depth;
1188 	}
1189 	for(i=0; i<src->depth; i++)
1190 	{
1191 		dst->path[i] = src->path[i];
1192 		dst->index[i] = src->index[i];
1193 	}
1194 	dst->depth = src->depth;
1195 }
1196 
1197 /************************* VIEWS *************************/
1198 
1199 /*
1200  * routine to allocate a view and return its address.  The routine returns
1201  * NOVIEW if allocation fails.
1202  */
allocview(void)1203 VIEW *allocview(void)
1204 {
1205 	REGISTER VIEW *v;
1206 
1207 	v = (VIEW *)emalloc((sizeof (VIEW)), db_cluster);
1208 	if (v == 0) return((VIEW *)db_error(DBNOMEM|DBALLOCVIEW));
1209 	v->viewname = v->sviewname = NOSTRING;
1210 	v->nextview = NOVIEW;
1211 	v->temp1 = v->temp2 = 0;
1212 	v->viewstate = 0;
1213 	v->firstvar = NOVARIABLE;
1214 	v->numvar = 0;
1215 	return(v);
1216 }
1217 
1218 /*
1219  * routine to return view "v" to free memory
1220  */
freeview(VIEW * v)1221 void freeview(VIEW *v)
1222 {
1223 	if (v == NOVIEW) return;
1224 	if (v->numvar != 0) db_freevars(&v->firstvar, &v->numvar);
1225 	efree((CHAR *)v);
1226 }
1227 
1228 /*
1229  * routine to create a new view with name "vname" and short view name "svname".
1230  * The address of the view is returned (NOVIEW if an error occurs).
1231  */
newview(CHAR * vname,CHAR * svname)1232 VIEW *newview(CHAR *vname, CHAR *svname)
1233 {
1234 	REGISTER VIEW *v;
1235 	REGISTER CHAR *ptr;
1236 
1237 	/* error checks */
1238 	for(ptr = vname; *ptr != 0; ptr++)
1239 		if (*ptr <= ' ' || *ptr == ':' || *ptr >= 0177)
1240 	{
1241 		db_donextchangequietly = FALSE;
1242 		return((VIEW *)db_error(DBBADNAME|DBNEWVIEW));
1243 	}
1244 	for(ptr = svname; *ptr != 0; ptr++)
1245 		if (*ptr <= ' ' || *ptr == ':' || *ptr >= 0177)
1246 	{
1247 		db_donextchangequietly = 0;
1248 		return((VIEW *)db_error(DBBADNAME|DBNEWVIEW));
1249 	}
1250 	for(v = el_views; v != NOVIEW; v = v->nextview)
1251 		if (namesame(v->viewname, vname) == 0 || namesame(v->sviewname, svname) == 0)
1252 	{
1253 		db_donextchangequietly = 0;
1254 		return((VIEW *)db_error(DBDUPLICATE|DBNEWVIEW));
1255 	}
1256 
1257 	/* create the view */
1258 	v = allocview();
1259 	if (v == NOVIEW)
1260 	{
1261 		db_donextchangequietly = 0;
1262 		return(NOVIEW);
1263 	}
1264 
1265 	/* initialize view names */
1266 	if (allocstring(&v->viewname, vname, db_cluster))
1267 	{
1268 		db_donextchangequietly = 0;
1269 		return(NOVIEW);
1270 	}
1271 	if (allocstring(&v->sviewname, svname, db_cluster))
1272 	{
1273 		db_donextchangequietly = 0;
1274 		return(NOVIEW);
1275 	}
1276 	if (namesamen(vname, x_("Schematic-Page-"), 15) == 0) v->viewstate |= MULTIPAGEVIEW;
1277 
1278 	/* insert in list of views */
1279 	v->nextview = el_views;
1280 	el_views = v;
1281 
1282 	/* handle change control and broadcast */
1283 	if (db_donextchangequietly == 0 && !db_dochangesquietly)
1284 	{
1285 		(void)db_change((INTBIG)v, OBJECTNEW, VVIEW, 0, 0, 0, 0, 0);
1286 	}
1287 	db_donextchangequietly = 0;
1288 
1289 	/* mark a change to the database */
1290 	db_changetimestamp++;
1291 
1292 	/* return the view */
1293 	return(v);
1294 }
1295 
1296 /*
1297  * routine to remove view "view".  Returns true on error
1298  */
killview(VIEW * view)1299 BOOLEAN killview(VIEW *view)
1300 {
1301 	REGISTER VIEW *v, *lastv;
1302 	REGISTER LIBRARY *lib;
1303 	REGISTER NODEPROTO *np;
1304 
1305 	/* make sure it is not one of the permanent (undeletable) views */
1306 	if ((view->viewstate&PERMANENTVIEW) != 0)
1307 	{
1308 		db_donextchangequietly = 0;
1309 		return(TRUE);
1310 	}
1311 
1312 	/* make sure it is not in any cell */
1313 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1314 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1315 			if (np->cellview == view)
1316 	{
1317 		db_donextchangequietly = 0;
1318 		return(TRUE);
1319 	}
1320 
1321 	/* find the view */
1322 	lastv = NOVIEW;
1323 	for(v = el_views; v != NOVIEW; v = v->nextview)
1324 	{
1325 		if (v == view) break;
1326 		lastv = v;
1327 	}
1328 	if (v == NOVIEW) return(TRUE);
1329 
1330 	/* delete the view */
1331 	if (lastv == NOVIEW) el_views = v->nextview; else
1332 		lastv->nextview = v->nextview;
1333 
1334 	/* handle change control and broadcast */
1335 	if (db_donextchangequietly == 0 && !db_dochangesquietly)
1336 	{
1337 		(void)db_change((INTBIG)v, OBJECTKILL, VVIEW, 0, 0, 0, 0, 0);
1338 	}
1339 	db_donextchangequietly = 0;
1340 
1341 	/* mark a change to the database */
1342 	db_changetimestamp++;
1343 	return(FALSE);
1344 }
1345 
1346 /*
1347  * routine to change the view type of cell "np" to "view".  Returns true
1348  * upon error.
1349  */
changecellview(NODEPROTO * np,VIEW * view)1350 BOOLEAN changecellview(NODEPROTO *np, VIEW *view)
1351 {
1352 	REGISTER NODEPROTO *rnp, *otheringrp;
1353 
1354 	if ((np->cellview->viewstate&TEXTVIEW) != 0 && (view->viewstate&TEXTVIEW) == 0)
1355 	{
1356 		ttyputerr(_("Sorry, textual views cannot be made nontextual"));
1357 		return(TRUE);
1358 	}
1359 	if ((np->cellview->viewstate&TEXTVIEW) == 0 && (view->viewstate&TEXTVIEW) != 0)
1360 	{
1361 		ttyputerr(_("Sorry, nontextual views cannot be made textual"));
1362 		return(TRUE);
1363 	}
1364 
1365 	/* remove this cell from the linked lists */
1366 	otheringrp = np->nextcellgrp;
1367 	db_retractnodeproto(np);
1368 
1369 	/* assign a newer version number if there are others with the new view */
1370 	if (otheringrp != np)
1371 	{
1372 		FOR_CELLGROUP(rnp, otheringrp)
1373 		{
1374 			if (rnp->cellview == view)
1375 			{
1376 				if (np->version <= rnp->version) np->version = rnp->version + 1;
1377 				break;
1378 			}
1379 		}
1380 	}
1381 
1382 	/* set the view (sorry, not undoable) */
1383 	np->cellview = view;
1384 
1385 	/* reinsert the cell in the linked lists */
1386 	db_insertnodeproto(np);
1387 	return(FALSE);
1388 }
1389 
1390 /*
1391  * Routine to return true if cell "np" is a schematic view
1392  */
isschematicview(NODEPROTO * np)1393 BOOLEAN isschematicview(NODEPROTO *np)
1394 {
1395 	if (np->cellview == el_schematicview ||
1396 		(np->cellview->viewstate&MULTIPAGEVIEW) != 0) return(TRUE);
1397 	return(FALSE);
1398 }
1399 
1400 /*
1401  * Routine to return true if cell "np" is an icon view
1402  */
isiconview(NODEPROTO * np)1403 BOOLEAN isiconview(NODEPROTO *np)
1404 {
1405 	if (np->cellview == el_iconview) return(TRUE);
1406 	return(FALSE);
1407 }
1408 
1409 /*
1410  * Routine to return TRUE if cells "a" and "b" are in the same cell group.
1411  */
insamecellgrp(NODEPROTO * a,NODEPROTO * b)1412 BOOLEAN insamecellgrp(NODEPROTO *a, NODEPROTO *b)
1413 {
1414 	REGISTER NODEPROTO *np;
1415 
1416 	a = a->newestversion;
1417 	b = b->newestversion;
1418 	FOR_CELLGROUP(np, a)
1419 		if (np == b) return(TRUE);
1420 	return(FALSE);
1421 }
1422 
1423 /*
1424  * Routine to return TRUE if cell "subnp" is an icon of the cell "cell".
1425  */
isiconof(NODEPROTO * subnp,NODEPROTO * cell)1426 BOOLEAN isiconof(NODEPROTO *subnp, NODEPROTO *cell)
1427 {
1428 	REGISTER NODEPROTO *np;
1429 	REGISTER INTBIG i;
1430 
1431 	if (subnp->cellview != el_iconview) return(FALSE);
1432 	subnp = subnp->newestversion;
1433 	i = 0;
1434 	FOR_CELLGROUP(np, cell)
1435 	{
1436 		if (np == subnp) return(TRUE);
1437 		if (i++ > 1000)
1438 		{
1439 			db_correctcellgroups(cell);
1440 			break;
1441 		}
1442 	}
1443 	return(FALSE);
1444 }
1445 
1446 /*
1447  * Routine to repair the cellgroups when there is an infinite loop
1448  */
db_correctcellgroups(NODEPROTO * cell)1449 void db_correctcellgroups(NODEPROTO *cell)
1450 {
1451 	NODEPROTO *onp, *first, *thefirst;
1452 
1453 	ttyputerr(_("Problems with cell group %s...repairing"), describenodeproto(cell));
1454 	first = NONODEPROTO;
1455 	for(onp = cell->lib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
1456 	{
1457 		if (namesame(onp->protoname, cell->protoname) != 0) continue;
1458 		if (onp->newestversion != onp)
1459 		{
1460 			onp->nextcellgrp = onp;
1461 			continue;
1462 		}
1463 		if (first != NONODEPROTO) first->nextcellgrp = onp; else
1464 			thefirst = onp;
1465 		first = onp;
1466 	}
1467 	if (first != NONODEPROTO) first->nextcellgrp = thefirst;
1468 }
1469 
1470 /*
1471  * routine to see if cell "np" has an icon, and if so, return that cell.
1472  * Returns NONODEPROTO if the cell does not have an icon
1473  */
iconview(NODEPROTO * np)1474 NODEPROTO *iconview(NODEPROTO *np)
1475 {
1476 	REGISTER NODEPROTO *rnp;
1477 	REGISTER INTBIG i;
1478 
1479 	if (np == NONODEPROTO) return(NONODEPROTO);
1480 
1481 	/* must be complex */
1482 	if (np->primindex != 0) return(NONODEPROTO);
1483 
1484 	/* can only get icon view if this is a schematic */
1485 	if (!isschematicview(np)) return(NONODEPROTO);
1486 
1487 	/* now look for views */
1488 	i = 0;
1489 	FOR_CELLGROUP(rnp, np)
1490 	{
1491 		if (rnp->cellview == el_iconview) return(rnp);
1492 		if (i++ > 1000)
1493 		{
1494 			db_correctcellgroups(np);
1495 			break;
1496 		}
1497 	}
1498 
1499 	return(NONODEPROTO);
1500 }
1501 
1502 /*
1503  * routine to see if cell "np" is an icon or a skeleton, and if so, return its
1504  * true contents cell.  Returns NONODEPROTO if the contents cell is not found.
1505  */
contentsview(NODEPROTO * np)1506 NODEPROTO *contentsview(NODEPROTO *np)
1507 {
1508 	REGISTER NODEPROTO *rnp;
1509 	REGISTER INTBIG i;
1510 
1511 	/* primitives have no contents view */
1512 	if (np == NONODEPROTO) return(NONODEPROTO);
1513 	if (np->primindex != 0) return(NONODEPROTO);
1514 
1515 	/* can only consider contents if this cell is an icon */
1516 	if (np->cellview != el_iconview && np->cellview != el_skeletonview)
1517 		return(NONODEPROTO);
1518 
1519 	/* first check to see if there is a schematics link */
1520 	i = 0;
1521 	FOR_CELLGROUP(rnp, np)
1522 	{
1523 		if (rnp->cellview == el_schematicview) return(rnp);
1524 		if ((rnp->cellview->viewstate&MULTIPAGEVIEW) != 0) return(rnp);
1525 		if (i++ > 1000)
1526 		{
1527 			db_correctcellgroups(np);
1528 			break;
1529 		}
1530 	}
1531 
1532 	/* now check to see if there is any layout link */
1533 	FOR_CELLGROUP(rnp, np)
1534 		if (rnp->cellview == el_layoutview) return(rnp);
1535 
1536 	/* finally check to see if there is any "unknown" link */
1537 	FOR_CELLGROUP(rnp, np)
1538 		if (rnp->cellview == el_unknownview) return(rnp);
1539 
1540 	/* no contents found */
1541 	return(NONODEPROTO);
1542 }
1543 
1544 /* function to return a layout view if there is one */
layoutview(NODEPROTO * np)1545 NODEPROTO *layoutview(NODEPROTO *np)
1546 {
1547 	REGISTER NODEPROTO *rnp, *laynp, *schnp, *uknnp;
1548 	REGISTER INTBIG i;
1549 
1550 	if (np == NONODEPROTO) return(NONODEPROTO);
1551 
1552 	/* must be complex */
1553 	if (np->primindex != 0) return(NONODEPROTO);
1554 
1555 	/* Now look for views */
1556 	laynp = schnp = uknnp = NONODEPROTO;
1557 	i = 0;
1558 	FOR_CELLGROUP(rnp, np)
1559 	{
1560 		if (rnp->cellview == el_layoutview) laynp = rnp;
1561 		if (rnp->cellview == el_schematicview ||
1562 			(rnp->cellview->viewstate&MULTIPAGEVIEW) != 0) schnp = rnp;
1563 		if (rnp->cellview == el_unknownview) uknnp = rnp;
1564 		if (i++ > 1000)
1565 		{
1566 			db_correctcellgroups(np);
1567 			break;
1568 		}
1569 	}
1570 
1571 	/* if this is an icon, look for schematic first */
1572 	if (np->cellview == el_iconview && schnp != NONODEPROTO) return(schnp);
1573 
1574 	/* layout has first precedence */
1575 	if (laynp != NONODEPROTO) return(laynp);
1576 
1577 	/* schematics come next */
1578 	if (schnp != NONODEPROTO) return(schnp);
1579 
1580 	/* then look for unknown */
1581 	if (uknnp != NONODEPROTO) return(uknnp);
1582 
1583 	/* keep what we have */
1584 	return(NONODEPROTO);
1585 }
1586 
1587 /*
1588  * routine to see if cell "np" has an equivalent of view "v", and if so, return that cell.
1589  * Returns NONODEPROTO if the cell does not have an equivalent in that view.
1590  */
anyview(NODEPROTO * np,VIEW * v)1591 NODEPROTO *anyview(NODEPROTO *np, VIEW *v)
1592 {
1593 	REGISTER NODEPROTO *rnp;
1594 	REGISTER INTBIG i;
1595 
1596 	/* primitives have no icon view */
1597 	if (np->primindex != 0) return(NONODEPROTO);
1598 
1599 	/* return self if already that type */
1600 	if (np->cellview == v) return(np);
1601 
1602 	i = 0;
1603 	FOR_CELLGROUP(rnp, np)
1604 	{
1605 		if (rnp->cellview == v) return(rnp);
1606 		if (i++ > 1000)
1607 		{
1608 			db_correctcellgroups(np);
1609 			break;
1610 		}
1611 	}
1612 	return(NONODEPROTO);
1613 }
1614 
1615 /*
1616  * routine to obtain the equivalent port on a cell with an alternate view.
1617  * The original cell is "np" with port "pp".  The routine returns the port
1618  * on equivalent cell "enp".  If the port association cannot be found,
1619  * the routine returns NOPORTPROTO and prints an error message.
1620  */
equivalentport(NODEPROTO * np,PORTPROTO * pp,NODEPROTO * enp)1621 PORTPROTO *equivalentport(NODEPROTO *np, PORTPROTO *pp, NODEPROTO *enp)
1622 {
1623 	REGISTER PORTPROTO *epp, *opp;
1624 
1625 	/* don't waste time searching if the two views are the same */
1626 	if (np == enp) return(pp);
1627 	if (pp == NOPORTPROTO) return(pp);
1628 
1629 	/* load the cache if not already there */
1630 	if (enp != np->cachedequivcell)
1631 	{
1632 		for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
1633 			opp->cachedequivport = NOPORTPROTO;
1634 		for(opp = np->firstportproto; opp != NOPORTPROTO; opp = opp->nextportproto)
1635 		{
1636 			epp = getportproto(enp, opp->protoname);
1637 			if (epp != NOPORTPROTO) opp->cachedequivport = epp;
1638 		}
1639 		np->cachedequivcell = enp;
1640 	}
1641 	epp = pp->cachedequivport;
1642 	if (epp != 0 && epp != NOPORTPROTO) return(epp);
1643 
1644 	/* don't report errors for global ports not on icons */
1645 	if (epp == NOPORTPROTO)
1646 	{
1647 		if (enp->cellview != el_iconview || (pp->userbits&BODYONLY) == 0)
1648 			ttyputerr(_("Warning: no port in cell %s corresponding to port %s in cell %s"),
1649 				describenodeproto(enp), pp->protoname, describenodeproto(np));
1650 	}
1651 	pp->cachedequivport = (PORTPROTO *)0;
1652 	return(NOPORTPROTO);
1653 }
1654 
1655 /************************* SCHEMATIC WINDOWS *************************/
1656 
1657 static GRAPHICS tech_frame = {LAYERO, MENTXT, SOLIDC, SOLIDC,
1658 	{0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
1659 	0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF}, NOVARIABLE, 0};
1660 
1661 #define FRAMESCALE  700.0f
1662 #define HASCHXSIZE  ( 8.5f  / FRAMESCALE)
1663 #define HASCHYSIZE  ( 5.5f  / FRAMESCALE)
1664 #define ASCHXSIZE   (11.0f  / FRAMESCALE)
1665 #define ASCHYSIZE   ( 8.5f  / FRAMESCALE)
1666 #define BSCHXSIZE   (17.0f  / FRAMESCALE)
1667 #define BSCHYSIZE   (11.0f  / FRAMESCALE)
1668 #define CSCHXSIZE   (24.0f  / FRAMESCALE)
1669 #define CSCHYSIZE   (17.0f  / FRAMESCALE)
1670 #define DSCHXSIZE   (36.0f  / FRAMESCALE)
1671 #define DSCHYSIZE   (24.0f  / FRAMESCALE)
1672 #define ESCHXSIZE   (48.0f  / FRAMESCALE)
1673 #define ESCHYSIZE   (36.0f  / FRAMESCALE)
1674 #define FRAMEWID    ( 0.15f / FRAMESCALE)
1675 #define XLOGOBOX    ( 2.0f  / FRAMESCALE)
1676 #define YLOGOBOX    ( 1.0f  / FRAMESCALE)
1677 
1678 /*
1679  * routine to determine whether cell "np" should have a schematic frame drawn in it.
1680  * Returns 0: there should be a frame whose size is absolute (sets "x" and "y" to the size of the frame)
1681  * Returns 1: there should be a frame but it combines with other stuff in the cell ("x/y" are frame size)
1682  * Returns 2: there is no frame.
1683  */
framesize(INTBIG * x,INTBIG * y,NODEPROTO * np)1684 INTBIG framesize(INTBIG *x, INTBIG *y, NODEPROTO *np)
1685 {
1686 	CHAR *size;
1687 	float wid, hei;
1688 	REGISTER INTBIG retval;
1689 
1690 	size = db_getframedesc(np);
1691 	if (*size == 0) return(2);
1692 	retval = 0;
1693 	switch (size[0])
1694 	{
1695 		case 'h': wid = HASCHXSIZE;  hei = HASCHYSIZE;  break;
1696 		case 'a': wid = ASCHXSIZE;   hei = ASCHYSIZE;   break;
1697 		case 'b': wid = BSCHXSIZE;   hei = BSCHYSIZE;   break;
1698 		case 'c': wid = CSCHXSIZE;   hei = CSCHYSIZE;   break;
1699 		case 'd': wid = DSCHXSIZE;   hei = DSCHYSIZE;   break;
1700 		case 'e': wid = ESCHXSIZE;   hei = ESCHYSIZE;   break;
1701 		case 'x': wid = XLOGOBOX+FRAMEWID;    hei = YLOGOBOX+FRAMEWID;    retval = 1;   break;
1702 		default:  wid = DSCHXSIZE;   hei = DSCHYSIZE;   break;
1703 	}
1704 	if (size[1] == 'v')
1705 	{
1706 		*x = scalefromdispunit(hei - FRAMEWID, DISPUNITINCH);
1707 		*y = scalefromdispunit(wid - FRAMEWID, DISPUNITINCH);
1708 	} else
1709 	{
1710 		*x = scalefromdispunit(wid - FRAMEWID, DISPUNITINCH);
1711 		*y = scalefromdispunit(hei - FRAMEWID, DISPUNITINCH);
1712 	}
1713 	return(retval);
1714 }
1715 
framepolys(NODEPROTO * np)1716 INTBIG framepolys(NODEPROTO *np)
1717 {
1718 	REGISTER INTBIG xsections, ysections, tpolys, total;
1719 	CHAR *size;
1720 
1721 	size = db_getframedesc(np);
1722 	if (*size == 0) return(0);
1723 	if (size[0] == 'x')
1724 	{
1725 		tpolys = 12;
1726 		xsections = 0;
1727 		ysections = 0;
1728 	} else
1729 	{
1730 		tpolys = 12;
1731 		size++;
1732 		while (*size != 0)
1733 		{
1734 			if (*size == 'n') tpolys = 0;
1735 			size++;
1736 		}
1737 		xsections = 4;
1738 		ysections = 8;
1739 	}
1740 
1741 	/* compute number of polygons */
1742 	total = 2+tpolys;
1743 	if (xsections > 0 && ysections > 0)
1744 		total += (xsections-1)*2+(ysections-1)*2+xsections*2+ysections*2;
1745 	return(total);
1746 }
1747 
framepoly(INTBIG boxnum,POLYGON * poly,NODEPROTO * np)1748 void framepoly(INTBIG boxnum, POLYGON *poly, NODEPROTO *np)
1749 {
1750 	REGISTER INTBIG pindex, xsections, ysections, dotitle, titleoffset;
1751 	REGISTER INTBIG xsecsize, ysecsize, schxsize, schysize, framewid, xlogobox, ylogobox;
1752 	float wid, hei;
1753 	REGISTER VARIABLE *var;
1754 	static CHAR line[10];
1755 	static CHAR ndstring[320];
1756 	CHAR *size;
1757 
1758 	/* get true sizes */
1759 	size = db_getframedesc(np);
1760 	dotitle = 1;
1761 	xsections = 8;
1762 	ysections = 4;
1763 	switch (size[0])
1764 	{
1765 		case 'h': wid = HASCHXSIZE;  hei = HASCHYSIZE;  break;
1766 		case 'a': wid = ASCHXSIZE;   hei = ASCHYSIZE;   break;
1767 		case 'b': wid = BSCHXSIZE;   hei = BSCHYSIZE;   break;
1768 		case 'c': wid = CSCHXSIZE;   hei = CSCHYSIZE;   break;
1769 		case 'd': wid = DSCHXSIZE;   hei = DSCHYSIZE;   break;
1770 		case 'e': wid = ESCHXSIZE;   hei = ESCHYSIZE;   break;
1771 		case 'x': wid = XLOGOBOX+FRAMEWID*3;    hei = YLOGOBOX+FRAMEWID*3;    xsections = ysections = 0; break;
1772 		default:  wid = DSCHXSIZE;   hei = DSCHYSIZE;   break;
1773 	}
1774 	size++;
1775 	framewid = scalefromdispunit(FRAMEWID, DISPUNITINCH);
1776 	xlogobox = scalefromdispunit(XLOGOBOX, DISPUNITINCH);
1777 	ylogobox = scalefromdispunit(YLOGOBOX, DISPUNITINCH);
1778 	schxsize = scalefromdispunit(wid, DISPUNITINCH) - framewid;
1779 	schysize = scalefromdispunit(hei, DISPUNITINCH) - framewid;
1780 	while (*size != 0)
1781 	{
1782 		if (*size == 'v')
1783 		{
1784 			xsections = 4;
1785 			ysections = 8;
1786 			schxsize = scalefromdispunit(hei, DISPUNITINCH) - framewid;
1787 			schysize = scalefromdispunit(wid, DISPUNITINCH) - framewid;
1788 		} else if (*size == 'n')
1789 		{
1790 			dotitle = 0;
1791 		}
1792 		size++;
1793 	}
1794 
1795 	if (poly->limit < 4) (void)extendpolygon(poly, 4);
1796 
1797 	if (xsections > 0 && ysections > 0)
1798 	{
1799 		xsecsize = (schxsize - framewid*2) / xsections;
1800 		ysecsize = (schysize - framewid*2) / ysections;
1801 		titleoffset = (xsections-1)*2+(ysections-1)*2+xsections*2+ysections*2;
1802 	} else titleoffset = 0;
1803 	if (boxnum <= 1)
1804 	{
1805 		/* draw the frame */
1806 		if (xsections == 0)
1807 		{
1808 			if (boxnum == 0)
1809 			{
1810 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
1811 				poly->yv[0] = -schysize/2 + framewid;
1812 				poly->xv[1] =  schxsize/2 - framewid;
1813 				poly->yv[1] = -schysize/2 + framewid;
1814 			} else
1815 			{
1816 				poly->xv[0] =  schxsize/2 - framewid;
1817 				poly->yv[0] = -schysize/2 + framewid + ylogobox;
1818 				poly->xv[1] =  schxsize/2 - framewid;
1819 				poly->yv[1] = -schysize/2 + framewid;
1820 			}
1821 		} else
1822 		{
1823 			if (boxnum == 0)
1824 			{
1825 				poly->xv[0] = -schxsize/2;   poly->yv[0] = -schysize/2;
1826 				poly->xv[1] =  schxsize/2;   poly->yv[1] =  schysize/2;
1827 			} else
1828 			{
1829 				poly->xv[0] = -schxsize/2 + framewid;
1830 				poly->yv[0] = -schysize/2 + framewid;
1831 				poly->xv[1] =  schxsize/2 - framewid;
1832 				poly->yv[1] =  schysize/2 - framewid;
1833 			}
1834 		}
1835 		poly->count = 2;
1836 		poly->style = CLOSEDRECT;
1837 	} else if (boxnum < xsections*2)
1838 	{
1839 		/* tick marks along top and bottom sides */
1840 		pindex = (boxnum-2) % (xsections-1) + 1;
1841 		poly->xv[0] = poly->xv[1] = pindex * xsecsize - (schxsize/2 - framewid);
1842 		if (boxnum <= xsections)
1843 		{
1844 			poly->yv[0] = schysize/2 - framewid;
1845 			poly->yv[1] = schysize/2 - framewid/2;
1846 		} else
1847 		{
1848 			poly->yv[0] = -schysize/2 + framewid;
1849 			poly->yv[1] = -schysize/2 + framewid/2;
1850 		}
1851 		poly->count = 2;
1852 		poly->style = OPENED;
1853 	} else if (boxnum < xsections*2+ysections*2-2)
1854 	{
1855 		/* tick marks along left and right sides */
1856 		pindex = (boxnum-2-(xsections-1)*2) % (ysections-1) + 1;
1857 		poly->yv[0] = poly->yv[1] = pindex * ysecsize - (schysize/2 - framewid);
1858 		if (boxnum <= (xsections-1)*2+ysections)
1859 		{
1860 			poly->xv[0] = schxsize/2 - framewid;
1861 			poly->xv[1] = schxsize/2 - framewid/2;
1862 		} else
1863 		{
1864 			poly->xv[0] = -schxsize/2 + framewid;
1865 			poly->xv[1] = -schxsize/2 + framewid/2;
1866 		}
1867 		poly->count = 2;
1868 		poly->style = OPENED;
1869 	} else if (boxnum < xsections*2+ysections*2-2+xsections*2)
1870 	{
1871 		/* section numbers along top and bottom */
1872 		pindex = (boxnum-2-(xsections-1)*2-(ysections-1)*2) % xsections;
1873 		poly->xv[0] = poly->xv[1] = pindex * xsecsize - (schxsize/2 - framewid);
1874 		poly->xv[2] = poly->xv[3] = poly->xv[0] + xsecsize;
1875 		if (boxnum <= (xsections-1)*2+(ysections-1)*2+xsections+1)
1876 		{
1877 			poly->yv[0] = poly->yv[3] = schysize/2 - framewid;
1878 			poly->yv[1] = poly->yv[2] = schysize/2;
1879 		} else
1880 		{
1881 			poly->yv[0] = poly->yv[3] = -schysize/2;
1882 			poly->yv[1] = poly->yv[2] = -schysize/2 + framewid;
1883 		}
1884 		poly->count = 4;
1885 		poly->style = TEXTBOX;
1886 		TDCLEAR(poly->textdescript);
1887 		TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
1888 		poly->tech = el_curtech;
1889 		(void)esnprintf(line, 10, x_("%ld"), xsections-pindex);
1890 		poly->string = line;
1891 	} else if (boxnum < xsections*2+ysections*2-2+xsections*2+ysections*2)
1892 	{
1893 		/* section numbers along left and right */
1894 		pindex = (boxnum-2-(xsections-1)*2-(ysections-1)*2-xsections*2) % ysections;
1895 		poly->yv[0] = poly->yv[1] = pindex * ysecsize - (schysize/2 - framewid);
1896 		poly->yv[2] = poly->yv[3] = poly->yv[0] + ysecsize;
1897 		if (boxnum <= (xsections-1)*2+(ysections-1)*2+xsections*2+ysections+1)
1898 		{
1899 			poly->xv[0] = poly->xv[3] = schxsize/2 - framewid;
1900 			poly->xv[1] = poly->xv[2] = schxsize/2;
1901 		} else
1902 		{
1903 			poly->xv[0] = poly->xv[3] = -schxsize/2;
1904 			poly->xv[1] = poly->xv[2] = -schxsize/2 + framewid;
1905 		}
1906 		poly->count = 4;
1907 		poly->style = TEXTBOX;
1908 		TDCLEAR(poly->textdescript);
1909 		TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
1910 		poly->tech = el_curtech;
1911 		line[0] = (CHAR)('A' + pindex);
1912 		line[1] = 0;
1913 		poly->string = line;
1914 	} else
1915 	{
1916 		switch (boxnum-titleoffset-2)
1917 		{
1918 			case 0:			/* frame around cell name/version */
1919 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
1920 				poly->yv[0] = -schysize/2 + framewid + ylogobox / 3;
1921 				poly->xv[1] =  schxsize/2 - framewid;
1922 				poly->yv[1] = -schysize/2 + framewid + ylogobox / 2;
1923 				poly->count = 2;
1924 				poly->style = CLOSEDRECT;
1925 				break;
1926 			case 1:			/* cell name/version */
1927 				if (np == NONODEPROTO) break;
1928 				(void)esnprintf(ndstring, 320, _("Name: %s"), describenodeproto(np));
1929 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
1930 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
1931 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid + ylogobox;
1932 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 4;
1933 				poly->count = 4;
1934 				poly->style = TEXTBOX;
1935 				TDCLEAR(poly->textdescript);
1936 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
1937 				poly->tech = el_curtech;
1938 				poly->string = ndstring;
1939 				break;
1940 			case 2:			/* frame around designer name */
1941 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
1942 				poly->yv[0] = -schysize/2 + framewid + ylogobox / 2;
1943 				poly->xv[1] =  schxsize/2 - framewid;
1944 				poly->yv[1] = -schysize/2 + framewid + ylogobox / 4 * 3;
1945 				poly->count = 2;
1946 				poly->style = CLOSEDRECT;
1947 				break;
1948 			case 3:			/* designer name */
1949 				var = getval((INTBIG)np->lib, VLIBRARY, VSTRING, x_("USER_drawing_designer_name"));
1950 				if (var == NOVARIABLE)
1951 					var = getval((INTBIG)us_tool, VTOOL, VSTRING, x_("USER_drawing_designer_name"));
1952 				if (var == NOVARIABLE) break;
1953 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
1954 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
1955 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid + ylogobox / 2;
1956 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 4;
1957 				poly->count = 4;
1958 				poly->style = TEXTBOX;
1959 				TDCLEAR(poly->textdescript);
1960 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
1961 				poly->tech = el_curtech;
1962 				poly->string = (CHAR *)var->addr;
1963 				break;
1964 			case 4:			/* frame around creation date */
1965 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
1966 				poly->yv[0] = -schysize/2 + framewid + ylogobox / 6;
1967 				poly->xv[1] =  schxsize/2 - framewid;
1968 				poly->yv[1] = -schysize/2 + framewid + ylogobox / 3;
1969 				poly->count = 2;
1970 				poly->style = CLOSEDRECT;
1971 				break;
1972 			case 5:			/* creation date */
1973 				if (np == NONODEPROTO) break;
1974 				(void)esnprintf(ndstring, 320, _("Created: %s"), timetostring((time_t)np->creationdate));
1975 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
1976 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
1977 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid + ylogobox / 6;
1978 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 6;
1979 				poly->count = 4;
1980 				poly->style = TEXTBOX;
1981 				TDCLEAR(poly->textdescript);
1982 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
1983 				poly->tech = el_curtech;
1984 				poly->string = ndstring;
1985 				break;
1986 			case 6:			/* frame around revision date */
1987 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
1988 				poly->yv[0] = -schysize/2 + framewid;
1989 				poly->xv[1] =  schxsize/2 - framewid;
1990 				poly->yv[1] = -schysize/2 + framewid + ylogobox / 6;
1991 				poly->count = 2;
1992 				poly->style = CLOSEDRECT;
1993 				break;
1994 			case 7:			/* revision date */
1995 				if (np == NONODEPROTO) break;
1996 				(void)esnprintf(ndstring, 320, _("Revised: %s"), timetostring((time_t)np->revisiondate));
1997 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
1998 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
1999 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid;
2000 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 6;
2001 				poly->count = 4;
2002 				poly->style = TEXTBOX;
2003 				TDCLEAR(poly->textdescript);
2004 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
2005 				poly->tech = el_curtech;
2006 				poly->string = ndstring;
2007 				break;
2008 			case 8:			/* frame around project name */
2009 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
2010 				poly->yv[0] = -schysize/2 + framewid + ylogobox / 4 * 3;
2011 				poly->xv[1] =  schxsize/2 - framewid;
2012 				poly->yv[1] = -schysize/2 + framewid + ylogobox;
2013 				poly->count = 2;
2014 				poly->style = CLOSEDRECT;
2015 				break;
2016 			case 9:			/* project name */
2017 				var = getval((INTBIG)np->lib, VLIBRARY, VSTRING, x_("USER_drawing_project_name"));
2018 				if (var == NOVARIABLE)
2019 					var = getval((INTBIG)us_tool, VTOOL, VSTRING, x_("USER_drawing_project_name"));
2020 				if (var == NOVARIABLE) break;
2021 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
2022 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
2023 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid + ylogobox / 4 * 3;
2024 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 4;
2025 				poly->count = 4;
2026 				poly->style = TEXTBOX;
2027 				TDCLEAR(poly->textdescript);
2028 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
2029 				poly->tech = el_curtech;
2030 				poly->string = (CHAR *)var->addr;
2031 				break;
2032 			case 10:		/* frame around company name */
2033 				poly->xv[0] =  schxsize/2 - framewid - xlogobox;
2034 				poly->yv[0] = -schysize/2 + framewid + ylogobox;
2035 				poly->xv[1] =  schxsize/2 - framewid;
2036 				poly->yv[1] = -schysize/2 + framewid + ylogobox / 4 * 5;
2037 				poly->count = 2;
2038 				poly->style = CLOSEDRECT;
2039 				break;
2040 			case 11:		/* company name */
2041 				var = getval((INTBIG)np->lib, VLIBRARY, VSTRING, x_("USER_drawing_company_name"));
2042 				if (var == NOVARIABLE)
2043 					var = getval((INTBIG)us_tool, VTOOL, VSTRING, x_("USER_drawing_company_name"));
2044 				if (var == NOVARIABLE) break;
2045 				poly->xv[0] = poly->xv[3] = schxsize/2 - framewid - xlogobox;
2046 				poly->xv[1] = poly->xv[2] = schxsize/2 - framewid;
2047 				poly->yv[0] = poly->yv[1] = -schysize/2 + framewid + ylogobox / 3;
2048 				poly->yv[2] = poly->yv[3] = poly->yv[0] + ylogobox / 6;
2049 				poly->count = 4;
2050 				poly->style = TEXTBOX;
2051 				TDCLEAR(poly->textdescript);
2052 				TDSETSIZE(poly->textdescript, TXTSETQLAMBDA(PRINTFRAMEQLAMBDA));
2053 				poly->tech = el_curtech;
2054 				poly->string = (CHAR *)var->addr;
2055 				break;
2056 		}
2057 	}
2058 	poly->layer = -1;
2059 	poly->desc = &tech_frame;
2060 }
2061 
db_getframedesc(NODEPROTO * np)2062 CHAR *db_getframedesc(NODEPROTO *np)
2063 {
2064 	static CHAR line[10];
2065 	REGISTER VARIABLE *var;
2066 	REGISTER CHAR *pt;
2067 
2068 	(void)estrcpy(line, x_(""));
2069 	if (np != NONODEPROTO)
2070 	{
2071 		var = getvalkey((INTBIG)np, VNODEPROTO, VSTRING, el_schematic_page_size_key);
2072 		if (var != NOVARIABLE)
2073 		{
2074 			(void)estrncpy(line, (CHAR *)var->addr, 9);
2075 			for(pt = line; *pt != 0; pt++) if (isupper(*pt)) *pt = tolower(*pt);
2076 		}
2077 	}
2078 	return(line);
2079 }
2080 
2081 /************************* COPYING AND REPLACING *************************/
2082 
2083 /*
2084  * routine to copy nodeproto "fromnt" to the library "tolib" with the nodeproto
2085  * name "toname".  If "useexisting" is TRUE, then a cross-library copy that finds
2086  * instances in the destination library which are the same as in the source library
2087  * will use those existing instances instead of making them into cross-library references.
2088  * Returns address of new nodeproto copy if sucessful, NONODEPROTO if not.
2089  */
copynodeproto(NODEPROTO * fromnp,LIBRARY * tolib,CHAR * toname,BOOLEAN useexisting)2090 NODEPROTO *copynodeproto(NODEPROTO *fromnp, LIBRARY *tolib, CHAR *toname, BOOLEAN useexisting)
2091 {
2092 	NODEINST *ono[2];
2093 	PORTPROTO *opt[2];
2094 	REGISTER NODEINST *ni, *toni;
2095 	REGISTER ARCINST *ai, *toai;
2096 	REGISTER NODEPROTO *np, *lnt;
2097 	REGISTER PORTARCINST *pi;
2098 	REGISTER PORTEXPINST *pe;
2099 	REGISTER BOOLEAN maysubstitute;
2100 	REGISTER INTBIG i, failures, res;
2101 	REGISTER PORTPROTO *pp, *ppt, *p1, *p2;
2102 	REGISTER CHAR *ptr, *cellname;
2103 	REGISTER LIBRARY *destlib;
2104 	INTBIG lx, hx, ly, hy;
2105 	REGISTER void *infstr;
2106 
2107 	/* check for validity */
2108 	if (fromnp == NONODEPROTO) return((NODEPROTO *)db_error(DBBADCELL|DBCOPYNODEPROTO));
2109 	if (tolib == NOLIBRARY) return((NODEPROTO *)db_error(DBBADLIB|DBCOPYNODEPROTO));
2110 	if (fromnp->primindex != 0) return((NODEPROTO *)db_error(DBPRIMITIVE|DBCOPYNODEPROTO));
2111 
2112 	/* make sure name of new cell is valid */
2113 	for(ptr = toname; *ptr != 0; ptr++)
2114 		if (*ptr <= ' ' || *ptr == ':' || *ptr >= 0177)
2115 			return((NODEPROTO *)db_error(DBBADNAME|DBCOPYNODEPROTO));
2116 
2117 	/* determine whether this copy is to a different library */
2118 	if (tolib == fromnp->lib) destlib = NOLIBRARY; else destlib = tolib;
2119 
2120 	/* mark the proper prototype to use for each node */
2121 	for(ni = fromnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2122 		ni->temp1 = (INTBIG)ni->proto;
2123 
2124 	/* if doing a cross-library copy and can use existing ones from new library, do it */
2125 	if (destlib != NOLIBRARY)
2126 	{
2127 		/* scan all subcells to see if they are found in the new library */
2128 		for(ni = fromnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2129 		{
2130 			if (ni->proto->primindex != 0) continue;
2131 
2132 			/* keep cross-library references */
2133 			if (ni->proto->lib != fromnp->lib) continue;
2134 
2135 			maysubstitute = useexisting;
2136 			if (!maysubstitute)
2137 			{
2138 				/* force substitution for documentation icons */
2139 				if (ni->proto->cellview == el_iconview)
2140 				{
2141 					if (isiconof(ni->proto, fromnp)) maysubstitute = TRUE;
2142 				}
2143 			}
2144 			if (!maysubstitute) continue;
2145 
2146 			/* search for cell with same name and view in new library */
2147 			for(lnt = tolib->firstnodeproto; lnt != NONODEPROTO; lnt = lnt->nextnodeproto)
2148 				if (namesame(lnt->protoname, ni->proto->protoname) == 0 &&
2149 					lnt->cellview == ni->proto->cellview) break;
2150 			if (lnt == NONODEPROTO) continue;
2151 
2152 			/* make sure all used ports can be found on the uncopied cell */
2153 			for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2154 			{
2155 				pp = pi->proto;
2156 				ppt = getportproto(lnt, pp->protoname);
2157 				if (ppt != NOPORTPROTO)
2158 				{
2159 					/* the connections must match, too */
2160 					if (pp->connects != ppt->connects) ppt = NOPORTPROTO;
2161 				}
2162 				if (ppt == NOPORTPROTO)
2163 				{
2164 					ttyputerr(_("Cannot use subcell %s in library %s: exports don't match"),
2165 						nldescribenodeproto(lnt), destlib->libname);
2166 					break;
2167 				}
2168 			}
2169 			if (pi != NOPORTARCINST) continue;
2170 			for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2171 			{
2172 				pp = pe->proto;
2173 				ppt = getportproto(lnt, pp->protoname);
2174 				if (ppt != NOPORTPROTO)
2175 				{
2176 					/* the connections must match, too */
2177 					if (pp->connects != ppt->connects) ppt = NOPORTPROTO;
2178 				}
2179 				if (ppt == NOPORTPROTO)
2180 				{
2181 					ttyputerr(_("Cannot use subcell %s in library %s: exports don't match"),
2182 						nldescribenodeproto(lnt), destlib->libname);
2183 					break;
2184 				}
2185 			}
2186 
2187 			/* match found: use the prototype from the destination library */
2188 			ni->temp1 = (INTBIG)lnt;
2189 		}
2190 	}
2191 
2192 	/* create the nodeproto */
2193 	if (toname[estrlen(toname)-1] == '}' || fromnp->cellview->sviewname[0] == 0)
2194 		cellname = toname; else
2195 	{
2196 		infstr = initinfstr();
2197 		addstringtoinfstr(infstr, toname);
2198 		addtoinfstr(infstr, '{');
2199 		addstringtoinfstr(infstr, fromnp->cellview->sviewname);
2200 		addtoinfstr(infstr, '}');
2201 		cellname = returninfstr(infstr);
2202 	}
2203 	np = newnodeproto(cellname, tolib);
2204 	if (np == NONODEPROTO) return(NONODEPROTO);
2205 	np->userbits = fromnp->userbits;
2206 
2207 	/* zero the count of variables that failed to copy */
2208 	failures = 0;
2209 
2210 	/* copy nodes */
2211 	for(ni = fromnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2212 	{
2213 		/* create the new nodeinst */
2214 		lnt = (NODEPROTO *)ni->temp1;
2215 		toni = db_newnodeinst(lnt, ni->lowx, ni->highx, ni->lowy, ni->highy,
2216 			ni->transpose, ni->rotation, np);
2217 		if (toni == NONODEINST) return(NONODEPROTO);
2218 
2219 		/* save the new nodeinst address in the old nodeinst */
2220 		ni->temp1 = (UINTBIG)toni;
2221 
2222 		/* copy miscellaneous information */
2223 		TDCOPY(toni->textdescript, ni->textdescript);
2224 		toni->userbits = ni->userbits;
2225 	}
2226 
2227 	/* now copy the variables on the nodes */
2228 	for(ni = fromnp->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
2229 	{
2230 		toni = (NODEINST *)ni->temp1;
2231 		res = db_copyxlibvars((INTBIG)ni, VNODEINST, (INTBIG)toni, VNODEINST, fromnp, destlib);
2232 		if (res < 0) return(NONODEPROTO);
2233 		failures += res;
2234 
2235 		/* variables may affect geometry size */
2236 		boundobj(toni->geom, &lx, &hx, &ly, &hy);
2237 		if (lx != toni->geom->lowx || hx != toni->geom->highx ||
2238 			ly != toni->geom->lowy || hy != toni->geom->highy)
2239 				updategeom(toni->geom, toni->parent);
2240 	}
2241 
2242 	/* copy arcs */
2243 	for(ai = fromnp->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
2244 	{
2245 		/* find the nodeinst and portinst connections for this arcinst */
2246 		for(i=0; i<2; i++)
2247 		{
2248 			opt[i] = NOPORTPROTO;
2249 			ono[i] = (NODEINST *)ai->end[i].nodeinst->temp1;
2250 			if (ono[i]->proto->primindex != 0)
2251 			{
2252 				/* primitives associate ports directly */
2253 				for(pp = ai->end[i].nodeinst->proto->firstportproto,
2254 					ppt = ono[i]->proto->firstportproto; pp != NOPORTPROTO;
2255 						pp = pp->nextportproto, ppt = ppt->nextportproto)
2256 							if (pp == ai->end[i].portarcinst->proto)
2257 				{
2258 					opt[i] = ppt;
2259 					break;
2260 				}
2261 			} else
2262 			{
2263 				/* cells associate ports by name */
2264 				pp = ai->end[i].portarcinst->proto;
2265 				ppt = getportproto(ono[i]->proto, pp->protoname);
2266 				if (ppt != NOPORTPROTO) opt[i] = ppt;
2267 			}
2268 			if (opt[i] == NOPORTPROTO)
2269 				ttyputerr(_("Error: no port for %s arc on %s node"), describearcproto(ai->proto),
2270 					describenodeproto(ai->end[i].nodeinst->proto));
2271 		}
2272 
2273 		/* create the arcinst */
2274 		toai = db_newarcinst(ai->proto, ai->width, ai->userbits, ono[0],opt[0],
2275 			ai->end[0].xpos, ai->end[0].ypos, ono[1],opt[1], ai->end[1].xpos, ai->end[1].ypos, np);
2276 		if (toai == NOARCINST) return(NONODEPROTO);
2277 
2278 		/* copy arcinst variables */
2279 		res = db_copyxlibvars((INTBIG)ai, VARCINST, (INTBIG)toai, VARCINST, fromnp, destlib);
2280 		if (res < 0) return(NONODEPROTO);
2281 		failures += res;
2282 		res = db_copyxlibvars((INTBIG)ai->end[0].portarcinst, VPORTARCINST,
2283 			(INTBIG)toai->end[0].portarcinst, VPORTARCINST, fromnp, destlib);
2284 		if (res < 0) return(NONODEPROTO);
2285 		failures += res;
2286 		res = db_copyxlibvars((INTBIG)ai->end[1].portarcinst, VPORTARCINST,
2287 			(INTBIG)toai->end[1].portarcinst, VPORTARCINST, fromnp, destlib);
2288 		if (res < 0) return(NONODEPROTO);
2289 		failures += res;
2290 
2291 		/* copy miscellaneous information */
2292 		toai->userbits = ai->userbits;
2293 
2294 		/* variables may affect geometry size */
2295 		boundobj(toai->geom, &lx, &hx, &ly, &hy);
2296 		if (lx != toai->geom->lowx || hx != toai->geom->highx ||
2297 			ly != toai->geom->lowy || hy != toai->geom->highy)
2298 				updategeom(toai->geom, toai->parent);
2299 	}
2300 
2301 	/* copy the portprotos */
2302 	for(pp = fromnp->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
2303 	{
2304 		/* match sub-portproto in old nodeinst to sub-portproto in new one */
2305 		ni = (NODEINST *)pp->subnodeinst->temp1;
2306 		for(p1 = ni->proto->firstportproto, p2 = pp->subnodeinst->proto->firstportproto;
2307 			p1 != NOPORTPROTO && p2 != NOPORTPROTO;
2308 				p1 = p1->nextportproto, p2 = p2->nextportproto)
2309 					if (pp->subportproto == p2) break;
2310 		if (pp->subportproto != p2)
2311 			ttyputerr(_("Error: no port on %s cell"), describenodeproto(pp->subnodeinst->proto));
2312 
2313 		/* create the nodeinst portinst */
2314 		ppt = db_newportproto(np, ni, p1, pp->protoname);
2315 		if (ppt == NOPORTPROTO) return(NONODEPROTO);
2316 
2317 		/* copy portproto variables */
2318 		res = db_copyxlibvars((INTBIG)pp, VPORTPROTO, (INTBIG)ppt, VPORTPROTO, fromnp, destlib);
2319 		if (res < 0) return(NONODEPROTO);
2320 		failures += res;
2321 		res = db_copyxlibvars((INTBIG)pp->subportexpinst, VPORTEXPINST,
2322 			(INTBIG)ppt->subportexpinst, VPORTEXPINST, fromnp, destlib);
2323 		if (res < 0) return(NONODEPROTO);
2324 		failures += res;
2325 
2326 		/* copy miscellaneous information */
2327 		ppt->userbits = pp->userbits;
2328 		TDCOPY(ppt->textdescript, pp->textdescript);
2329 	}
2330 
2331 	/* copy cell variables */
2332 	res = db_copyxlibvars((INTBIG)fromnp, VNODEPROTO, (INTBIG)np, VNODEPROTO, fromnp, destlib);
2333 	if (res < 0) return(NONODEPROTO);
2334 	failures += res;
2335 
2336 	/* report failures to copy variables across libraries */
2337 	if (failures != 0)
2338 		ttyputmsg(_("WARNING: cross-library copy of cell %s deleted %ld variables"),
2339 			describenodeproto(fromnp), failures);
2340 
2341 	/* reset (copy) date information */
2342 	np->creationdate = fromnp->creationdate;
2343 	np->revisiondate = fromnp->revisiondate;
2344 
2345 	return(np);
2346 }
2347 
2348 /*
2349  * helper routine for "copynodeproto()" to copy the variables in the list
2350  * "fromfirstvar"/"fromnumvar" to "tofirstvar"/"tonumvar".  The variables are
2351  * originally part of cell "cell",  If "destlib" is not NOLIBRARY,
2352  * this copy is from a different library and, therefore, pointers to objects in one library
2353  * should be converted if possible, or not copied.  Returns negative on error, positive
2354  * to indicate the number of variables that could not be copied (because of cross-
2355  * library reference inablilties), and zero for complete success.
2356  *
2357  * Note that there is only a limited number of cross-library conversion functions
2358  * implemented, specifically those that are used elsewhere in Electric.
2359  * At this time, the routine can copy NODEPROTO pointers (scalar and array)
2360  * and it can copy NODEINST pointers (scalar only).
2361  */
db_copyxlibvars(INTBIG fromaddr,INTBIG fromtype,INTBIG toaddr,INTBIG totype,NODEPROTO * cell,LIBRARY * destlib)2362 INTBIG db_copyxlibvars(INTBIG fromaddr, INTBIG fromtype, INTBIG toaddr,
2363 	INTBIG totype, NODEPROTO *cell, LIBRARY *destlib)
2364 {
2365 	REGISTER INTBIG i, j, failures;
2366 	BOOLEAN skipit;
2367 	REGISTER INTBIG key, addr, type, len, *newaddr;
2368 	INTSML *numvar;
2369 	VARIABLE **firstvar;
2370 	REGISTER VARIABLE *var;
2371 	REGISTER NODEPROTO *np, *onp;
2372 	REGISTER NODEINST *ni;
2373 
2374 	if (db_getvarptr(fromaddr, fromtype, &firstvar, &numvar)) return(-1);
2375 
2376 	failures = 0;
2377 	for(i=0; i<(*numvar); i++)
2378 	{
2379 		key = (*firstvar)[i].key;
2380 		addr = (*firstvar)[i].addr;
2381 		type = (*firstvar)[i].type;
2382 		newaddr = 0;
2383 		skipit = FALSE;
2384 		if (destlib != NOLIBRARY)
2385 		{
2386 			switch (type&VTYPE)
2387 			{
2388 				case VNODEPROTO:
2389 					if ((type&VISARRAY) == 0)
2390 					{
2391 						np = (NODEPROTO *)addr;
2392 						if (np == NONODEPROTO) break;
2393 						if (np->primindex != 0) break;
2394 						for(onp = destlib->firstnodeproto; onp != NONODEPROTO; onp = onp->nextnodeproto)
2395 							if (namesame(onp->protoname, np->protoname) == 0 &&
2396 								onp->cellview == np->cellview) break;
2397 						if (onp != NONODEPROTO)
2398 						{
2399 							addr = (INTBIG)onp;
2400 							break;
2401 						}
2402 					} else
2403 					{
2404 						len = (type&VLENGTH) >> VLENGTHSH;
2405 						if (len != 0)
2406 						{
2407 							newaddr = (INTBIG *)emalloc(len * SIZEOFINTBIG, el_tempcluster);
2408 							if (newaddr == 0) return(-1);
2409 							for(j=0; j<len; j++)
2410 							{
2411 								np = ((NODEPROTO **)addr)[j];
2412 								newaddr[j] = (INTBIG)np;
2413 								if (np == NONODEPROTO) continue;
2414 								if (np->primindex != 0) continue;
2415 								for(onp = destlib->firstnodeproto; onp != NONODEPROTO;
2416 									onp = onp->nextnodeproto)
2417 										if (namesame(onp->protoname, np->protoname) == 0 &&
2418 											onp->cellview == np->cellview) break;
2419 								newaddr[j] = (INTBIG)onp;
2420 								if (onp == NONODEPROTO) failures++;
2421 							}
2422 							addr = (INTBIG)newaddr;
2423 							break;
2424 						}
2425 					}
2426 					skipit = TRUE;
2427 					failures++;
2428 					break;
2429 				case VPORTARCINST:
2430 				case VPORTEXPINST:
2431 				case VPORTPROTO:
2432 				case VARCINST:
2433 				case VGEOM:
2434 				case VRTNODE:
2435 				case VLIBRARY:
2436 					skipit = TRUE;
2437 					failures++;
2438 					break;
2439 			}
2440 		}
2441 
2442 		/* always convert NODEINST references, regardless of destination library */
2443 		if ((type&VTYPE) == VNODEINST)
2444 		{
2445 			if ((type&VISARRAY) == 0)
2446 			{
2447 				ni = (NODEINST *)addr;
2448 				if (ni != NONODEINST)
2449 				{
2450 					if (ni->parent == cell) addr = ni->temp1;
2451 				}
2452 			} else
2453 			{
2454 				skipit = TRUE;
2455 				failures++;
2456 			}
2457 		}
2458 
2459 		if (skipit) continue;
2460 		var = setvalkey(toaddr, totype, key, addr, type);
2461 		if (var == NOVARIABLE) return(-1);
2462 		TDCOPY(var->textdescript, (*firstvar)[i].textdescript);
2463 		if (newaddr != 0) efree((CHAR *)newaddr);
2464 	}
2465 	return(failures);
2466 }
2467 
2468 /*
2469  * routine to replace nodeinst "ni" with one of type "np", leaving all arcs
2470  * intact.  The routine returns the address of the new replacing nodeinst if
2471  * successful, NONODEINST if the replacement cannot be done.  If "ignoreportnames"
2472  * is true, do not use port names when determining association between old
2473  * and new prototype.  If "allowmissingports" is true, allow replacement
2474  * to have missing ports and, therefore, delete the arcs that used to be there.
2475  */
replacenodeinst(NODEINST * ni,NODEPROTO * np,BOOLEAN ignoreportnames,BOOLEAN allowmissingports)2476 NODEINST *replacenodeinst(NODEINST *ni, NODEPROTO *np, BOOLEAN ignoreportnames,
2477 	BOOLEAN allowmissingports)
2478 {
2479 	REGISTER PORTPROTO *opt, **newportprotos, **newexpportprotos;
2480 	INTBIG endx[2], endy[2], bx, by, cx, cy, lxo, hxo, lyo, hyo, lx, hx, ly, hy, psx, psy,
2481 		arcdx, arcdy, arccount, expcount, thisend, xp, yp, other;
2482 	REGISTER INTBIG i, j, zigzag, ang, fixedalignment, dx, dy, alignment, lambda;
2483 	XARRAY trans;
2484 	REGISTER VARIABLE *varold, *varnew;
2485 	NODEINST *endnodeinst[2];
2486 	PORTPROTO *endportproto[2];
2487 	REGISTER PORTARCINST *pi, **newports;
2488 	REGISTER PORTEXPINST *pe, **newexports;
2489 	REGISTER ARCINST *ai, *newai, *newai2, *aiswap;
2490 	REGISTER NODEPROTO *pinnp;
2491 	REGISTER NODEINST *newno, *newni;
2492 	static POLYGON *poly = NOPOLYGON;
2493 
2494 	/* make sure there is a polygon */
2495 	(void)needstaticpolygon(&poly, 4, db_cluster);
2496 
2497 	/* check for recursion */
2498 	if (isachildof(ni->parent, np))
2499 		return((NODEINST *)db_error(DBRECURSIVE|DBREPLACENODEINST));
2500 
2501 	/* set the new node size */
2502 	fixedalignment = 0;
2503 	if (np->primindex == 0)
2504 	{
2505 		/* cell replacement: determine location of replacement */
2506 		if (ni->proto->primindex == 0)
2507 		{
2508 			/* replacing one cell with another */
2509 			lx = ni->lowx - ni->proto->lowx + np->lowx;
2510 			hx = ni->highx - ni->proto->highx + np->highx;
2511 			ly = ni->lowy - ni->proto->lowy + np->lowy;
2512 			hy = ni->highy - ni->proto->highy + np->highy;
2513 		} else
2514 		{
2515 			/* replacing a primitive with a cell */
2516 			lx = (ni->lowx+ni->highx)/2 - (np->highx-np->lowx)/2;
2517 			hx = lx + np->highx-np->lowx;
2518 			ly = (ni->lowy+ni->highy)/2 - (np->highy-np->lowy)/2;
2519 			hy = ly + np->highy-np->lowy;
2520 		}
2521 
2522 		/* adjust position for cell center alignment */
2523 		varold = getvalkey((INTBIG)ni->proto, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
2524 		varnew = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_prototype_center_key);
2525 		if (varold != NOVARIABLE && varnew != NOVARIABLE)
2526 		{
2527 			fixedalignment = 1;
2528 			corneroffset(ni, ni->proto, ni->rotation, ni->transpose, &bx, &by, FALSE);
2529 			corneroffset(NONODEINST, np, ni->rotation, ni->transpose, &cx, &cy, FALSE);
2530 			lx = ni->lowx;   hx = lx + np->highx - np->lowx;
2531 			ly = ni->lowy;   hy = ly + np->highy - np->lowy;
2532 			lx += bx-cx;   hx += bx-cx;
2533 			ly += by-cy;   hy += by-cy;
2534 		} else
2535 		{
2536 			/* no cell centers: if sizes differ, make sure alignment is sensible */
2537 			if (ni->highx-ni->lowx != np->highx-np->lowx ||
2538 				ni->highy-ni->lowy != np->highy-np->lowy)
2539 			{
2540 				lambda = lambdaofcell(np);
2541 				alignment = muldiv(us_alignment_ratio, lambda, WHOLE);
2542 				dx = lx - us_alignvalue(lx, alignment, &other);
2543 				lx -= dx;   hx -= dx;
2544 				dy = ly - us_alignvalue(ly, alignment, &other);
2545 				ly -= dy;   hy -= dy;
2546 			}
2547 		}
2548 	} else
2549 	{
2550 		/* primitive replacement: compute proper size of new part */
2551 		nodesizeoffset(ni, &lxo, &lyo, &hxo, &hyo);
2552 		nodeprotosizeoffset(np, &lx, &ly, &hx, &hy, ni->parent);
2553 		lx = ni->lowx + lxo - lx;   hx = ni->highx - hxo + hx;
2554 		ly = ni->lowy + lyo - ly;   hy = ni->highy - hyo + hy;
2555 	}
2556 
2557 	/* first create the new nodeinst in place */
2558 	newno = db_newnodeinst(np, lx, hx, ly, hy, ni->transpose, ni->rotation, ni->parent);
2559 	if (newno == NONODEINST) return(NONODEINST);
2560 
2561 	/* set the change cell environment */
2562 	db_setchangecell(ni->parent);
2563 
2564 	/* draw new node expanded if appropriate */
2565 	if (ni->proto->primindex == 0)
2566 	{
2567 		/* replacing an instance: copy the expansion bit */
2568 		newno->userbits |= ni->userbits&NEXPAND;
2569 	} else
2570 	{
2571 		/* replacing a primitive: use default expansion for the cell */
2572 		if ((np->userbits&WANTNEXPAND) != 0) newno->userbits |= NEXPAND;
2573 	}
2574 
2575 	/* associate the ports between these nodes */
2576 	if (db_portassociate(ni, newno, ignoreportnames))
2577 	{
2578 		db_killnodeinst(newno);
2579 		return((NODEINST *)db_error(DBNOMEM|DBREPLACENODEINST));
2580 	}
2581 	expcount = 0;
2582 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst) expcount++;
2583 	if (expcount > 0)
2584 	{
2585 		newexports = (PORTEXPINST **)emalloc(expcount * (sizeof (PORTEXPINST *)), db_cluster);
2586 		if (newexports == 0) return((NODEINST *)db_error(DBNOMEM|DBREPLACENODEINST));
2587 		newexpportprotos = (PORTPROTO **)emalloc(expcount * (sizeof (PORTPROTO *)), db_cluster);
2588 		if (newexpportprotos == 0) return((NODEINST *)db_error(DBNOMEM|DBREPLACENODEINST));
2589 		expcount = 0;
2590 		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2591 		{
2592 			newexports[expcount] = pe;
2593 			newexpportprotos[expcount] = (PORTPROTO *)pe->proto->temp1;
2594 			expcount++;
2595 		}
2596 	}
2597 
2598 	/* see if the old arcs can connect to ports */
2599 	arcdx = arcdy = 0;
2600 	arccount = 0;
2601 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2602 	{
2603 		/* make sure there is an association for this port */
2604 		opt = (PORTPROTO *)pi->proto->temp1;
2605 		if (opt == NOPORTPROTO)
2606 		{
2607 			if (allowmissingports) continue;
2608 			if (db_printerrors)
2609 				ttyputmsg(_("No port on new node corresponds to old port: %s"), pi->proto->protoname);
2610 			db_killnodeinst(newno);
2611 			return((NODEINST *)db_error(DBPORTMM|DBREPLACENODEINST));
2612 		}
2613 
2614 		/* make sure the arc can connect to this type of port */
2615 		ai = pi->conarcinst;
2616 		for(i = 0; opt->connects[i] != NOARCPROTO; i++)
2617 			if (opt->connects[i] == ai->proto) break;
2618 		if (opt->connects[i] == NOARCPROTO)
2619 		{
2620 			if (allowmissingports) continue;
2621 			if (db_printerrors)
2622 				ttyputmsg(_("%s arc on old port %s cannot connect to new port %s"),
2623 					describearcinst(ai), pi->proto->protoname, opt->protoname);
2624 			db_killnodeinst(newno);
2625 			return((NODEINST *)db_error(DBPORTMM|DBREPLACENODEINST));
2626 		}
2627 
2628 		/* see if the arc fits in the new port */
2629 		if (ai->end[0].portarcinst == pi) thisend = 0; else thisend = 1;
2630 		shapeportpoly(newno, opt, poly, FALSE);
2631 		if (!isinside(ai->end[thisend].xpos, ai->end[thisend].ypos, poly))
2632 		{
2633 			/* arc doesn't fit: accumulate error distance */
2634 			portposition(newno, opt, &xp, &yp);
2635 			arcdx += xp - ai->end[thisend].xpos;
2636 			arcdy += yp - ai->end[thisend].ypos;
2637 		}
2638 		arccount++;
2639 	}
2640 	if (fixedalignment == 0)
2641 	{
2642 		if (arccount > 0)
2643 		{
2644 			arcdx /= arccount;   arcdy /= arccount;
2645 			lambda = lambdaofcell(np);
2646 			alignment = muldiv(us_alignment_ratio, lambda, WHOLE);
2647 			arcdx = us_alignvalue(arcdx, alignment, &other);
2648 			arcdy = us_alignvalue(arcdy, alignment, &other);
2649 			if (arcdx != 0 || arcdy != 0)
2650 			{
2651 				makeangle(newno->rotation, newno->transpose, trans);
2652 				xform(arcdx, arcdy, &arcdx, &arcdy, trans);
2653 				modifynodeinst(newno, arcdx, arcdy, arcdx, arcdy, 0, 0);
2654 			}
2655 		}
2656 	}
2657 
2658 	/* see if the old exports are the same */
2659 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2660 	{
2661 		/* make sure there is an association for this port */
2662 		opt = (PORTPROTO *)pe->proto->temp1;
2663 		if (opt == NOPORTPROTO)
2664 		{
2665 			if (db_printerrors)
2666 				ttyputmsg(_("No port on new node corresponds to old port: %s"),
2667 					pe->proto->protoname);
2668 			db_killnodeinst(newno);
2669 			return((NODEINST *)db_error(DBPORTMM|DBREPLACENODEINST));
2670 		}
2671 
2672 		/* ensure that all arcs connected at exports still connect */
2673 		if (db_doesntconnect(opt->connects, pe->exportproto))
2674 		{
2675 			db_killnodeinst(newno);
2676 			return((NODEINST *)db_error(DBPORTMM|DBREPLACENODEINST));
2677 		}
2678 	}
2679 
2680 	/* preserve the information about the replacement ports */
2681 	arccount = 0;
2682 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst) arccount++;
2683 	if (arccount > 0)
2684 	{
2685 		newports = (PORTARCINST **)emalloc(arccount * (sizeof (PORTARCINST *)), db_cluster);
2686 		if (newports == 0) return((NODEINST *)db_error(DBNOMEM|DBREPLACENODEINST));
2687 		newportprotos = (PORTPROTO **)emalloc(arccount * (sizeof (PORTPROTO *)), db_cluster);
2688 		if (newportprotos == 0) return((NODEINST *)db_error(DBNOMEM|DBREPLACENODEINST));
2689 		arccount = 0;
2690 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2691 		{
2692 			newports[arccount] = pi;
2693 			newportprotos[arccount] = (PORTPROTO *)pi->proto->temp1;
2694 			arccount++;
2695 		}
2696 
2697 		/* replace the arcs */
2698 		for(j=0; j<arccount; j++)
2699 		{
2700 			pi = newports[j];
2701 			ai = pi->conarcinst;
2702 			if ((ai->userbits&DEADA) != 0) continue;
2703 			for(i=0; i<2; i++)
2704 			{
2705 				if (ai->end[i].portarcinst == pi)
2706 				{
2707 					endnodeinst[i] = newno;
2708 					endportproto[i] = newportprotos[j];
2709 					if (endportproto[i] == NOPORTPROTO)
2710 					{
2711 						if (!allowmissingports)
2712 							ttyputerr(_("Cannot re-connect %s arc"), describearcinst(ai));
2713 						break;
2714 					}
2715 					endx[i] = ai->end[i].xpos;   endy[i] = ai->end[i].ypos;
2716 					shapeportpoly(newno, endportproto[i], poly, FALSE);
2717 					if (!isinside(endx[i], endy[i], poly)) getcenter(poly, &endx[i], &endy[i]);
2718 				} else
2719 				{
2720 					endnodeinst[i] = ai->end[i].nodeinst;
2721 					endportproto[i] = ai->end[i].portarcinst->proto;
2722 					endx[i] = ai->end[i].xpos;   endy[i] = ai->end[i].ypos;
2723 				}
2724 			}
2725 			if (endportproto[0] == NOPORTPROTO || endportproto[1] == NOPORTPROTO)
2726 			{
2727 				if (!allowmissingports)
2728 				{
2729 					ttyputerr(_("Cannot re-connect %s arc"), describearcinst(ai));
2730 				} else
2731 				{
2732 					db_killarcinst(ai);
2733 				}
2734 				continue;
2735 			}
2736 
2737 			/* see if a bend must be made in the wire */
2738 			zigzag = 0;
2739 			if ((ai->userbits&FIXANG) != 0)
2740 			{
2741 				if (endx[0] != endx[1] || endy[0] != endy[1])
2742 				{
2743 					i = figureangle(endx[0],endy[0], endx[1],endy[1]);
2744 					ang = ((ai->userbits&AANGLE) >> AANGLESH) * 10;
2745 					if (i%1800 != ang%1800) zigzag = 1;
2746 				}
2747 			}
2748 			if (zigzag != 0)
2749 			{
2750 				/* make that two wires */
2751 				cx = endx[0];   cy = endy[1];
2752 				pinnp = getpinproto(ai->proto);
2753 				defaultnodesize(pinnp, &psx, &psy);
2754 				lx = cx - psx / 2;
2755 				hx = lx + psx;
2756 				ly = cy - psy / 2;
2757 				hy = ly + psy;
2758 				newni = db_newnodeinst(pinnp, lx,hx, ly,hy, 0, 0, ni->parent);
2759 				newai = db_newarcinst(ai->proto, ai->width, ai->userbits, endnodeinst[0],
2760 					endportproto[0], endx[0],endy[0], newni, pinnp->firstportproto, cx, cy, ni->parent);
2761 				i = ai->userbits;
2762 				if ((i&ISNEGATED) != 0) i &= ~ISNEGATED;
2763 				newai2 = db_newarcinst(ai->proto, ai->width, i, newni, pinnp->firstportproto, cx, cy,
2764 					endnodeinst[1], endportproto[1], endx[1],endy[1], ni->parent);
2765 				if (newai == NOARCINST || newai2 == NOARCINST) return(NONODEINST);
2766 				if (copyvars((INTBIG)ai->end[0].portarcinst, VPORTARCINST,
2767 					(INTBIG)newai->end[0].portarcinst, VPORTARCINST, FALSE))
2768 						return((NODEINST *)db_error(DBREPLACENODEINST|DBNOMEM));
2769 				if (copyvars((INTBIG)ai->end[1].portarcinst, VPORTARCINST,
2770 					(INTBIG)newai2->end[1].portarcinst, VPORTARCINST, FALSE))
2771 						return((NODEINST *)db_error(DBREPLACENODEINST|DBNOMEM));
2772 				if (endnodeinst[1] == ni)
2773 				{
2774 					aiswap = newai;   newai = newai2;   newai2 = aiswap;
2775 				}
2776 				if (copyvars((INTBIG)ai, VARCINST, (INTBIG)newai, VARCINST, FALSE))
2777 					return((NODEINST *)db_error(DBREPLACENODEINST|DBNOMEM));
2778 			} else
2779 			{
2780 				/* replace the arc with another arc */
2781 				newai = db_newarcinst(ai->proto, ai->width, ai->userbits, endnodeinst[0],
2782 					endportproto[0], endx[0],endy[0], endnodeinst[1], endportproto[1], endx[1],endy[1],
2783 						ni->parent);
2784 				if (newai == NOARCINST) return(NONODEINST);
2785 				if (copyvars((INTBIG)ai, VARCINST, (INTBIG)newai, VARCINST, FALSE))
2786 					return((NODEINST *)db_error(DBREPLACENODEINST|DBNOMEM));
2787 				for(i=0; i<2; i++)
2788 					if (copyvars((INTBIG)ai->end[i].portarcinst, VPORTARCINST,
2789 						(INTBIG)newai->end[i].portarcinst, VPORTARCINST, FALSE))
2790 							return((NODEINST *)db_error(DBREPLACENODEINST|DBNOMEM));
2791 			}
2792 			db_killarcinst(ai);
2793 		}
2794 		efree((CHAR *)newports);
2795 		efree((CHAR *)newportprotos);
2796 	}
2797 
2798 	/* now replace the exports */
2799 	if (expcount > 0)
2800 	{
2801 		for(j=0; j<expcount; j++)
2802 		{
2803 			pe = newexports[j];
2804 			if (moveportproto(ni->parent, pe->exportproto, newno, newexpportprotos[j])) break;
2805 		}
2806 		efree((CHAR *)newexports);
2807 		efree((CHAR *)newexpportprotos);
2808 	}
2809 
2810 	/* copy all variables on the nodeinst */
2811 	(void)copyvars((INTBIG)ni, VNODEINST, (INTBIG)newno, VNODEINST, FALSE);
2812 	TDCOPY(newno->textdescript, ni->textdescript);
2813 
2814 	/* now delete the original nodeinst */
2815 	db_killnodeinst(ni);
2816 	return(newno);
2817 }
2818 
2819 /*
2820  * routine to associate the ports on node instances "ni1" and "ni2".
2821  * Each port prototype on "ni1" will have the address of the corresponding
2822  * port prototype on "ni2" in its "temp1" field (NOPORTPROTO if the match
2823  * cannot be found).  The routine returns true if there is an error.
2824  */
db_portassociate(NODEINST * ni1,NODEINST * ni2,BOOLEAN ignoreportnames)2825 BOOLEAN db_portassociate(NODEINST *ni1, NODEINST *ni2, BOOLEAN ignoreportnames)
2826 {
2827 	REGISTER INTBIG i, j, total2;
2828 	REGISTER INTBIG *xpos2, *ypos2;
2829 	INTBIG xpos1, ypos1;
2830 	REGISTER PORTPROTO *pp1, *pp2, *mpt;
2831 	static POLYGON *poly1 = NOPOLYGON, *poly2 = NOPOLYGON;
2832 
2833 	/* make sure there is a polygon */
2834 	(void)needstaticpolygon(&poly1, 4, db_cluster);
2835 	(void)needstaticpolygon(&poly2, 4, db_cluster);
2836 
2837 	/* initialize */
2838 	for(total2 = 0, pp2 = ni2->proto->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
2839 	{
2840 		pp2->temp1 = (INTBIG)NOPORTPROTO;
2841 		total2++;
2842 	}
2843 	for(pp1 = ni1->proto->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
2844 		pp1->temp1 = (INTBIG)NOPORTPROTO;
2845 
2846 	/* create center-position arrays for the ports on node 2 */
2847 	xpos2 = emalloc((total2 * SIZEOFINTBIG), el_tempcluster);
2848 	if (xpos2 == 0) return(TRUE);
2849 	ypos2 = emalloc((total2 * SIZEOFINTBIG), el_tempcluster);
2850 	if (ypos2 == 0) return(TRUE);
2851 	for(i = 0, pp2 = ni2->proto->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto, i++)
2852 	{
2853 		shapeportpoly(ni2, pp2, poly2, FALSE);
2854 		getcenter(poly2, &xpos2[i], &ypos2[i]);
2855 	}
2856 
2857 	/* associate on port name matches */
2858 	if (!ignoreportnames)
2859 	{
2860 		for(pp1 = ni1->proto->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
2861 		{
2862 			for(pp2 = ni2->proto->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto)
2863 				if (pp2->temp1 == -1)
2864 			{
2865 				/* stop if the ports have different name */
2866 				if (namesame(pp2->protoname, pp1->protoname) != 0) continue;
2867 
2868 				/* store the correct association of ports */
2869 				pp1->temp1 = (INTBIG)pp2;
2870 				pp2->temp1 = (INTBIG)pp1;
2871 			}
2872 		}
2873 	}
2874 
2875 	/* associate ports that are in the same position */
2876 	for(pp1 = ni1->proto->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
2877 		if ((PORTPROTO *)pp1->temp1 == NOPORTPROTO)
2878 	{
2879 		shapeportpoly(ni1, pp1, poly1, FALSE);
2880 		getcenter(poly1, &xpos1, &ypos1);
2881 		for(i = 0, pp2 = ni2->proto->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto, i++)
2882 		{
2883 			/* if this port is already associated, ignore it */
2884 			if (pp2->temp1 != -1) continue;
2885 
2886 			/* if the port centers are different, go no further */
2887 			if (xpos2[i] != xpos1 || ypos2[i] != ypos1) continue;
2888 
2889 			/* compare actual polygons to be sure */
2890 			shapeportpoly(ni2, pp2, poly2, FALSE);
2891 			if (!polysame(poly1, poly2)) continue;
2892 
2893 			/* handle confusion if multiple ports have the same polygon */
2894 			if ((PORTPROTO *)pp1->temp1 != NOPORTPROTO)
2895 			{
2896 				mpt = (PORTPROTO *)pp1->temp1;
2897 
2898 				/* see if one of the associations has the same connectivity */
2899 				for(j=0; mpt->connects[j] != NOARCPROTO && pp1->connects[j] != NOARCPROTO; j++)
2900 					if (mpt->connects[j] != pp1->connects[j]) break;
2901 				if (mpt->connects[j] == NOARCPROTO && pp1->connects[j] == NOARCPROTO) continue;
2902 			}
2903 
2904 			/* store the correct association of ports */
2905 			if ((PORTPROTO *)pp1->temp1 != NOPORTPROTO)
2906 				((PORTPROTO *)pp1->temp1)->temp1 = (INTBIG)NOPORTPROTO;
2907 			pp1->temp1 = (INTBIG)pp2;
2908 			pp2->temp1 = (INTBIG)pp1;
2909 		}
2910 	}
2911 
2912 	/* finally, associate ports that have the same center */
2913 	for(pp1 = ni1->proto->firstportproto; pp1 != NOPORTPROTO; pp1 = pp1->nextportproto)
2914 		if ((PORTPROTO *)pp1->temp1 == NOPORTPROTO)
2915 	{
2916 		shapeportpoly(ni1, pp1, poly1, FALSE);
2917 		getcenter(poly1, &xpos1, &ypos1);
2918 		for(i = 0, pp2 = ni2->proto->firstportproto; pp2 != NOPORTPROTO; pp2 = pp2->nextportproto, i++)
2919 		{
2920 			/* if this port is already associated, ignore it */
2921 			if (pp2->temp1 != -1) continue;
2922 
2923 			/* if the port centers are different, go no further */
2924 			if (xpos2[i] != xpos1 || ypos2[i] != ypos1) continue;
2925 
2926 			/* handle confusion if multiple ports have the same polygon */
2927 			if ((PORTPROTO *)pp1->temp1 != NOPORTPROTO)
2928 			{
2929 				mpt = (PORTPROTO *)pp1->temp1;
2930 
2931 				/* see if one of the associations has the same connectivity */
2932 				for(j=0; mpt->connects[j] != NOARCPROTO && pp1->connects[j] != NOARCPROTO; j++)
2933 					if (mpt->connects[j] != pp1->connects[j]) break;
2934 				if (mpt->connects[j] == NOARCPROTO && pp1->connects[j] == NOARCPROTO) continue;
2935 			}
2936 
2937 			/* store the correct association of ports */
2938 			if ((PORTPROTO *)pp1->temp1 != NOPORTPROTO)
2939 				((PORTPROTO *)pp1->temp1)->temp1 = (INTBIG)NOPORTPROTO;
2940 			pp1->temp1 = (INTBIG)pp2;
2941 			pp2->temp1 = (INTBIG)pp1;
2942 		}
2943 	}
2944 
2945 	/* free the port center information */
2946 	efree((CHAR *)xpos2);
2947 	efree((CHAR *)ypos2);
2948 	return(FALSE);
2949 }
2950 
2951 /*
2952  * helper routine to ensure that all arcs connected to port "pp" or any of
2953  * its export sites can connect to the list in "conn".  Returns true
2954  * if the connection cannot be made
2955  */
db_doesntconnect(ARCPROTO * conn[],PORTPROTO * pp)2956 BOOLEAN db_doesntconnect(ARCPROTO *conn[], PORTPROTO *pp)
2957 {
2958 	REGISTER NODEINST *ni;
2959 	REGISTER PORTARCINST *pi;
2960 	REGISTER PORTEXPINST *pe;
2961 	REGISTER INTBIG i;
2962 
2963 	/* check every instance of this node */
2964 	for(ni = pp->parent->firstinst; ni != NONODEINST; ni = ni->nextinst)
2965 	{
2966 		/* make sure all arcs on this port can connect */
2967 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
2968 		{
2969 			if (pi->proto != pp) continue;
2970 			for(i=0; conn[i] != NOARCPROTO; i++)
2971 				if (conn[i] == pi->conarcinst->proto) break;
2972 			if (conn[i] == NOARCPROTO)
2973 			{
2974 				if (db_printerrors)
2975 					ttyputmsg(_("%s arc in cell %s cannot connect to port %s"),
2976 						describearcinst(pi->conarcinst), describenodeproto(ni->parent), pp->protoname);
2977 				return(TRUE);
2978 			}
2979 		}
2980 
2981 		/* make sure all further exports are still valid */
2982 		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
2983 		{
2984 			if (pe->proto != pp) continue;
2985 			if (db_doesntconnect(conn, pe->exportproto)) return(TRUE);
2986 		}
2987 	}
2988 	return(FALSE);
2989 }
2990 
2991 /*
2992  * routine to move an export to a different nodeinst in the cell.
2993  * The routine expects both ports to be in the same place and simply shifts
2994  * the arcs without re-constraining them. The portproto is currently "oldpt"
2995  * in cell "cell".  It is to be moved to nodeinst "newno", subportproto
2996  * "newsubpt".  The routine returns true if there is an error
2997  */
moveportproto(NODEPROTO * cell,PORTPROTO * oldpt,NODEINST * newno,PORTPROTO * newsubpt)2998 BOOLEAN moveportproto(NODEPROTO *cell, PORTPROTO *oldpt, NODEINST *newno, PORTPROTO *newsubpt)
2999 {
3000 	REGISTER NODEINST *oldni;
3001 	REGISTER PORTPROTO *oldpp;
3002 
3003 	/* error checks */
3004 	if (oldpt == NOPORTPROTO)
3005 	{
3006 		db_donextchangequietly = 0;
3007 		return(db_error(DBBADPROTO|DBMOVEPORTPROTO) != 0);
3008 	}
3009 	if (cell == NONODEPROTO)
3010 	{
3011 		db_donextchangequietly = 0;
3012 		return(db_error(DBBADCELL|DBMOVEPORTPROTO) != 0);
3013 	}
3014 	if (oldpt->parent != cell)
3015 	{
3016 		db_donextchangequietly = 0;
3017 		return(db_error(DBBADCELL|DBMOVEPORTPROTO) != 0);
3018 	}
3019 	if (newno == NONODEINST)
3020 	{
3021 		db_donextchangequietly = 0;
3022 		return(db_error(DBBADINST|DBMOVEPORTPROTO) != 0);
3023 	}
3024 	if (newsubpt == NOPORTPROTO)
3025 	{
3026 		db_donextchangequietly = 0;
3027 		return(db_error(DBBADSUBPORT|DBMOVEPORTPROTO) != 0);
3028 	}
3029 	if (newno->parent != oldpt->parent)
3030 	{
3031 		db_donextchangequietly = 0;
3032 		return(db_error(DBBADPARENT|DBMOVEPORTPROTO) != 0);
3033 	}
3034 	if (newsubpt->parent != newno->proto)
3035 	{
3036 		db_donextchangequietly = 0;
3037 		return(db_error(DBBADSUBPORT|DBMOVEPORTPROTO) != 0);
3038 	}
3039 	if (db_doesntconnect(newsubpt->connects, oldpt))
3040 	{
3041 		db_donextchangequietly = 0;
3042 		return(db_error(DBPORTMM|DBMOVEPORTPROTO) != 0);
3043 	}
3044 
3045 	/* remember old state */
3046 	oldni = oldpt->subnodeinst;
3047 	oldpp = oldpt->subportproto;
3048 
3049 	/* change the port origin */
3050 	db_changeport(oldpt, newno, newsubpt);
3051 
3052 	/* handle change control, constraint, and broadcast */
3053 	if (db_donextchangequietly == 0 && !db_dochangesquietly)
3054 	{
3055 		/* announce the change */
3056 		oldpt->changeaddr = (CHAR *)db_change((INTBIG)oldpt, PORTPROTOMOD, (INTBIG)oldni,
3057 			(INTBIG)oldpp, 0, 0, 0, 0);
3058 
3059 		/* tell constraint system about modified port */
3060 		(*el_curconstraint->modifyportproto)(oldpt, oldni, oldpp);
3061 
3062 		/* mark this as changed */
3063 		db_setchangecell(oldpt->parent);
3064 		db_forcehierarchicalanalysis(oldpt->parent);
3065 	}
3066 	db_donextchangequietly = 0;
3067 
3068 	return(FALSE);
3069 }
3070 
3071 /*
3072  * routine to change the origin of complex port "pp" to subnode "newsubno",
3073  * subport "newsubpt"
3074  */
db_changeport(PORTPROTO * pp,NODEINST * newsubno,PORTPROTO * newsubpt)3075 void db_changeport(PORTPROTO *pp, NODEINST *newsubno, PORTPROTO *newsubpt)
3076 {
3077 	REGISTER PORTEXPINST *pe;
3078 
3079 	/* remove the old linkage */
3080 	db_removeportexpinst(pp);
3081 
3082 	/* create the new linkage */
3083 	pp->subnodeinst = newsubno;
3084 	pp->subportproto = newsubpt;
3085 	pp->userbits = (pp->userbits & STATEBITS) |
3086 		(newsubpt->userbits & (PORTANGLE|PORTARANGE|PORTNET|PORTISOLATED));
3087 	pp->connects = newsubpt->connects;
3088 	pe = allocportexpinst(pp->parent->lib->cluster);
3089 	pe->proto = newsubpt;
3090 	db_addportexpinst(newsubno, pe);
3091 	pe->exportproto = pp;
3092 	pp->subportexpinst = pe;
3093 
3094 	/* update all port characteristics exported from this one */
3095 	changeallports(pp);
3096 
3097 	/* mark a change to the database */
3098 	db_changetimestamp++;
3099 }
3100 
3101 /*
3102  * routine to recursively alter the "userbits" and "connects" fields of
3103  * export "pp", given that it changed or moved.  Also changes associated
3104  * icon and contents cells.
3105  */
changeallports(PORTPROTO * pp)3106 void changeallports(PORTPROTO *pp)
3107 {
3108 	REGISTER NODEPROTO *np, *onp;
3109 	REGISTER PORTPROTO *opp;
3110 
3111 	/* look at all instances of the cell that had export motion */
3112 	db_changeallports(pp);
3113 
3114 	/* look at associated cells and change their ports */
3115 	np = pp->parent;
3116 	if (np->cellview == el_iconview)
3117 	{
3118 		/* changed an export on an icon: find contents and change it there */
3119 		onp = contentsview(np);
3120 		if (onp != NONODEPROTO)
3121 		{
3122 			opp = equivalentport(np, pp, onp);
3123 			if (opp != NOPORTPROTO)
3124 			{
3125 				opp->userbits = (opp->userbits & ~STATEBITS) | (pp->userbits & STATEBITS);
3126 				db_changeallports(opp);
3127 			}
3128 		}
3129 		return;
3130 	}
3131 
3132 	/* see if there is an icon to change */
3133 	onp = iconview(np);
3134 	if (onp != NONODEPROTO)
3135 	{
3136 		opp = equivalentport(np, pp, onp);
3137 		if (opp != NOPORTPROTO)
3138 		{
3139 			opp->userbits = (opp->userbits & ~STATEBITS) | (pp->userbits & STATEBITS);
3140 			db_changeallports(opp);
3141 		}
3142 	}
3143 }
3144 
3145 /*
3146  * routine to recursively alter the "userbits" and "connects" fields of
3147  * export "pp", given that it changed or moved.
3148  */
db_changeallports(PORTPROTO * pp)3149 void db_changeallports(PORTPROTO *pp)
3150 {
3151 	REGISTER NODEINST *ni;
3152 	REGISTER PORTEXPINST *pe;
3153 
3154 	/* look at all instances of the cell that had port motion */
3155 	for(ni = pp->parent->firstinst; ni != NONODEINST; ni = ni->nextinst)
3156 	{
3157 		/* see if an instance reexports the port */
3158 		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
3159 		{
3160 			if (pe->proto != pp) continue;
3161 
3162 			/* change this port and recurse up the hierarchy */
3163 			if (pe->exportproto->userbits != pp->userbits)
3164 			{
3165 				setval((INTBIG)pe->exportproto, VPORTPROTO, x_("userbits"),
3166 					pp->userbits, VINTEGER);
3167 			}
3168 			pe->exportproto->connects = pp->connects;
3169 			db_changeallports(pe->exportproto);
3170 		}
3171 	}
3172 }
3173 
3174 /*
3175  * routine to replace arcinst "ai" with one of type "ap".  The routine
3176  * returns the address of the new replaced arcinst if successful, NOARCINST
3177  * if the replacement cannot be done.
3178  */
replacearcinst(ARCINST * ai,ARCPROTO * ap)3179 ARCINST *replacearcinst(ARCINST *ai, ARCPROTO *ap)
3180 {
3181 	REGISTER PORTPROTO *pp1, *pp2;
3182 	REGISTER INTBIG i;
3183 	REGISTER INTBIG newwid;
3184 	REGISTER NODEINST *ni1, *ni2;
3185 	REGISTER ARCINST *newar;
3186 
3187 	/* check for connection allowance */
3188 	ni1 = ai->end[0].nodeinst;   ni2 = ai->end[1].nodeinst;
3189 	pp1 = ai->end[0].portarcinst->proto;
3190 	pp2 = ai->end[1].portarcinst->proto;
3191 	for(i=0; pp1->connects[i] != NOARCPROTO; i++)
3192 		if (pp1->connects[i] == ap) break;
3193 	if (pp1->connects[i] == NOARCPROTO)
3194 		return((ARCINST *)db_error(DBBADENDAC|DBREPLACEARCINST));
3195 	for(i=0; pp2->connects[i] != NOARCPROTO; i++)
3196 		if (pp2->connects[i] == ap) break;
3197 	if (pp2->connects[i] == NOARCPROTO)
3198 		return((ARCINST *)db_error(DBBADENDBC|DBREPLACEARCINST));
3199 
3200 	/* compute the new width */
3201 	newwid = ai->width - arcwidthoffset(ai) + arcprotowidthoffset(ap);
3202 
3203 	/* first create the new nodeinst in place */
3204 	newar = db_newarcinst(ap, newwid, ai->userbits, ni1,pp1, ai->end[0].xpos,ai->end[0].ypos,
3205 		ni2,pp2,ai->end[1].xpos, ai->end[1].ypos, ai->parent);
3206 	if (newar == NOARCINST) return(NOARCINST);
3207 
3208 	/* copy all variables on the arcinst */
3209 	(void)copyvars((INTBIG)ai, VARCINST, (INTBIG)newar, VARCINST, FALSE);
3210 
3211 	/* set the change cell environment */
3212 	db_setchangecell(ai->parent);
3213 
3214 	/* now delete the original nodeinst */
3215 	db_killarcinst(ai);
3216 	return(newar);
3217 }
3218