1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dbcreate.c
6  * Database general manipulation routines
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "egraphics.h"
34 #include "database.h"
35 
36 /* prototypes for local routines */
37 static ARCPROTO *db_allocarcproto(CLUSTER*);
38 static INTBIG    db_checkshortening(NODEINST*, PORTPROTO*);
39 static void      db_killportproto(PORTPROTO*);
40 
41 /************************* NODES *************************/
42 
43 /*
44  * Routine to allocate a nodeinst from memory cluster "cluster".
45  * The routine returns NONODEINST if allocation fails.
46  */
allocnodeinst(CLUSTER * cluster)47 NODEINST *allocnodeinst(CLUSTER *cluster)
48 {
49 	REGISTER NODEINST *ni;
50 
51 	ni = (NODEINST *)emalloc((sizeof (NODEINST)), cluster);
52 	if (ni == 0) return(NONODEINST);
53 	ni->lowx = ni->highx = ni->lowy = ni->highy = 0;
54 	ni->transpose = 0;
55 	ni->rotation = 0;
56 	ni->proto = NONODEPROTO;
57 	ni->parent = NONODEPROTO;
58 	ni->prevnodeinst = NONODEINST;
59 	ni->nextnodeinst = NONODEINST;
60 	ni->geom = NOGEOM;
61 	ni->previnst = NONODEINST;
62 	ni->nextinst = NONODEINST;
63 	ni->firstportarcinst = NOPORTARCINST;
64 	ni->firstportexpinst = NOPORTEXPINST;
65 	TDCLEAR(ni->textdescript);
66 	defaulttextsize(5, ni->textdescript);
67 	TDSETPOS(ni->textdescript, VTPOSBOXED);
68 	ni->arraysize = 0;
69 	ni->changeaddr = (CHAR *)NOCHANGE;
70 	ni->changed = 0;
71 	ni->userbits = ni->temp1 = ni->temp2 = 0;
72 	ni->firstvar = NOVARIABLE;
73 	ni->numvar = 0;
74 	return(ni);
75 }
76 
77 /*
78  * routine to return nodeinst "ni" to the pool of free nodes
79  */
freenodeinst(NODEINST * ni)80 void freenodeinst(NODEINST *ni)
81 {
82 	if (ni == NONODEINST) return;
83 	if (ni->numvar != 0) db_freevars(&ni->firstvar, &ni->numvar);
84 	efree((CHAR *)ni);
85 }
86 
startobjectchange(INTBIG addr,INTBIG type)87 void startobjectchange(INTBIG addr, INTBIG type)
88 {
89 	/* handle change control and broadcast */
90 	if (!db_donextchangequietly && !db_dochangesquietly)
91 	{
92 		(void)db_change(addr, OBJECTSTART, type, 0, 0, 0, 0, 0);
93 	}
94 	db_donextchangequietly = FALSE;
95 }
96 
endobjectchange(INTBIG addr,INTBIG type)97 void endobjectchange(INTBIG addr, INTBIG type)
98 {
99 	/* handle change control and broadcast */
100 	if (!db_donextchangequietly && !db_dochangesquietly)
101 	{
102 		(void)db_change(addr, OBJECTEND, type, 0, 0, 0, 0, 0);
103 	}
104 	db_donextchangequietly = FALSE;
105 }
106 
107 /*
108  * create a new nodeinst of proto "typ" located at (lx-hx, ly-hy), transposed
109  * if "trans" is nonzero, rotated "angle" tenth-degrees.  The nodeinst is located
110  * in cell "parnt". The address of the nodeinst is returned.
111  * If NONODEINST is returned, there is an error creating the nodeinst.
112  */
newnodeinst(NODEPROTO * np,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG trans,INTBIG angle,NODEPROTO * parnt)113 NODEINST *newnodeinst(NODEPROTO *np, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy,
114 	INTBIG trans, INTBIG angle, NODEPROTO *parnt)
115 {
116 	/* make sure values are correct */
117 	if (trans != 0 && trans != 1)
118 	{
119 		db_donextchangequietly = FALSE;
120 		return((NODEINST *)db_error(DBBADTRANS|DBNEWNODEINST));
121 	}
122 	if (angle < 0 || angle >= 3600)
123 	{
124 		db_donextchangequietly = FALSE;
125 		return((NODEINST *)db_error(DBBADROT|DBNEWNODEINST));
126 	}
127 	if (np == NONODEPROTO)
128 	{
129 		db_donextchangequietly = FALSE;
130 		return((NODEINST *)db_error(DBBADPROTO|DBNEWNODEINST));
131 	}
132 	if (parnt == NONODEPROTO)
133 	{
134 		db_donextchangequietly = FALSE;
135 		return((NODEINST *)db_error(DBBADPARENT|DBNEWNODEINST));
136 	}
137 	if (isachildof(parnt, np))
138 	{
139 		db_donextchangequietly = FALSE;
140 		return((NODEINST *)db_error(DBRECURSIVE|DBNEWNODEINST));
141 	}
142 
143 	/* build the nodeinst */
144 	return(db_newnodeinst(np, lx, hx, ly, hy, trans, angle, parnt));
145 }
146 
147 /*
148  * internal procedure to create a new nodeinst of proto "np" located at
149  * (lx-hx, ly-hy), transposed if "trans" is nonzero, rotated "angle" tenth-degrees.
150  * The nodeinst is located in cell "parnt". The address of the nodeinst is
151  * returned.  NONODEINST is returned upon error.
152  */
db_newnodeinst(NODEPROTO * np,INTBIG lx,INTBIG hx,INTBIG ly,INTBIG hy,INTBIG trans,INTBIG angle,NODEPROTO * parnt)153 NODEINST *db_newnodeinst(NODEPROTO *np, INTBIG lx, INTBIG hx, INTBIG ly, INTBIG hy,
154 	INTBIG trans, INTBIG angle, NODEPROTO *parnt)
155 {
156 	REGISTER NODEINST *ni;
157 
158 	/* allocate the nodeinst */
159 	ni = allocnodeinst(parnt->lib->cluster);
160 	if (ni == NONODEINST)
161 	{
162 		db_donextchangequietly = FALSE;
163 		return(NONODEINST);
164 	}
165 
166 	/* initialize the nodeinst */
167 	ni->proto = np;
168 	ni->rotation = (INTSML)angle;   ni->transpose = (INTSML)trans;
169 	ni->lowx = lx;       ni->highx = hx;
170 	ni->lowy = ly;       ni->highy = hy;
171 	ni->parent = parnt;
172 
173 	/* create its ports */
174 	ni->firstportarcinst = NOPORTARCINST;
175 	ni->firstportexpinst = NOPORTEXPINST;
176 
177 	/* make a geometry module for this nodeinst */
178 	ni->geom = allocgeom(parnt->lib->cluster);
179 	if (ni->geom == NOGEOM) return(NONODEINST);
180 	ni->geom->entryisnode = TRUE;   ni->geom->entryaddr.ni = ni;
181 
182 	/* put it in appropriate lists */
183 	db_enternodeinst(ni);
184 
185 	/* handle change control, constraint, and broadcast */
186 	if (!db_donextchangequietly && !db_dochangesquietly)
187 	{
188 		/* set the change cell environment */
189 		db_setchangecell(parnt);
190 
191 		/* tell all tools about this nodeinst */
192 		ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTNEW, 0, 0, 0, 0, 0, 0);
193 
194 		/* tell constraint system about new node */
195 		(*el_curconstraint->newobject)((INTBIG)ni, VNODEINST);
196 	}
197 	db_donextchangequietly = FALSE;
198 
199 	/* report nodeinst address */
200 	return(ni);
201 }
202 
203 /*
204  * routine to enter nodeinst "ni" in the database structure by linking it into
205  * the list of nodes of this proto, putting it in the geometry list,
206  * and announcing the existance of the nodeinst.
207  */
db_enternodeinst(NODEINST * ni)208 void db_enternodeinst(NODEINST *ni)
209 {
210 	REGISTER NODEPROTO *np, *par;
211 
212 	/* link the geometry entry for the nodeinst */
213 	par = ni->parent;
214 	linkgeom(ni->geom, par);
215 
216 	/* adjust parent size if this node extends over edge */
217 
218 	/* put in list of nodes of this proto */
219 	np = ni->proto;
220 	if (np->firstinst != NONODEINST) np->firstinst->previnst = ni;
221 	ni->nextinst = np->firstinst;
222 	ni->previnst = NONODEINST;
223 	np->firstinst = ni;
224 
225 	/* put in list of nodes in this cell */
226 	if (par->firstnodeinst != NONODEINST) par->firstnodeinst->prevnodeinst = ni;
227 	ni->nextnodeinst = par->firstnodeinst;
228 	ni->prevnodeinst = NONODEINST;
229 	par->firstnodeinst = ni;
230 
231 	/* mark the nodeinst alive */
232 	ni->userbits &= ~DEADN;
233 
234 	/* mark a change to the database */
235 	db_changetimestamp++;
236 }
237 
238 /*
239  * free up network nodeinst pointed to by "ni".
240  * Returns true if the nodeinst has arcs still connected to it (an error)
241  */
killnodeinst(NODEINST * ni)242 BOOLEAN killnodeinst(NODEINST *ni)
243 {
244 	/* error if nodeinst is not valid */
245 	if (ni == NONODEINST)
246 	{
247 		db_donextchangequietly = FALSE;
248 		return(db_error(DBBADINST|DBKILLNODEINST) != 0);
249 	}
250 
251 	/* error if any arcs still connected to this nodeinst */
252 	if (ni->firstportarcinst != NOPORTARCINST)
253 	{
254 		db_donextchangequietly = FALSE;
255 		return(db_error(DBHASARCS|DBKILLNODEINST) != 0);
256 	}
257 
258 	/* error if any exports */
259 	if (ni->firstportexpinst != NOPORTEXPINST)
260 	{
261 		db_donextchangequietly = FALSE;
262 		return(db_error(DBHASPORTS|DBKILLNODEINST) != 0);
263 	}
264 
265 	/* remove nodeinst from lists */
266 	db_killnodeinst(ni);
267 
268 	/* freeing of the nodeinst and ports will be done later */
269 	return(FALSE);
270 }
271 
272 /*
273  * routine to remove nodeinst "ni" from the database structure by un-linking
274  * it from the list of nodes of this proto, removing it from the geometry
275  * list, and announcing the deletion of the nodeinst.
276  */
db_killnodeinst(NODEINST * ni)277 void db_killnodeinst(NODEINST *ni)
278 {
279 	db_retractnodeinst(ni);
280 
281 	/* handle change control, constraint, and broadcast */
282 	if (!db_donextchangequietly && !db_dochangesquietly)
283 	{
284 		/* set the change cell environment */
285 		db_setchangecell(ni->parent);
286 
287 		/* tell all tools about this nodeinst */
288 		ni->changeaddr = (CHAR *)db_change((INTBIG)ni, NODEINSTKILL, 0, 0, 0, 0, 0, 0);
289 
290 		/* tell constraint system about killed node */
291 		(*el_curconstraint->killobject)((INTBIG)ni, VNODEINST);
292 	} else
293 	{
294 		/* must deallocate now: no change control */
295 		freegeom(ni->geom);
296 		freenodeinst(ni);
297 	}
298 	db_donextchangequietly = FALSE;
299 }
300 
301 /*
302  * routine to remove nodeinst "ni" from the database structure by un-linking
303  * it from the list of nodes of this proto and removing it from the geometry list.
304  */
db_retractnodeinst(NODEINST * ni)305 void db_retractnodeinst(NODEINST *ni)
306 {
307 	/* remove from list of nodes of this kind */
308 	if (ni->nextinst != NONODEINST) ni->nextinst->previnst = ni->previnst;
309 	if (ni->previnst != NONODEINST) ni->previnst->nextinst = ni->nextinst; else
310 		ni->proto->firstinst = ni->nextinst;
311 
312 	/* remove from list of nodes in this cell */
313 	if (ni->nextnodeinst != NONODEINST) ni->nextnodeinst->prevnodeinst = ni->prevnodeinst;
314 	if (ni->prevnodeinst != NONODEINST) ni->prevnodeinst->nextnodeinst = ni->nextnodeinst; else
315 		ni->parent->firstnodeinst = ni->nextnodeinst;
316 
317 	/* remove from R-tree */
318 	undogeom(ni->geom, ni->parent);
319 
320 	/* mark the nodeinst dead */
321 	ni->userbits |= DEADN;
322 
323 	/* mark a change to the database */
324 	db_changetimestamp++;
325 }
326 
327 /*
328  * Routine to determine the default size of primitive node "np" and return it in
329  * (xs, ys).
330  */
defaultnodesize(NODEPROTO * np,INTBIG * xs,INTBIG * ys)331 void defaultnodesize(NODEPROTO *np, INTBIG *xs, INTBIG *ys)
332 {
333 	REGISTER VARIABLE *var;
334 
335 	/* take default size from the prototype */
336 	*xs = np->highx - np->lowx;
337 	*ys = np->highy - np->lowy;
338 
339 	/* cells always use this */
340 	if (np->primindex == 0) return;
341 
342 	/* see if there is an override on the node */
343 	var = getvalkey((INTBIG)np, VNODEPROTO, VINTEGER|VISARRAY, el_node_size_default_key);
344 	if (var != NOVARIABLE)
345 	{
346 		*xs = ((INTBIG *)var->addr)[0] * el_curlib->lambda[np->tech->techindex] / WHOLE;
347 		*ys = ((INTBIG *)var->addr)[1] * el_curlib->lambda[np->tech->techindex] / WHOLE;
348 	}
349 }
350 
351 /*
352  * routine to modify nodeinst "ni" by "deltalx" in low X, "deltaly" in low Y,
353  * "deltahx" in high X, "deltahy" in high Y, "deltarot" in rotation, and
354  * "deltatrans" in transgeometry.
355  */
modifynodeinst(NODEINST * ni,INTBIG deltalx,INTBIG deltaly,INTBIG deltahx,INTBIG deltahy,INTBIG deltarot,INTBIG deltatrans)356 void modifynodeinst(NODEINST *ni, INTBIG deltalx, INTBIG deltaly, INTBIG deltahx,
357 	INTBIG deltahy, INTBIG deltarot, INTBIG deltatrans)
358 {
359 	/* examine the nature of the changes */
360 	deltarot = deltarot % 3600;
361 	if (deltarot < 0) deltarot += 3600;
362 	if (deltatrans != 0) deltatrans = 1;
363 	if (deltalx == 0 && deltahx == 0 && deltaly == 0 && deltahy == 0 && deltarot == 0 &&
364 		deltatrans == 0)
365 	{
366 		db_donextchangequietly = FALSE;
367 		return;
368 	}
369 
370 	if (!db_donextchangequietly && !db_dochangesquietly)
371 	{
372 		/* tell constraint system about modified node */
373 		(*el_curconstraint->modifynodeinst)(ni, deltalx, deltaly, deltahx, deltahy, deltarot, deltatrans);
374 	} else
375 	{
376 		/* make the change now: no constraint system */
377 		ni->lowx += deltalx;   ni->highx += deltahx;
378 		ni->lowy += deltaly;   ni->highy += deltahy;
379 		ni->rotation += (INTSML)deltarot;
380 		ni->transpose += (INTSML)deltatrans;
381 	}
382 	db_donextchangequietly = FALSE;
383 
384 	/* set the change cell environment */
385 	db_setchangecell(ni->parent);
386 
387 	/* mark a change to the database */
388 	db_changetimestamp++;
389 }
390 
391 /*
392  * routine to modify "count" nodeinsts "ni" by "deltalx" in low X, "deltaly" in low Y,
393  * "deltahx" in high X, "deltahy" in high Y, "deltarot" in rotation, and
394  * "deltatrans" in transgeometry.
395  */
modifynodeinsts(INTBIG count,NODEINST ** nis,INTBIG * deltalxs,INTBIG * deltalys,INTBIG * deltahxs,INTBIG * deltahys,INTBIG * deltarots,INTBIG * deltatranss)396 void modifynodeinsts(INTBIG count, NODEINST **nis, INTBIG *deltalxs, INTBIG *deltalys,
397 	INTBIG *deltahxs, INTBIG *deltahys, INTBIG *deltarots, INTBIG *deltatranss)
398 {
399 	REGISTER INTBIG i, deltalx, deltaly, deltahx, deltahy;
400 	REGISTER INTBIG deltarot, deltatrans;
401 	REGISTER NODEINST *ni;
402 
403 	if (!db_donextchangequietly && !db_dochangesquietly)
404 	{
405 		/* tell constraint system about modified node */
406 		(*el_curconstraint->modifynodeinsts)(count, nis, deltalxs, deltalys, deltahxs, deltahys, deltarots, deltatranss);
407 		for(i=0; i<count; i++)
408 			db_setchangecell(nis[i]->parent);
409 	} else
410 	{
411 		for(i=0; i<count; i++)
412 		{
413 			ni = nis[i];
414 			deltalx = deltalxs[i];
415 			deltaly = deltalys[i];
416 			deltahx = deltahxs[i];
417 			deltahy = deltahys[i];
418 			deltarot = deltarots[i];
419 			deltatrans = deltatranss[i];
420 
421 			/* examine the nature of the changes */
422 			deltarot = deltarot % 3600;
423 			if (deltarot < 0) deltarot += 3600;
424 			if (deltatrans != 0) deltatrans = 1;
425 			if (deltalx == 0 && deltahx == 0 && deltaly == 0 && deltahy == 0 && deltarot == 0 &&
426 				deltatrans == 0) continue;
427 
428 			/* make the change now: no constraint system */
429 			ni->lowx += deltalx;   ni->highx += deltahx;
430 			ni->lowy += deltaly;   ni->highy += deltahy;
431 			ni->rotation += (INTSML)deltarot;
432 			ni->transpose += (INTSML)deltatrans;
433 		}
434 	}
435 
436 	/* turn off temp flag for quiet changes */
437 	db_donextchangequietly = FALSE;
438 
439 	/* mark a change to the database */
440 	db_changetimestamp++;
441 }
442 
443 /*
444  * routine to initialize a nodeinst that it not in the
445  * database (for miscellaneous use)
446  */
initdummynode(NODEINST * ni)447 void initdummynode(NODEINST *ni)
448 {
449 	ni->rotation = 0;
450 	ni->transpose = 0;
451 	ni->proto = el_technologies->firstnodeproto;
452 	ni->parent = NONODEPROTO;
453 	ni->firstportarcinst = NOPORTARCINST;
454 	ni->firstportexpinst = NOPORTEXPINST;
455 	ni->userbits = 0;
456 	TDCLEAR(ni->textdescript);
457 	defaulttextsize(5, ni->textdescript);
458 	TDSETPOS(ni->textdescript, VTPOSBOXED);
459 	ni->numvar = 0;
460 }
461 
462 /************************* ARC PROTOTYPES *************************/
463 
464 /*
465  * routine to allocate "count" arcprotos from memory cluster "cluster"
466  * (arcprotos are never freed) and place them in the array at "addresses".
467  * The routine sets an element to NOARCPROTO if allocation fails.
468  */
db_allocarcproto(CLUSTER * cluster)469 ARCPROTO *db_allocarcproto(CLUSTER *cluster)
470 {
471 	REGISTER ARCPROTO *ap;
472 
473 	ap = (ARCPROTO *)emalloc((sizeof (ARCPROTO)), cluster);
474 	if (ap == 0) return(NOARCPROTO);
475 	ap->userbits = ap->temp1 = ap->temp2 = 0;
476 	ap->numvar = 0;
477 	ap->protoname = NOSTRING;
478 	ap->firstvar = NOVARIABLE;
479 	ap->tech = NOTECHNOLOGY;
480 	ap->nextarcproto = NOARCPROTO;
481 	ap->nominalwidth = 0;
482 	ap->arcindex = 0;
483 	return(ap);
484 }
485 
486 /*
487  * routine to return arcproto "ap" to the pool of free arcs
488  */
db_freearcproto(ARCPROTO * ap)489 void db_freearcproto(ARCPROTO *ap)
490 {
491 	if (ap == NOARCPROTO) return;
492 	if (ap->numvar != 0) db_freevars(&ap->firstvar, &ap->numvar);
493 	efree((CHAR *)ap);
494 }
495 
496 /*
497  * routine to create a new arc prototype with name "name" and default wire
498  * width "nomwidth".  The arc prototype is placed in technology "tech".  The
499  * index of the arc prototype is in "ind" which must be a unique number that
500  * identifies this arcinst proto.  The routine returns zero if the new arc
501  * prototype has successfully been created.  It returns NOARCPROTO if the arc
502  * prototype cannot be created.
503  */
db_newarcproto(TECHNOLOGY * tech,CHAR * name,INTBIG nomwidth,INTBIG ind)504 ARCPROTO *db_newarcproto(TECHNOLOGY *tech, CHAR *name, INTBIG nomwidth, INTBIG ind)
505 {
506 	REGISTER ARCPROTO *ap;
507 	REGISTER ARCPROTO *lat, *tat;
508 	REGISTER CHAR *pp;
509 
510 	/* make sure name is valid */
511 	for(pp = name; *pp != 0; pp++) if (*pp <= ' ' || *pp >= 0177) return(NOARCPROTO);
512 
513 	ap = db_allocarcproto(tech->cluster);
514 	if (ap == NOARCPROTO) return(NOARCPROTO);
515 	for(lat = NOARCPROTO, tat = tech->firstarcproto; tat != NOARCPROTO; tat = tat->nextarcproto)
516 		lat = tat;
517 	if (lat == NOARCPROTO) tech->firstarcproto = ap; else
518 		lat->nextarcproto = ap;
519 	ap->nextarcproto = NOARCPROTO;
520 	if (allocstring(&ap->protoname, name, tech->cluster)) return(NOARCPROTO);
521 	ap->nominalwidth = nomwidth;
522 	ap->arcindex = ind;
523 	ap->tech = tech;
524 	return(ap);
525 }
526 
527 /************************* ARC INSTANCES *************************/
528 
529 /*
530  * Routine to allocate an arcinst from memory cluster "cluster".
531  * The routine returns NOARCINST if allocation fails.
532  */
allocarcinst(CLUSTER * cluster)533 ARCINST *allocarcinst(CLUSTER *cluster)
534 {
535 	REGISTER ARCINST *ai;
536 
537 	ai = (ARCINST *)emalloc((sizeof (ARCINST)), cluster);
538 	if (ai == 0) return(NOARCINST);
539 	ai->changed = 0;   ai->changeaddr = (CHAR *)NOCHANGE;
540 	ai->userbits = ai->temp1 = ai->temp2 = 0;
541 	ai->numvar = 0;
542 	ai->network = NONETWORK;
543 	ai->firstvar = NOVARIABLE;
544 	ai->nextarcinst = NOARCINST;
545 	ai->geom = NOGEOM;
546 	ai->proto = NOARCPROTO;
547 	ai->length = 0;
548 	ai->width = 0;
549 	ai->endshrink = 0;
550 	ai->end[0].xpos = ai->end[0].ypos = 0;
551 	ai->end[1].xpos = ai->end[1].ypos = 0;
552 	ai->end[0].nodeinst = ai->end[1].nodeinst = NONODEINST;
553 	ai->end[0].portarcinst = ai->end[1].portarcinst = NOPORTARCINST;
554 	ai->prevarcinst = NOARCINST;
555 	ai->parent = NONODEPROTO;
556 	return(ai);
557 }
558 
559 /*
560  * routine to return arcinst "ai" to the pool of free arc instances
561  */
freearcinst(ARCINST * ai)562 void freearcinst(ARCINST *ai)
563 {
564 	if (ai == NOARCINST) return;
565 	if (ai->numvar != 0) db_freevars(&ai->firstvar, &ai->numvar);
566 	efree((CHAR *)ai);
567 }
568 
569 /*
570  * Routine to allocate a portarcinst from memory cluster "cluster".
571  * The routine returns NOPORTARCINST if allocation fails.
572  */
allocportarcinst(CLUSTER * cluster)573 PORTARCINST *allocportarcinst(CLUSTER *cluster)
574 {
575 	REGISTER PORTARCINST *pi;
576 
577 	pi = (PORTARCINST *)emalloc((sizeof (PORTARCINST)), cluster);
578 	if (pi == 0) return(NOPORTARCINST);
579 	pi->firstvar = NOVARIABLE;
580 	pi->numvar = 0;
581 	pi->nextportarcinst = NOPORTARCINST;
582 	pi->proto = NOPORTPROTO;
583 	pi->conarcinst = NOARCINST;
584 	return(pi);
585 }
586 
587 /*
588  * routine to return portarcinst "pi" to the pool of free ports
589  */
freeportarcinst(PORTARCINST * pi)590 void freeportarcinst(PORTARCINST *pi)
591 {
592 	if (pi == NOPORTARCINST) return;
593 	if (pi->numvar != 0) db_freevars(&pi->firstvar, &pi->numvar);
594 	efree((CHAR *)pi);
595 }
596 
597 /*
598  * create a new arcinst of proto "typ", width "width", and initial userbits in
599  * "initialbits".  One end is connected to portproto "pA" of nodeinst
600  * "nA" and the other end is connected to portproto "pB" of nodeinst "nB".
601  * The arcinst is located in cell "parnt".  The address of the arcinst is
602  * returned (NOARCINST if an error is found).
603  */
newarcinst(ARCPROTO * typ,INTBIG wid,INTBIG initialbits,NODEINST * nA,PORTPROTO * pA,INTBIG xA,INTBIG yA,NODEINST * nB,PORTPROTO * pB,INTBIG xB,INTBIG yB,NODEPROTO * parnt)604 ARCINST *newarcinst(ARCPROTO *typ, INTBIG wid, INTBIG initialbits, NODEINST *nA,
605 	PORTPROTO *pA, INTBIG xA, INTBIG yA, NODEINST *nB, PORTPROTO *pB, INTBIG xB, INTBIG yB,
606 	NODEPROTO *parnt)
607 {
608 	REGISTER INTBIG i, lambda;
609 	REGISTER PORTPROTO *pp;
610 	REGISTER ARCINST *ai;
611 	REGISTER POLYGON *poly;
612 
613 	/* check for missing specifications */
614 	if (typ == NOARCPROTO)
615 	{
616 		db_donextchangequietly = FALSE;
617 		return((ARCINST *)db_error(DBBADPROTO|DBNEWARCINST));
618 	}
619 	if (parnt == NONODEPROTO)
620 	{
621 		db_donextchangequietly = FALSE;
622 		return((ARCINST *)db_error(DBBADPARENT|DBNEWARCINST));
623 	}
624 	if (wid < 0)
625 	{
626 		db_donextchangequietly = FALSE;
627 		return((ARCINST *)db_error(DBBADWIDTH|DBNEWARCINST));
628 	}
629 	if (nA == NONODEINST || pA == NOPORTPROTO)
630 	{
631 		db_donextchangequietly = FALSE;
632 		return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
633 	}
634 	if (nB == NONODEINST || pB == NOPORTPROTO)
635 	{
636 		db_donextchangequietly = FALSE;
637 		return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
638 	}
639 
640 	/* make sure that the node is in the cell */
641 	if (nA->parent != parnt)
642 	{
643 		db_donextchangequietly = FALSE;
644 		return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
645 	}
646 	if (nB->parent != parnt)
647 	{
648 		db_donextchangequietly = FALSE;
649 		return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
650 	}
651 
652 	/* make sure that the port proto is on the node */
653 	for(pp = nA->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
654 		if (pp == pA) break;
655 	if (pp == NOPORTPROTO)
656 	{
657 		db_donextchangequietly = FALSE;
658 		return((ARCINST *)db_error(DBBADENDAN|DBNEWARCINST));
659 	}
660 	for(pp = nB->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
661 		if (pp == pB) break;
662 	if (pp == NOPORTPROTO)
663 	{
664 		db_donextchangequietly = FALSE;
665 		return((ARCINST *)db_error(DBBADENDBN|DBNEWARCINST));
666 	}
667 
668 	/* make sure that the arc can connect to the port */
669 	for(i=0; pA->connects[i] != NOARCPROTO; i++)
670 		if (pA->connects[i] == typ) break;
671 	if (pA->connects[i] == NOARCPROTO)
672 	{
673 		(void)db_error(DBBADENDAC|DBNEWARCINST);
674 		if (db_printerrors)
675 			ttyputmsg(_("Port %s of node %s in cell %s cannot connect to arc %s"), pA->protoname,
676 				describenodeinst(nA), describenodeproto(parnt), describearcproto(typ));
677 		db_donextchangequietly = FALSE;
678 		return(NOARCINST);
679 	}
680 	for(i=0; pB->connects[i] != NOARCPROTO; i++)
681 		if (pB->connects[i] == typ) break;
682 	if (pB->connects[i] == NOARCPROTO)
683 	{
684 		(void)db_error(DBBADENDBC|DBNEWARCINST);
685 		if (db_printerrors)
686 			ttyputmsg(_("Port %s of node %s in cell %s cannot connect to arc %s"), pB->protoname,
687 				describenodeinst(nB), describenodeproto(parnt), describearcproto(typ));
688 		db_donextchangequietly = FALSE;
689 		return(NOARCINST);
690 	}
691 
692 	/* check that arcinst ends in proper portinst area */
693 	poly = allocpolygon(4, db_cluster);
694 	shapeportpoly(nA, pA, poly, FALSE);
695 	if (!isinside(xA, yA, poly))
696 	{
697 		(void)db_error(DBBADENDAP|DBNEWARCINST);
698 		if (db_printerrors)
699 		{
700 			lambda = lambdaofcell(parnt);
701 			ttyputmsg(_("Point (%s,%s) not inside port %s of node %s in cell %s"), latoa(xA, lambda),
702 				latoa(yA, lambda), pA->protoname, describenodeinst(nA), describenodeproto(parnt));
703 		}
704 		db_donextchangequietly = FALSE;
705 		freepolygon(poly);
706 		return(NOARCINST);
707 	}
708 	shapeportpoly(nB, pB, poly, FALSE);
709 	if (!isinside(xB, yB, poly))
710 	{
711 		(void)db_error(DBBADENDBP|DBNEWARCINST);
712 		if (db_printerrors)
713 		{
714 			lambda = lambdaofcell(parnt);
715 			ttyputmsg(_("Point (%s,%s) not inside port %s of node %s in cell %s"), latoa(xB, lambda),
716 				latoa(yB, lambda), pB->protoname, describenodeinst(nB), describenodeproto(parnt));
717 		}
718 		db_donextchangequietly = FALSE;
719 		freepolygon(poly);
720 		return(NOARCINST);
721 	}
722 	freepolygon(poly);
723 
724 	/* mark the start of a change to any cell instances that this arc connects */
725 	if (nA->proto->primindex == 0) startobjectchange((INTBIG)nA, VNODEINST);
726 	if (nB->proto->primindex == 0) startobjectchange((INTBIG)nB, VNODEINST);
727 
728 	/* create the arcinst */
729 	ai = db_newarcinst(typ, wid, initialbits, nA,pA,xA,yA, nB,pB,xB,yB, parnt);
730 
731 	/* mark the start of a change to any cell instances that this arc connects */
732 	if (nA->proto->primindex == 0) endobjectchange((INTBIG)nA, VNODEINST);
733 	if (nB->proto->primindex == 0) endobjectchange((INTBIG)nB, VNODEINST);
734 
735 	return(ai);
736 }
737 
738 /*
739  * internal procedure to create a new arcinst of proto "typ" with width
740  * "width".  One end is connected to portproto "pA" of nodeinst "nA" and the
741  * other end is connected to portproto "pB" of nodeinst "nB".  The arcinst
742  * is located in cell "parnt".  The address of the arcinst is returned.
743  * NOARCINST is returned upon error.
744  */
db_newarcinst(ARCPROTO * typ,INTBIG wid,INTBIG initialbits,NODEINST * nA,PORTPROTO * pA,INTBIG xA,INTBIG yA,NODEINST * nB,PORTPROTO * pB,INTBIG xB,INTBIG yB,NODEPROTO * parnt)745 ARCINST *db_newarcinst(ARCPROTO *typ, INTBIG wid, INTBIG initialbits, NODEINST *nA,
746 	PORTPROTO *pA, INTBIG xA, INTBIG yA, NODEINST *nB, PORTPROTO *pB, INTBIG xB, INTBIG yB,
747 	NODEPROTO *parnt)
748 {
749 	REGISTER ARCINST *ai;
750 	REGISTER PORTARCINST *npi;
751 
752 	ai = allocarcinst(parnt->lib->cluster);
753 	if (ai == NOARCINST)
754 	{
755 		db_donextchangequietly = FALSE;
756 		return(NOARCINST);
757 	}
758 	ai->proto = typ;
759 	ai->width = wid;
760 	ai->length = computedistance(xA,yA, xB,yB);
761 	ai->end[0].xpos = xA;   ai->end[0].ypos = yA;
762 	ai->end[1].xpos = xB;   ai->end[1].ypos = yB;
763 	ai->end[0].nodeinst = nA;
764 	ai->end[1].nodeinst = nB;
765 	ai->userbits = initialbits;
766 	determineangle(ai);
767 	ai->parent = parnt;
768 	ai->endshrink = 0;
769 
770 	ai->end[0].portarcinst = allocportarcinst(parnt->lib->cluster);
771 	npi = ai->end[0].portarcinst;
772 	npi->proto = pA;
773 	db_addportarcinst(nA, npi);
774 
775 	ai->end[1].portarcinst = allocportarcinst(parnt->lib->cluster);
776 	npi = ai->end[1].portarcinst;
777 	npi->proto = pB;
778 	db_addportarcinst(nB, npi);
779 
780 	/* create a geometry module for this arcinst */
781 	ai->geom = allocgeom(parnt->lib->cluster);
782 	if (ai->geom == NOGEOM) return(NOARCINST);
783 	ai->geom->entryisnode = FALSE;
784 	ai->geom->entryaddr.ai = ai;
785 
786 	/* enter arcinst in appropriate lists */
787 	db_enterarcinst(ai);
788 
789 	/* handle change control, constraint, and broadcast */
790 	if (!db_donextchangequietly && !db_dochangesquietly)
791 	{
792 		/* set the change cell environment */
793 		db_setchangecell(parnt);
794 
795 		/* tell all tools about this arcinst */
796 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTNEW, 0, 0, 0, 0, 0, 0);
797 
798 		/* tell constraint system about new arc */
799 		(*el_curconstraint->newobject)((INTBIG)ai, VARCINST);
800 	}
801 	db_donextchangequietly = FALSE;
802 
803 	/* report arcinst address */
804 	return(ai);
805 }
806 
807 /*
808  * routine to enter arcinst "ai" in the database structure by including it in
809  * the nodes it connects, putting it in the geometry list, and announcing
810  * the existance of the arcinst.
811  */
db_enterarcinst(ARCINST * ai)812 void db_enterarcinst(ARCINST *ai)
813 {
814 	REGISTER INTBIG i;
815 	REGISTER NODEPROTO *np;
816 	REGISTER NODEINST *ni;
817 	REGISTER ARCINST *oai;
818 	REGISTER PORTARCINST *pi;
819 
820 	/* place connection in the nodes */
821 	for(i=0; i<2; i++) ai->end[i].portarcinst->conarcinst = ai;
822 
823 	/* put in list of arcs in this cell */
824 	np = ai->parent;
825 	if (np->firstarcinst != NOARCINST) np->firstarcinst->prevarcinst = ai;
826 	ai->nextarcinst = np->firstarcinst;
827 	ai->prevarcinst = NOARCINST;
828 	np->firstarcinst = ai;
829 	(void)setshrinkvalue(ai, TRUE);
830 
831 	/* link the geometry entry for this arcinst */
832 	linkgeom(ai->geom, np);
833 
834 	/* special case: if connecting nodes that change size with connectivity, update their geometry */
835 	if ((ai->end[0].nodeinst->proto->userbits&WIPEON1OR2) != 0)
836 		updategeom(ai->end[0].nodeinst->geom, np);
837 	if ((ai->end[1].nodeinst->proto->userbits&WIPEON1OR2) != 0)
838 		updategeom(ai->end[1].nodeinst->geom, np);
839 
840 	/* special case: update end shrinkage on connected arcs */
841 	for(i=0; i<2; i++)
842 	{
843 		ni = ai->end[i].nodeinst;
844 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
845 		{
846 			oai = pi->conarcinst;
847 			if (oai == ai) continue;
848 			if (setshrinkvalue(oai, TRUE) != 0)
849 				updategeom(oai->geom, oai->parent);
850 		}
851 	}
852 
853 	/* mark the arcinst alive */
854 	ai->userbits &= ~DEADA;
855 
856 	/* mark a change to the database */
857 	db_changetimestamp++;
858 }
859 
860 /*
861  * routine to add portarcinst "npi" to the list of portarcinsts on nodeinst
862  * "ni".
863  */
db_addportarcinst(NODEINST * ni,PORTARCINST * npi)864 void db_addportarcinst(NODEINST *ni, PORTARCINST *npi)
865 {
866 	REGISTER PORTPROTO *pp, *pr;
867 	REGISTER PORTARCINST *pi, *lpi;
868 
869 	pr = npi->proto;
870 	pp = ni->proto->firstportproto;
871 	lpi = NOPORTARCINST;
872 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
873 	{
874 		while (pp != pi->proto && pp != pr) pp = pp->nextportproto;
875 		if (pp == pr)
876 		{
877 			if (lpi == NOPORTARCINST) ni->firstportarcinst = npi; else
878 				lpi->nextportarcinst = npi;
879 			npi->nextportarcinst = pi;
880 			return;
881 		}
882 		lpi = pi;
883 	}
884 
885 	/* not found inside list: append to end */
886 	if (lpi == NOPORTARCINST) ni->firstportarcinst = npi; else
887 		lpi->nextportarcinst = npi;
888 	npi->nextportarcinst = NOPORTARCINST;
889 }
890 
891 /*
892  * routine to remove arcinst "ai" from the database.  The routine returns
893  * FALSE if successful, TRUE if an error.
894  */
killarcinst(ARCINST * ai)895 BOOLEAN killarcinst(ARCINST *ai)
896 {
897 	REGISTER NODEINST *ni1, *ni2;
898 
899 	if (ai == NOARCINST)
900 	{
901 		db_donextchangequietly = FALSE;
902 		return(db_error(DBBADINST|DBKILLARCINST) != 0);
903 	}
904 
905 	/* mark the start of a change to any cell instances that this arc connects */
906 	ni1 = ai->end[0].nodeinst;   ni2 = ai->end[1].nodeinst;
907 	if (ni1->proto->primindex == 0) startobjectchange((INTBIG)ni1, VNODEINST);
908 	if (ni2->proto->primindex == 0) startobjectchange((INTBIG)ni2, VNODEINST);
909 
910 	/* remove arcinst from appropriate lists */
911 	db_killarcinst(ai);
912 
913 	/* mark the start of a change to any cell instances that this arc connects */
914 	if (ni1->proto->primindex == 0) endobjectchange((INTBIG)ni1, VNODEINST);
915 	if (ni2->proto->primindex == 0) endobjectchange((INTBIG)ni2, VNODEINST);
916 
917 	/* freeing of the arcinst will be done later */
918 	return(FALSE);
919 }
920 
db_killarcinst(ARCINST * ai)921 void db_killarcinst(ARCINST *ai)
922 {
923 	db_retractarcinst(ai);
924 
925 	/* handle change control, constraint, and broadcast */
926 	if (!db_donextchangequietly && !db_dochangesquietly)
927 	{
928 		/* set the change cell environment */
929 		db_setchangecell(ai->parent);
930 
931 		/* tell all tools about this arcinst */
932 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTKILL, 0, 0, 0, 0, 0, 0);
933 
934 		/* tell constraint system about killed arc */
935 		(*el_curconstraint->killobject)((INTBIG)ai, VARCINST);
936 	} else
937 	{
938 		/* delete the arc now: no change control */
939 		freeportarcinst(ai->end[0].portarcinst);
940 		freeportarcinst(ai->end[1].portarcinst);
941 		freegeom(ai->geom);
942 		freearcinst(ai);
943 	}
944 	db_donextchangequietly = FALSE;
945 }
946 
db_retractarcinst(ARCINST * ai)947 void db_retractarcinst(ARCINST *ai)
948 {
949 	REGISTER INTBIG i;
950 	REGISTER NODEINST *ni;
951 	REGISTER PORTARCINST *pi, *lpi;
952 	REGISTER ARCINST *oai;
953 
954 	/* remove from list of arcs in this cell */
955 	if (ai->nextarcinst != NOARCINST) ai->nextarcinst->prevarcinst = ai->prevarcinst;
956 	if (ai->prevarcinst != NOARCINST) ai->prevarcinst->nextarcinst = ai->nextarcinst; else
957 		ai->parent->firstarcinst = ai->nextarcinst;
958 
959 	/* remove from R-tree geometric list */
960 	undogeom(ai->geom, ai->parent);
961 
962 	/* now update any nodes touching this arcinst */
963 	for(i=0; i<2; i++)
964 	{
965 		ni = ai->end[i].nodeinst;
966 		if (ni == NONODEINST) continue;
967 		if ((ni->userbits&DEADN) != 0) continue;
968 		lpi = NOPORTARCINST;
969 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
970 		{
971 			if (pi->conarcinst == ai)
972 			{
973 				if (lpi == NOPORTARCINST) ni->firstportarcinst = pi->nextportarcinst; else
974 					lpi->nextportarcinst = pi->nextportarcinst;
975 			}
976 			lpi = pi;
977 		}
978 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
979 		{
980 			oai = pi->conarcinst;
981 			if (oai == ai) continue;
982 			if (setshrinkvalue(oai, TRUE) != 0)
983 				updategeom(oai->geom, oai->parent);
984 		}
985 
986 		/* special case: if disconnecting from node that changes size with connectivity, update its geometry */
987 		if ((ni->proto->userbits&WIPEON1OR2) != 0)
988 			updategeom(ni->geom, ai->parent);
989 	}
990 
991 	/* mark it dead */
992 	ai->userbits |= DEADA;
993 
994 	/* mark a change to the database */
995 	db_changetimestamp++;
996 }
997 
998 /*
999  * Routine to return the default width of primitive arc "ap".
1000  */
defaultarcwidth(ARCPROTO * ap)1001 INTBIG defaultarcwidth(ARCPROTO *ap)
1002 {
1003 	REGISTER INTBIG width;
1004 	REGISTER VARIABLE *var;
1005 
1006 	/* take default width from the prototype */
1007 	width = ap->nominalwidth;
1008 
1009 	/* see if there is an override on the arc */
1010 	var = getvalkey((INTBIG)ap, VARCPROTO, VINTEGER, el_arc_width_default_key);
1011 	if (var != NOVARIABLE)
1012 		width = var->addr * el_curlib->lambda[ap->tech->techindex] / WHOLE;
1013 
1014 	return(width);
1015 }
1016 
1017 /*
1018  * routine to modify arcinst "ai" by "deltawid" in width, "deltax" in both
1019  * ends X geometry and "deltay" in both ends Y position.  The X and Y
1020  * motion must not cause the connecting nodes to move.  The routine
1021  * returns true if an error is detected.  Null arc motion is allowed
1022  * to queue some other change to the arc.
1023  */
modifyarcinst(ARCINST * ai,INTBIG deltawid,INTBIG deltax1,INTBIG deltay1,INTBIG deltax2,INTBIG deltay2)1024 BOOLEAN modifyarcinst(ARCINST *ai, INTBIG deltawid, INTBIG deltax1, INTBIG deltay1,
1025 	INTBIG deltax2, INTBIG deltay2)
1026 {
1027 	REGISTER INTBIG oldwid, oldlen, oldx0, oldy0, oldx1, oldy1;
1028 	REGISTER BOOLEAN e1, e2;
1029 
1030 	if (ai == NOARCINST)
1031 	{
1032 		db_donextchangequietly = FALSE;
1033 		return(db_error(DBBADINST|DBMODIFYARCINST) != 0);
1034 	}
1035 	if (ai->width + deltawid < 0)
1036 	{
1037 		db_donextchangequietly = FALSE;
1038 		return(db_error(DBBADNEWWID|DBMODIFYARCINST) != 0);
1039 	}
1040 	if (deltax1 != 0 || deltay1 != 0 || deltax2 != 0 || deltay2 != 0)
1041 	{
1042 		if ((ai->userbits&(FIXED|CANTSLIDE)) != 0)
1043 		{
1044 			db_donextchangequietly = FALSE;
1045 			return(db_error(DBNOSLIDING|DBMODIFYARCINST) != 0);
1046 		}
1047 
1048 		/* temporarily set the width and check for port validity */
1049 		ai->width += deltawid;
1050 		e1 = db_stillinport(ai, 0, ai->end[0].xpos+deltax1, ai->end[0].ypos+deltay1);
1051 		e2 = db_stillinport(ai, 1, ai->end[1].xpos+deltax2, ai->end[1].ypos+deltay2);
1052 		ai->width -= deltawid;
1053 		if (!e1 || !e2)
1054 		{
1055 			db_donextchangequietly = FALSE;
1056 			return(db_error(DBNOTINPORT|DBMODIFYARCINST) != 0);
1057 		}
1058 	}
1059 
1060 	/* change the arcinst */
1061 	oldwid = ai->width;   oldlen = ai->length;
1062 	ai->width += deltawid;
1063 	oldx0 = ai->end[0].xpos;   ai->end[0].xpos += deltax1;
1064 	oldy0 = ai->end[0].ypos;   ai->end[0].ypos += deltay1;
1065 	oldx1 = ai->end[1].xpos;   ai->end[1].xpos += deltax2;
1066 	oldy1 = ai->end[1].ypos;   ai->end[1].ypos += deltay2;
1067 	ai->length = computedistance(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
1068 	(void)setshrinkvalue(ai, TRUE);
1069 	updategeom(ai->geom, ai->parent);
1070 
1071 	/* handle change control, constraint, and broadcast */
1072 	if (!db_donextchangequietly && !db_dochangesquietly)
1073 	{
1074 		/* tell all tools about this arcinst */
1075 		ai->changeaddr = (CHAR *)db_change((INTBIG)ai, ARCINSTMOD, oldx0, oldy0, oldx1, oldy1,
1076 			oldwid, oldlen);
1077 
1078 		/* tell constraint system about modified arc */
1079 		(*el_curconstraint->modifyarcinst)(ai, oldx0, oldy0, oldx1, oldy1, oldwid, oldlen);
1080 
1081 		/* set the change cell environment */
1082 		db_setchangecell(ai->parent);
1083 	}
1084 	db_donextchangequietly = FALSE;
1085 
1086 	/* mark a change to the database */
1087 	db_changetimestamp++;
1088 
1089 	return(FALSE);
1090 }
1091 
1092 /*
1093  * Routine to set the "endshrink" value for arcinst "ai".  Returns
1094  * nonzero if the value changed.
1095  */
setshrinkvalue(ARCINST * ai,BOOLEAN extend)1096 INTBIG setshrinkvalue(ARCINST *ai, BOOLEAN extend)
1097 {
1098 	REGISTER INTBIG i, result, ret;
1099 	INTBIG shorter[2];
1100 	REGISTER NODEINST *ni;
1101 	REGISTER ARCINST *oar;
1102 	REGISTER INTBIG newshrink;
1103 	REGISTER PORTARCINST *pi;
1104 
1105 	/* reset bits and compute shortening amount for each end of arcinst */
1106 	for(i=0; i<2; i++)
1107 	{
1108 		if (ai->end[i].nodeinst == NONODEINST) return(0);
1109 		ai->end[i].nodeinst->userbits &= ~NSHORT;
1110 if (ai->end[i].portarcinst->proto == NOPORTPROTO) continue;
1111 		shorter[i] = db_checkshortening(ai->end[i].nodeinst, ai->end[i].portarcinst->proto);
1112 	}
1113 
1114 	/* reset the arcinst shortening bits */
1115 	ai->userbits &= ~ASHORT;
1116 
1117 	/* set shortening bits if the shortening factor is nonzero */
1118 	result = (shorter[1] << 16) | shorter[0];
1119 	if (result != 0)
1120 	{
1121 		ai->userbits |= ASHORT;
1122 		for(i=0; i<2; i++) if (shorter[i] != 0)
1123 		{
1124 			ni = ai->end[i].nodeinst;
1125 			if ((ni->proto->userbits&NODESHRINK) != 0) ni->userbits |= NSHORT;
1126 			if (!extend) continue;
1127 			if ((ni->proto->userbits&ARCSHRINK) != 0)
1128 			{
1129 
1130 				for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1131 				{
1132 					oar = pi->conarcinst;
1133 					if (oar == ai) continue;
1134 					oar->userbits |= ASHORT;
1135 					if (oar->end[0].nodeinst == ni)
1136 						newshrink = (oar->endshrink & 0xFFFF0000) | shorter[i]; else
1137 							newshrink = (oar->endshrink & 0xFFFF) | (shorter[i] << 16);
1138 					if (newshrink != oar->endshrink)
1139 					{
1140 						oar->endshrink = newshrink;
1141 						updategeom(oar->geom, oar->parent);
1142 					}
1143 				}
1144 			}
1145 		}
1146 	}
1147 
1148 	/* return the shrink value for this arcinst */
1149 	if (ai->endshrink == result) ret = 0; else ret = 1;
1150 	ai->endshrink = result;
1151 	return(ret);
1152 }
1153 
1154 /*
1155  * routine to determine, for the arcinst on portinst "pp" of nodeinst "ni",
1156  * whether the nodeinst and/or the arcs must be shortened to compensate
1157  * for nonmanhattan geometry and if so, by what amount.  The
1158  * routine returns the angle between the nonmanhattan objects.
1159  */
1160 #define MAXANGLES 3
db_checkshortening(NODEINST * ni,PORTPROTO * pp)1161 INTBIG db_checkshortening(NODEINST *ni, PORTPROTO *pp)
1162 {
1163 	REGISTER PORTARCINST *pi;
1164 	REGISTER NODEPROTO *np;
1165 	REGISTER ARCINST *ai;
1166 	REGISTER INTBIG ang, off90, total;
1167 	INTBIG angles[MAXANGLES];
1168 
1169 	/* quit now if we don't have to worry about this kind of nodeinst */
1170 	np = ni->proto;
1171 	if (np == NONODEPROTO) return(0);
1172 	if ((np->userbits&(NODESHRINK|ARCSHRINK)) == 0) return(0);
1173 
1174 	/* gather the angles of the nodes/arcs */
1175 	total = off90 = 0;
1176 	for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = pi->nextportarcinst)
1177 	{
1178 		ai = pi->conarcinst;
1179 
1180 		/* ignore zero-size arcs */
1181 		if (ai->width == 0) continue;
1182 
1183 		/* ignore this arcinst if it is not on the desired port */
1184 		if ((np->userbits&ARCSHRINK) == 0 && pi->proto != pp) continue;
1185 
1186 		/* compute the angle */
1187 		ang = (ai->userbits&AANGLE) >> AANGLESH;
1188 		if (ai->end[1].portarcinst->proto == pi->proto && ai->end[1].nodeinst == ni) ang += 180;
1189 		ang %= 360;
1190 		if ((ang%90) != 0) off90++;
1191 		if (total < MAXANGLES) angles[total++] = ang; else
1192 			break;
1193 	}
1194 
1195 	/* throw in the nodeinst rotation factor if it is important */
1196 	if ((np->userbits&NODESHRINK) != 0)
1197 	{
1198 		ang = (pp->userbits & PORTANGLE) >> PORTANGLESH;
1199 		ang += (ni->rotation+5) / 10;
1200 		if (ni->transpose != 0) { ang = 270 - ang; if (ang < 0) ang += 360; }
1201 		ang = (ang+180)%360;
1202 		if ((ang%90) != 0) off90++;
1203 		if (total < MAXANGLES) angles[total++] = ang;
1204 	}
1205 
1206 	/* all fine if all manhattan angles involved */
1207 	if (off90 == 0) return(0);
1208 
1209 	/* give up if too many arcinst angles */
1210 	if (total != 2) return(0);
1211 
1212 	/* compute and return factor */
1213 	ang = abs(angles[1]-angles[0]);
1214 	if (ang > 180) ang = 360 - ang;
1215 	if (ang > 90) ang = 180 - ang;
1216 	return(ang);
1217 }
1218 
1219 /*
1220  * routine to set the angle offset of arcinst "ai".  This value is the number
1221  * of degrees counter-clockwise from the right in the range of 0 to 359.
1222  * It is kept in the "userbits" field.
1223  */
determineangle(ARCINST * ai)1224 void determineangle(ARCINST *ai)
1225 {
1226 	INTBIG ang;
1227 	REGISTER NODEINST *ni;
1228 	REGISTER INTBIG cx0, cy0, cx1, cy1;
1229 
1230 	/* if length is zero, look at nodes */
1231 	if (ai->end[0].xpos == ai->end[1].xpos && ai->end[0].ypos == ai->end[1].ypos)
1232 	{
1233 		ni = ai->end[0].nodeinst;
1234 		cx0 = (ni->lowx + ni->highx) / 2;
1235 		cy0 = (ni->lowy + ni->highy) / 2;
1236 		ni = ai->end[1].nodeinst;
1237 		cx1 = (ni->lowx + ni->highx) / 2;
1238 		cy1 = (ni->lowy + ni->highy) / 2;
1239 		if (abs(cx0-cx1) > abs(cy0-cy1))
1240 		{
1241 			/* horizontal nodes */
1242 			if (cx0 > cx1) ang = 1800; else
1243 				ang = 0;
1244 		} else
1245 		{
1246 			if (cy0 > cy1) ang = 900; else
1247 				ang = 2700;
1248 		}
1249 	} else
1250 	{
1251 		ang = figureangle(ai->end[0].xpos, ai->end[0].ypos, ai->end[1].xpos, ai->end[1].ypos);
1252 	}
1253 	ai->userbits = (ai->userbits & ~AANGLE) | ((ang+5)/10 << AANGLESH);
1254 }
1255 
1256 /*
1257  * routine to return the address of an arcinst that it not in the
1258  * database for miscellaneous use
1259  */
initdummyarc(ARCINST * ai)1260 void initdummyarc(ARCINST *ai)
1261 {
1262 	static NODEINST node;
1263 
1264 	ai->proto = el_technologies->firstarcproto;
1265 	ai->length = 0;
1266 	ai->parent = NONODEPROTO;
1267 	ai->width = 0;
1268 	ai->endshrink = 0;
1269 	initdummynode(&node);
1270 	ai->end[0].nodeinst = ai->end[1].nodeinst = &node;
1271 	ai->end[0].portarcinst = ai->end[1].portarcinst = NOPORTARCINST;
1272 	ai->userbits = 0;
1273 	ai->numvar = 0;
1274 }
1275 
1276 /******************** PORT PROTOTYPES *************************/
1277 
1278 /*
1279  * Routine to allocate a portproto from memory cluster "cluster".
1280  * The routine returns NOPORTPROTO if allocation fails.
1281  */
allocportproto(CLUSTER * cluster)1282 PORTPROTO *allocportproto(CLUSTER *cluster)
1283 {
1284 	REGISTER PORTPROTO *pp;
1285 
1286 	pp = (PORTPROTO *)emalloc((sizeof (PORTPROTO)), cluster);
1287 	if (pp == 0) return(NOPORTPROTO);
1288 	pp->userbits = pp->temp1 = pp->temp2 = 0;
1289 	pp->network = NONETWORK;
1290 	pp->connects = 0;
1291 	pp->numvar = 0;
1292 	pp->changeaddr = (CHAR *)NOCHANGE;
1293 	pp->protoname = (CHAR *)-1;
1294 	pp->firstvar = NOVARIABLE;
1295 	pp->nextportproto = NOPORTPROTO;
1296 	pp->parent = NONODEPROTO;
1297 	pp->subnodeinst = NONODEINST;
1298 	pp->subportexpinst = NOPORTEXPINST;
1299 	pp->subportproto = NOPORTPROTO;
1300 	TDCLEAR(pp->textdescript);
1301 	defaulttextsize(1, pp->textdescript);
1302 	TDSETPOS(pp->textdescript, VTPOSBOXED);
1303 	pp->cachedequivport = NOPORTPROTO;
1304 	return(pp);
1305 }
1306 
1307 /*
1308  * routine to return portproto "pp" to the pool of free ports
1309  */
freeportproto(PORTPROTO * pp)1310 void freeportproto(PORTPROTO *pp)
1311 {
1312 	if (pp == NOPORTPROTO) return;
1313 	if (pp->numvar != 0) db_freevars(&pp->firstvar, &pp->numvar);
1314 	efree((CHAR *)pp);
1315 }
1316 
1317 /*
1318  * routine to allocate a portexpinst from memory cluster "cluster".
1319  * The routine returns NOPORTEXPINST if allocation fails.
1320  */
allocportexpinst(CLUSTER * cluster)1321 PORTEXPINST *allocportexpinst(CLUSTER *cluster)
1322 {
1323 	REGISTER PORTEXPINST *pe;
1324 
1325 	pe = (PORTEXPINST *)emalloc((sizeof (PORTEXPINST)), cluster);
1326 	if (pe == 0) return(NOPORTEXPINST);
1327 	pe->numvar = 0;
1328 	pe->firstvar = NOVARIABLE;
1329 	pe->nextportexpinst = NOPORTEXPINST;
1330 	pe->proto = NOPORTPROTO;
1331 	pe->exportproto = NOPORTPROTO;
1332 	return(pe);
1333 }
1334 
1335 /*
1336  * routine to return portexpinst "pe" to the pool of free ports
1337  */
freeportexpinst(PORTEXPINST * pe)1338 void freeportexpinst(PORTEXPINST *pe)
1339 {
1340 	if (pe == NOPORTEXPINST) return;
1341 	if (pe->numvar != 0) db_freevars(&pe->firstvar, &pe->numvar);
1342 	efree((CHAR *)pe);
1343 }
1344 
1345 /*
1346  * routine to add a portproto to a cell.  The port proto is in cell "np".
1347  * The location of the portproto is based on a sub-nodeinst and portproto on
1348  * that sub-nodeinst: the subnodeinst is "nodeinst" and the subportproto is
1349  * portproto "portaddress".  The name of the portproto is in "name".  The
1350  * routine returns the address of the portproto if sucessful, NOPORTPROTO
1351  * if the portproto cannot be created.
1352  */
newportproto(NODEPROTO * np,NODEINST * nodeinst,PORTPROTO * portaddress,CHAR * name)1353 PORTPROTO *newportproto(NODEPROTO *np, NODEINST *nodeinst, PORTPROTO *portaddress,
1354 	CHAR *name)
1355 {
1356 	REGISTER PORTPROTO *pp;
1357 	REGISTER CHAR *ptr;
1358 
1359 	/* error checks */
1360 	if (np == NONODEPROTO)
1361 	{
1362 		db_donextchangequietly = FALSE;
1363 		return((PORTPROTO *)db_error(DBBADCELL|DBNEWPORTPROTO));
1364 	}
1365 	if (*name == 0)
1366 	{
1367 		db_donextchangequietly = FALSE;
1368 		return((PORTPROTO *)db_error(DBBADNAME|DBNEWPORTPROTO));
1369 	}
1370 	for(ptr = name; *ptr != 0; ptr++) if (*ptr <= ' ' || *ptr >= 0177)
1371 	{
1372 		db_donextchangequietly = FALSE;
1373 		return((PORTPROTO *)db_error(DBBADNAME|DBNEWPORTPROTO));
1374 	}
1375 
1376 	/* check the validity of the sub-nodeinst */
1377 	for(pp = nodeinst->proto->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1378 		if (pp == portaddress) break;
1379 	if (pp == NOPORTPROTO)
1380 	{
1381 		db_donextchangequietly = FALSE;
1382 		return((PORTPROTO *)db_error(DBBADSUBPORT|DBNEWPORTPROTO));
1383 	}
1384 
1385 	/* look for duplicate names */
1386 	pp = getportproto(np, name);
1387 	if (pp != NOPORTPROTO)
1388 	{
1389 		db_donextchangequietly = FALSE;
1390 		return((PORTPROTO *)db_error(DBDUPLICATE|DBNEWPORTPROTO));
1391 	}
1392 
1393 	return(db_newportproto(np, nodeinst, portaddress, name));
1394 }
1395 
1396 /*
1397  * routine to do actual port prototype creation.  returns NOPORTPROTO
1398  * upon error
1399  */
db_newportproto(NODEPROTO * np,NODEINST * nodeinst,PORTPROTO * portaddress,CHAR * name)1400 PORTPROTO *db_newportproto(NODEPROTO *np, NODEINST *nodeinst, PORTPROTO *portaddress,
1401 	CHAR *name)
1402 {
1403 	REGISTER PORTPROTO *pp;
1404 
1405 	/* allocate a new portproto */
1406 	pp = allocportproto(np->lib->cluster);
1407 	if (pp == NOPORTPROTO)
1408 	{
1409 		db_donextchangequietly = FALSE;
1410 		return(NOPORTPROTO);
1411 	}
1412 
1413 	/* load up the port prototype */
1414 	pp->connects = portaddress->connects;
1415 	pp->parent = np;
1416 	pp->subnodeinst = nodeinst;
1417 	pp->subportproto = portaddress;
1418 	pp->userbits = portaddress->userbits;
1419 	TDCLEAR(pp->textdescript);
1420 	defaulttextdescript(pp->textdescript, nodeinst->geom);
1421 	defaulttextsize(1, pp->textdescript);
1422 	if (allocstring(&pp->protoname, name, np->lib->cluster))
1423 	{
1424 		db_donextchangequietly = FALSE;
1425 		return(NOPORTPROTO);
1426 	}
1427 
1428 	/* link it in and announce it */
1429 	db_enterportproto(pp);
1430 
1431 	/* handle change control, constraint, and broadcast */
1432 	if (!db_donextchangequietly && !db_dochangesquietly)
1433 	{
1434 		/* set the change cell environment */
1435 		db_setchangecell(np);
1436 
1437 		/* announce it */
1438 		pp->changeaddr = (CHAR *)db_change((INTBIG)pp, PORTPROTONEW, 0, 0, 0, 0, 0, 0);
1439 
1440 		/* tell constraint system about new port */
1441 		(*el_curconstraint->newobject)((INTBIG)pp, VPORTPROTO);
1442 	}
1443 	db_donextchangequietly = FALSE;
1444 
1445 	return(pp);
1446 }
1447 
db_enterportproto(PORTPROTO * pp)1448 void db_enterportproto(PORTPROTO *pp)
1449 {
1450 	REGISTER INTBIG i, j;
1451 	REGISTER PORTPROTO *p, *last;
1452 	REGISTER NODEPROTO *cell;
1453 	REGISTER PORTEXPINST *pe;
1454 
1455 	cell = pp->parent;
1456 	for(last = NOPORTPROTO, p = cell->firstportproto; p != NOPORTPROTO; p = p->nextportproto)
1457 		last = p;
1458 	if (last == NOPORTPROTO) cell->firstportproto = pp; else
1459 		last->nextportproto = pp;
1460 	pp->nextportproto = NOPORTPROTO;
1461 
1462 	/* load up this portexpinst description */
1463 	pe = allocportexpinst(cell->lib->cluster);
1464 	pe->proto = pp->subportproto;
1465 	db_addportexpinst(pp->subnodeinst, pe);
1466 	pe->exportproto = pp;
1467 	pp->subportexpinst = pe;
1468 
1469 	/* insert into hash table of portproto names */
1470 	cell->numportprotos++;
1471 	if (cell->numportprotos < cell->portprotohashtablesize/2)
1472 	{
1473 		i = db_namehash(pp->protoname) % cell->portprotohashtablesize;
1474 		for(j=1; j<=cell->portprotohashtablesize; j += 2)
1475 		{
1476 			if (cell->portprotohashtable[i] == NOPORTPROTO)
1477 			{
1478 				cell->portprotohashtable[i] = pp;
1479 				break;
1480 			}
1481 			i += j;
1482 			if (i >= cell->portprotohashtablesize) i -= cell->portprotohashtablesize;
1483 		}
1484 	} else
1485 	{
1486 		db_buildportprotohashtable(cell);
1487 	}
1488 
1489 	/* special case: if port is on node that changes size with connectivity, update its geometry */
1490 	if ((pp->subnodeinst->proto->userbits&WIPEON1OR2) != 0)
1491 		updategeom(pp->subnodeinst->geom, cell);
1492 
1493 	/* clear cache of port associations in this cell */
1494 	db_clearportcache(cell);
1495 
1496 	/* mark a change to the database */
1497 	db_changetimestamp++;
1498 }
1499 
db_clearportcache(NODEPROTO * cell)1500 void db_clearportcache(NODEPROTO *cell)
1501 {
1502 	REGISTER NODEPROTO *np, *onp;
1503 
1504 	FOR_CELLGROUP(np, cell)
1505 	{
1506 		for(onp = np; onp != NONODEPROTO; onp = onp->prevversion)
1507 			onp->cachedequivcell = NONODEPROTO;
1508 	}
1509 }
1510 
1511 /*
1512  * routine to add portexpinst "npe" to the list of portexpinsts on nodeinst
1513  * "ni".
1514  */
db_addportexpinst(NODEINST * ni,PORTEXPINST * npe)1515 void db_addportexpinst(NODEINST *ni, PORTEXPINST *npe)
1516 {
1517 	REGISTER PORTPROTO *pp, *pr;
1518 	REGISTER PORTEXPINST *pe, *lpe;
1519 
1520 	pr = npe->proto;
1521 	pp = ni->proto->firstportproto;
1522 	lpe = NOPORTEXPINST;
1523 	for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1524 	{
1525 		while (pp != pe->proto && pp != pr) pp = pp->nextportproto;
1526 		if (pp == pr)
1527 		{
1528 			if (lpe == NOPORTEXPINST) ni->firstportexpinst = npe; else
1529 				lpe->nextportexpinst = npe;
1530 			npe->nextportexpinst = pe;
1531 			return;
1532 		}
1533 		lpe = pe;
1534 	}
1535 
1536 	/* not found inside list: append to end */
1537 	if (lpe == NOPORTEXPINST) ni->firstportexpinst = npe; else
1538 		lpe->nextportexpinst = npe;
1539 	npe->nextportexpinst = NOPORTEXPINST;
1540 }
1541 
1542 /*
1543  * routine to add a portproto to a primitive nodeproto.  The nodeproto is in
1544  * "np" and the portproto can connect to arcs listed in the array "arcs".
1545  * The name of the portproto is in "name".  The routine returns the address of
1546  * the portproto if sucessful, NOPORTPROTO if the portproto cannot be created.
1547  */
db_newprimportproto(NODEPROTO * np,ARCPROTO ** arcs,CHAR * name)1548 PORTPROTO *db_newprimportproto(NODEPROTO *np, ARCPROTO **arcs, CHAR *name)
1549 {
1550 	REGISTER PORTPROTO *last;
1551 	REGISTER PORTPROTO *pp;
1552 	REGISTER CHAR *ptr;
1553 
1554 	/* error checks */
1555 	if (np == NONODEPROTO) return(NOPORTPROTO);
1556 
1557 	/* name must not have blank space in it */
1558 	for(ptr = name; *ptr != 0; ptr++) if (*ptr <= ' ' || *ptr >= 0177)
1559 	{
1560 		ttyputmsg(_("Port '%s' on primitive '%s' has bad name (spaces?)"), name, np->protoname);
1561 		return(NOPORTPROTO);
1562 	}
1563 
1564 	/* must be during initialization: no instances of this nodeproto */
1565 	if (np->firstinst != NONODEINST) return(NOPORTPROTO);
1566 
1567 	/* reject if there is already a portproto with this name */
1568 	for(pp = np->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1569 		if (namesame((last=pp)->protoname, name) == 0) return(NOPORTPROTO);
1570 
1571 	/* create the portproto and link it into the nodeinst proto */
1572 	pp = allocportproto(np->tech->cluster);
1573 	if (pp == NOPORTPROTO) return(NOPORTPROTO);
1574 	if (np->firstportproto == NOPORTPROTO) np->firstportproto = pp; else
1575 		last->nextportproto = pp;
1576 	pp->nextportproto = NOPORTPROTO;
1577 
1578 	/* initialize the values in the portproto */
1579 	pp->userbits = pp->temp1 = pp->temp2 = 0;
1580 	pp->network = NONETWORK;
1581 	pp->parent = np;       pp->connects = arcs;
1582 	pp->subnodeinst = NONODEINST;  pp->subportproto = NOPORTPROTO;
1583 	if (allocstring(&pp->protoname, name, np->tech->cluster)) return(NOPORTPROTO);
1584 
1585 	return(pp);
1586 }
1587 
1588 /*
1589  * routine to delete portproto "pp" from nodeproto "np".  Returns FALSE if
1590  * successful, TRUE if the portproto could not be deleted (does not
1591  * exist).  If the portproto has arcs connected to it in instances of the
1592  * cell, the arcs are deleted.  If the portproto is an export of
1593  * the cell, those exports are deleted (recursively).
1594  */
killportproto(NODEPROTO * np,PORTPROTO * pp)1595 BOOLEAN killportproto(NODEPROTO *np, PORTPROTO *pp)
1596 {
1597 	REGISTER PORTPROTO *spt;
1598 
1599 	/* look at all portprotos to ensure it exists */
1600 	if (np == NONODEPROTO)
1601 	{
1602 		db_donextchangequietly = FALSE;
1603 		return(db_error(DBBADCELL|DBKILLPORTPROTO) != 0);
1604 	}
1605 	for(spt = np->firstportproto; spt != NOPORTPROTO; spt = spt->nextportproto)
1606 		if (spt == pp) break;
1607 	if (spt == NOPORTPROTO)
1608 	{
1609 		db_donextchangequietly = FALSE;
1610 		return(db_error(DBBADPROTO|DBKILLPORTPROTO) != 0);
1611 	}
1612 
1613 	/* cannot delete port on primitive nodeproto */
1614 	if (pp->subnodeinst == NONODEINST)
1615 	{
1616 		db_donextchangequietly = FALSE;
1617 		return(db_error(DBPRIMITIVE|DBKILLPORTPROTO) != 0);
1618 	}
1619 
1620 	db_killportproto(pp);
1621 	return(FALSE);
1622 }
1623 
db_killportproto(PORTPROTO * pp)1624 void db_killportproto(PORTPROTO *pp)
1625 {
1626 	REGISTER PORTARCINST *pi, *nextpi;
1627 	REGISTER PORTEXPINST *pe, *nextpe;
1628 	REGISTER NODEINST *ni;
1629 	REGISTER NODEPROTO *np;
1630 
1631 	/* look at all instances of nodeproto for port use */
1632 	np = pp->parent;
1633 	for(ni = np->firstinst; ni != NONODEINST; ni = ni->nextinst)
1634 	{
1635 		/* see if this port has arcs on it on the higher-level instances */
1636 		for(pi = ni->firstportarcinst; pi != NOPORTARCINST; pi = nextpi)
1637 		{
1638 			nextpi = pi->nextportarcinst;
1639 			if (pi->proto == pp)
1640 			{
1641 				if ((pi->conarcinst->userbits&DEADA) != 0) continue;
1642 				startobjectchange((INTBIG)pi->conarcinst, VARCINST);
1643 				db_killarcinst(pi->conarcinst);
1644 			}
1645 		}
1646 
1647 		/* see if this port is an export */
1648 		for(pe = ni->firstportexpinst; pe != NOPORTEXPINST; pe = nextpe)
1649 		{
1650 			nextpe = pe->nextportexpinst;
1651 			if (pe->proto != pp) continue;
1652 			db_killportproto(pe->exportproto);
1653 			db_forcehierarchicalanalysis(np);
1654 		}
1655 	}
1656 
1657 	/* remove the port prototype */
1658 	db_retractportproto(pp);
1659 
1660 	/* handle change control, constraint, and broadcast */
1661 	if (!db_donextchangequietly && !db_dochangesquietly)
1662 	{
1663 		/* set the change cell environment */
1664 		db_setchangecell(np);
1665 
1666 		/* report the change */
1667 		pp->changeaddr = (CHAR *)db_change((INTBIG)pp, PORTPROTOKILL, 0, 0, 0, 0, 0, 0);
1668 
1669 		/* tell constraint system about killed port */
1670 		(*el_curconstraint->killobject)((INTBIG)pp, VPORTPROTO);
1671 	} else
1672 	{
1673 		/* delete the export now: no change control */
1674 		efree(pp->protoname);
1675 		freeportproto(pp);
1676 	}
1677 	db_donextchangequietly = FALSE;
1678 }
1679 
db_retractportproto(PORTPROTO * pp)1680 void db_retractportproto(PORTPROTO *pp)
1681 {
1682 	REGISTER PORTPROTO *ppo, *lpo;
1683 	REGISTER NODEPROTO *np;
1684 
1685 	/* remove portexpinst linkage from database */
1686 	db_removeportexpinst(pp);
1687 
1688 	/* remove the portproto from the list */
1689 	lpo = NOPORTPROTO;
1690 	np = pp->parent;
1691 	for(ppo = np->firstportproto; ppo != NOPORTPROTO; ppo = ppo->nextportproto)
1692 	{
1693 		if (ppo == pp)
1694 		{
1695 			if (lpo == NOPORTPROTO) np->firstportproto = pp->nextportproto; else
1696 				lpo->nextportproto = pp->nextportproto;
1697 			break;
1698 		}
1699 		lpo = ppo;
1700 	}
1701 
1702 	/* rebuild hash table of cell names */
1703 	np->numportprotos--;
1704 	db_buildportprotohashtable(np);
1705 
1706 	/* special case: if port is on node that changes size with connectivity, update its geometry */
1707 	if ((pp->subnodeinst->proto->userbits&WIPEON1OR2) != 0)
1708 		updategeom(pp->subnodeinst->geom, np);
1709 
1710 	/* clear cache of port associations in this cell */
1711 	db_clearportcache(pp->parent);
1712 
1713 	/* mark a change to the database */
1714 	db_changetimestamp++;
1715 }
1716 
1717 /*
1718  * routine to remove the portexpinst corresponding to portproto "pp" and its
1719  * linkage in the nodeinst
1720  */
db_removeportexpinst(PORTPROTO * pp)1721 void db_removeportexpinst(PORTPROTO *pp)
1722 {
1723 	REGISTER PORTEXPINST *pe, *lpe;
1724 
1725 	lpe = NOPORTEXPINST;
1726 	for(pe = pp->subnodeinst->firstportexpinst; pe != NOPORTEXPINST; pe = pe->nextportexpinst)
1727 	{
1728 		if (pe->exportproto == pp)
1729 		{
1730 			if (lpe == NOPORTEXPINST)
1731 				pp->subnodeinst->firstportexpinst = pe->nextportexpinst; else
1732 					lpe->nextportexpinst = pe->nextportexpinst;
1733 			freeportexpinst(pe);
1734 			break;
1735 		}
1736 		lpe = pe;
1737 	}
1738 }
1739 
1740 /*
1741  * Routine to rebuild the hash table of cell names in library "lib".
1742  */
db_buildportprotohashtable(NODEPROTO * cell)1743 void db_buildportprotohashtable(NODEPROTO *cell)
1744 {
1745 	REGISTER INTBIG i, j, numportprotos, hashtablesize;
1746 	REGISTER PORTPROTO *pp;
1747 
1748 	/* free the former table */
1749 	if (cell->portprotohashtablesize > 0)
1750 	{
1751 		cell->portprotohashtablesize = 0;
1752 		efree((CHAR *)cell->portprotohashtable);
1753 	}
1754 
1755 	/* determine the size of the hash table */
1756 	numportprotos = 0;
1757 	for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto) numportprotos++;
1758 	if (numportprotos != cell->numportprotos)
1759 	{
1760 		ttyputmsg(_("Warning: number of ports in cell %s corrected"), describenodeproto(cell));
1761 		cell->numportprotos = numportprotos;
1762 	}
1763 	hashtablesize = pickprime(numportprotos * 4);
1764 
1765 	/* create the hash table and clear it */
1766 	cell->portprotohashtable = (PORTPROTO **)emalloc(hashtablesize * (sizeof (PORTPROTO *)),
1767 		cell->lib->cluster);
1768 	if (cell->portprotohashtable == 0) return;
1769 	for(i=0; i<hashtablesize; i++)
1770 		cell->portprotohashtable[i] = NOPORTPROTO;
1771 	cell->portprotohashtablesize = hashtablesize;
1772 
1773 	/* insert all cells into the table */
1774 	for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
1775 	{
1776 		i = db_namehash(pp->protoname) % hashtablesize;
1777 		for(j=1; j<=hashtablesize; j += 2)
1778 		{
1779 			if (cell->portprotohashtable[i] == NOPORTPROTO)
1780 			{
1781 				cell->portprotohashtable[i] = pp;
1782 				break;
1783 			}
1784 			i += j;
1785 			if (i >= hashtablesize) i -= hashtablesize;
1786 		}
1787 	}
1788 }
1789