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