1 /*-------------------------------------------------------------------------*/
2 /* schema.c --- xcircuit routines specific to the schematic capture system */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	   */
4 /*-------------------------------------------------------------------------*/
5 
6 /*-------------------------------------------------------------------------*/
7 /*      written by Tim Edwards, 10/13/97    				   */
8 /*-------------------------------------------------------------------------*/
9 
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #ifndef XC_WIN32
15 #include <X11/Intrinsic.h>
16 #include <X11/StringDefs.h>
17 #endif
18 
19 #ifdef TCL_WRAPPER
20 #include <tk.h>
21 #else
22 #ifndef XC_WIN32
23 #include "Xw/Xw.h"
24 #include "Xw/MenuBtn.h"
25 #endif
26 #endif
27 
28 /*-------------------------------------------------------------------------*/
29 /* Local includes							   */
30 /*-------------------------------------------------------------------------*/
31 
32 #include "xcircuit.h"
33 #include "colordefs.h"
34 #include "menudep.h"
35 
36 /*----------------------------------------------------------------------*/
37 /* Function prototype declarations                                      */
38 /*----------------------------------------------------------------------*/
39 #include "prototypes.h"
40 
41 /*------------------------------------------------------------------------*/
42 /* External Variable definitions                                          */
43 /*------------------------------------------------------------------------*/
44 
45 #ifdef TCL_WRAPPER
46 extern Tcl_Interp *xcinterp;
47 #endif
48 
49 extern Globaldata xobjs;
50 extern XCWindowData *areawin;
51 extern xcWidget     menuwidgets[];
52 extern xcWidget	  netbutton;
53 extern char	  _STR[150];
54 extern char	  _STR2[250];
55 extern colorindex *colorlist;
56 
57 /*-------------------------------------------------------------------------*/
58 
59 extern xcWidget wsymb, wschema;
60 
61 /*--------------------------------------------------------*/
62 /* Menu calls (procedure wrappers)			  */
63 /*--------------------------------------------------------*/
64 
callwritenet(xcWidget w,pointertype mode,caddr_t calldata)65 void callwritenet(xcWidget w, pointertype mode, caddr_t calldata)
66 {
67    switch(mode) {
68      case 0:
69         writenet(topobject, "spice", "spc");
70 	break;
71      case 1:
72         writenet(topobject, "flatsim", "sim");
73 	break;
74      case 2:
75         writenet(topobject, "pcb", "pcbnet");
76 	break;
77      case 3:
78         writenet(topobject, "flatspice", "fspc");
79 	break;
80      case 4:
81 	/* Auto-numbering:  no output generated */
82 	writenet(topobject, "indexpcb", "");
83 	break;
84    }
85 }
86 
87 /*----------------------------------------------------------------------*/
88 /* Find the page object and instance with the indicated name.		*/
89 /*----------------------------------------------------------------------*/
90 
NameToPageObject(char * objname,objinstptr * ret_inst,int * ret_page)91 objectptr NameToPageObject(char *objname, objinstptr *ret_inst, int *ret_page)
92 {
93    int i;
94 
95    for (i = 0; i < xobjs.pages; i++) {
96       if (xobjs.pagelist[i]->pageinst == NULL) continue;
97       if (!strcmp(objname, xobjs.pagelist[i]->pageinst->thisobject->name)) {
98 	 if (ret_inst) *ret_inst = xobjs.pagelist[i]->pageinst;
99 	 if (ret_page) *ret_page = i;
100 	 return xobjs.pagelist[i]->pageinst->thisobject;
101       }
102    }
103    return NULL;
104 }
105 
106 /*--------------------------------------------------------------*/
107 /* Get the canonical name of an object (the part without the	*/
108 /* technology prefix, if any).					*/
109 /*--------------------------------------------------------------*/
110 
GetCanonicalName(char * fullname)111 char *GetCanonicalName(char *fullname)
112 {
113    char *canonname = strstr(fullname, "::");
114    if (canonname == NULL) return fullname;
115    return canonname + 2;
116 }
117 
118 /*----------------------------------------------------------------------*/
119 /* Find the object with the indicated name.				*/
120 /*									*/
121 /* WARNING:  If no library technology is given, XCircuit will return	*/
122 /* the first object with the name "objname".  If there are multiple	*/
123 /* objects of the same name in different technologies, then the one	*/
124 /* returned may not be the one expected!				*/
125 /*									*/
126 /* UPDATE (4/17/2020):  The "technology prefer" option can be used to	*/
127 /* set a precedence of one library over another in case of a naming	*/
128 /* conflict.  This should be used appropriately, as ambiguous object	*/
129 /* names in two technologies both marked "preferred" cannot be		*/
130 /* disambiguated.							*/
131 /*----------------------------------------------------------------------*/
132 
NameToObject(char * objname,objinstptr * ret_inst,Boolean dopages)133 objectptr NameToObject(char *objname, objinstptr *ret_inst, Boolean dopages)
134 {
135    int i;
136    liblistptr spec;
137    Boolean notech = FALSE;
138    Boolean preferred = FALSE;
139    char *techptr;
140    objectptr retobj = NULL;
141    TechPtr nsp;
142 
143    if (strstr(objname, "::") == NULL) notech = TRUE;
144 
145    for (i = 0; i < xobjs.numlibs; i++) {
146       for (spec = xobjs.userlibs[i].instlist; spec != NULL; spec = spec->next) {
147 	 techptr = spec->thisinst->thisobject->name;
148 	 if (notech)
149 	    techptr = GetCanonicalName(spec->thisinst->thisobject->name);
150          if (!strcmp(objname, techptr)) {
151 	    if ((retobj == NULL) || ((retobj != NULL) && (preferred == FALSE))) {
152 		if (ret_inst) *ret_inst = spec->thisinst;
153 		retobj = spec->thisinst->thisobject;
154 		nsp = GetObjectTechnology(retobj);
155 		preferred = (nsp && (nsp->flags & TECH_PREFER)) ? TRUE : FALSE;
156 	    }
157 	 }
158       }
159    }
160 
161    if (retobj != NULL) return retobj;
162 
163    if (dopages)
164       return NameToPageObject(objname, ret_inst, NULL);
165    else
166       return NULL;
167 }
168 
169 /*------------------------------------------------------------------------*/
170 /* Ensure that a page name is unique (required for schematic association) */
171 /*------------------------------------------------------------------------*/
172 
checkpagename(objectptr thispageobj)173 int checkpagename(objectptr thispageobj)
174 {
175    int p, thispage;
176    /* char *objname = thispageobj->name; (jdk) */
177    Boolean changed;
178    Boolean update = False;
179    char *clnptr = NULL;
180    int n;
181 
182    /* Check for ":n" suffix and prepare for possible update */
183    clnptr = strrchr(thispageobj->name, ':');
184    if (clnptr != NULL)
185       if (sscanf(clnptr + 1, "%d", &n) != 1)
186 	 clnptr = NULL;
187 
188    /* Find the page number of this page object */
189    for (p = 0; p < xobjs.pages; p++) {
190       if (xobjs.pagelist[p]->pageinst != NULL) {
191 	 if (xobjs.pagelist[p]->pageinst->thisobject == thispageobj) {
192 	    thispage = p;
193 	    break;
194 	 }
195       }
196    }
197    if (p == xobjs.pages) {
198       Fprintf(stderr, "Error:  Object is not a page object!\n");
199       return 0;
200    }
201 
202    /* Look for any other pages with the same name */
203    do {
204       changed = False;
205       for (p = 0; p < xobjs.pages; p++) {
206 	 if (p == thispage) continue;
207          if (xobjs.pagelist[p]->pageinst != NULL) {
208 	    if (!filecmp(xobjs.pagelist[p]->pageinst->thisobject->name,
209 			thispageobj->name)) {
210 	       /* append ":2" to name or update suffix to ensure uniqueness */
211 	       if (clnptr == NULL)
212 		  sprintf(thispageobj->name, "%s:2", thispageobj->name);
213 	       else
214 		  sprintf(clnptr + 1, "%d", n + 1);
215 	       changed = True;
216 	       update = True;
217 	       break;
218 	    }
219 	 }
220       }
221    } while (changed);
222    if (update) {
223       renamepage(thispage);
224       return -1;
225    }
226    return 0;
227 }
228 
229 /*--------------------------------------*/
230 /* Find connectivity of a selection	*/
231 /*--------------------------------------*/
232 
startconnect(xcWidget button,caddr_t clientdata,caddr_t calldata)233 void startconnect(xcWidget button, caddr_t clientdata, caddr_t calldata)
234 {
235    if (areawin->selects > 0)
236       connectivity(button, clientdata, calldata);
237 }
238 
239 /*----------------------------------------------------------------------*/
240 /* connectivity():  Find electrical connections into a node.		*/
241 /* (does recursive search on object heirarchy)				*/
242 /*----------------------------------------------------------------------*/
243 
connectivity(xcWidget button,caddr_t clientdata,caddr_t calldata)244 void connectivity(xcWidget button, caddr_t clientdata, caddr_t calldata)
245 {
246    short *gsel = NULL;
247    selection *rselect = NULL, *nextselect;
248    genericptr ggen = NULL;
249    Genericlist *netlist = NULL;
250    int depth, lbus, netid, subnetid;
251    buslist *sbus;
252    pushlistptr seltop, nextptr;
253    objectptr nettop, pschem; /*  thisobj, (jdk) */
254    char *snew;
255    stringpart *ppin;
256 
257    /* erase any existing highlighted network */
258    highlightnetlist(topobject, areawin->topinstance, 0);
259 
260    seltop = (pushlistptr)malloc(sizeof(pushlist));
261    seltop->thisinst = areawin->topinstance;
262    seltop->next = NULL;
263 
264    /* pick the first selection that looks like a valid network part */
265 
266    if (areawin->selects > 0) {
267       for (gsel = areawin->selectlist; gsel < areawin->selectlist +
268 		areawin->selects; gsel++) {
269 	 ggen = *(topobject->plist + *gsel);
270          if (SELECTTYPE(gsel) == LABEL) {
271 	    labelptr glab = SELTOLABEL(gsel);
272 	    if (glab->pin == LOCAL || glab->pin == GLOBAL) break;
273          }
274 	 else if (SELECTTYPE(gsel) == POLYGON) {
275 	    polyptr gpoly = SELTOPOLY(gsel);
276 	    if (!nonnetwork(gpoly)) break;
277 	 }
278       }
279    }
280    if ((areawin->selects == 0) || (gsel == areawin->selectlist
281 		+ areawin->selects)) {
282       rselect = recurselect(POLYGON | LABEL | OBJINST, MODE_CONNECT, &seltop);
283       /* Should look at the top scorer, when scoring has been implemented */
284       if (rselect && (rselect->selects > 0)) {
285 	 for (nextselect = rselect; (nextselect->next != NULL) &&
286 		(nextselect->selects > 0); nextselect = nextselect->next);
287          ggen = *(nextselect->thisinst->thisobject->plist + *(nextselect->selectlist));
288 	 while (rselect != NULL) {
289 	    nextselect = rselect->next;
290             free(rselect->selectlist);
291 	    free(rselect);
292 	    rselect = nextselect;
293 	 }
294       }
295    }
296 
297    /* Determine the net and the topmost object in which that	*/
298    /* net appears.  Then build the transformation matrix down	*/
299    /* to that object.  highlightnet() should end by popping the */
300    /* entire matrix stack.					*/
301 
302    if (ggen != NULL) {
303       if (checkvalid(topobject) == -1) {
304          destroynets(topobject);
305          createnets(areawin->topinstance, FALSE);
306       }
307       if ((netlist = is_resolved(&ggen, seltop, &nettop)) != NULL) {
308          depth = pushnetwork(seltop, nettop);
309          /* Fprintf(stdout, ">> Pushed network %d levels deep\n", depth); */
310          nextptr = seltop;
311          while (nextptr->thisinst->thisobject != nettop)
312 	    nextptr = nextptr->next;
313 
314 	 nextptr->thisinst->thisobject->highlight.netlist = netlist;
315 	 nextptr->thisinst->thisobject->highlight.thisinst = nextptr->thisinst;
316 	 highlightnetlist(nettop, nextptr->thisinst, 1);
317 
318          /* pop the matrix stack */
319          while (depth-- > 0)
320 	    UPopCTM();
321 
322 	 /* get the primary schematic */
323 	 pschem = (nettop->schemtype == SECONDARY) ? nettop->symschem : nettop;
324 
325 	 /* print the net name to the message window */
326 
327 	 if (netlist->subnets == 0) {
328 	    ppin = nettopin(netlist->net.id, pschem, NULL);
329             snew = textprint(ppin, areawin->topinstance);
330 	    sprintf(_STR2, "Network is \"%s\" in %s", snew, nettop->name);
331             free(snew);
332 	 }
333 	 else {
334 	    char *sptr;
335 	    sprintf(_STR2, "Network(s): ");
336 	    sptr = _STR2 + strlen(_STR2);
337 	    for (lbus = 0; lbus < netlist->subnets; lbus++) {
338 	       sbus = netlist->net.list + lbus;
339 	       netid = sbus->netid;
340 	       subnetid = sbus->subnetid;
341 	       ppin = nettopin(netid, pschem, NULL);
342                snew = textprintsubnet(ppin, areawin->topinstance, subnetid);
343 	       sprintf(sptr, "%s ", snew);
344 	       sptr += strlen(snew) + 1;
345 	       free(snew);
346 	    }
347 	    sprintf(sptr, "in %s", nettop->name);
348 	 }
349 	 Wprintf("%s", _STR2);
350 
351 #ifdef TCL_WRAPPER
352 	 Tcl_SetObjResult(xcinterp, Tcl_NewStringObj(snew, strlen(snew)));
353 #endif
354       }
355       else
356 	 Wprintf("Selected element is not part of a valid network.");
357    }
358    else {
359       Wprintf("No networks found near the cursor position");
360       netid = 0;
361    }
362 
363    /* free up linked list */
364 
365    while (seltop != NULL) {
366       nextptr = seltop->next;
367       free(seltop);
368       seltop = nextptr;
369    }
370 }
371 
372 /*--------------------------------------------------------------*/
373 /* Set object type to FUNDAMENTAL if it contains one or more	*/
374 /* info labels and is not associated with a schematic/symbol.	*/
375 /*								*/
376 /* Return TRUE if the object has electrically relevant		*/
377 /* networks, FALSE if not.					*/
378 /*--------------------------------------------------------------*/
379 
setobjecttype(objectptr cschem)380 Boolean setobjecttype(objectptr cschem)
381 {
382    genericptr *cgen;
383    labelptr clab;
384 
385    /* If networks are specifically prohibited. . . */
386    if (cschem->schemtype == NONETWORK) return False;
387 
388    /* Apply only to schematic objects */
389 
390    if ((cschem->schemtype != PRIMARY) && (cschem->schemtype != SECONDARY)) {
391       if (cschem->schemtype == FUNDAMENTAL)
392 	 cschem->schemtype = SYMBOL;
393       if (cschem->symschem == NULL) {
394          for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
395             if (IS_LABEL(*cgen)) {
396                clab = TOLABEL(cgen);
397 	       if (clab->pin == INFO) {
398 	          cschem->schemtype = FUNDAMENTAL;
399 	          break;
400 	       }
401 	    }
402          }
403       }
404    }
405 
406    if ((cschem->symschem != NULL) && (cschem->schemtype == SYMBOL))
407       return False;
408    else if ((cschem->schemtype == TRIVIAL) || (cschem->schemtype == FUNDAMENTAL))
409       return False;
410 
411    return True;
412 }
413 
414 /*------------------------------------------------------*/
415 /* Pin conversion subroutine for dopintype()		*/
416 /*------------------------------------------------------*/
417 
pinconvert(labelptr thislab,pointertype mode)418 void pinconvert(labelptr thislab, pointertype mode)
419 {
420    thislab->pin = mode;
421    switch (mode) {
422       case NORMAL:
423 	 thislab->color = DEFAULTCOLOR;		/* nominally black */
424 	 break;
425       case GLOBAL:
426 	 thislab->color = GLOBALPINCOLOR;	/* orange */
427          break;
428       case LOCAL:
429 	 thislab->color = LOCALPINCOLOR;	/* red */
430          break;
431       case INFO:
432 	 thislab->color = INFOLABELCOLOR;	/* green */
433          break;
434    }
435 }
436 
437 /*---------------------------------------------------------*/
438 /* Change a label's type to NORMAL, GLOBAL, INFO, or LOCAL */
439 /*---------------------------------------------------------*/
440 
dopintype(xcWidget w,pointertype mode,caddr_t calldata)441 void dopintype(xcWidget w, pointertype mode, caddr_t calldata)
442 {
443    short *gsel;
444    char typestr[40];
445    short savetype = -1;
446 
447    if (areawin->selects == 0) {
448       Wprintf("Must first select a label to change type");
449       return;
450    }
451 
452    strcpy(typestr, "Changed label to ");
453    switch(mode) {
454       case NORMAL:
455 	 strcat(typestr, "normal label");
456          break;
457       case GLOBAL:
458 	 strcat(typestr, "global pin");
459          break;
460       case LOCAL:
461 	 strcat(typestr, "local pin");
462          break;
463       case INFO:
464 	 strcat(typestr, "info-label");
465          break;
466    }
467 
468    for (gsel = areawin->selectlist; gsel < areawin->selectlist +
469 		areawin->selects; gsel++)
470       if (SELECTTYPE(gsel) == LABEL) {
471 	 labelptr glab = SELTOLABEL(gsel);
472 	 savetype = glab->pin;
473 	 pinconvert(glab, mode);
474 	 setobjecttype(topobject);
475       }
476 
477    if (savetype >= 0) {
478       unselect_all();
479       drawarea(NULL, NULL, NULL);
480       Wprintf("%s", typestr);
481    }
482    else {
483       Wprintf("No labels selected.");
484    }
485 }
486 
487 /*----------------------------------------------------------*/
488 /* Set colors on the symbol/schematic buttons appropriately */
489 /*----------------------------------------------------------*/
490 
491 #ifdef TCL_WRAPPER
492 
setsymschem()493 void setsymschem()
494 {
495    XcInternalTagCall(xcinterp, 1, "schematic");
496 }
497 
498 #else
499 
setsymschem()500 void setsymschem()
501 {
502    Arg aargs[2], bargs[2];
503 
504    /* Set menu items appropriately for this object */
505 
506    if (topobject->symschem != NULL) {
507       if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
508          XtSetArg(aargs[0], XtNlabel, "Go To Symbol");
509          XtSetArg(bargs[0], XtNlabel, "Disassociate Symbol");
510       }
511       else {
512          XtSetArg(aargs[0], XtNlabel, "Go To Schematic");
513          XtSetArg(bargs[0], XtNlabel, "Disassociate Schematic");
514       }
515    }
516    else {
517       if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
518          XtSetArg(aargs[0], XtNlabel, "Make Matching Symbol");
519          XtSetArg(bargs[0], XtNlabel, "Associate with Symbol");
520       }
521       else {
522          XtSetArg(aargs[0], XtNlabel, "Make Matching Schematic");
523          XtSetArg(bargs[0], XtNlabel, "Associate with Schematic");
524       }
525    }
526    XtSetValues(NetlistMakeMatchingSymbolButton, aargs, 1);
527    XtSetValues(NetlistAssociatewithSymbolButton, bargs, 1);
528 
529    /* Set colors on the symbol and schematic buttons */
530 
531    if (topobject->schemtype == PRIMARY || topobject->schemtype == SECONDARY) {
532       if (topobject->symschem == NULL) {
533          XtSetArg(aargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
534 	 XtSetArg(aargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
535       }
536       else {
537          XtSetArg(aargs[0], XtNbackground, colorlist[BACKGROUND].color.pixel);
538 	 XtSetArg(aargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
539       }
540 
541       XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
542       XtSetArg(bargs[0], XtNbackground, colorlist[SNAPCOLOR].color.pixel);
543    }
544    else {
545       if (topobject->symschem != NULL) {
546          XtSetArg(bargs[0], XtNbackground, colorlist[BACKGROUND].color.pixel);
547 	 XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
548       }
549       else {
550          XtSetArg(bargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
551 	 XtSetArg(bargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
552       }
553 
554       XtSetArg(aargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
555       if (topobject->schemtype == FUNDAMENTAL)
556          XtSetArg(aargs[0], XtNbackground, colorlist[AUXCOLOR].color.pixel);
557       else if (topobject->schemtype == TRIVIAL || topobject->symschem != NULL)
558          XtSetArg(aargs[0], XtNbackground, colorlist[SNAPCOLOR].color.pixel);
559       else {
560          XtSetArg(aargs[0], XtNbackground, colorlist[OFFBUTTONCOLOR].color.pixel);
561 	 XtSetArg(aargs[1], XtNforeground, colorlist[OFFBUTTONCOLOR].color.pixel);
562          XtSetArg(bargs[0], XtNbackground, colorlist[BBOXCOLOR].color.pixel);
563          XtSetArg(bargs[1], XtNforeground, colorlist[FOREGROUND].color.pixel);
564       }
565    }
566 
567    XtSetValues(wsymb, aargs, 2);
568    XtSetValues(wschema, bargs, 2);
569 }
570 
571 #endif
572 
573 /*--------------------------------------------------------*/
574 /* Find the page number for an object			  */
575 /*--------------------------------------------------------*/
576 
findpageobj(objectptr pobj)577 int findpageobj(objectptr pobj)
578 {
579    int tpage;
580 
581    for (tpage = 0; tpage < xobjs.pages; tpage++)
582       if (xobjs.pagelist[tpage]->pageinst != NULL)
583          if (xobjs.pagelist[tpage]->pageinst->thisobject == pobj)
584 	    return tpage;
585 
586    return -1;
587 }
588 
589 /*------------------------------------------------------*/
590 /* Enumerate all of the pages which are subschematics   */
591 /* of "toppage" and return the result in the list	*/
592 /* passed as a pointer.  The list is assumed to be	*/
593 /* already allocated, and equal to the total number of	*/
594 /* pages (xobjs.pages).					*/
595 /*							*/
596 /* Avoid possible recursion problems by limiting the	*/
597 /* number of recursion levels.  Presumably no circuit	*/
598 /* would have more than several hundred hierarchical 	*/
599 /* levels.						*/
600 /*							*/
601 /* If "dolinks" is TRUE, ennumerate and follow pages	*/
602 /* that are descendents of the top level when the	*/
603 /* calling symbol instance has a "link" parameter and	*/
604 /* that parameter points to the same filename as the	*/
605 /* page.  This indicates a multiple-file project; the	*/
606 /* pages keep separate records of changes, and are	*/
607 /* saved independently, and loaded through the link	*/
608 /* dependency method.					*/
609 /*------------------------------------------------------*/
610 
findsubschems(int toppage,objectptr cschem,int level,short * pagelist,Boolean dolinks)611 int findsubschems(int toppage, objectptr cschem, int level, short *pagelist,
612 	Boolean dolinks)
613 {
614    genericptr *cgen;
615 
616    if (level == HIERARCHY_LIMIT) return -1;	/* sanity check */
617 
618    for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
619       if (IS_OBJINST(*cgen)) {
620 	 objinstptr cinst = TOOBJINST(cgen);
621 	 objectptr cobj = cinst->thisobject;
622 
623 	 if (cobj->symschem != NULL) {
624 	    int pageno = findpageobj(cobj->symschem);
625 
626 	    if ((pageno >= 0) && (pageno < xobjs.pages)) {
627 
628 	       /* Look for a "link" parameter */
629 	       if (dolinks == FALSE) {
630 		  oparamptr ops;
631 	          ops = find_param(cinst, "link");
632 	          if ((ops != NULL) && (ops->type == XC_STRING)) {
633 	             char *filename = textprint(ops->parameter.string, cinst);
634 		     if (!strcmp(filename, "%n") || !strcmp(filename, "%N") ||
635 			  !strcmp(filename, xobjs.pagelist[pageno]->filename)) {
636 		        free(filename);
637 		        continue;
638 		     }
639 	             free(filename);
640 		  }
641 	       }
642 
643 	       /* Add this page to the list */
644 	       pagelist[pageno]++;
645 	    }
646 
647 	    /* A symbol on its own schematic page is allowed for clarity */
648 	    /* of the schematic, but this cannot be a functional part of */
649 	    /* the schematic circuit!					 */
650 
651 	    if (cobj->symschem != cschem) {
652 	       if (findsubschems(toppage, cobj->symschem,
653 				level + 1, pagelist, dolinks) == -1)
654 		  return -1;
655 	    }
656 	 }
657 	 else if (cobj->schemtype != FUNDAMENTAL && cobj->schemtype != TRIVIAL) {
658 	    /* Check symbols acting as their own schematics */
659 	    if (findsubschems(toppage, cobj, level + 1, pagelist, dolinks) == -1)
660 	       return -1;
661 	 }
662       }
663    }
664    return 0;
665 }
666 
667 /*------------------------------------------------------*/
668 /* Recursively find all sub-circuits associated with	*/
669 /* the top-level circuit.  For each schematic that does	*/
670 /* not have a valid filename, set the filename equal to	*/
671 /* the filename of the "toppage" schematic.		*/
672 /*------------------------------------------------------*/
673 
collectsubschems(int toppage)674 void collectsubschems(int toppage)
675 {
676    int loctop;
677    objectptr cschem;
678    short *pagelist, pageno;
679    Pagedata *curpage;
680 
681    loctop = toppage;
682    curpage = xobjs.pagelist[loctop];
683    if (curpage->pageinst == NULL) return;
684    cschem = curpage->pageinst->thisobject;
685    if (cschem->schemtype == SECONDARY) {
686       cschem = cschem->symschem;
687       loctop = is_page(cschem);
688       if (loctop < 0) return;
689       curpage = xobjs.pagelist[loctop];
690    }
691 
692    pagelist = (short *)malloc(xobjs.pages * sizeof(short));
693 
694    for (pageno = 0; pageno < xobjs.pages; pageno++)
695       pagelist[pageno] = 0;
696 
697    findsubschems(loctop, cschem, 0, pagelist, FALSE);
698 
699    for (pageno = 0; pageno < xobjs.pages; pageno++) {
700       if (pageno == loctop) continue;
701       if (pagelist[pageno] > 0) {
702 	 if (xobjs.pagelist[pageno]->filename != NULL)
703 	    free(xobjs.pagelist[pageno]->filename);
704 	 xobjs.pagelist[pageno]->filename =
705 	    strdup(xobjs.pagelist[loctop]->filename);
706       }
707    }
708    free((char *)pagelist);
709 }
710 
711 /*------------------------------------------------------*/
712 /* compare_qualified(str1, str2) ---			*/
713 /*							*/
714 /* 	String comparison for technology-qualified	*/
715 /*	names.  str2" must be a fully-qualified name.	*/
716 /*	"str1" may be qualified or unqualified.  If	*/
717 /*	unqualified, compare_qualified will return TRUE	*/
718 /*	for any "str2" that matches the name part of	*/
719 /*	the technology::name format.			*/
720 /*							*/
721 /*	Return value:  TRUE if match, FALSE if not.	*/
722 /*------------------------------------------------------*/
723 
compare_qualified(char * str1,char * str2)724 Boolean compare_qualified(char *str1, char *str2)
725 {
726    char *sptr1, *sptr2;
727    Boolean qual1, qual2;
728 
729    sptr2 = strstr(str2, "::");
730    qual2 = (sptr2 == NULL) ? FALSE : TRUE;
731 
732    if (!qual2) return (!strcmp(str1, str2));
733 
734    sptr1 = strstr(str1, "::");
735    qual1 = (sptr1 == NULL) ? FALSE : TRUE;
736 
737    if (qual1) return (!strcmp(str1, str2));
738 
739    sptr2 += 2;
740    return (!strcmp(str1, sptr2));
741 }
742 
743 /*-------------------------------------------------------*/
744 /* Check if top-level page is the same name as a library */
745 /* object; if so, connect it.				 */
746 /* Note that it is not an error not to find the matching */
747 /* symbol/schematic.  "is_schematic" and "is_symbol"	 */
748 /* comments in the .ps file are complementary, so the	 */
749 /* first one encountered will always fail, and the other */
750 /* will succeed.					 */
751 /*-------------------------------------------------------*/
752 
checkschem(objectptr thisobj,char * cname)753 int checkschem(objectptr thisobj, char *cname)
754 {
755    objectptr *tlib;
756    short i, j;
757 
758    if (thisobj->symschem != NULL) return 0;
759 
760    for (i = 0; i < xobjs.numlibs; i++) {
761       for (j = 0; j < xobjs.userlibs[i].number; j++) {
762 	 tlib = xobjs.userlibs[i].library + j;
763 
764          if (compare_qualified(cname, (*tlib)->name)) {
765 	    thisobj->symschem = (*tlib);
766 	    thisobj->schemtype = PRIMARY;
767 	    (*tlib)->symschem = thisobj;
768 	    (*tlib)->schemtype = SYMBOL;
769 	    return 1;
770 	 }
771       }
772    }
773    return 0;
774 }
775 
776 /*--------------------------------------------------------*/
777 /* Complement to the above routine:  If a library object  */
778 /* name is the same as a top-level page, connect them.	  */
779 /*--------------------------------------------------------*/
780 
checksym(objectptr symobj,char * cname)781 int checksym(objectptr symobj, char *cname)
782 {
783    short cpage;
784    objectptr checkpage;
785 
786    if (symobj->symschem != NULL) return 0;
787 
788    for (cpage = 0; cpage < xobjs.pages; cpage++) {
789       if (xobjs.pagelist[cpage]->pageinst != NULL) {
790          checkpage = xobjs.pagelist[cpage]->pageinst->thisobject;
791          if (compare_qualified(cname, checkpage->name)) {
792 	    symobj->symschem = checkpage;
793 	    symobj->schemtype = SYMBOL;
794 	    checkpage->symschem = symobj;
795 	    checkpage->schemtype = PRIMARY;
796 	    return 1;
797 	 }
798       }
799    }
800    return 0;
801 }
802 
803 /*----------------------------------------------------------------------*/
804 /* Find location of corresponding pin in symbol/schematic and change it */
805 /* to the text of the indicated label.					*/
806 /*									*/
807 /* Return the number of other pins changed.  zero indicates that no	*/
808 /* corresponding pins were found, and therefore nothing was changed.	*/
809 /*----------------------------------------------------------------------*/
810 
changeotherpins(labelptr newlabel,stringpart * oldstring)811 int changeotherpins(labelptr newlabel, stringpart *oldstring)
812 {
813    objectptr other = topobject->symschem;
814    genericptr *tgen;
815    labelptr tlab;
816    int rval = 0;
817 
818    if (other == NULL) return rval;
819 
820    for (tgen = other->plist; tgen < other->plist + other->parts; tgen++) {
821       if (IS_LABEL(*tgen)) {
822 	 tlab = TOLABEL(tgen);
823 	 if (tlab->pin != LOCAL) continue;
824 	 if (!stringcomp(tlab->string, oldstring)) {
825 	    if (newlabel != NULL) {
826 	       free(tlab->string);
827 	       tlab->string = stringcopy(newlabel->string);
828 	       rval++;
829 	    }
830 	 }
831       }
832    }
833    return rval;
834 }
835 
836 /*----------------------------------------------------------------------*/
837 /* Xt wrapper for swapschem()						*/
838 /*----------------------------------------------------------------------*/
839 
xlib_swapschem(xcWidget w,pointertype mode,caddr_t calldata)840 void xlib_swapschem(xcWidget w, pointertype mode, caddr_t calldata)
841 {
842    swapschem((int)mode, -1, NULL);
843 }
844 
845 /*----------------------------------------------------------------------*/
846 /* Swap object schematic and symbol pages.   				*/
847 /*  allow_create = 0 disallows creation of a new schematic or symbol;	*/
848 /*  i.e., if there is no corresponding schematic/symbol, nothing	*/
849 /*  happens.								*/
850 /*----------------------------------------------------------------------*/
851 
swapschem(int allow_create,int libnum,char * fullname)852 void swapschem(int allow_create, int libnum, char *fullname)
853 {
854    objectptr savepage = topobject;
855    labelptr  *pinlab;
856    genericptr *plab;
857    Boolean lflag;
858    pushlistptr stacktop;
859    short loclibnum = libnum;
860    char *canonname;
861 
862    if (libnum == -1) loclibnum = USERLIB - LIBRARY;
863 
864    /* Create symbol or schematic, if allowed by allow_create */
865 
866    if ((topobject->symschem == NULL) && (allow_create != 0)
867 		&& (topobject->schemtype != SECONDARY)) {
868 
869       if (topobject->schemtype != PRIMARY) {
870 	 int tpage;
871 
872 	 /* create a new page for the new schematic */
873 
874 	 for (tpage = 0; tpage < xobjs.pages; tpage++)
875 	    if (xobjs.pagelist[tpage]->pageinst == NULL) break;
876 
877 	 /* Push the current instance onto the push stack */
878 	 /* Change the page without destroying the pushlist */
879 
880 	 push_stack(&areawin->stack, areawin->topinstance, NULL);
881 	 stacktop = areawin->stack;
882 	 areawin->stack = NULL;
883          changepage(tpage);
884 	 areawin->stack = stacktop;
885       }
886       else {
887 	 objectptr *newobject;
888 
889 	 /* create a new library object for the new symbol */
890 
891 	 xobjs.userlibs[loclibnum].library = (objectptr *)
892 		realloc(xobjs.userlibs[loclibnum].library,
893 		++xobjs.userlibs[loclibnum].number * sizeof(objectptr));
894 	 newobject = xobjs.userlibs[loclibnum].library
895 		+ xobjs.userlibs[loclibnum].number - 1;
896          *newobject = (objectptr) malloc(sizeof(object));
897 	 initmem(*newobject);
898 	 (*newobject)->schemtype = SYMBOL;
899 	 (*newobject)->hidden = False;
900 
901 	 incr_changes(*newobject);
902 
903 	 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
904 	    standard_element_delete(ERASE);
905 	 else
906 	    unselect_all();
907 
908 	 /* Generate a library instance for this object and set the */
909 	 /* top instance to point to it.			    */
910 
911 	 topobject->viewscale = areawin->vscale;
912 	 topobject->pcorner = areawin->pcorner;
913 	 push_stack(&areawin->stack, areawin->topinstance, NULL);
914 	 areawin->topinstance = addtoinstlist(loclibnum, *newobject, FALSE);
915 
916 	 /* Generate the default bounding box for a size-zero object */
917 	 calcbbox(areawin->topinstance);
918       }
919 
920       /* set links between the two objects */
921 
922       savepage->symschem = topobject;
923       topobject->symschem = savepage;
924 
925       /* Make the name of the new object equal to that of the old, */
926       /* except that symbols get the full name while schematics	   */
927       /* get the canonical name (without the technology prefix)	   */
928 
929       if (fullname == NULL)
930 	 canonname = GetCanonicalName(savepage->name);
931       else {
932          canonname = strstr(fullname, "::");
933 	 if ((canonname == NULL) || (topobject->schemtype != PRIMARY))
934 	    canonname = fullname;
935 	 else
936 	    canonname += 2;
937       }
938       strcpy(topobject->name, canonname);
939       checkname(topobject);
940 
941       /* copy all pin labels into the new object */
942 
943       for (plab = savepage->plist; plab < savepage->plist + savepage->parts;
944 		plab++) {
945 	 if (IS_LABEL(*plab)) {
946 	    genericptr *tgen;
947 	    labelptr tlab, lpin = (labelptr)*plab;
948 
949 	    if (lpin->pin == LOCAL) {
950 
951       	       /* Only make one copy of each pin name */
952 
953 	       lflag = False;
954                for (tgen = topobject->plist; tgen <
955 			topobject->plist + topobject->parts; tgen++) {
956 		  if (IS_LABEL(*tgen)) {
957 		     tlab = TOLABEL(tgen);
958 	             if (!stringcomp(tlab->string, lpin->string)) lflag = True;
959 		  }
960       	       }
961 	       if (lflag == True) continue;
962 
963 	       NEW_LABEL(pinlab, topobject);
964 	       (*pinlab)->pin = lpin->pin;
965 	       (*pinlab)->color = lpin->color;
966 	       (*pinlab)->rotation = 0.0;
967 	       (*pinlab)->scale = 1.0;
968 	       (*pinlab)->anchor = areawin->anchor;
969 	       (*pinlab)->position.x = 0;
970 	       (*pinlab)->position.y = topobject->parts * (TEXTHEIGHT + 10);
971 	       (*pinlab)->passed = NULL;
972 	       (*pinlab)->cycle = NULL;
973 	       u2u_snap(&((*pinlab)->position));
974 	       (*pinlab)->string = stringcopy(lpin->string);
975 	       incr_changes(topobject);
976 	    }
977          }
978       }
979       calcbbox(areawin->topinstance);
980 
981       /* Recreate the user library with the new symbol */
982       if (savepage->schemtype != SYMBOL) composelib(loclibnum + LIBRARY);
983    }
984    else if (topobject->symschem != NULL) {
985 
986       /* If symschem matches the last entry on the push stack, then we	*/
987       /* pop; otherwise, we push.					*/
988 
989       if (areawin->stack && areawin->stack->thisinst->thisobject
990 			== topobject->symschem) {
991 	 topobject->viewscale = areawin->vscale;
992 	 topobject->pcorner = areawin->pcorner;
993 	 areawin->topinstance = areawin->stack->thisinst;
994 	 pop_stack(&areawin->stack);
995       }
996       else {
997 	 int p;
998 	 objinstptr syminst = NULL;
999 	 liblistptr symlist;
1000 
1001 	 /* If symschem is a schematic, find the appropriate page */
1002 
1003 	 for (p = 0; p < xobjs.pages; p++) {
1004 	    syminst = xobjs.pagelist[p]->pageinst;
1005 	    if (syminst != NULL)
1006 	       if (syminst->thisobject == topobject->symschem)
1007 		  break;
1008 	 }
1009 	 if (p == xobjs.pages) {
1010 
1011 	    /* If symschem is a symbol, and it wasn't on the push stack, */
1012 	    /* get the library default symbol and go there.		 */
1013 
1014 	    for (p = 0; p < xobjs.numlibs; p++) {
1015 	       for (symlist = xobjs.userlibs[p].instlist; symlist != NULL;
1016 			symlist = symlist->next) {
1017 	          syminst = symlist->thisinst;
1018 	          if (syminst->thisobject == topobject->symschem &&
1019 			symlist->virtual == FALSE)
1020 		     break;
1021 	       }
1022 	       if (symlist != NULL) break;
1023 	    }
1024 	    if (p == xobjs.numlibs) {
1025 	       Fprintf(stderr, "swapschem(): BAD SYMSCHEM\n");
1026 	       return;
1027 	    }
1028          }
1029 
1030 	 if (eventmode == MOVE_MODE || eventmode == COPY_MODE)
1031 	    delete_for_xfer(NORMAL, areawin->selectlist, areawin->selects);
1032 
1033 	 topobject->viewscale = areawin->vscale;
1034 	 topobject->pcorner = areawin->pcorner;
1035 	 push_stack(&areawin->stack, areawin->topinstance, NULL);
1036 	 areawin->topinstance = syminst;
1037       }
1038    }
1039 
1040    /* If there was no action, then there is nothing more to do. */
1041 
1042    if (topobject == savepage) return;
1043 
1044    setpage(TRUE);
1045    transferselects();
1046    refresh(NULL, NULL, NULL);
1047    setsymschem();
1048 }
1049 
1050 #ifndef TCL_WRAPPER
1051 
1052 /*----------------------------------------------------------------------*/
1053 /* Wrapper for swapschem() when generating a new symbol.		*/
1054 /*----------------------------------------------------------------------*/
1055 
makesymbol(xcWidget w,caddr_t calldata)1056 void makesymbol(xcWidget w, caddr_t calldata)
1057 {
1058    /* copy name from popup prompt buffer and check */
1059 
1060    swapschem(1, -1, _STR2);
1061 }
1062 
1063 /*----------------------------------------------------------------------*/
1064 /* Check name before doing a swap:  If name begins with "Page", prompt	*/
1065 /* for the object name, as you would when doing selectsave().		*/
1066 /*----------------------------------------------------------------------*/
1067 
dobeforeswap(xcWidget w,caddr_t clientdata,caddr_t calldata)1068 void dobeforeswap(xcWidget w, caddr_t clientdata, caddr_t calldata)
1069 {
1070    buttonsave *popdata = (buttonsave *)malloc(sizeof(buttonsave));
1071 
1072    /* Check for requirement to change the name before creating the symbol */
1073 
1074    if ((topobject->symschem == NULL) && (topobject->schemtype == PRIMARY)
1075 		&& (strstr(topobject->name, "Page ") != NULL)) {
1076 
1077       /* Get a name for the new object */
1078 
1079       eventmode = NORMAL_MODE;
1080       popdata->dataptr = NULL;
1081       popdata->button = NULL; /* indicates that no button is assc'd w/ the popup */
1082       popupprompt(w, "Enter name for new object:", "\0", makesymbol, popdata, NULL);
1083    }
1084    else
1085       swapschem(1, -1, NULL);
1086 }
1087 
1088 #endif /* !TCL_WRAPPER */
1089 
1090 /*------------------------------------------*/
1091 /* Disassociate a symbol from its schematic */
1092 /*------------------------------------------*/
1093 
schemdisassoc()1094 void schemdisassoc()
1095 {
1096    if (eventmode != NORMAL) {
1097       Wprintf("Cannot disassociate schematics in this mode");
1098    }
1099    else {
1100       topobject->symschem->symschem = NULL;
1101       topobject->symschem = NULL;
1102       setsymschem();
1103       Wprintf("Schematic and symbol are now unlinked.");
1104    }
1105 }
1106 
1107 /*--------------------------------------------------------------*/
1108 /* Schematic<-->symbol association.  Determine whether action	*/
1109 /* acts on a symbol or a schematic from context.		*/
1110 /* mode == 0 associate only.					*/
1111 /* mode == 1 determine action (associate or disassociate) from 	*/
1112 /* context (toggle)						*/
1113 /*--------------------------------------------------------------*/
1114 
startschemassoc(xcWidget w,pointertype mode,caddr_t calldata)1115 void startschemassoc(xcWidget w, pointertype mode, caddr_t calldata)
1116 {
1117    if ((topobject->symschem != NULL) && (mode == 1))
1118       schemdisassoc();
1119    else if ((topobject->symschem != NULL) && (mode == 0)) {
1120       Wprintf("Refusing to undo current association.");
1121    }
1122    else if (topobject->schemtype == SECONDARY) {
1123       Wprintf("Cannot attach symbol to a secondary schematic page.");
1124    }
1125    else {
1126       eventmode = ASSOC_MODE;
1127       if (topobject->schemtype == PRIMARY) {
1128 	 /* Find a symbol to associate */
1129 	 startcatalog(w, LIBLIB, NULL);
1130 	 Wprintf("Select library page, then symbol to associate.");
1131       }
1132       else {
1133 	 /* Find a schematic (page) to associate */
1134 	 startcatalog(w, PAGELIB, NULL);
1135 	 Wprintf("Select schematic page to associate.");
1136       }
1137    }
1138 }
1139 
1140 /*--------------------------------------------------------------*/
1141 /* Callback procedures on the schematic/symbol buttons.		*/
1142 /*--------------------------------------------------------------*/
1143 
schemassoc(objectptr schemobj,objectptr symbolobj)1144 Boolean schemassoc(objectptr schemobj, objectptr symbolobj)
1145 {
1146    if (schemobj->symschem != NULL || symbolobj->symschem != NULL) {
1147       Wprintf("Both objects must be disassociated first.");
1148 #if TCL_WRAPPER
1149       Tcl_SetResult(xcinterp, "Both objects must be disassociated first.", NULL);
1150 #endif
1151       return False;
1152    }
1153    else {
1154       schemobj->symschem = symbolobj;
1155       symbolobj->symschem = schemobj;
1156       if (symbolobj->schemtype == TRIVIAL)
1157 	 symbolobj->schemtype = SYMBOL;
1158 
1159       /* Schematic takes the name of its associated symbol, by default */
1160       /* Don't copy any technology prefix. */
1161       strcpy(schemobj->name, GetCanonicalName(symbolobj->name));
1162 
1163       /* Ensure that schematic (page) name is unique */
1164       while (checkpagename(schemobj) < 0);
1165       setsymschem();	/* Set buttons and menu items appropriately */
1166    }
1167    return True;
1168 }
1169 
1170 /*-------------------------------------------------------------------------*/
1171