1 /*----------------------------------------------------------------------*/
2 /* netlist.c --- xcircuit routines specific to schematic capture and	*/
3 /*		 netlist generation					*/
4 /*----------------------------------------------------------------------*/
5 /*  Copyright (c) 2004 Tim Edwards, Johns Hopkins University,		*/
6 /*	MultiGiG, Inc., and Open Circuit Design, Inc.			*/
7 /*  Copyright (c) 2005 Tim Edwards, MultiGiG, Inc.			*/
8 /*									*/
9 /*  Written April 1998 to January 2004				   	*/
10 /*  Original version for Pcb netlisting 3/20/98 by Chow Seong Hwai,	*/
11 /*			Leeds University, U.K.				*/
12 /*									*/
13 /*  Greatly modified 4/2/04 to handle bus notation; net identifier	*/
14 /*  changed from a listing by net to two listings, by polygon and by	*/
15 /*  label, with nets and subnets identifiers attached to each element,	*/
16 /*  rather than vice versa.						*/
17 /*----------------------------------------------------------------------*/
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 
24 #include <sys/types.h>	/* For preventing multiple file inclusions, use stat() */
25 #include <sys/stat.h>
26 #ifndef _MSC_VER
27 #include <unistd.h>
28 #endif
29 
30 #ifdef HAVE_PYTHON
31 #include <Python.h>
32 #endif
33 
34 #ifndef _MSC_VER
35 #include <X11/Intrinsic.h>
36 #include <X11/StringDefs.h>
37 #endif
38 
39 #ifdef TCL_WRAPPER
40 #include <tk.h>
41 #endif
42 
43 /*----------------------------------------------------------------------*/
44 /* Local includes							*/
45 /*----------------------------------------------------------------------*/
46 
47 #include "xcircuit.h"
48 #include "colordefs.h"
49 
50 /*----------------------------------------------------------------------*/
51 /* Function prototype declarations                                      */
52 /*----------------------------------------------------------------------*/
53 #include "prototypes.h"
54 
55 #ifdef HAVE_PYTHON
56 extern PyObject *PyGetStringParts(stringpart *);
57 #endif
58 #ifdef TCL_WRAPPER
59 extern Tcl_Interp *xcinterp;
60 extern Tcl_Obj *TclGetStringParts(stringpart *);
61 #endif
62 
63 /*----------------------------------------------------------------------*/
64 /* Externally declared global variables					*/
65 /*----------------------------------------------------------------------*/
66 
67 extern Display  *dpy;
68 
69 /*----------------------------------------------------------------------*/
70 
71 extern char _STR[150];
72 extern char _STR2[250];
73 extern XCWindowData *areawin;
74 extern Globaldata xobjs;
75 extern Boolean load_in_progress;
76 extern int number_colors;
77 extern colorindex *colorlist;
78 
79 LabellistPtr global_labels;
80 
81 typedef struct _flatindex *fidxptr;
82 
83 typedef struct _flatindex {
84    char *devname;
85    u_int index;
86    fidxptr next;
87 } flatindex;	/* count for labeling devices in a flattened file */
88 
89 flatindex *flatrecord = NULL;
90 
91 static char *spice_devname = "X";   /* SPICE subcircuit device name */
92 Boolean spice_end = True;	    /* whether or not to write a .end statement */
93 ino_t *included_files = NULL;	/* Files included with "%F" escape	*/
94 
95 #define EndPoint(n)	(((n == 1) ? 1 : (int)(n - 1)))
96 #define NextPoint(n)	(((n == 1) ? 0 : 1))
97 
98 /* For bus matching:  whether to match by number of nets, by		*/
99 /* exact match of sub-bus numbers, or an exact match of net IDs.	*/
100 
101 #define MATCH_EXACT	0
102 #define MATCH_SUBNETS	1
103 #define MATCH_SIZE	2
104 #define MATCH_OVERLAP	3
105 
106 /*----------------------------------------------------------------------*/
107 /* Check whether two line segments attach to or cross each other	*/
108 /* Return 1 if attached, 0 if not (INLINE code)				*/
109 /* int onsegment(XPoint *a, XPoint *b, XPoint *x) {}			*/
110 /*----------------------------------------------------------------------*/
111 
112 #define ONDIST 4  /* "slack" in algorithm for almost-touching lines */
113 #define onsegment(a, b, x) (finddist(a, b, x) <= ONDIST)
114 
115 /*--------------------------------------------------------------*/
116 /* d36a:  Base 36 to string conversion				*/
117 /*--------------------------------------------------------------*/
118 
d36a(int number)119 char *d36a(int number)
120 {
121    static char bconv[10];
122    int i, locn, rem;
123 
124 
125    bconv[9] = '\0';
126    i = 8;
127    locn = number;
128    while ((locn > 0) && (i >= 0)) {
129       rem = locn % 36;
130       locn /= 36;
131       bconv[i--] = (rem < 10) ? (rem + '0') : (rem - 10 + 'A');
132    }
133    return &bconv[i + 1];
134 }
135 
136 /*--------------------------------------------------------------*/
137 /* Translate a pin name to a position relative to an object's	*/
138 /* point of origin.  This is used by the ASG module to find	*/
139 /* the placement of pins based on names from a netlist.		*/
140 /*								*/
141 /* returns 0 on success and fills x_ret and y_ret with the	*/
142 /* pin position coordinates.  Returns -1 if the pin name did	*/
143 /* not match any label names in the object.			*/
144 /*--------------------------------------------------------------*/
145 
NameToPinLocation(objinstptr thisinst,char * pinname,int * x_ret,int * y_ret)146 int NameToPinLocation(objinstptr thisinst, char *pinname, int *x_ret, int *y_ret)
147 {
148    objectptr thisobj = thisinst->thisobject;
149    genericptr *pgen;
150    labelptr plab;
151 
152    if (thisobj->schemtype == SECONDARY)
153       thisobj = thisobj->symschem;
154 
155    for (pgen = thisobj->plist; pgen < thisobj->plist + thisobj->parts; pgen++) {
156       if (IS_LABEL(*pgen)) {
157 	 plab = TOLABEL(pgen);
158 	 if (plab->pin != False && plab->pin != INFO) {
159 	    if (!textcomp(plab->string, pinname, thisinst)) {
160 	       *x_ret = (int)plab->position.x;
161 	       *y_ret = (int)plab->position.y;
162 	       return 0;
163 	    }
164 	 }
165       }
166    }
167    return -1;
168 }
169 
170 /*--------------------------------------------------------------*/
171 /* Translate a point position back to the calling object's	*/
172 /* coordinate system.						*/
173 /* Original value is passed in "thispoint"; Translated value	*/
174 /* is put in "refpoint".					*/
175 /*--------------------------------------------------------------*/
176 
ReferencePosition(objinstptr thisinst,XPoint * thispoint,XPoint * refpoint)177 void ReferencePosition(objinstptr thisinst, XPoint *thispoint, XPoint *refpoint)
178 {
179   /* objectptr thisobj = thisinst->thisobject; (jdk) */
180    Matrix locctm;
181 
182    /* Translate pin position back to originating object */
183 
184    UResetCTM(&locctm);
185    UPreMultCTM(&locctm, thisinst->position, thisinst->scale,
186                         thisinst->rotation);
187    UTransformbyCTM(&locctm, thispoint, refpoint, 1);
188 }
189 
190 /*--------------------------------------------------------------*/
191 /* Variant of NameToPinLocation():  Given a port number, find	*/
192 /* the pin associated with that port.				*/
193 /*--------------------------------------------------------------*/
194 
PortToLabel(objinstptr thisinst,int portno)195 labelptr PortToLabel(objinstptr thisinst, int portno)
196 {
197    labelptr plab;
198    objectptr thisobj = thisinst->thisobject;
199    PortlistPtr ports;
200 
201    if ((thisobj->schemtype == SYMBOL) && (thisobj->symschem != NULL))
202       ports = thisobj->symschem->ports;
203    else
204       ports = thisobj->ports;
205 
206    for (; ports != NULL; ports = ports->next) {
207       if (ports->portid == portno) {
208 	 plab = NetToLabel(ports->netid, thisobj);	/* in the symbol! */
209 	 return plab;
210       }
211    }
212    return NULL;
213 }
214 
215 /*--------------------------------------------------------------*/
216 /* This function is the same as PortToLocalPosition() but	*/
217 /* returns the point in the coordinate system of the parent.	*/
218 /*--------------------------------------------------------------*/
219 
PortToPosition(objinstptr thisinst,int portno,XPoint * refpoint)220 Boolean PortToPosition(objinstptr thisinst, int portno, XPoint *refpoint)
221 {
222    labelptr plab = PortToLabel(thisinst, portno);
223    if (plab)
224       ReferencePosition(thisinst, &(plab->position), refpoint);
225 
226    return (plab != NULL) ? TRUE : FALSE;
227 }
228 
229 /*----------------------------------------------------------------------*/
230 /* Get the hierarchy of the page stack and return as a string		*/
231 /*----------------------------------------------------------------------*/
232 
getnexthier(pushlistptr stack,char ** hierstr,objinstptr callinst,Boolean canonical)233 Boolean getnexthier(pushlistptr stack, char **hierstr, objinstptr callinst,
234 	Boolean canonical)
235 {
236    objectptr topobj, thisobj;
237    CalllistPtr calls;
238    /* char numstr[12]; (jdk) */
239    int hierlen, devlen;
240    char *devstr;
241 
242    if (!stack) return False;
243 
244    /* Recurse so we build up the prefix string from top down */
245    if (stack->next != NULL) {
246       if (getnexthier(stack->next, hierstr, stack->thisinst, canonical) == False)
247 	 return False;
248    }
249    else {
250       topobj = stack->thisinst->thisobject;
251 
252       if (topobj->schemtype != PRIMARY && topobj->symschem != NULL)
253 	 topobj = topobj->symschem;
254 
255       if (topobj->calls == NULL) {
256 	 if (topobj->schemtype == FUNDAMENTAL) {
257 	    /* Top level is a fundamental symbol */
258 	    return True;
259 	 }
260 	 else if ((updatenets(stack->thisinst, FALSE) <= 0) || (topobj->calls == NULL)) {
261 	    Wprintf("Error in generating netlists!");
262 	    return False;
263 	 }
264       }
265    }
266 
267    thisobj = stack->thisinst->thisobject;
268 
269    /* When we have "traveled" through both a symbol and its	*/
270    /* associated schematic, only append the prefix for the	*/
271    /* first one encountered.					*/
272 
273 /*
274    if (thisobj->symschem != NULL && stack->next != NULL &&
275 		thisobj->symschem == stack->next->thisinst->thisobject)
276       return True;
277 */
278 
279    if ((thisobj->calls == NULL) && (thisobj->schemtype != PRIMARY)
280 		&& (thisobj->symschem != NULL))
281       thisobj = thisobj->symschem;
282 
283    /* Check for resolved device indices and generate them if necessary */
284 
285    for (calls = thisobj->calls; calls != NULL; calls = calls->next)
286       if (calls->callinst == callinst)
287 	 if (calls->devindex == -1) {
288 	    cleartraversed(thisobj);
289 	    resolve_indices(thisobj, FALSE);
290 	    break;
291 	 }
292 
293    /* Add this level of the hierarchy to the prefix string */
294 
295    for (calls = thisobj->calls; calls != NULL; calls = calls->next) {
296       if (calls->callinst == callinst) {
297 	 devlen = (canonical || calls->devname == NULL) ?
298 		strlen(callinst->thisobject->name) : strlen(calls->devname);
299 	 devstr = d36a(calls->devindex);
300 	 devlen += strlen(devstr) + 1;
301 	 if (*hierstr == NULL) {
302 	    *hierstr = malloc(devlen);
303 	    hierlen = 0;
304 	 }
305 	 else {
306 	    hierlen = strlen(*hierstr) + 2;
307 	    *hierstr = realloc(*hierstr, hierlen + devlen);
308 	 }
309 	 if (canonical)
310 	    sprintf(*hierstr + hierlen, "%s%s(%s)",
311 		((hierlen > 0) ? "/" : ""),
312 		callinst->thisobject->name, devstr);
313 	 else
314 	    sprintf(*hierstr + hierlen, "%s%s%s",
315 		((hierlen > 0) ? "/" : ""),
316 		(calls->devname == NULL) ?  callinst->thisobject->name
317 			: calls->devname, devstr);
318 	 break;
319       }
320    }
321    return True;
322 }
323 
324 /*----------------------------------------------------------------------*/
325 
GetHierarchy(pushlistptr * stackptr,Boolean canonical)326 char *GetHierarchy(pushlistptr *stackptr, Boolean canonical)
327 {
328    Boolean pushed_top = FALSE;
329    char *snew = NULL;
330 
331    if ((*stackptr) && ((*stackptr)->thisinst != areawin->topinstance)) {
332       pushed_top = TRUE;
333       push_stack(stackptr, areawin->topinstance, NULL);
334    }
335 
336    getnexthier(*stackptr, &snew, NULL, canonical);
337 
338    if (pushed_top) pop_stack(stackptr);
339 
340    return snew;
341 }
342 
343 /*----------------------------------------------------------------------*/
344 /* Invalidate a netlist.  The invalidation must be referred to the	*/
345 /* master schematic, if the object is a secondary schematic.		*/
346 /*----------------------------------------------------------------------*/
347 
invalidate_netlist(objectptr thisobject)348 void invalidate_netlist(objectptr thisobject)
349 {
350    if (thisobject->schemtype != NONETWORK) {
351       if (thisobject->schemtype == SECONDARY)
352          thisobject->symschem->valid = False;
353       else
354          thisobject->valid = False;
355    }
356 }
357 
358 /*--------------------------------------------------------------*/
359 /* Check if the selected items are relevant to the netlist.	*/
360 /* Only invalidate the current netlist if they are.		*/
361 /*--------------------------------------------------------------*/
362 
select_invalidate_netlist()363 void select_invalidate_netlist()
364 {
365    int i;
366    Boolean netcheck = FALSE;
367 
368    for (i = 0; i < areawin->selects; i++) {
369       genericptr gptr = SELTOGENERIC(areawin->selectlist + i);
370       switch (gptr->type) {
371 	 case POLYGON:
372 	    if (!nonnetwork(TOPOLY(&gptr)))
373 	       netcheck = TRUE;
374 	    break;
375 	 case LABEL:
376             if ((TOLABEL(&gptr))->pin == LOCAL || (TOLABEL(&gptr))->pin == GLOBAL)
377 	       netcheck = TRUE;
378 	    break;
379 	 case OBJINST:
380 	    if ((TOOBJINST(&gptr))->thisobject->schemtype != NONETWORK)
381 	       netcheck = TRUE;
382 	    break;
383       }
384    }
385    if (netcheck) invalidate_netlist(topobject);
386 }
387 
388 /*------------------------------------------------------------------*/
389 /* Check proximity of two points (within roundoff tolerance ONDIST) */
390 /*------------------------------------------------------------------*/
391 
proximity(XPoint * point1,XPoint * point2)392 Boolean proximity(XPoint *point1, XPoint *point2)
393 {
394    int dx, dy;
395 
396    dx = point1->x - point2->x;
397    dy = point1->y - point2->y;
398 
399    if ((abs(dx) < ONDIST) && (abs(dy) < ONDIST)) return True;
400    else return False;
401 }
402 
403 /*----------------------------------------------------------------------*/
404 /* createnets(): Generate netlist structures				*/
405 /*									*/
406 /* Result is the creation of three linked lists inside each object in   */
407 /* the circuit hierarchy:						*/
408 /*									*/
409 /*    1) the netlist:  assigns a number to each network of polygons and	*/
410 /*	 	pin labels on the object.				*/
411 /*    2) the ports:  a list of every network which is connected from	*/
412 /*		objects above this one in the hierarchy.		*/
413 /*    3) the calls: a list of calls made from the object to all sub-	*/
414 /*		circuits.  Each calls indicates the object and/or	*/
415 /*		instance being called, and a port list which must match */
416 /*		the ports of the object being called (the port lists */
417 /*		are not reflexive, as the caller does not have to call	*/
418 /*		all of the instance's ports, but the instance must have	*/
419 /*		a port defined for every connection being called).	*/
420 /*    (see structure definitions in xcircuit.h).	   		*/
421 /*----------------------------------------------------------------------*/
422 
createnets(objinstptr thisinst,Boolean quiet)423 void createnets(objinstptr thisinst, Boolean quiet)
424 {
425    objectptr thisobject = thisinst->thisobject;
426 
427    if (!setobjecttype(thisobject)) {
428 
429       /* New in 3.3.32:  Generating a netlist from a symbol is	*/
430       /* okay if the symbol has an associated netlist.		*/
431 
432       if (thisobject->schemtype == SYMBOL && thisobject->symschem != NULL)
433 	 thisobject = thisobject->symschem;
434       else {
435 	 if (!quiet)
436             Wprintf("Error:  attempt to generate netlist for a symbol.");
437 	 return;
438       }
439    }
440 
441 
442    /* Wprintf("Generating netlists"); */ /* Diagnostic */
443    gennetlist(thisinst);
444    gencalls(thisobject);
445    cleartraversed(thisobject);
446    resolve_devnames(thisobject);
447    /* Wprintf("Finished netlists"); */
448 
449 }
450 
451 /*----------------------------------------------------------------------*/
452 /* Free all memory associated with the netlists.			*/
453 /*----------------------------------------------------------------------*/
454 
destroynets(objectptr thisobject)455 void destroynets(objectptr thisobject)
456 {
457    objectptr pschem;
458 
459    pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
460 		thisobject;
461 
462    freetemplabels(pschem);
463    freenets(pschem);
464    freeglobals();
465 }
466 
467 /*----------------------------------------------------------------------*/
468 /* Polygon types which are ignored when considering if a polygon	*/
469 /* belongs to a circuit network:					*/
470 /* 	Closed polygons (boxes)						*/
471 /*	Bounding box polygons						*/
472 /*	Filled polygons							*/
473 /*	Dashed and dotted line-style polygons				*/
474 /*----------------------------------------------------------------------*/
475 
nonnetwork(polyptr cpoly)476 Boolean nonnetwork(polyptr cpoly)
477 {
478    if (!(cpoly->style & UNCLOSED)) return True;
479    if (cpoly->style & (DASHED | DOTTED | FILLSOLID | BBOX))
480       return True;
481    return False;
482 }
483 
484 /*----------------------------------------------------------------------*/
485 /* Return the largest (most negative) net number in the global netlist	*/
486 /*----------------------------------------------------------------------*/
487 
globalmax()488 int globalmax()
489 {
490    LabellistPtr gl;
491    int bidx, sbus;
492    buslist *lbus;
493    int smin = 0;
494 
495    for (gl = global_labels; gl != NULL; gl = gl->next) {
496       if (!(gl->subnets)) {
497 	 if (gl->net.id < smin)
498 	    smin = gl->net.id;
499       }
500       else {
501 	 for (bidx = 0; bidx < gl->subnets; bidx++) {
502 	    lbus = gl->net.list + bidx;
503 	    sbus = lbus->netid;
504 	    if (sbus < smin)
505 	       smin = sbus;
506 	 }
507       }
508    }
509    return smin;
510 }
511 
512 /*----------------------------------------------------------------------*/
513 /* Return the largest net number in an object's netlist			*/
514 /*----------------------------------------------------------------------*/
515 
netmax(objectptr cschem)516 int netmax(objectptr cschem)
517 {
518    PolylistPtr gp;
519    LabellistPtr gl;
520    int bidx, sbus;
521    buslist *lbus;
522    int smax = 0;
523 
524    for (gp = cschem->polygons; gp != NULL; gp = gp->next) {
525       if (!(gp->subnets)) {
526 	 if (gp->net.id > smax)
527 	    smax = gp->net.id;
528       }
529       else {
530 	 for (bidx = 0; bidx < gp->subnets; bidx++) {
531 	    lbus = gp->net.list + bidx;
532 	    sbus = lbus->netid;
533 	    if (sbus > smax)
534 	       smax = sbus;
535 	 }
536       }
537    }
538    for (gl = cschem->labels; gl != NULL; gl = gl->next) {
539       if (!(gl->subnets)) {
540 	 if (gl->net.id > smax)
541 	    smax = gl->net.id;
542       }
543       else {
544 	 for (bidx = 0; bidx < gl->subnets; bidx++) {
545 	    lbus = gl->net.list + bidx;
546 	    sbus = lbus->netid;
547 	    if (sbus > smax)
548 	       smax = sbus;
549 	 }
550       }
551    }
552    return smax;
553 }
554 
555 /*----------------------------------------------------------------------*/
556 /* Resolve nets and pins for the indicated object			*/
557 /*									*/
558 /* When encountering object instances, call gennetlist() on the object  */
559 /* if it does not have a valid netlist, then recursively call		*/
560 /* gennetlist().  Ignore "info" labels, which are not part of the	*/
561 /* network.								*/
562 /*									*/
563 /*----------------------------------------------------------------------*/
564 
gennetlist(objinstptr thisinst)565 void gennetlist(objinstptr thisinst)
566 {
567    genericptr *cgen;
568    labelptr olabel, clab;
569    polyptr cpoly, tpoly;
570    objectptr thisobject, callobj, cschem, pschem;
571    objinstptr cinst, labinst;
572    int old_parts; /* netid, tmpid, sub_bus, (jdk) */
573    stringpart *cstr;
574    Boolean visited;
575 
576    XPoint *tpt, *tpt2, *endpt, *endpt2;
577    int i, j, n, nextnet, lbus;
578    buslist *sbus;
579    PolylistPtr plist;
580    LabellistPtr lseek;
581    Genericlist *netlist, *tmplist, *buspins, *resolved_net, newlist;
582 
583    newlist.subnets = 0;
584    newlist.net.id = -1;
585 
586    /* Determine the type of object being netlisted */
587    thisobject = thisinst->thisobject;
588    setobjecttype(thisobject);
589 
590    if (thisobject->schemtype == NONETWORK) return;
591 
592    /* Has this object been visited before? */
593    visited = ((thisobject->labels != NULL) || (thisobject->polygons != NULL)) ?
594 		TRUE : FALSE;
595 
596    if (!visited && (thisobject->schemtype == SYMBOL)
597 		&& (thisobject->symschem != NULL)) {
598 
599       /* Make sure that schematics are netlisted before their symbols */
600 
601       if ((thisobject->symschem->polygons == NULL) &&
602 			(thisobject->symschem->labels == NULL)) {
603 	 n = is_page(thisobject->symschem);
604 	 if (n == -1) {
605 	    Fprintf(stderr, "Error: associated schematic is not a page!\n");
606 	    return;
607 	 }
608 	 else
609 	    gennetlist(xobjs.pagelist[n]->pageinst);
610       }
611 
612       /* Sanity check on symbols with schematics:  Are there any pins? */
613 
614       for (i = 0; i < thisobject->parts; i++) {
615 	 cgen = thisobject->plist + i;
616 	 if (IS_LABEL(*cgen)) {
617 	    clab = TOLABEL(cgen);
618 	    if (clab->pin != False && clab->pin != INFO)
619 	       break;
620 	 }
621       }
622       if (i == thisobject->parts)
623 	 /* Don't warn if schematic has infolabels */
624 	 if (thisobject->symschem == NULL || thisobject->symschem->infolabels == 0)
625 	    Fprintf(stderr, "Warning:  Symbol %s has no pins!\n", thisobject->name);
626    }
627 
628    /* If this is a secondary schematic, run on the primary (master) */
629    pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
630 		thisobject;
631 
632    nextnet = netmax(pschem) + 1;
633 
634    /* We start the loop for schematics but will modify the loop	*/
635    /* variable to execute just once in the case of a symbol.	*/
636    /* It's just not worth the trouble to turn this into a	*/
637    /* separate subroutine. . .					*/
638 
639    for (j = 0; j < xobjs.pages; j++) {
640       if (pschem->schemtype != PRIMARY) {
641 	 j = xobjs.pages;
642 	 cinst = thisinst;
643 	 cschem = thisobject;
644       }
645       else {
646          cinst = xobjs.pagelist[j]->pageinst;
647          if ((cinst == NULL) ||
648 		((cinst->thisobject != pschem) &&
649 		((cinst->thisobject->schemtype != SECONDARY) ||
650 		(cinst->thisobject->symschem != pschem)))) continue;
651          cschem = cinst->thisobject;
652       }
653 
654       /* Determine the existing number of parts.  We do not want to	*/
655       /* search over elements that we create in this routine.		*/
656 
657       old_parts = cschem->parts;
658 
659       /* Part 1:  Recursion */
660       /* Schematic pages and symbols acting as their own schematics are the	*/
661       /* two types on which we recurse to find all sub-schematics.		*/
662 
663       if ((!visited) && (cschem->schemtype == PRIMARY ||
664 		cschem->schemtype == SECONDARY ||
665 		(cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
666 
667          for (i = 0; i < old_parts; i++) {
668             cgen = cschem->plist + i;
669             if (IS_OBJINST(*cgen)) {
670 	       objinstptr geninst, callinst;
671 	       geninst = TOOBJINST(cgen);
672 
673 	       if (geninst->thisobject->symschem != NULL) {
674 	          callobj = geninst->thisobject->symschem;
675 		  n = is_page(callobj);
676 		  if (n == -1) {
677 		     Fprintf(stderr, "Error: associated schematic is not a page!\n");
678 		     continue;
679 		  }
680 		  else
681 	             callinst = xobjs.pagelist[n]->pageinst;
682 	       }
683 	       else {
684 	          callobj = geninst->thisobject;
685 	          callinst = geninst;
686 	       }
687 
688 	       /* object on its own schematic */
689 	       if (callobj == pschem) continue;
690 
691 	       gennetlist(callinst);
692 
693 	       /* Also generate netlist for pins in the corresponding symbol */
694 	       if (geninst->thisobject->symschem != NULL)
695 	          gennetlist(geninst);
696             }
697 	 }
698       }
699 
700       /* Part 2:  Match pin labels to nets, and add to list of globals	*/
701       /* if appropriate.  We do all pins first, before ennumerating	*/
702       /* polygons, so that buses can be handled correctly, generating	*/
703       /* overlapping subnets.						*/
704 
705       for (i = 0; i < old_parts; i++) {
706          cgen = cschem->plist + i;
707          if (IS_LABEL(*cgen)) {
708 	    clab = TOLABEL(cgen);
709 	    if (clab->pin != False && clab->pin != INFO) {
710 
711 	       /* Check if the label has a non-default parameter. */
712 	       /* If so, we want to record the instance along	  */
713 	       /* the other netlist information about the pin.	  */
714 
715 	       labinst = NULL;
716 	       for (cstr = clab->string; cstr != NULL; cstr = cstr->nextpart)
717 		  if (cstr->type == PARAM_START)
718 		     if (match_instance_param(cinst, cstr->data.string) != NULL) {
719 			labinst = cinst;
720 			break;
721 		     }
722 
723 	       /* If we have netlisted this object before, and the	*/
724 	       /* label is already in the netlist, then ignore it.	*/
725 	       /* This only happens for labels without instanced	*/
726 	       /* parameter values.					*/
727 
728 	       if (visited && (labinst == NULL)) {
729 		  for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next)
730 		     if ((lseek->label == clab) && (lseek->cinst == NULL))
731 		        break;
732 		  if (lseek != NULL) continue;
733 	       }
734 	       else if (visited)
735 		  netlist = NULL;
736 	       else
737 		  /* Note any overlapping labels. */
738 	          netlist = pointtonet(cschem, cinst, &clab->position);
739 
740                if (pschem->symschem != NULL && pschem->schemtype == SYMBOL) {
741 
742 		  /* For symbols:  Check that this pin has a		*/
743 		  /* corresponding pin in the schematic.  The schematic	*/
744 		  /* is netlisted before the symbol, so the netlist	*/
745 		  /* should be there.					*/
746 
747 		  tmplist = pintonet(pschem->symschem, cinst, clab);
748 
749 	          /* There is always the possibility that the symbol	*/
750 		  /* pin does not correspond to anything in the		*/
751 		  /* schematic.  If so, it gets its own unique net	*/
752 		  /* number.  HOWEVER, this situation usually indicates	*/
753 		  /* an error in the schematic, so output a warning	*/
754 		  /* message.						*/
755 
756 	          if (tmplist == NULL) {
757 		     char *snew = NULL;
758 		     snew = textprint(clab->string, NULL),
759 		     Fprintf(stderr, "Warning:  Pin \"%s\" in symbol %s has no "
760 				"connection in schematic %s\n",
761 				snew, pschem->name,
762 				pschem->symschem->name);
763 		     free(snew);
764 		     newlist.net.id = nextnet++;
765 		     tmplist = &newlist;
766 	          }
767 	       }
768 	       else {
769 		  /* For schematics:  Relate the pin to a network */
770 		  tmplist = pintonet(pschem, cinst, clab);
771 
772 		  /* If only part of a bus has been identified, generate */
773 		  /* the net ID's of the parts that haven't been.	 */
774 
775 		  if ((tmplist != NULL) && (tmplist->subnets > 0)) {
776 		     for (lbus = 0; lbus < tmplist->subnets; lbus++) {
777 		        sbus = tmplist->net.list + lbus;
778 		        if (sbus->netid == 0)
779 			   sbus->netid = nextnet++;
780 		     }
781 		  }
782 	       }
783 
784 	       if (clab->pin == LOCAL) {
785 	          if (tmplist != NULL) {
786 	             addpin(cschem, labinst, clab, tmplist);
787 	             if (netlist != NULL)
788 	                mergenets(pschem, netlist, tmplist);
789 	          }
790 	          else {
791 	             if (netlist == NULL) {
792 		        netlist = &newlist;
793 		        netlist->net.id = nextnet++;
794 			buspins = break_up_bus(clab, cinst, netlist);
795 			if (buspins != NULL) {
796 			   buslist *sbus = buspins->net.list + buspins->subnets - 1;
797 			   nextnet = sbus->netid + 1;
798 	                   netlist = addpin(cschem, labinst, clab, buspins);
799 			}
800 			else {
801 	                   tmplist = addpin(cschem, labinst, clab, netlist);
802 		           netlist = tmplist;
803 			}
804 		     }
805 		     else {
806 	                tmplist = addpin(cschem, labinst, clab, netlist);
807 		        netlist = tmplist;
808 		     }
809 	          }
810 	       }
811 	       else if (clab->pin == GLOBAL) {
812 	          if (tmplist == NULL) {
813 		     tmplist = &newlist;
814 	             tmplist->net.id = globalmax() - 1;
815 		  }
816 	          addglobalpin(cschem, cinst, clab, tmplist);
817 	          addpin(cschem, labinst, clab, tmplist);
818 	          if (netlist != NULL)
819 	             mergenets(pschem, netlist, tmplist);
820 	       }
821 	    }
822 	    else if (clab->pin == INFO) {
823 	       /* Note the presence of info labels in the	*/
824 	       /* subcircuit, indicating that it needs to be	*/
825 	       /* called because it will generate output.	*/
826                cschem->infolabels = TRUE;
827 	    }
828          }
829       }
830 
831       /* Part 3: Polygon enumeration				*/
832       /* Assign network numbers to all polygons in the object.	*/
833       /* (Ignore symbols, as we only consider pins on symbols)	*/
834       /* Where multiple pins occur at a point (bus notation),	*/
835       /* polygons will be duplicated in each network.		*/
836 
837       if ((!visited) && (cschem->schemtype == PRIMARY ||
838 		cschem->schemtype == SECONDARY ||
839 		(cschem->schemtype == SYMBOL && cschem->symschem == NULL))) {
840 
841 	 for (i = 0; i < old_parts; i++) {
842 	    cgen = cschem->plist + i;
843             if (IS_POLYGON(*cgen)) {
844  	       cpoly = TOPOLY(cgen);
845 
846 	       /* Ignore non-network (closed, bbox, filled) polygons */
847 	       if (nonnetwork(cpoly)) continue;
848 
849 	       resolved_net = (Genericlist *)NULL;
850 
851 	       /* Check for attachment of each segment of this polygon	*/
852 	       /* to position of every recorded pin label.			*/
853 
854 	       for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
855 		  if (lseek->cschem != cschem) continue;
856 		  else if ((lseek->cinst != NULL) && (lseek->cinst != cinst))
857 		     continue;
858 		  olabel = lseek->label;
859 		  tmplist = (Genericlist *)lseek;
860 	          for (endpt = cpoly->points; endpt < cpoly->points
861 		   		+ EndPoint(cpoly->number); endpt++) {
862 	             endpt2 = endpt + NextPoint(cpoly->number);
863 		     if (onsegment(endpt, endpt2, &olabel->position)) {
864 
865 			if (resolved_net != NULL) {
866 			   if (mergenets(pschem, resolved_net, tmplist))
867 			      resolved_net = tmplist;
868 		        }
869 			if (resolved_net == NULL) {
870 		           addpoly(cschem, cpoly, tmplist);
871 			   resolved_net = tmplist;
872 		        }
873 		     }
874 	          }
875 	          /* if we've encountered a unique instance, then con-	*/
876 	          /* tinue past all other instances using this label.	*/
877 	          if (lseek->cinst != NULL)
878 		     while (lseek->next && (lseek->next->label == lseek->label))
879 		        lseek = lseek->next;
880 	       }
881 
882 	       /* Check for attachment of each segment of this polygon */
883 	       /* to endpoints of every recorded network polygon.      */
884 
885 	       for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
886 		  if (plist->cschem != cschem) continue;
887 	          else if ((tpoly = plist->poly) == cpoly) continue;
888 		  tpt = tpoly->points;
889 		  tpt2 = tpoly->points + tpoly->number - 1;
890 		  tmplist = (Genericlist *)plist;
891 
892 	          for (endpt = cpoly->points; endpt < cpoly->points
893 		   		+ EndPoint(cpoly->number); endpt++) {
894 	             endpt2 = endpt + NextPoint(cpoly->number);
895 
896 		     if (onsegment(endpt, endpt2, tpt) ||
897 				onsegment(endpt, endpt2, tpt2)) {
898 
899 		        /* Nets previously counted distinct have    */
900 		        /* been connected together by this polygon. */
901 			if (resolved_net != NULL) {
902 			   if (mergenets(pschem, resolved_net, tmplist))
903 		   	      resolved_net = tmplist;
904 		        }
905 		        if (resolved_net == NULL) {
906 		           addpoly(cschem, cpoly, tmplist);
907 		           resolved_net = tmplist;
908 		        }
909 		     }
910 		  }
911 
912 	          /* Check for attachment of the endpoints of this polygon */
913 	          /* to each segment of every recorded network polygon.	   */
914 
915 		  endpt = cpoly->points;
916 		  endpt2 = cpoly->points + cpoly->number - 1;
917 	          for (tpt = tpoly->points; tpt < tpoly->points
918 			   + EndPoint(tpoly->number); tpt++) {
919 		     tpt2 = tpt + NextPoint(tpoly->number);
920 
921 	     	     if (onsegment(tpt, tpt2, endpt) ||
922 			   	onsegment(tpt, tpt2, endpt2)) {
923 
924 		        /* Nets previously counted distinct have    */
925 		        /* been connected together by this polygon. */
926 			if (resolved_net != 0) {
927 			   if (mergenets(pschem, resolved_net, tmplist))
928 		              resolved_net = tmplist;
929 		        }
930 		        if (resolved_net == 0) {
931 		           addpoly(cschem, cpoly, tmplist);
932 		           resolved_net = tmplist;
933 		        }
934 		     }
935 	          }
936 	       }
937 	       if (resolved_net == 0) {
938 
939 	          /* This polygon belongs to an unvisited	*/
940 	          /* network.  Give this polygon a new net	*/
941 	          /* number and add to the net list.		*/
942 
943 		  newlist.net.id = nextnet++;
944 	          addpoly(cschem, cpoly, &newlist);
945 	       }
946 	    }
947          }
948       }
949    }
950 }
951 
952 /*--------------------------------------------------------------*/
953 /* Search a sibling of object instance "cinst" for connections	*/
954 /* between the two.  Recurse through the schematic hierarchy of	*/
955 /* the sibling object.						*/
956 /*--------------------------------------------------------------*/
957 
search_on_siblings(objinstptr cinst,objinstptr isib,pushlistptr schemtop,short llx,short lly,short urx,short ury)958 void search_on_siblings(objinstptr cinst, objinstptr isib, pushlistptr schemtop,
959 	short llx, short lly, short urx, short ury)
960 {
961    XPoint *tmppts, sbbox[2];
962    int i;
963    labelptr olabel;
964    polyptr tpoly;
965    PolylistPtr pseek;
966    LabellistPtr lseek;
967 
968    genericptr *iseek;
969    objinstptr subsibinst;
970    pushlistptr psearch, newlist;
971    objectptr  sibling = isib->thisobject;
972 
973    tmppts = (XPoint *)malloc(sizeof(XPoint));
974 
975    /* If the sibling is a symbol or fundamental or trivial object, then */
976    /* we just look at pin labels from the parts list and return.	*/
977 
978    if (sibling->symschem != NULL || sibling->schemtype == FUNDAMENTAL
979 		|| sibling->schemtype == TRIVIAL) {
980       for (lseek = sibling->labels; lseek != NULL; lseek = lseek->next) {
981 	 olabel = lseek->label;
982 
983 	 tmppts = (XPoint *)realloc(tmppts, sizeof(XPoint));
984 	 UTransformPoints(&(olabel->position), tmppts, 1,
985 			isib->position, isib->scale, isib->rotation);
986 
987 	 /* Transform all the way up to the level above cinst */
988 	 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
989 	    subsibinst = psearch->thisinst;
990 	    UTransformPoints(tmppts, tmppts, 1, subsibinst->position,
991 				subsibinst->scale, subsibinst->rotation);
992 	 }
993 	 searchconnect(tmppts, 1, cinst, lseek->subnets);
994       }
995    }
996 
997    /* If the sibling is a schematic, we look at connections to pins and */
998    /* polygons, and recursively search subschematics of the sibling.	*/
999 
1000    else {
1001 
1002       /* Look for pins connecting to pins in the object */
1003 
1004       for (lseek = sibling->labels; lseek != NULL; lseek = lseek->next) {
1005 	 olabel = lseek->label;
1006 	 tmppts = (XPoint *)realloc(tmppts, sizeof(XPoint));
1007 	 UTransformPoints(&(olabel->position), tmppts, 1,
1008 			isib->position, isib->scale, isib->rotation);
1009 
1010 	 /* Transform all the way up to the level above cinst */
1011 	 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1012 	    subsibinst = psearch->thisinst;
1013 	    UTransformPoints(tmppts, tmppts, 1, subsibinst->position,
1014 			subsibinst->scale, subsibinst->rotation);
1015 	 }
1016 	 searchconnect(tmppts, 1, cinst, lseek->subnets);
1017       }
1018 
1019       /* Look for polygon ends connecting into the object */
1020 
1021       for (pseek = sibling->polygons; pseek != NULL; pseek = pseek->next) {
1022 	 tpoly = pseek->poly;
1023 	 tmppts = (XPoint *)realloc(tmppts, tpoly->number * sizeof(XPoint));
1024 	 UTransformPoints(tpoly->points, tmppts, tpoly->number,
1025 				isib->position, isib->scale, isib->rotation);
1026 
1027 	 /* Transform all the way up to the level above cinst */
1028 	 for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1029 	    subsibinst = psearch->thisinst;
1030 	    UTransformPoints(tmppts, tmppts, tpoly->number, subsibinst->position,
1031 				subsibinst->scale, subsibinst->rotation);
1032 	 }
1033 	 searchconnect(tmppts, tpoly->number, cinst, pseek->subnets);
1034       }
1035 
1036       /* Recursively search all schematic children of the sibling */
1037 
1038       for (i = 0; i < sibling->parts; i++) {
1039 	 iseek = sibling->plist + i;
1040 	 if (IS_OBJINST(*iseek)) {
1041 
1042 	   /* objinstptr iinst = (objinstptr)iseek; (jdk) */
1043 
1044 	    /* Don't search this instance unless the bounding box 	*/
1045 	    /* overlaps the bounding box of the calling object.		*/
1046 
1047 	    calcinstbbox(iseek, &sbbox[0].x, &sbbox[0].y, &sbbox[1].x, &sbbox[1].y);
1048 	    for (psearch = schemtop; psearch != NULL; psearch = psearch->next) {
1049 	       subsibinst = psearch->thisinst;
1050 	       UTransformPoints(sbbox, sbbox, 2, subsibinst->position,
1051 			subsibinst->scale, subsibinst->rotation);
1052 	    }
1053 	    if ((llx > sbbox[1].x) || (urx < sbbox[0].x) || (lly > sbbox[1].y)
1054 			|| (ury < sbbox[0].y))
1055 	       continue;
1056 
1057 	    subsibinst = TOOBJINST(iseek);
1058 
1059 	    /* push stack */
1060 	    newlist = (pushlistptr)malloc(sizeof(pushlist));
1061 	    newlist->thisinst = isib;
1062 	    newlist->next = schemtop;
1063 	    schemtop = newlist;
1064 
1065 	    search_on_siblings(cinst, subsibinst, schemtop, llx, lly, urx, ury);
1066 
1067 	    /* pop stack */
1068 
1069 	    newlist = schemtop;
1070 	    schemtop = schemtop->next;
1071 	    free(newlist);
1072 	 }
1073       }
1074    }
1075    free(tmppts);
1076 }
1077 
1078 /*--------------------------------------------------------------*/
1079 /* Generate ports from pin labels for all objects:		*/
1080 /* For each pin-label in the subcircuit or symbol, generate	*/
1081 /* a port in the object or instance's object.			*/
1082 /* Translate the pin position to the level above and search	*/
1083 /* for & link in any connecting nets.				*/
1084 /*								*/
1085 /* Generate calls to object instances.				*/
1086 /* Pick up any other nets which might be formed by		*/
1087 /* juxtaposition of object instances on different levels of the */
1088 /* schematic hierarchy (e.g., pin-to-pin connections).		*/
1089 /*--------------------------------------------------------------*/
1090 
gencalls(objectptr thisobject)1091 void gencalls(objectptr thisobject)
1092 {
1093    genericptr *cgen, *iseek;
1094    Matrix locctm;
1095    objinstptr cinst, isib, callinst;
1096    objectptr callobj, callsymb, cschem, pschem;
1097    XPoint xpos;
1098    short ibllx, iblly, iburx, ibury, sbllx, sblly, sburx, sbury;
1099    int i, j, k; /* , lbus; (jdk) */
1100    /* buslist *sbus; (jdk) */
1101    labelptr olabel;
1102    polyptr tpoly;
1103    PolylistPtr pseek;
1104    /* CalllistPtr cseek; (jdk) */
1105    LabellistPtr lseek;
1106    Genericlist *netfrom, *netto; /* , *othernet; (jdk) */
1107 
1108    /* The netlist is always kept in the master schematic */
1109    pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem :
1110 		thisobject;
1111 
1112    pschem->traversed = True;	/* This object has been dealt with */
1113    pschem->valid = True;	/* This object has a valid netlist */
1114 
1115    /* We start the loop for schematics but will modify the loop	*/
1116    /* variable to execute just once in the case of a symbol.	*/
1117    /* (see gennetlist(), where the same thing is done)		*/
1118 
1119    for (j = 0; j < xobjs.pages; j++) {
1120       if (pschem->schemtype != PRIMARY) {
1121 	 j = xobjs.pages;
1122 	 cschem = thisobject;
1123       }
1124       else {
1125          cinst = xobjs.pagelist[j]->pageinst;
1126          if ((cinst == NULL) ||
1127 		((cinst->thisobject != pschem) &&
1128 		((cinst->thisobject->schemtype != SECONDARY) ||
1129 		(cinst->thisobject->symschem != pschem)))) continue;
1130          cschem = cinst->thisobject;
1131       }
1132 
1133       for (i = 0; i < cschem->parts; i++) {
1134          cgen = cschem->plist + i;
1135          if (IS_OBJINST(*cgen)) {
1136 	    callinst = TOOBJINST(cgen);
1137 
1138 	    /* Ignore any instance that is specifically marked non-netlistable */
1139 	    if (callinst->style & INST_NONETLIST) continue;
1140 
1141 	    /* Determine where the hierarchy continues downward */
1142 
1143 	    if (callinst->thisobject->symschem != NULL)
1144 	       callobj = callinst->thisobject->symschem;
1145 	    else
1146 	       callobj = callinst->thisobject;
1147 
1148 	    /* Always ignore any object on its own schematic */
1149 
1150 	    if (callobj == pschem) continue;
1151 
1152 	    callsymb = callinst->thisobject;
1153 
1154 	    /* Note:  callobj is the next schematic in the hierarchy.	*/
1155 	    /* callsymb is the next visible object in the hierarchy, 	*/
1156 	    /* which may be either a schematic or a symbol.		*/
1157 
1158 	    /*--------------------------------------------------------------*/
1159 	    /* For object instances which are their own schematics (i.e.,   */
1160 	    /* have netlist elements), don't rely on any pin list but make  */
1161 	    /* a survey of how polygons connect into the object.	    */
1162 	    /*--------------------------------------------------------------*/
1163 
1164 	    if (callsymb->symschem == NULL
1165 			&& callobj->schemtype != FUNDAMENTAL
1166 			&& callobj->schemtype != TRIVIAL) {
1167 
1168                /* Fprintf(stdout, "*** Analyzing connections from %s"
1169 			" to instance of %s\n", cschem->name,
1170 			callinst->thisobject->name); */
1171 
1172 	       /* Look for pins connecting to pins in the object */
1173 
1174 	       for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1175 		  if (lseek->cschem != cschem) continue;
1176 		  else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1177 		     continue;
1178 	 	  olabel = lseek->label;
1179 	          searchconnect(&(olabel->position), 1, callinst, lseek->subnets);
1180 	          /* if we've encountered a unique instance, then con-	*/
1181 	          /* tinue past all other instances using this label.	*/
1182 	          if (lseek->cinst != NULL)
1183 		     while (lseek->next && (lseek->next->label == lseek->label))
1184 		        lseek = lseek->next;
1185 	       }
1186 
1187 	       /* Look for polygon ends connecting into the object */
1188 
1189 	       for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1190 		  if (pseek->cschem != cschem) continue;
1191 		  tpoly = pseek->poly;
1192 	          searchconnect(tpoly->points, tpoly->number, callinst, pseek->subnets);
1193 	       }
1194 
1195 	       /* For each call to a schematic or symbol which is NOT the	*/
1196 	       /* one under consideration, see if it touches or overlaps	*/
1197 	       /* the object under consideration.  Search for connections	*/
1198 	       /* between the two objects.					*/
1199 
1200 	       calcinstbbox(cgen, &ibllx, &iblly, &iburx, &ibury);
1201 
1202 	       /* Only need to look forward from the current position. */
1203    	       for (k = i + 1; k < cschem->parts; k++) {
1204 
1205 	          iseek = cschem->plist + k;
1206 	          if (IS_OBJINST(*iseek)) {
1207 		     calcinstbbox(iseek, &sbllx, &sblly, &sburx, &sbury);
1208 
1209 		     /* Check intersection of the two object instances;	*/
1210 		     /* don't do a search if they are disjoint.		*/
1211 
1212 		     if ((ibllx <= sburx) && (iburx >= sbllx) &&
1213 				(iblly <= sbury) && (ibury >= sblly)) {
1214 		        isib = TOOBJINST(iseek);
1215 		        search_on_siblings(callinst, isib, NULL,
1216 				ibllx, iblly, iburx, ibury);
1217 		     }
1218 	          }
1219 	       }
1220 	    }
1221 
1222 	    /*----------------------------------------------------------*/
1223 	    /* Recursively call gencalls() on the schematic.		*/
1224 	    /*----------------------------------------------------------*/
1225 
1226 	    if (callobj->traversed == False)
1227 	       gencalls(callobj);
1228 
1229 	    /*----------------------------------------------------------*/
1230 	    /* Create a call to the object callsymb from object cschem	*/
1231 	    /*----------------------------------------------------------*/
1232 
1233 	    addcall(cschem, callobj, callinst);
1234 
1235 	    /*----------------------------------------------------------*/
1236 	    /* Search again on symbol pins to generate calls to ports. 	*/
1237 	    /*----------------------------------------------------------*/
1238 
1239 	    UResetCTM(&locctm);
1240 	    UPreMultCTM(&locctm, callinst->position, callinst->scale,
1241 				callinst->rotation);
1242 
1243 	    for (lseek = callsymb->labels; lseek != NULL; lseek = lseek->next) {
1244 	      /* LabellistPtr slab; (jdk) */
1245 	      /* labelptr slabel; (jdk) */
1246 
1247 	       if (lseek->cschem != callsymb) continue;
1248 	       else if ((lseek->cinst != NULL) && (lseek->cinst != callinst))
1249 		  continue;
1250 
1251 	       olabel = lseek->label;
1252 	       netto = (Genericlist *)lseek;
1253 
1254 	       /* Translate pin position back to object cschem */
1255 	       UTransformbyCTM(&locctm, &(olabel->position), &xpos, 1);
1256 
1257 	       /* What net in the calling object connects to this point? */
1258 	       netfrom = pointtonet(cschem, callinst, &xpos);
1259 
1260 	       /* If there's no net, we make one */
1261 	       if (netfrom == NULL)
1262 		  netfrom = make_tmp_pin(cschem, callinst, &xpos, netto);
1263 
1264 	       /* Generate a port call for a global signal if the	*/
1265 	       /* global label appears in the symbol (9/29/04).  This	*/
1266 	       /* is a change from previous behavior, which was to not	*/
1267 	       /* make the call.					*/
1268 
1269 	       if ((netto->subnets == 0) && (netto->net.id < 0))
1270 		  mergenets(pschem, netfrom, netto);
1271 
1272 	       /* Attempt to generate a port in the object.	*/
1273 	       addport(callobj, netto);
1274 
1275 	       /* Generate the call to the port */
1276 	       if (addportcall(pschem, netfrom, netto) == FALSE) {
1277 
1278 		  // If object is "dot" then copy the bus from the
1279 		  // net to the dot.  The dot takes on whatever
1280 		  // dimension the bus is.
1281 
1282 		  if (strstr(callobj->name, "::dot") != NULL) {
1283 		     copy_bus(netto, netfrom);
1284 		  }
1285 		  else {
1286 		     Fprintf(stderr, "Error:  attempt to connect bus size "
1287 				"%d in %s to bus size %d in %s\n",
1288 				netfrom->subnets, cschem->name,
1289 				netto->subnets, callobj->name);
1290 		  }
1291 	       }
1292 
1293 	       /* if we've encountered a unique instance, then continue	*/
1294 	       /* past all other instances using this label.		*/
1295 	       if (lseek->cinst != NULL)
1296 		  while (lseek->next && (lseek->next->label == lseek->label))
1297 		     lseek = lseek->next;
1298 	    }
1299 
1300 	    /*----------------------------------------------------------*/
1301 	    /* If after all that, no ports were called, then remove the	*/
1302 	    /* call to this object instance.  However, we should check	*/
1303 	    /* for info labels, because the device may produce output	*/
1304 	    /* for the netlist even if it has no declared ports.  This	*/
1305 	    /* is irrespective of the presence of pin labels---if the	*/
1306 	    /* symbol is "trivial", the netlist connections are		*/
1307 	    /* resolved in the level of the hierarchy above, and there	*/
1308 	    /* may be no ports, but the call list must retain the call	*/
1309 	    /* to ensure that the netlist output is generated.		*/
1310 	    /*----------------------------------------------------------*/
1311 
1312 	    if (pschem->calls->ports == NULL)
1313                if (pschem->infolabels == FALSE)
1314 	          removecall(pschem, pschem->calls);	/* Remove the call */
1315          }
1316       }
1317    }
1318 }
1319 
1320 /*----------------------------------------------------------------------*/
1321 /* Translate a net number down in the calling hierarchy			*/
1322 /*----------------------------------------------------------------------*/
1323 
translatedown(int rnet,int portid,objectptr nextobj)1324 int translatedown(int rnet, int portid, objectptr nextobj)
1325 {
1326    PortlistPtr nport;
1327    int downnet = 0;
1328 
1329    for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1330       if (nport->portid == portid) {
1331 	 downnet = nport->netid;
1332 	 break;
1333       }
1334    }
1335    return downnet;
1336 }
1337 
1338 /*----------------------------------------------------------------------*/
1339 /* Translate a netlist up in the calling hierarchy			*/
1340 /*									*/
1341 /* This routine creates a new netlist header which needs to be freed	*/
1342 /* by the calling routine.						*/
1343 /*									*/
1344 /* Note that if the entire netlist cannot be translated up, then the	*/
1345 /* routine returns NULL.  This could be modified to return the part of	*/
1346 /* the network that can be translated up.				*/
1347 /*----------------------------------------------------------------------*/
1348 
translateup(Genericlist * rlist,objectptr thisobj,objectptr nextobj,objinstptr nextinst)1349 Genericlist *translateup(Genericlist *rlist, objectptr thisobj,
1350 	objectptr nextobj, objinstptr nextinst)
1351 {
1352    PortlistPtr nport;
1353    CalllistPtr ccall;
1354    int portid = 0;
1355    int upnet = 0;
1356    int rnet, lbus;
1357    buslist *sbus;
1358    Genericlist *tmplist;
1359 
1360    tmplist = (Genericlist *)malloc(sizeof(Genericlist));
1361    tmplist->subnets = 0;
1362    tmplist->net.id = 0;
1363    copy_bus(tmplist, rlist);
1364 
1365    for (lbus = 0;; ) {
1366       if (rlist->subnets == 0)
1367 	 rnet = rlist->net.id;
1368       else {
1369 	 sbus = rlist->net.list + lbus;
1370 	 rnet = sbus->netid;
1371       }
1372       for (nport = nextobj->ports; nport != NULL; nport = nport->next) {
1373          if (nport->netid == rnet) {
1374 	    portid = nport->portid;
1375 	    break;
1376          }
1377       }
1378 
1379       upnet = 0;
1380       for (ccall = thisobj->calls; ccall != NULL; ccall = ccall->next) {
1381          if (ccall->callinst == nextinst) {
1382 	    for (nport = ccall->ports; nport != NULL; nport = nport->next) {
1383 	       if (nport->portid == portid) {
1384 	          upnet = nport->netid;
1385 	          break;
1386 	       }
1387 	    }
1388 	    if (nport != NULL) break;
1389          }
1390       }
1391       if (upnet == 0) {
1392 	 freegenlist(tmplist);
1393 	 return NULL;
1394       }
1395       else {
1396 	 if (tmplist->subnets == 0) {
1397 	    tmplist->net.id = upnet;
1398 	 }
1399 	 else {
1400 	    sbus = tmplist->net.list + lbus;
1401 	    sbus->netid = upnet;
1402 	    sbus->subnetid = getsubnet(upnet, thisobj);
1403 	 }
1404       }
1405       if (++lbus >= rlist->subnets) break;
1406    }
1407    return tmplist;
1408 }
1409 
1410 /*----------------------------------------------------------------------*/
1411 /* Check whether the indicated polygon is already resolved into the	*/
1412 /* netlist of the object hierarchy described by seltop.			*/
1413 /* Return the netlist if resolved, NULL otherwise.  The netlist		*/
1414 /* returned is referred (translated) upward through the calling		*/
1415 /* hierarchy to the topmost object containing that net or those nets.	*/
1416 /* This topmost object is returned in parameter topobj.			*/
1417 /* Note that the netlist returned does not necessarily correspond to	*/
1418 /* any object in the top level.  It is allocated and must be freed by	*/
1419 /* the calling routine.							*/
1420 /*----------------------------------------------------------------------*/
1421 
is_resolved(genericptr * rgen,pushlistptr seltop,objectptr * topobj)1422 Genericlist *is_resolved(genericptr *rgen, pushlistptr seltop, objectptr *topobj)
1423 {
1424    objectptr thisobj = seltop->thisinst->thisobject;
1425    objectptr pschem;
1426    PolylistPtr pseek;
1427    LabellistPtr lseek;
1428    Genericlist *rlist = NULL, *newlist;
1429 
1430    pschem = (thisobj->schemtype == SECONDARY) ? thisobj->symschem : thisobj;
1431 
1432    /* Recursively call self, since we have to back out from the bottom of  */
1433    /* the stack.							   */
1434 
1435    if (seltop->next != NULL) {
1436       rlist = is_resolved(rgen, seltop->next, topobj);
1437 
1438       /* Translate network ID up the hierarchy to the topmost object in which */
1439       /* the network exists.						   */
1440 
1441       if (rlist != NULL) {
1442          newlist = translateup(rlist, pschem, seltop->next->thisinst->thisobject,
1443 			seltop->next->thisinst);
1444          if (newlist == NULL)
1445             /* Net does not exist upwards of this object.  Pass net ID	*/
1446             /* upward with "topobj" unchanged.				*/
1447 	    return rlist;
1448          else {
1449 	    freegenlist(rlist);
1450 	    rlist = newlist;
1451 	 }
1452       }
1453    }
1454    else {
1455 
1456       /* Find the net ID for the listed object, which should be in the object */
1457       /* on the bottom of the pushlist stack.				   */
1458 
1459       if (IS_POLYGON(*rgen)) {
1460 	 for (pseek = pschem->polygons; pseek != NULL; pseek = pseek->next) {
1461             if (pseek->poly == TOPOLY(rgen)) {
1462  	       rlist = (Genericlist *)pseek;
1463 	       break;
1464 	    }
1465 	 }
1466       }
1467       else if (IS_LABEL(*rgen)) {
1468 	 for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1469             if (lseek->label == TOLABEL(rgen)) {
1470  	       rlist = (Genericlist *)lseek;
1471 	       break;
1472 	    }
1473 	 }
1474       }
1475 
1476       if (rlist != NULL) {
1477          /* Make a copy of the netlist header from this element */
1478          newlist = (Genericlist *)malloc(sizeof(Genericlist));
1479          newlist->subnets = 0;
1480          copy_bus(newlist, rlist);
1481          rlist = newlist;
1482       }
1483    }
1484 
1485    /* done: (jdk) */
1486    *topobj = (rlist == NULL) ? NULL : seltop->thisinst->thisobject;
1487    return rlist;
1488 }
1489 
1490 /*--------------------------------------------------------------*/
1491 /* Highlight all the polygons and pin labels in a network	*/
1492 /* (recursively, downward).  Pin labels are drawn only on the	*/
1493 /* topmost schematic object.					*/
1494 /* Returns true if some part of the hierarchy declared a net to	*/
1495 /* be highlighted.						*/
1496 /* mode = 1 highlight, mode = 0 erase				*/
1497 /*--------------------------------------------------------------*/
1498 
highlightnet(objectptr cschem,objinstptr cinst,int netid,u_char mode)1499 Boolean highlightnet(objectptr cschem, objinstptr cinst, int netid, u_char mode)
1500 {
1501    CalllistPtr calls;
1502    PortlistPtr ports;
1503    PolylistPtr plist;
1504    LabellistPtr llist;
1505    polyptr cpoly;
1506    labelptr clabel;
1507    objinstptr ccinst;
1508    int netto, locnetid, lbus;
1509    int curcolor = AUXCOLOR;
1510    Boolean rval = FALSE;
1511    objectptr pschem;
1512 
1513    SetForeground(dpy, areawin->gc, curcolor);
1514 
1515    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1516 
1517    for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
1518       if (plist->cschem != cschem) continue;
1519       cpoly = plist->poly;
1520       for (lbus = 0;;) {
1521 	 if (plist->subnets == 0)
1522 	    locnetid = plist->net.id;
1523 	 else
1524 	    locnetid = (plist->net.list + lbus)->netid;
1525 	 if (locnetid == netid) {
1526 	    /* Fprintf(stdout, " >> Found polygon belonging to net %d at (%d, %d)\n",
1527 	       		locnetid, cpoly->points[0].x, cpoly->points[0].y); */
1528             if (mode == 0 && cpoly->color != curcolor) {
1529 	       curcolor = cpoly->color;
1530 	       XTopSetForeground(curcolor);
1531 	    }
1532             UDrawPolygon(cpoly, xobjs.pagelist[areawin->page]->wirewidth);
1533 	    break;
1534 	 }
1535 	 if (++lbus >= plist->subnets) break;
1536       }
1537    }
1538 
1539    /* Highlight labels if they belong to the top-level object */
1540 
1541    if (cschem == topobject) {
1542       for (llist = pschem->labels; llist != NULL; llist = llist->next) {
1543 	 if (llist->cschem != cschem) continue;
1544          else if ((llist->cinst != NULL) && (llist->cinst != cinst)) continue;
1545 	 clabel = llist->label;
1546          for (lbus = 0;;) {
1547 	    if (llist->subnets == 0)
1548 	       locnetid = llist->net.id;
1549 	    else
1550 	       locnetid = (llist->net.list + lbus)->netid;
1551 	    if (locnetid == netid) {
1552 	       if (clabel->string->type == FONT_NAME) {  /* don't draw temp labels */
1553 		  if ((mode == 0) && (clabel->color != curcolor)) {
1554 		     curcolor = clabel->color;
1555 	             UDrawString(clabel, curcolor, cinst);
1556 		  }
1557 		  else
1558 	             UDrawString(clabel, DOFORALL, cinst);
1559 	          /* Fprintf(stdout, " >> Found label belonging to net "
1560 				"%d at (%d, %d)\n",
1561 	       			locnetid, clabel->position.x,
1562 				clabel->position.y); */
1563 
1564 	       }
1565 	       break;
1566 	    }
1567 	    if (++lbus >= llist->subnets) break;
1568 	 }
1569 	 /* if we've encountered a unique instance, then continue	*/
1570 	 /* past all other instances using this label.			*/
1571 	 if (llist->cinst != NULL)
1572 	    while (llist->next && (llist->next->label == llist->label))
1573 	       llist = llist->next;
1574       }
1575 
1576       /* Highlight all pins connecting this net to symbols */
1577 
1578    }
1579 
1580    /* Connectivity recursion */
1581 
1582    for (calls = pschem->calls; calls != NULL; calls = calls->next) {
1583       if (calls->cschem != cschem) continue;
1584       for (ports = calls->ports; ports != NULL; ports = ports->next) {
1585          if (ports->netid == netid) {
1586 	    ccinst = calls->callinst;
1587 
1588 	    /* Recurse only on objects for which network polygons are visible	*/
1589 	    /* from the calling object:  i.e., non-trivial, non-fundamental	*/
1590 	    /* objects acting as their own schematics.			 	*/
1591 
1592 	    UPushCTM();
1593 	    UPreMultCTM(DCTM, ccinst->position, ccinst->scale, ccinst->rotation);
1594 
1595 	    if (ccinst->thisobject->symschem == NULL &&
1596 			ccinst->thisobject->schemtype != FUNDAMENTAL &&
1597 			ccinst->thisobject->schemtype != TRIVIAL) {
1598 
1599 	       netto = translatedown(netid, ports->portid, calls->callobj);
1600 
1601 	       /* Fprintf(stdout, " > Calling object %s at (%d, %d)\n",
1602 		    calls->callobj->name, ccinst->position.x, ccinst->position.y); */
1603 	       /* Fprintf(stdout, " > Net translation from %d to %d (port %d)\n",
1604 		    netid, netto, ports->portid); */
1605 
1606 	       if (highlightnet(calls->callobj, calls->callinst, netto, mode))
1607 		  rval = TRUE;
1608 	    }
1609 	    else {
1610 	       /* Otherwise (symbols, fundamental, trivial, etc., objects), we	*/
1611 	       /* highlight the pin position of the port.			*/
1612 	       if ((clabel = PortToLabel(ccinst, ports->portid)))
1613 		  UDrawXDown(clabel);
1614 	    }
1615 	    UPopCTM();
1616 	 }
1617       }
1618    }
1619    return rval;
1620 }
1621 
1622 /*----------------------------------------------------------------------*/
1623 /* Highlight whatever nets are listed in the current object instance,	*/
1624 /* if any.								*/
1625 /*----------------------------------------------------------------------*/
1626 
highlightnetlist(objectptr nettop,objinstptr cinst,u_char mode)1627 void highlightnetlist(objectptr nettop, objinstptr cinst, u_char mode)
1628 {
1629    int lbus, netid;
1630    buslist *sbus;
1631    Genericlist *netlist = cinst->thisobject->highlight.netlist;
1632    objinstptr nextinst = cinst->thisobject->highlight.thisinst;
1633 
1634    if (netlist == NULL) return;
1635 
1636    for (lbus = 0;; ) {
1637       if (netlist->subnets == 0)
1638          netid = netlist->net.id;
1639       else {
1640          sbus = netlist->net.list + lbus;
1641          netid = sbus->netid;
1642       }
1643       highlightnet(nettop, nextinst, netid, mode);
1644       if (++lbus >= netlist->subnets) break;
1645    }
1646 
1647    /* If we are erasing, remove the netlist entry from the object */
1648    if (mode == (u_char)0) {
1649       freegenlist(netlist);
1650       cinst->thisobject->highlight.netlist = NULL;
1651       cinst->thisobject->highlight.thisinst = NULL;
1652    }
1653 }
1654 
1655 /*----------------------------------------------------------------------*/
1656 /* Push the matrix stack for each object (instance) until the indicated	*/
1657 /* object is reached.  This works similarly to highlightnet() above, 	*/
1658 /* but makes calls according to the hierarchy described by the 		*/
1659 /* pushlistptr parameter.						*/
1660 /* Returns the number of stack objects to pop after we're done.		*/
1661 /*----------------------------------------------------------------------*/
1662 
pushnetwork(pushlistptr seltop,objectptr nettop)1663 int pushnetwork(pushlistptr seltop, objectptr nettop)
1664 {
1665    pushlistptr cursel = seltop;
1666    objinstptr sinst;
1667    int rno = 0;
1668 
1669    while ((cursel->thisinst->thisobject != nettop) && (cursel->next != NULL)) {
1670       cursel = cursel->next;
1671       sinst = cursel->thisinst;
1672       UPushCTM();
1673       UPreMultCTM(DCTM, sinst->position, sinst->scale, sinst->rotation);
1674       rno++;
1675    }
1676 
1677    if (cursel->thisinst->thisobject != nettop) {
1678       Fprintf(stderr, "Error:  object does not exist in calling stack!\n");
1679       rno = 0;
1680    }
1681 
1682    return rno;
1683 }
1684 
1685 /*----------------------------------------------------------------------*/
1686 /* Determine if two netlists match.  If "mode" is MATCH_EXACT, the	*/
1687 /* net numbers and subnet numbers must all be the same.  If		*/
1688 /* MATCH_SUBNETS, then the subnet numbers must be the same.  If		*/
1689 /* MATCH_SIZE, then they need only have the same number of subnets.	*/
1690 /*----------------------------------------------------------------------*/
1691 
match_buses(Genericlist * list1,Genericlist * list2,int mode)1692 Boolean match_buses(Genericlist *list1, Genericlist *list2, int mode)
1693 {
1694    int i;
1695    buslist *bus1, *bus2;
1696 
1697    if (list1->subnets != list2->subnets) {
1698       // A wire (no subnets) matches a bus of 1 subnet.  All others
1699       // are non-matching.
1700 
1701       if (list1->subnets != 0 && list2->subnets != 0)
1702          return FALSE;
1703       else if (list1->subnets != 1 && list2->subnets != 1)
1704 	 return FALSE;
1705    }
1706 
1707    if (mode == MATCH_SIZE) return TRUE;
1708 
1709    if (list1->subnets == 0) {
1710       if (mode == MATCH_SUBNETS) return TRUE;
1711       if (list2->subnets != 0) {
1712 	 bus2 = list2->net.list + 0;
1713 	 if (list1->net.id != bus2->netid) return FALSE;
1714       }
1715       else if (list1->net.id != list2->net.id) return FALSE;
1716    }
1717    else if (list2->subnets == 0) {
1718       if (mode == MATCH_SUBNETS) return TRUE;
1719       bus1 = list1->net.list + 0;
1720       if (bus1->netid != list2->net.id) return FALSE;
1721    }
1722    else {
1723       for (i = 0; i < list1->subnets; i++) {
1724          bus1 = list1->net.list + i;
1725          bus2 = list2->net.list + i;
1726 	 /* A subnetid of < 0 indicates an unassigned bus */
1727          if ((bus1->subnetid != -1) && (bus1->subnetid != bus2->subnetid))
1728 	    return FALSE;
1729       }
1730       if (mode == MATCH_SUBNETS) return TRUE;
1731 
1732       for (i = 0; i < list1->subnets; i++) {
1733          bus1 = list1->net.list + i;
1734          bus2 = list2->net.list + i;
1735          if (bus1->netid != bus2->netid)
1736 	    return FALSE;
1737       }
1738    }
1739    return TRUE;
1740 }
1741 
1742 /*----------------------------------------------------------------------*/
1743 /* Copy the netlist structure from one netlist element to another	*/
1744 /*----------------------------------------------------------------------*/
1745 
copy_bus(Genericlist * dest,Genericlist * source)1746 void copy_bus(Genericlist *dest, Genericlist *source)
1747 {
1748    buslist *sbus, *dbus;
1749    int i;
1750 
1751    if (dest->subnets > 0)
1752       free(dest->net.list);
1753 
1754    dest->subnets = source->subnets;
1755    if (source->subnets == 0)
1756       dest->net.id = source->net.id;
1757    else {
1758       dest->net.list = (buslist *)malloc(dest->subnets * sizeof(buslist));
1759       for (i = 0; i < dest->subnets; i++) {
1760 	 sbus = source->net.list + i;
1761 	 dbus = dest->net.list + i;
1762 	 dbus->netid = sbus->netid;
1763 	 dbus->subnetid = sbus->subnetid;
1764       }
1765    }
1766 }
1767 
1768 /*------------------------------------------------------*/
1769 /* Create a new "temporary" label object for a pin	*/
1770 /* This type of label is never drawn, so it doesn't	*/
1771 /* need font info.  It is identified as "temporary" by	*/
1772 /* this lack of a leading font record.			*/
1773 /*------------------------------------------------------*/
1774 
new_tmp_pin(objectptr cschem,XPoint * pinpt,char * pinstring,char * prefix,Genericlist * netlist)1775 Genericlist *new_tmp_pin(objectptr cschem, XPoint *pinpt, char *pinstring,
1776 		char *prefix, Genericlist *netlist)
1777 {
1778    labelptr *newlabel;
1779    stringpart *strptr;
1780 
1781    if (pinpt == NULL) {
1782       Fprintf(stderr, "NULL label location!\n");
1783       return NULL;
1784    }
1785 
1786    NEW_LABEL(newlabel, cschem);
1787    labeldefaults(*newlabel, LOCAL, pinpt->x, pinpt->y);
1788    (*newlabel)->anchor = 0;
1789    (*newlabel)->color = DEFAULTCOLOR;
1790    strptr = (*newlabel)->string;
1791    strptr->type = TEXT_STRING;
1792    if (pinstring != NULL) {
1793       strptr->data.string = (char *)malloc(strlen(pinstring));
1794       strcpy(strptr->data.string, pinstring);
1795    }
1796    else {
1797       strptr->data.string = textprintnet(prefix, NULL, netlist);
1798    }
1799 
1800    /* Add label to object's netlist and return a pointer to the */
1801    /* netlist entry.						*/
1802 
1803    return (addpin(cschem, NULL, *newlabel, netlist));
1804 }
1805 
1806 /*------------------------------------------------------*/
1807 /* Create a label for use in the list of global nets.	*/
1808 /* This label is like a temporary label (new_tmp_pin) 	*/
1809 /* except that it is not represented in any object.	*/
1810 /* The string contains the verbatim contents of any	*/
1811 /* parameter substitutions in the original label.	*/
1812 /*------------------------------------------------------*/
1813 
new_global_pin(labelptr clabel,objinstptr cinst)1814 labelptr new_global_pin(labelptr clabel, objinstptr cinst)
1815 {
1816    labelptr newlabel;
1817 
1818    newlabel = (labelptr) malloc(sizeof(label));
1819    newlabel->type = LABEL;
1820    labeldefaults(newlabel, GLOBAL, 0, 0);
1821    newlabel->anchor = 0;
1822    newlabel->color = DEFAULTCOLOR;
1823    free(newlabel->string);
1824    newlabel->string = stringcopyall(clabel->string, cinst);
1825 
1826    /* Add label to the global netlist and return a pointer to	*/
1827    /* the netlist entry.					*/
1828 
1829    return newlabel;
1830 }
1831 
1832 /*----------------------------------------------------------------------*/
1833 /* Create a temporary I/O pin (becomes part of netlist and also part of	*/
1834 /* the object itself).							*/
1835 /*----------------------------------------------------------------------*/
1836 
make_tmp_pin(objectptr cschem,objinstptr cinst,XPoint * pinpt,Genericlist * sublist)1837 Genericlist *make_tmp_pin(objectptr cschem, objinstptr cinst, XPoint *pinpt,
1838 		Genericlist *sublist)
1839 {
1840    LabellistPtr lseek;
1841    objectptr pschem;
1842    char *pinstring = NULL;
1843    /* buslist *sbus; (jdk) */
1844    /* int lbus; (jdk) */
1845    Genericlist *netlist, *tmplist, newlist;
1846 
1847    newlist.subnets = 0;
1848    newlist.net.id = 0;
1849 
1850    /* Primary schematic (contains the netlist) */
1851    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
1852 
1853    /* Determine a netlist for this pin */
1854 
1855    netlist = pointtonet(cschem, cinst, pinpt);
1856    if (netlist == NULL) {
1857       newlist.net.id = netmax(pschem) + 1;
1858       netlist = &newlist;
1859    }
1860 
1861    /* If there is any other pin at this location, don't	make another	*/
1862    /* one.  If there is already a temporary pin associated with the	*/
1863    /* net, use its name.						*/
1864 
1865    else {
1866       for (lseek = pschem->labels; lseek != NULL; lseek = lseek->next) {
1867 	 if (lseek->cschem != cschem) continue;
1868 	 else if ((lseek->cinst != NULL) && (lseek->cinst != cinst)) continue;
1869 	 tmplist = (Genericlist *)lseek;
1870 	 if (match_buses(netlist, tmplist, MATCH_EXACT)) {
1871 	    if (proximity(&(lseek->label->position), pinpt))
1872 	       return (Genericlist *)lseek;
1873 	    else if (lseek->label->string->type == TEXT_STRING)
1874 	       pinstring = lseek->label->string->data.string;
1875 	 }
1876 	 /* if we've encountered a unique instance, then continue past	*/
1877 	 /* all other instances using this label.			*/
1878 	 if (lseek->cinst != NULL)
1879 	    while (lseek->next && (lseek->next->label == lseek->label))
1880 	       lseek = lseek->next;
1881       }
1882    }
1883    return (new_tmp_pin(cschem, pinpt, pinstring, "ext", netlist));
1884 }
1885 
1886 /*--------------------------------------------------------------*/
1887 /* Search for connections into a non-symbol subcircuit, based	*/
1888 /* on various combinations of polygon and pin label overlaps.	*/
1889 /*--------------------------------------------------------------*/
1890 
searchconnect(XPoint * points,int number,objinstptr cinst,int subnets)1891 int searchconnect(XPoint *points, int number, objinstptr cinst, int subnets)
1892 {
1893    XPoint *tmppts, *tpt, *tpt2, *endpt, *endpt2, *pinpt, opinpt;
1894    objinstptr tinst;
1895    genericptr *cgen;
1896    polyptr tpoly;
1897    labelptr tlab;
1898    objectptr tobj, cobj = cinst->thisobject;
1899    LabellistPtr tseek;
1900    PolylistPtr pseek;
1901    int i; /* , lbus, sub_bus; (jdk) */
1902    int found = 0;
1903 
1904    /* Generate temporary polygon in the coordinate system of	*/
1905    /* the object instance in question			 	*/
1906 
1907    tmppts = (XPoint *)malloc(number * sizeof(XPoint));
1908    InvTransformPoints(points, tmppts, number,
1909 		cinst->position, cinst->scale, cinst->rotation);
1910    /* Fprintf(stdout, "Info: translated polygon w.r.t. object %s\n",	*/
1911    /* 		cinst->thisobject->name);				*/
1912 
1913    /* Recursion on all appropriate sub-schematics. */
1914    /* (Use parts list, not call list, as call list may not have created yet) */
1915 
1916    for (i = 0; i < cobj->parts; i++) {
1917       cgen = cobj->plist + i;
1918       if (IS_OBJINST(*cgen)) {
1919 	 tinst = TOOBJINST(cgen);
1920 	 if (tinst->thisobject->symschem == NULL) {
1921             tobj = tinst->thisobject;
1922 	    if (tobj->schemtype != FUNDAMENTAL && tobj->schemtype != TRIVIAL)
1923 	       found += searchconnect(tmppts, number, tinst, subnets);
1924 	 }
1925       }
1926    }
1927 
1928    for (endpt = tmppts; endpt < tmppts + EndPoint(number); endpt++) {
1929       endpt2 = endpt + NextPoint(number);
1930       for (i = 0; i < cobj->parts; i++) {
1931 	 cgen = cobj->plist + i;
1932          if (!IS_OBJINST(*cgen)) continue;
1933 	 tinst = TOOBJINST(cgen);
1934 
1935 	 /* Look at the object only (symbol, or schematic if it has no symbol) */
1936          tobj = tinst->thisobject;
1937 
1938 	 /* Search for connections to pin labels */
1939 
1940 	 for (tseek = tobj->labels; tseek != NULL; tseek = tseek->next) {
1941 	    tlab = tseek->label;
1942 	    UTransformPoints(&(tlab->position), &opinpt, 1, tinst->position,
1943 				tinst->scale, tinst->rotation);
1944 	    if (onsegment(endpt2, endpt, &opinpt)) {
1945 	       /* Fprintf(stdout, "%s connects to pin %s of %s in %s\n",	*/
1946 	       /*	((number > 1) ? "Polygon" : "Pin"),			*/
1947 	       /*	tlab->string + 2, tinst->thisobject->name,	*/
1948 	       /* 	cinst->thisobject->name);		*/
1949 	       make_tmp_pin(cobj, cinst, &opinpt, (Genericlist *)tseek);
1950 	       found += (tseek->subnets == 0) ? 1 : tseek->subnets;
1951 	       break;
1952 	    }
1953 	 }
1954       }
1955 
1956       for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1957 	 tpoly = pseek->poly;
1958 
1959 	 /* Search for connections from segments passed to this	*/
1960 	 /* function to endpoints of polygons in the netlist.	*/
1961 
1962 	 pinpt = NULL;
1963 	 tpt = tpoly->points;
1964 	 tpt2 = tpoly->points + tpoly->number - 1;
1965 	 if (onsegment(endpt2, endpt, tpt)) pinpt = tpt;
1966 	 if (onsegment(endpt2, endpt, tpt2)) pinpt = tpt2;
1967 
1968 	 /* Create new pinlabel (only if there is not one already there) */
1969 
1970 	 if (pinpt != NULL) {
1971 	    make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1972 	    found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1973 	 }
1974       }
1975    }
1976 
1977    endpt = tmppts;
1978    endpt2 = tmppts + EndPoint(number) - 1;
1979 
1980    /* Search for connections from endpoints passed to this	*/
1981    /* function to segments of polygons in the netlist.		*/
1982 
1983    for (pseek = cobj->polygons; pseek != NULL; pseek = pseek->next) {
1984 
1985       tpoly = pseek->poly;
1986       for (tpt = tpoly->points; tpt < tpoly->points
1987 			+ EndPoint(tpoly->number); tpt++) {
1988 	 tpt2 = tpt + NextPoint(tpoly->number);
1989 
1990   	 pinpt = NULL;
1991 	 if (onsegment(tpt2, tpt, endpt)) pinpt = endpt;
1992 	 if (onsegment(tpt2, tpt, endpt2)) pinpt = endpt2;
1993 
1994 	 /* Create new pinlabel (only if there is not one already there) */
1995 
1996 	 if (pinpt != NULL) {
1997 	    make_tmp_pin(cobj, cinst, pinpt, (Genericlist *)pseek);
1998 	    found += (pseek->subnets == 0) ? 1 : pseek->subnets;
1999 	 }
2000       }
2001    }
2002    free(tmppts);
2003    return(found);
2004 }
2005 
2006 /*----------------------------------------------------------------------*/
2007 /* Associate polygon with given netlist and add to the object's list	*/
2008 /* of network polygons (i.e., wires).					*/
2009 /*----------------------------------------------------------------------*/
2010 
addpoly(objectptr cschem,polyptr poly,Genericlist * netlist)2011 Genericlist *addpoly(objectptr cschem, polyptr poly, Genericlist *netlist)
2012 {
2013    PolylistPtr newpoly;
2014    objectptr pschem;
2015    /* buslist *sbus; (jdk) */
2016    /* int lbus, sub_bus; (jdk) */
2017 
2018    /* Netlist is in the master schematic */
2019    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2020 
2021    /* If this polygon is already in the list, then add an extra subnet	*/
2022    /* if necessary.							*/
2023 
2024    for (newpoly = pschem->polygons; newpoly != NULL; newpoly = newpoly->next) {
2025       if (newpoly->poly == poly) {
2026 	 if (!match_buses((Genericlist *)newpoly, netlist, MATCH_EXACT)) {
2027 	    Fprintf(stderr, "addpoly:  Error in bus assignment\n");
2028 	    return NULL;
2029 	 }
2030 	 return (Genericlist *)newpoly;
2031       }
2032    }
2033 
2034    /* Create a new entry and link to polygon list of this object */
2035 
2036    newpoly = (PolylistPtr) malloc(sizeof(Polylist));
2037    newpoly->cschem = cschem;
2038    newpoly->poly = poly;
2039    newpoly->subnets = 0;
2040    copy_bus((Genericlist *)newpoly, netlist);
2041    newpoly->next = pschem->polygons;
2042    pschem->polygons = newpoly;
2043 
2044    return (Genericlist *)newpoly;
2045 }
2046 
2047 /*-------------------------------------------------------------------------*/
2048 
zsign(long a,long b)2049 long zsign(long a, long b)
2050 {
2051    if (a > b) return 1;
2052    else if (a < b) return -1;
2053    else return 0;
2054 }
2055 
2056 /*----------------------------------------------------------------------*/
2057 /* Promote a single net to a bus.  The bus size will be equal to the	*/
2058 /* value "subnets".							*/
2059 /*----------------------------------------------------------------------*/
2060 
promote_net(objectptr cschem,Genericlist * netfrom,int subnets)2061 void promote_net(objectptr cschem, Genericlist *netfrom, int subnets)
2062 {
2063    Genericlist *netref = NULL;
2064    CalllistPtr calls;
2065    PortlistPtr ports;
2066    PolylistPtr plist;
2067    LabellistPtr llist;
2068    int netid, firstid, lbus; /* curid, (jdk) */
2069    buslist *sbus;
2070    Boolean foundlabel;
2071 
2072    /* If no promotion is required, don't do anything */
2073    if (netfrom->subnets == subnets) return;
2074 
2075    /* It "netfrom" is already a bus, but of different size than	*/
2076    /* subnets, then it cannot be changed.			*/
2077 
2078    if (netfrom->subnets != 0) {
2079       Fprintf(stderr, "Attempt to change the size of a bus!\n");
2080       return;
2081    }
2082 
2083    netid = netfrom->net.id;
2084 
2085    /* If "subnets" is 1, then "netfrom" can be promoted regardless.	*/
2086    /* Otherwise, if the "netfrom" net is used in any calls, then it	*/
2087    /* cannot be promoted.						*/
2088 
2089    if (subnets > 1) {
2090       for (calls = cschem->calls; calls != NULL; calls = calls->next)
2091          for (ports = calls->ports; ports != NULL; ports = ports->next)
2092             if (ports->netid == netid) {
2093 	       Fprintf(stderr, "Cannot promote net to bus: Net already connected"
2094 			" to single-wire port\n");
2095 	       return;
2096 	    }
2097       firstid = netmax(cschem) + 1;
2098    }
2099 
2100    for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2101       if ((plist->subnets == 0) && (plist->net.id == netid)) {
2102 	 plist->subnets = subnets;
2103          plist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2104 	 for (lbus = 0; lbus < subnets; lbus++) {
2105 	    sbus = plist->net.list + lbus;
2106 	    sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2107 	    sbus->subnetid = lbus; 	/* By default, number from zero */
2108 	 }
2109 	 netref = (Genericlist *)plist;
2110       }
2111 
2112    /* It's possible for a label without bus notation to be attached	*/
2113    /* to this net.							*/
2114 
2115    foundlabel = FALSE;
2116    for (llist = cschem->labels; llist != NULL; llist = llist->next)
2117       if ((llist->subnets == 0) && (llist->net.id == netid)) {
2118 	 llist->subnets = subnets;
2119          llist->net.list = (buslist *)malloc(subnets * sizeof(buslist));
2120 	 for (lbus = 0; lbus < subnets; lbus++) {
2121 	    sbus = llist->net.list + lbus;
2122 	    sbus->netid = (lbus == 0) ? netid : firstid + lbus;
2123 	    sbus->subnetid = lbus; 	/* By default, number from zero */
2124 	 }
2125 	 netref = (Genericlist *)llist;
2126 	 foundlabel = TRUE;
2127       }
2128 
2129    /* We need to create a temp label associated with this net to 	*/
2130    /* encompass the promoted bus size.  If this bus is later attached	*/
2131    /* to a known bus, that bus name will be canonical.  If no bus name	*/
2132    /* is ever assigned to the bus, this temp one will be used.		*/
2133 
2134    if (!foundlabel) {
2135       XPoint *pinpos;
2136       pinpos = NetToPosition(netid, cschem);
2137       new_tmp_pin(cschem, pinpos, NULL, "int", netref);
2138    }
2139 }
2140 
2141 /*----------------------------------------------------------------------*/
2142 /* Change any part of the netlist "testlist" that matches the net IDs	*/
2143 /* of "orignet" to the net IDs of "newnet".   It is assumed that the	*/
2144 /* lengths and sub-bus numbers of "orignet" and "newnet" match.		*/
2145 /*----------------------------------------------------------------------*/
2146 
mergenetlist(objectptr cschem,Genericlist * testlist,Genericlist * orignet,Genericlist * newnet)2147 Boolean mergenetlist(objectptr cschem, Genericlist *testlist,
2148 		Genericlist *orignet, Genericlist *newnet)
2149 {
2150    int obus, onetid, osub, nsub, nnetid, tbus;
2151    buslist *sbus;
2152    Boolean rval = FALSE;
2153 
2154    for (obus = 0;;) {
2155       if (orignet->subnets == 0) {
2156 	 onetid = orignet->net.id;
2157 	 osub = -1;
2158       }
2159       else {
2160 	 sbus = orignet->net.list + obus;
2161 	 onetid = sbus->netid;
2162 	 osub = sbus->subnetid;
2163       }
2164 
2165       if (newnet->subnets == 0) {
2166 	 nnetid = newnet->net.id;
2167 	 nsub = -1;
2168       }
2169       else {
2170 	 sbus = newnet->net.list + obus;
2171 	 nnetid = sbus->netid;
2172 	 nsub = sbus->subnetid;
2173       }
2174 
2175       if (testlist->subnets == 0) {
2176 	 if (testlist->net.id == onetid) {
2177 	    rval = TRUE;
2178 	    if (orignet->subnets == 0) {
2179 	       testlist->net.id = nnetid;
2180 	       return TRUE;
2181 	    }
2182 	    else {
2183 	       /* Promote testlist to a bus subnet of size 1 */
2184 	       testlist->subnets = 1;
2185 	       testlist->net.list = (buslist *)malloc(sizeof(buslist));
2186 	       sbus = testlist->net.list;
2187 	       sbus->netid = nnetid;
2188 	       sbus->subnetid = nsub;
2189 	       return rval;
2190 	    }
2191 	 }
2192       }
2193 
2194       for (tbus = 0; tbus < testlist->subnets; tbus++) {
2195 	 /* If the sub-bus numbers match, then change the net	*/
2196 	 /* ID to the new net number.  If the sub-bus is	*/
2197 	 /* unnamed (has no associated bus-notation label),	*/
2198 	 /* then it can take on the net and subnet numbers.	*/
2199 
2200 	 sbus = testlist->net.list + tbus;
2201 
2202 	 if (sbus->netid == onetid) {
2203 	    if (sbus->subnetid == osub) {
2204 	       sbus->netid = nnetid;
2205 	       sbus->subnetid = nsub;
2206 	       rval = TRUE;
2207 	    }
2208 	    else {
2209 	       labelptr blab = NetToLabel(nnetid, cschem);
2210 	       if (blab == NULL) {
2211 		  Fprintf(stderr, "Warning: isolated subnet?\n");
2212 		  sbus->netid = nnetid;
2213 		  /* Keep subnetid---but, does newnet need to be promoted? */
2214 	          return TRUE;
2215 	       }
2216 	       else if (blab->string->type != FONT_NAME) {
2217 	          sbus->netid = nnetid;
2218 	          sbus->subnetid = nsub;
2219 	          rval = TRUE;
2220 		  Fprintf(stderr, "Warning: Unexpected subnet value in mergenetlist!\n");
2221 	       }
2222 	    }
2223 	 }
2224       }
2225       if (++obus >= orignet->subnets) break;
2226    }
2227    return rval;
2228 }
2229 
2230 /*----------------------------------------------------------------------*/
2231 /* Combine two networks in an object's linked-list Netlist 		*/
2232 /* Parameters: cschem - pointer to object containing netlist		*/
2233 /*	       orignet - original netlist to be changed			*/
2234 /*	       newnet - new netlist to be changed to			*/
2235 /*									*/
2236 /* Attempts to merge different subnets in a bus are thwarted.		*/
2237 /*----------------------------------------------------------------------*/
2238 
netmerge(objectptr cschem,Genericlist * orignet,Genericlist * newnet)2239 Boolean netmerge(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2240 {
2241    PolylistPtr plist;
2242    LabellistPtr llist;
2243    CalllistPtr calls;
2244    PortlistPtr ports;
2245    Genericlist savenet;
2246    int i, net;
2247    buslist *obus, *nbus;
2248    Boolean rval;
2249 
2250    /* Trivial case; do nothing */
2251    if (match_buses(orignet, newnet, MATCH_EXACT)) return TRUE;
2252 
2253    /* Disallow an attempt to convert a global net to a local net: */
2254    /* The global net ID always dominates!			  */
2255 
2256    if ((orignet->subnets == 0) && (newnet->subnets == 0) &&
2257 	(orignet->net.id < 0) && (newnet->net.id > 0)) {
2258       int globnet = orignet->net.id;
2259       orignet->net.id = newnet->net.id;
2260       newnet->net.id = globnet;
2261    }
2262 
2263    /* Check that the lists of changes are compatible.  It appears to be	*/
2264    /* okay to have non-matching buses (although they should not be	*/
2265    /* merged), as this is a valid style in which buses do not have taps	*/
2266    /* but the sub-buses are explicitly called out with labels.  In such	*/
2267    /* case, the polygons of different sub-buses touch, and this routine	*/
2268    /* rejects them as being incompatible.				*/
2269 
2270    if (!match_buses(orignet, newnet, MATCH_SUBNETS)) {
2271       if (match_buses(orignet, newnet, MATCH_SIZE)) {
2272 	 labelptr nlab;
2273 	 /* If only the subnet numbers don't match up, check if	*/
2274 	 /* "orignet" has a temp label.				*/
2275 	 nbus = orignet->net.list;
2276 	 if ((nlab = NetToLabel(nbus->netid, cschem)) != NULL) {
2277 	    if (nlab->string->type != FONT_NAME)
2278 	       goto can_merge;
2279 	 }
2280       }
2281       else
2282          Fprintf(stderr, "netmerge warning: non-matching bus subnets touching.\n");
2283       return FALSE;
2284    }
2285 
2286 can_merge:
2287 
2288    /* If orignet is a bus size 1 and newnet is a wire, then promote	*/
2289    /* newnet to a bus size 1.						*/
2290 
2291    if (orignet->subnets == 1 && newnet->subnets == 0) {
2292       net = newnet->net.id;
2293       newnet->subnets = 1;
2294       newnet->net.list = (buslist *)malloc(sizeof(buslist));
2295       obus = orignet->net.list;
2296       nbus = newnet->net.list;
2297       nbus->subnetid = obus->subnetid;
2298       nbus->netid = net;
2299    }
2300 
2301    /* Make a copy of the net so we don't overwrite it */
2302    savenet.subnets = 0;
2303    copy_bus(&savenet, orignet);
2304 
2305    rval = FALSE;
2306    for (plist = cschem->polygons; plist != NULL; plist = plist->next)
2307       if (mergenetlist(cschem, (Genericlist *)plist, &savenet, newnet))
2308 	 rval = TRUE;
2309 
2310    for (llist = cschem->labels; llist != NULL; llist = llist->next)
2311       if (mergenetlist(cschem, (Genericlist *)llist, &savenet, newnet)) {
2312 	 int pinnet;
2313 	 char *newtext;
2314 	 rval = TRUE;
2315 
2316 	 /* Because nets that have been merged away may be re-used later, */
2317 	 /* change the name of any temporary labels to the new net number */
2318 
2319 	 if (llist->label->string->type != FONT_NAME) {
2320 	    newtext = llist->label->string->data.string;
2321 	    if (sscanf(newtext + 3, "%d", &pinnet) == 1) {
2322 	       if (pinnet == savenet.net.id) {
2323 		  *(newtext + 3) = '\0';
2324 		  llist->label->string->data.string = textprintnet(newtext,
2325 				NULL, newnet);
2326 		  free(newtext);
2327 	       }
2328 	    }
2329 	 }
2330       }
2331 
2332    if (rval) {
2333 
2334       /* Reflect the net change in the object's call list, if it has one. */
2335 
2336       for (calls = cschem->calls; calls != NULL; calls = calls->next) {
2337          for (ports = calls->ports; ports != NULL; ports = ports->next) {
2338             if (newnet->subnets == 0) {
2339                if (ports->netid == savenet.net.id)
2340 	          ports->netid = newnet->net.id;
2341 	    }
2342 	    else {
2343                for (i = 0; i < newnet->subnets; i++) {
2344 	          obus = savenet.net.list + i;
2345 	          nbus = newnet->net.list + i;
2346                   if (ports->netid == obus->netid)
2347 	             ports->netid = nbus->netid;
2348 	       }
2349 	    }
2350 	 }
2351       }
2352    }
2353 
2354    /* Free the copy of the bus that we made, if we made one */
2355    if (savenet.subnets > 0) free(savenet.net.list);
2356 
2357    return rval;
2358 }
2359 
2360 /*----------------------------------------------------------------------*/
2361 /* Wrapper to netmerge() to make sure change is made both to the symbol	*/
2362 /* and schematic, if both exist.					*/
2363 /*----------------------------------------------------------------------*/
2364 
mergenets(objectptr cschem,Genericlist * orignet,Genericlist * newnet)2365 Boolean mergenets(objectptr cschem, Genericlist *orignet, Genericlist *newnet)
2366 {
2367    Boolean merged;
2368 
2369    if (cschem->symschem != NULL)
2370       merged = netmerge(cschem->symschem, orignet, newnet);
2371    if (netmerge(cschem, orignet, newnet))
2372       merged = TRUE;
2373 
2374    return merged;
2375 }
2376 
2377 /*----------------------------------------------------------------------*/
2378 /* Remove a call to an object instance from the call list of cschem	*/
2379 /*----------------------------------------------------------------------*/
2380 
removecall(objectptr cschem,CalllistPtr dontcallme)2381 void removecall(objectptr cschem, CalllistPtr dontcallme)
2382 {
2383    CalllistPtr lastcall, seeklist;
2384    PortlistPtr ports, savelist;
2385 
2386    /* find the instance call before this one and link it to the one following */
2387 
2388    lastcall = NULL;
2389    for (seeklist = cschem->calls; seeklist != NULL; seeklist = seeklist->next) {
2390       if (seeklist == dontcallme)
2391 	 break;
2392       lastcall = seeklist;
2393    }
2394 
2395    if (seeklist == NULL) {
2396       Fprintf(stderr, "Error in removecall():  Call does not exist!\n");
2397       return;
2398    }
2399 
2400    if (lastcall == NULL)
2401       cschem->calls = dontcallme->next;
2402    else
2403       lastcall->next = dontcallme->next;
2404 
2405    ports = dontcallme->ports;
2406    while (ports != NULL) {
2407       savelist = ports;
2408       ports = ports->next;
2409       free (savelist);
2410    }
2411    free(dontcallme);
2412 }
2413 
2414 /*----------------------------------------------------------------------*/
2415 /* Add a pin label to the netlist					*/
2416 /* If cschem == NULL, add the pin to the list of global pins.		*/
2417 /* The new label entry in the netlist gets a copy of the netlist	*/
2418 /* "netlist".								*/
2419 /*----------------------------------------------------------------------*/
2420 
addpin(objectptr cschem,objinstptr cinst,labelptr pin,Genericlist * netlist)2421 Genericlist *addpin(objectptr cschem, objinstptr cinst, labelptr pin,
2422 		Genericlist *netlist)
2423 {
2424    LabellistPtr srchlab, newlabel, lastlabel = NULL;
2425    objectptr pschem;
2426    /* buslist *sbus; (jdk) */
2427    /* int lbus, sub_bus; (jdk) */
2428 
2429    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2430 
2431    for (srchlab = pschem->labels; srchlab != NULL; srchlab = srchlab->next) {
2432       if (srchlab->label == pin) {
2433 	 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2434 	    if (srchlab->cinst == cinst) {
2435 	       Fprintf(stderr, "addpin: Error in bus assignment\n");
2436 	       return NULL;
2437 	    }
2438 	 }
2439 	 else if (srchlab->cinst == NULL)
2440 	    return (Genericlist *)srchlab;
2441 	 break;		/* Stop at the first record for this label */
2442       }
2443       lastlabel = srchlab;
2444    }
2445 
2446    /* Create a new entry and link to label list of the object */
2447 
2448    newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2449    newlabel->cschem = cschem;
2450    newlabel->cinst = cinst;
2451    newlabel->label = pin;
2452    newlabel->subnets = 0;
2453    copy_bus((Genericlist *)newlabel, netlist);
2454 
2455    /* Always put the specific (instanced) cases	in front of	*/
2456    /* generic cases for the same label.				*/
2457 
2458    /* Generic case---make it the last record for this label */
2459    if ((cinst == NULL) && (lastlabel != NULL)) {
2460       while ((srchlab != NULL) && (srchlab->label == pin)) {
2461 	 lastlabel = srchlab;
2462 	 srchlab = srchlab->next;
2463       }
2464    }
2465    if (lastlabel != NULL) {
2466       newlabel->next = srchlab;
2467       lastlabel->next = newlabel;
2468    }
2469    else {
2470       newlabel->next = pschem->labels;
2471       pschem->labels = newlabel;
2472    }
2473    return (Genericlist *)newlabel;
2474 }
2475 
2476 /*----------------------------------------------------------------------*/
2477 /* Add a pin label to the list of global net names.			*/
2478 /* The new label entry in the netlist gets a copy of the netlist	*/
2479 /* "netlist" and a copy of label "pin" containing the *instance* value	*/
2480 /* of the text.								*/
2481 /*----------------------------------------------------------------------*/
2482 
addglobalpin(objectptr cschem,objinstptr cinst,labelptr pin,Genericlist * netlist)2483 Genericlist *addglobalpin(objectptr cschem, objinstptr cinst, labelptr pin,
2484 	Genericlist *netlist)
2485 {
2486    LabellistPtr srchlab, newlabel, lastlabel = NULL;
2487 
2488    if (cinst == NULL) {
2489       Fprintf(stderr, "Error:  Global pin does not have an associated instance!\n");
2490       return NULL;
2491    }
2492 
2493    for (srchlab = global_labels; srchlab != NULL; srchlab = srchlab->next) {
2494       if (srchlab->label == pin) {
2495 	 if (!match_buses(netlist, (Genericlist *)srchlab, MATCH_EXACT)) {
2496 	    if (srchlab->cinst == cinst) {
2497 	       Fprintf(stderr, "addglobalpin: Error in bus assignment\n");
2498 	       return NULL;
2499 	    }
2500 	 }
2501 	 else if (srchlab->cinst == NULL)
2502 	    return (Genericlist *)srchlab;
2503 	 break;		/* Stop at the first record for this label */
2504       }
2505       lastlabel = srchlab;
2506    }
2507 
2508    /* Create a new entry and link to label list of the object */
2509 
2510    newlabel = (LabellistPtr) malloc(sizeof(Labellist));
2511    newlabel->cschem = cschem;
2512    newlabel->cinst = cinst;
2513    newlabel->label = new_global_pin(pin, cinst);
2514    newlabel->subnets = 0;
2515    copy_bus((Genericlist *)newlabel, netlist);
2516 
2517    if (lastlabel != NULL) {
2518       newlabel->next = srchlab;
2519       lastlabel->next = newlabel;
2520    }
2521    else {
2522       newlabel->next = global_labels;
2523       global_labels = newlabel;
2524    }
2525    return (Genericlist *)newlabel;
2526 }
2527 
2528 /*----------------------------------------------------------------------*/
2529 /* Allocate memory for the new call list element             		*/
2530 /* Define the values for the new call list element		   	*/
2531 /* Insert new call into call list					*/
2532 /* The new call is at the beginning of the list.			*/
2533 /*----------------------------------------------------------------------*/
2534 
addcall(objectptr cschem,objectptr callobj,objinstptr callinst)2535 void addcall(objectptr cschem, objectptr callobj, objinstptr callinst)
2536 {
2537    CalllistPtr newcall;
2538    objectptr pschem;
2539 
2540    /* Netlist is on the master schematic */
2541    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2542 
2543    newcall = (CalllistPtr) malloc(sizeof(Calllist));
2544    newcall->cschem = cschem;
2545    newcall->callobj = callobj;
2546    newcall->callinst = callinst;
2547    newcall->devindex = -1;
2548    newcall->devname = NULL;
2549    newcall->ports = NULL;
2550    newcall->next = pschem->calls;
2551    pschem->calls = newcall;
2552 }
2553 
2554 /*----------------------------------------------------------------------*/
2555 /* Add a port to the object cschem which connects net "netto" to	*/
2556 /* the calling object. 	One port is created for each net ID in "netto"	*/
2557 /* (which may be a bus).  The port contains the net ID in the called	*/
2558 /* object.  The port may already exist, in which case this routine does	*/
2559 /* nothing.								*/
2560 /*----------------------------------------------------------------------*/
2561 
addport(objectptr cschem,Genericlist * netto)2562 void addport(objectptr cschem, Genericlist *netto)
2563 {
2564    PortlistPtr newport, seekport;
2565    int portid = 0, netid, lbus;
2566    buslist *sbus;
2567    Boolean duplicate;
2568 
2569    for (lbus = 0;;) {
2570 
2571       if (netto->subnets == 0)
2572 	 netid = netto->net.id;
2573       else {
2574 	 sbus = netto->net.list + lbus;
2575 	 netid = sbus->netid;
2576       }
2577 
2578       /* If a port already exists for this net, don't add another one! */
2579 
2580       duplicate = FALSE;
2581       for (seekport = cschem->ports; seekport != NULL; seekport = seekport->next) {
2582 	 if (seekport->netid != netid) {
2583 	    if (seekport->portid > portid)
2584 	       portid = seekport->portid;
2585 	 }
2586 	 else
2587 	    duplicate = TRUE;
2588       }
2589 
2590       if (!duplicate) {
2591 	 portid++;
2592 
2593 	 newport = (PortlistPtr)malloc(sizeof(Portlist));
2594 	 newport->netid = netid;
2595 	 newport->portid = portid;
2596 
2597 	 if (cschem->ports != NULL)
2598 	    newport->next = cschem->ports;
2599 	 else
2600 	    newport->next = NULL;
2601 
2602 	 cschem->ports = newport;
2603       }
2604       if (++lbus >= netto->subnets) break;
2605    }
2606 }
2607 
2608 /*----------------------------------------------------------------------*/
2609 /* Add a specific port connection from object cschem into the instance	*/
2610 /* cinst.  This equates a net number in the calling object cschem 	*/
2611 /* (netfrom) to a net number in the object instance being called	*/
2612 /* (netto).								*/
2613 /*									*/
2614 /* If we attempt to connect a bus of one size to a port of a different	*/
2615 /* size, return FALSE.  Otherwise, add the call and return TRUE.	*/
2616 /*----------------------------------------------------------------------*/
2617 
addportcall(objectptr cschem,Genericlist * netfrom,Genericlist * netto)2618 Boolean addportcall(objectptr cschem, Genericlist *netfrom, Genericlist *netto)
2619 {
2620    CalllistPtr ccall;
2621    PortlistPtr seekport, sp, newport;
2622    objectptr instobj;
2623    objinstptr cinst;
2624    int lbus, netid_from, netid_to;
2625    buslist *sbus, *tbus;
2626    Boolean duplicate;
2627 
2628    /* The call that we need to add a port to is the first on	*/
2629    /* the list for cschem, as created by addcall().		*/
2630    ccall = cschem->calls;
2631    instobj = ccall->callobj;
2632    cinst = ccall->callinst;
2633 
2634    if (netfrom->subnets != netto->subnets) {
2635       if (netfrom->subnets == 0) {
2636 	 /* It is possible that "netfrom" is an unlabeled polygon that	*/
2637 	 /* is implicitly declared a bus by its connection to this	*/
2638 	 /* port.  If so, we promote "netfrom" to a bus but set the	*/
2639 	 /* subnet entries to negative values, since we don't know what	*/
2640 	 /* they are yet.						*/
2641 	 promote_net(cschem, netfrom, netto->subnets);
2642       }
2643 
2644       /* Only other allowable condition is a bus size 1 connecting into	*/
2645       /* a single-wire port.  However, one should consider promotion of	*/
2646       /* the pin in the target object to a bus on a per-instance basis.	*/
2647       /* This has not yet been done. . .				*/
2648 
2649       else if ((netfrom->subnets != 1) || (netto->subnets != 0)) {
2650 	 /* Let the caller report error information, because it knows more */
2651 	 return FALSE;
2652       }
2653    }
2654 
2655    for (lbus = 0;;) {
2656       buslist bsingf, bsingo;
2657       Genericlist subnet_from, subnet_other;
2658 
2659       if (netfrom->subnets == 0) {
2660 	 netid_from = netfrom->net.id;
2661 
2662 	 subnet_from.subnets = 0;
2663 	 subnet_from.net.id = netid_from;
2664 
2665 	 subnet_other.subnets = 0;
2666       }
2667       else {
2668 	 sbus = netfrom->net.list + lbus;
2669 	 netid_from = sbus->netid;
2670 
2671 	 subnet_from.subnets = 1;
2672 	 subnet_from.net.list = &bsingf;
2673 	 bsingf.netid = netid_from;
2674 	 bsingf.subnetid = sbus->subnetid;
2675 
2676 	 bsingo.subnetid = lbus;
2677 	 subnet_other.subnets = 1;
2678 	 subnet_other.net.list = &bsingo;
2679       }
2680       if (netto->subnets == 0) {
2681 	 netid_to = netto->net.id;
2682       }
2683       else {
2684 	 tbus = netto->net.list + lbus;
2685 	 netid_to = tbus->netid;
2686       }
2687 
2688       /* Check the ports of the instance's object for the one matching	*/
2689       /* the "netto" net ID.						*/
2690 
2691       duplicate = FALSE;
2692       for (seekport = instobj->ports; seekport != NULL;
2693 		seekport = seekport->next) {
2694          if (seekport->netid == netid_to) {
2695 
2696 	    /* If there is already an entry for this port, then	*/
2697 	    /* we may need to merge nets in cschem.		*/
2698 
2699 	    for (sp = ccall->ports; sp != NULL; sp = sp->next)
2700 	       if (sp->portid == seekport->portid) {
2701 	          if (sp->netid != netid_from) {
2702 		     if (netfrom->subnets == 0)
2703 		        subnet_other.net.id = sp->netid;
2704 		     else {
2705 			bsingo.netid = sp->netid;
2706 		        bsingo.subnetid = getsubnet(bsingo.netid, cschem);
2707 		     }
2708 		     if (!mergenets(cschem, &subnet_other, &subnet_from)) {
2709 			/* Upon failure, see if we can merge the other	*/
2710 			/* direction.					*/
2711 			if (!mergenets(cschem, &subnet_from, &subnet_other))
2712 			   continue;
2713 			else {
2714 			   if (subnet_from.subnets == 0)
2715 			      subnet_from.net.id = subnet_other.net.id;
2716 			   else {
2717 			      bsingf.netid = bsingo.netid;
2718 			      bsingf.subnetid = bsingo.subnetid;
2719 			   }
2720 			}
2721 		     }
2722 	          }
2723 		  duplicate = TRUE;
2724 	       }
2725 
2726 	    if (!duplicate) {
2727 	       newport = (PortlistPtr)malloc(sizeof(Portlist));
2728 	       newport->netid = netid_from;
2729 	       newport->portid = seekport->portid;
2730 	       newport->next = ccall->ports;
2731 	       ccall->ports = newport;
2732 	    }
2733 	    break;
2734 	 }
2735       }
2736       if (++lbus >= netfrom->subnets) break;
2737    }
2738    return TRUE;
2739 }
2740 
2741 /*----------------------------------------------------------------------*/
2742 /* Find the net ID corresponding to the indicated port ID in the 	*/
2743 /* indicated object.							*/
2744 /*----------------------------------------------------------------------*/
2745 
porttonet(objectptr cschem,int portno)2746 int porttonet(objectptr cschem, int portno)
2747 {
2748    PortlistPtr plist;
2749 
2750    for (plist = cschem->ports; plist != NULL; plist = plist->next) {
2751       if (plist->portid == portno)
2752 	 return plist->netid;
2753    }
2754    return 0;
2755 }
2756 
2757 /*----------------------------------------------------------------------*/
2758 /* Traverse netlist and return netid of polygon or pin on which the	*/
2759 /*   indicated point is located.					*/
2760 /* If point is not on any polygon or does not match any pin position,	*/
2761 /*   return NULL.							*/
2762 /* Labels which have a non-NULL "cinst" record are instance-dependent	*/
2763 /*   and must match parameter "cinst".					*/
2764 /*									*/
2765 /* This routine checks to see if more than one net crosses the point	*/
2766 /*   of interest, and merges the nets if found.				*/
2767 /* (the method has been removed and must be reinstated, presumably)	*/
2768 /*----------------------------------------------------------------------*/
2769 
pointtonet(objectptr cschem,objinstptr cinst,XPoint * testpoint)2770 Genericlist *pointtonet(objectptr cschem, objinstptr cinst, XPoint *testpoint)
2771 {
2772    XPoint *tpt, *tpt2;
2773    PolylistPtr ppoly;
2774    LabellistPtr plab;
2775    Genericlist *preturn;
2776    objectptr pschem;	/* primary schematic */
2777 
2778    /* cschem is the object containing the point.  However, if the object */
2779    /* is a secondary schematic, the netlist is located in the master.	 */
2780 
2781    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
2782 
2783    for (plab = pschem->labels; plab != NULL; plab = plab->next) {
2784       if (plab->cschem != cschem) continue;
2785       else if ((plab->cinst != NULL) && (plab->cinst != cinst)) continue;
2786       tpt = &(plab->label->position);
2787       if (proximity(tpt, testpoint))
2788 	 return (Genericlist *)plab;
2789 
2790       /* if we've encountered a unique instance, then continue past	*/
2791       /* all other instances using this label.				*/
2792 
2793       if (plab->cinst != NULL)
2794 	 while (plab->next && (plab->next->label == plab->label))
2795 	    plab = plab->next;
2796    }
2797 
2798    /* Check against polygons.  We use this part of the routine to see	*/
2799    /* if there are crossing wires on top of a port.  If so, they are	*/
2800    /* merged together.							*/
2801 
2802    preturn = (Genericlist *)NULL;
2803    for (ppoly = pschem->polygons; ppoly != NULL; ppoly = ppoly->next) {
2804       if (ppoly->cschem != cschem) continue;
2805       for (tpt = ppoly->poly->points; tpt < ppoly->poly->points
2806 		+ EndPoint(ppoly->poly->number); tpt++) {
2807 	 tpt2 = tpt + NextPoint(ppoly->poly->number);
2808 
2809 	 if (onsegment(tpt, tpt2, testpoint)) {
2810 	    if (preturn == (Genericlist *)NULL)
2811 	       preturn = (Genericlist *)ppoly;
2812 	    else
2813 	       mergenets(pschem, (Genericlist *)ppoly, preturn);
2814 	 }
2815       }
2816    }
2817 
2818    return preturn;
2819 }
2820 
2821 /*----------------------------------------------------------------------*/
2822 /* localpin keeps track of temporary pin labels when flattening the	*/
2823 /* hierarchy without destroying the original pin names.			*/
2824 /*----------------------------------------------------------------------*/
2825 
makelocalpins(objectptr cschem,CalllistPtr clist,char * prefix)2826 void makelocalpins(objectptr cschem, CalllistPtr clist, char *prefix)
2827 {
2828    NetnamePtr netnames;
2829    PortlistPtr ports, plist;
2830    stringpart *locpin;
2831    int locnet, callnet;
2832    objectptr callobj = clist->callobj;
2833 
2834    /* Copy all net names which are passed from above through ports */
2835 
2836    for (ports = clist->ports; ports != NULL; ports = ports->next) {
2837       callnet = ports->netid;
2838       for (plist = callobj->ports; plist != NULL; plist = plist->next) {
2839 	 if (plist->portid == ports->portid) {
2840 	    locnet = plist->netid;
2841             locpin = nettopin(callnet, cschem, prefix);
2842 	    break;
2843 	 }
2844       }
2845 
2846       for (netnames = callobj->netnames; netnames != NULL; netnames = netnames->next)
2847 	 if (netnames->netid == locnet)
2848 	    break;
2849 
2850       if (netnames == NULL) {
2851          netnames = (NetnamePtr)malloc(sizeof(Netname));
2852          netnames->netid = locnet;
2853          netnames->localpin = stringcopy(locpin);
2854          netnames->next = callobj->netnames;
2855          callobj->netnames = netnames;
2856       }
2857    }
2858 }
2859 
2860 /*----------------------------------------------------------------------*/
2861 /* Find the first point associated with the net "netid" in the object	*/
2862 /* cschem.								*/
2863 /*----------------------------------------------------------------------*/
2864 
NetToPosition(int netid,objectptr cschem)2865 XPoint *NetToPosition(int netid, objectptr cschem)
2866 {
2867    PolylistPtr  plist;
2868    LabellistPtr llist;
2869    buslist *sbus;
2870    int lbus, locnetid; /* sub_bus, (jdk) */
2871 
2872    plist = cschem->polygons;
2873    for (; plist != NULL; plist = plist->next) {
2874       for (lbus = 0;;) {
2875          if (plist->subnets == 0) {
2876 	    locnetid = plist->net.id;
2877          }
2878          else {
2879 	    sbus = plist->net.list + lbus;
2880 	    locnetid = sbus->netid;
2881 	 }
2882 	 if (locnetid == netid) {
2883 	    return plist->poly->points;
2884 	 }
2885 	 if (++lbus >= plist->subnets) break;
2886       }
2887    }
2888 
2889    llist = (netid < 0) ? global_labels : cschem->labels;
2890    for (; llist != NULL; llist = llist->next) {
2891       for (lbus = 0;;) {
2892          if (llist->subnets == 0) {
2893 	    locnetid = llist->net.id;
2894          }
2895          else {
2896 	    sbus = llist->net.list + lbus;
2897 	    locnetid = sbus->netid;
2898 	 }
2899 	 if (locnetid == netid) {
2900 	    return (&(llist->label->position));
2901 	 }
2902 	 if (++lbus >= llist->subnets) break;
2903       }
2904    }
2905    return NULL;
2906 }
2907 
2908 /*----------------------------------------------------------------------*/
2909 /* Find a label element for the given net number.  In a symbol, this	*/
2910 /* will be the label representing the pin (or the first one found, if	*/
2911 /* the pin has multiple labels).  If no label is found, return NULL.	*/
2912 /* Preferably choose a non-temporary label, if one exists		*/
2913 /*----------------------------------------------------------------------*/
2914 
NetToLabel(int netid,objectptr cschem)2915 labelptr NetToLabel(int netid, objectptr cschem)
2916 {
2917    LabellistPtr llist;
2918    labelptr standby = NULL;
2919    buslist *sbus;
2920    int lbus, locnetid;
2921 
2922    llist = (netid < 0) ? global_labels : cschem->labels;
2923 
2924    for (; llist != NULL; llist = llist->next) {
2925       for (lbus = 0;;) {
2926          if (llist->subnets == 0) {
2927 	    locnetid = llist->net.id;
2928          }
2929          else {
2930 	    sbus = llist->net.list + lbus;
2931 	    locnetid = sbus->netid;
2932 	 }
2933 	 if (locnetid == netid) {
2934 	    if (llist->label->string->type == FONT_NAME)
2935 	       return llist->label;
2936 	    else if (standby == NULL)
2937 	       standby = llist->label;
2938 	 }
2939 	 if (++lbus >= llist->subnets) break;
2940       }
2941    }
2942    return standby;
2943 }
2944 
2945 
2946 /*----------------------------------------------------------------------*/
2947 /* Find the subnet number of the given net.  This routine should be	*/
2948 /* used only as a last resort in case the subnet number is not		*/
2949 /* available;  it has to look through the polygon and label lists in	*/
2950 /* detail to find the specified net ID.					*/
2951 /*----------------------------------------------------------------------*/
2952 
getsubnet(int netid,objectptr cschem)2953 int getsubnet(int netid, objectptr cschem)
2954 {
2955    PolylistPtr  plist;
2956    LabellistPtr llist;
2957    buslist *sbus;
2958    int lbus, sub_bus, locnetid;
2959 
2960    plist = cschem->polygons;
2961    for (; plist != NULL; plist = plist->next) {
2962       for (lbus = 0;;) {
2963          if (plist->subnets == 0) {
2964 	    locnetid = plist->net.id;
2965 	    sub_bus = -1;
2966          }
2967          else {
2968 	    sbus = plist->net.list + lbus;
2969 	    locnetid = sbus->netid;
2970 	    sub_bus = sbus->subnetid;
2971 	 }
2972 	 if (locnetid == netid) {
2973 	    return sub_bus;
2974 	 }
2975 	 if (++lbus >= plist->subnets) break;
2976       }
2977    }
2978 
2979    llist = (netid < 0) ? global_labels : cschem->labels;
2980    for (; llist != NULL; llist = llist->next) {
2981       for (lbus = 0;;) {
2982          if (llist->subnets == 0) {
2983 	    locnetid = llist->net.id;
2984 	    sub_bus = -1;
2985          }
2986          else {
2987 	    sbus = llist->net.list + lbus;
2988 	    locnetid = sbus->netid;
2989 	    sub_bus = sbus->subnetid;
2990 	 }
2991 	 if (locnetid == netid) {
2992 	    return sub_bus;
2993 	 }
2994 	 if (++lbus >= llist->subnets) break;
2995       }
2996    }
2997    return -1;
2998 }
2999 
3000 /*----------------------------------------------------------------------*/
3001 /* Find a pin name for the given net number				*/
3002 /* Either take the last pin name with the net, or generate a new pin,	*/
3003 /* assigning it a net number for a string.				*/
3004 /* prefix = NULL indicates spice netlist, but is also used for PCB-type	*/
3005 /* netlists because the calling hierarchy is generated elsewhere.	*/
3006 /*----------------------------------------------------------------------*/
3007 
nettopin(int netid,objectptr cschem,char * prefix)3008 stringpart *nettopin(int netid, objectptr cschem, char *prefix)
3009 {
3010    NetnamePtr netname;
3011    labelptr pinlab;
3012    LabellistPtr netlabel;
3013    char *newtext, *snew = NULL;
3014    XPoint *pinpos;
3015    int locnetid; /* lbus, (jdk) */
3016    /* buslist *sbus; (jdk) */
3017    Genericlist newnet;
3018    static stringpart *newstring = NULL;
3019 
3020    /* prefix is NULL for hierarchical (spice) netlists and for	*/
3021    /* internal netlist manipulation.				*/
3022 
3023    if (prefix == NULL) {
3024       pinlab = NetToLabel(netid, cschem);
3025       if (pinlab != NULL) {
3026 
3027 	 /* If this is a "temp label", regenerate the name according to	*/
3028 	 /* the actual net number, if it does not match.  This prevents	*/
3029 	 /* unrelated temp labels from getting the same name.		*/
3030 
3031 	 if (pinlab->string->type != FONT_NAME) {
3032 	    if (sscanf(pinlab->string->data.string + 3, "%d", &locnetid) == 1) {
3033 	       if (locnetid != netid) {
3034 		  newtext = pinlab->string->data.string;
3035 		  newtext[3] = '\0';
3036 		  newnet.subnets = 0;
3037 		  newnet.net.id = netid;
3038 		  pinlab->string->data.string = textprintnet(newtext, NULL, &newnet);
3039 		  free(newtext);
3040 	       }
3041 	    }
3042 	 }
3043 	 return pinlab->string;
3044       }
3045       else {
3046 	 /* If there's no label associated with this network, make one  */
3047 	 /* called "intn" where n is the net number (netid).  This is a	*/
3048 	 /* temp label (denoted by lack of a font specifier).		*/
3049 
3050 	 newnet.subnets = 0;
3051 	 newnet.net.id = netid;
3052 	 pinpos = NetToPosition(netid, cschem);
3053 	 netlabel = (LabellistPtr)new_tmp_pin(cschem, pinpos, NULL, "int", &newnet);
3054 	 return (netlabel) ? netlabel->label->string : NULL;
3055       }
3056    }
3057 
3058    /* Flattened (e.g., sim) netlists */
3059 
3060    for (netname = cschem->netnames; netname != NULL; netname = netname->next) {
3061       if (netname->netid == netid) {
3062 	 if (netname->localpin != NULL)
3063 	    return netname->localpin;
3064 	 break;
3065       }
3066    }
3067 
3068    /* Generate the string for the local instantiation of this pin	*/
3069    /* If this node does not have a pin, create one using the current	*/
3070    /* hierarchy prefix as the string.					*/
3071 
3072    pinlab = NetToLabel(netid, cschem);
3073    if (pinlab != NULL) {
3074       stringpart *tmpstring = pinlab->string;
3075       snew = textprint(tmpstring, NULL);
3076    }
3077    else {
3078       snew = (char *)malloc(12);
3079       sprintf(snew, "int%d", netid);
3080    }
3081 
3082    if (netid < 0) {
3083       newtext = snew;
3084    }
3085    else {
3086       newtext = (char *)malloc(1 + strlen(snew) + strlen(prefix));
3087       sprintf(newtext, "%s%s", prefix, snew);
3088       free(snew);
3089    }
3090 
3091    /* "newstring" is allocated only once and should not be free'd	*/
3092    /* by the calling routine.						*/
3093 
3094    if (newstring == NULL) {
3095       newstring = (stringpart *)malloc(sizeof(stringpart));
3096       newstring->nextpart = NULL;
3097       newstring->type = TEXT_STRING;
3098    }
3099    else {
3100       free(newstring->data.string);
3101    }
3102    newstring->data.string = newtext;
3103    return newstring;
3104 }
3105 
3106 /*----------------------------------------------------------------------*/
3107 /* Find the net which connects to the given pin label			*/
3108 /* Return NULL (no net) if no match was found. 				*/
3109 /*----------------------------------------------------------------------*/
3110 
pintonet(objectptr cschem,objinstptr cinst,labelptr testpin)3111 Genericlist *pintonet(objectptr cschem, objinstptr cinst, labelptr testpin)
3112 {
3113    LabellistPtr seeklabel;
3114    int lbus, matched;
3115    buslist *sbus, *tbus;
3116    Genericlist netlist, *tmplist;
3117 
3118    /* check against local pins, if this pin is declared local */
3119 
3120    seeklabel = (testpin->pin == GLOBAL) ? global_labels : cschem->labels;
3121 
3122    netlist.subnets = 0;
3123    for (; seeklabel != NULL; seeklabel = seeklabel->next)
3124       if (!stringcomprelaxed(seeklabel->label->string, testpin->string, cinst))
3125       {
3126 	 /* Quick simple case:  no bus */
3127 	 if (seeklabel->subnets == 0)
3128 	    return (Genericlist *)seeklabel;
3129 
3130 	 tmplist = break_up_bus(testpin, cinst, (Genericlist *)seeklabel);
3131 
3132 	 /* It is necessary to be able to put together a netlist from	*/
3133 	 /* partial sources none of which may be complete.		*/
3134 
3135 	 if (tmplist != NULL) {
3136 	    if (netlist.subnets == 0) copy_bus(&netlist, tmplist);
3137 	    matched = 0;
3138 	    for (lbus = 0; lbus < tmplist->subnets; lbus++) {
3139 	       sbus = netlist.net.list + lbus;
3140 	       tbus = tmplist->net.list + lbus;
3141 	       if (sbus->netid == 0)
3142 		  sbus->netid = tbus->netid;	/* copy forward */
3143 	       else if (tbus->netid == 0)
3144 		  tbus->netid = sbus->netid;	/* copy back */
3145 	       if (sbus->netid != 0)
3146 		  matched++;
3147 	    }
3148 	    if (matched == netlist.subnets) {
3149 	       free(netlist.net.list);
3150 	       return tmplist;
3151 	    }
3152 	 }
3153       }
3154 
3155    if (netlist.subnets != 0) {
3156       free(netlist.net.list);
3157       return tmplist;		/* Returns list with partial entries */
3158    }
3159    return NULL;			/* No matches found */
3160 }
3161 
3162 #ifdef TCL_WRAPPER
3163 
3164 /*----------------------------------------------------------------------*/
3165 /* Automatic Schematic Generation stuff---				*/
3166 /* Blow away all polygons in the schematic and replace them with a	*/
3167 /* simple rat's-nest (point-to-point).					*/
3168 /*									*/
3169 /* This routine is more of a proof-of-concept than a useful routine,	*/
3170 /* intended to be called only from the Tcl console.  It is intended to	*/
3171 /* reveal most of the issues related to automatic schematic generation,	*/
3172 /* in which the netlist, or a portion of it, can be redrawn as objects	*/
3173 /* are moved around to maintain the relationships present in the 	*/
3174 /* calls structure.							*/
3175 /*----------------------------------------------------------------------*/
3176 
ratsnest(objinstptr thisinst)3177 void ratsnest(objinstptr thisinst)
3178 {
3179    CalllistPtr calls;
3180    PortlistPtr ports;
3181    PolylistPtr plist;
3182    LabellistPtr llist;
3183    objectptr pschem, cschem;
3184    objinstptr cinst;
3185    polyptr ppoly, *newpoly;
3186    buslist *sbus;
3187    int i, netid, points, sub_bus, lbus;
3188    XPoint portpos;
3189    Boolean result;
3190 
3191    cschem = thisinst->thisobject;
3192    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
3193 
3194    /* Go through the netlist and remove all polygons.  Remove	*/
3195    /* the polygons from the object as well as from the netlist.	*/
3196 
3197    for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
3198       ppoly = plist->poly;
3199       ppoly->type += REMOVE_TAG;	/* tag for removal */
3200    }
3201    freepolylist(&pschem->polygons);
3202 
3203    /* for each schematic (PRIMARY or SECONDARY), remove the tagged	*/
3204    /* elements.								*/
3205 
3206    for (i = 0; i < xobjs.pages; i++) {
3207       cinst = xobjs.pagelist[i]->pageinst;
3208       if (cinst && (cinst->thisobject->schemtype == SECONDARY) &&
3209 		(cinst->thisobject->symschem == pschem)) {
3210 	 delete_tagged(cinst);
3211       }
3212       else if (cinst == thisinst)
3213 	 delete_tagged(cinst);
3214    }
3215 
3216    /* Now, for each net ID in the label list, run through the calls and	*/
3217    /* find all points in the calls connecting to that net.  Link	*/
3218    /* them with as many polygons as needed.  These polygons are point-	*/
3219    /* to-point lines, turning the schematic representation into a 	*/
3220    /* "rat's-nest".							*/
3221 
3222    for (llist = pschem->labels; llist != NULL; llist = llist->next) {
3223       for (lbus = 0;;) {
3224 	 if (llist->subnets == 0) {
3225 	    netid = llist->net.id;
3226 	    sub_bus = -1;
3227 	 }
3228 	 else {
3229 	    sbus = llist->net.list + lbus;
3230 	    netid = sbus->netid;
3231 	    sub_bus = sbus->subnetid;
3232 	 }
3233 
3234          points = 0;
3235          for (calls = pschem->calls; calls != NULL; calls = calls->next) {
3236 
3237             /* Determine schematic object from the object called.  Start a */
3238 	    /* new polygon any time we switch schematic pages.		   */
3239 
3240 	    if (calls->cschem != cschem)
3241 	       points = 0;
3242 	    cschem = calls->cschem;
3243 
3244 	    for (ports = calls->ports; ports != NULL; ports = ports->next) {
3245 	       if (ports->netid == netid) {
3246 
3247 	          /* Find the location of this port in the coordinates of cschem */
3248 
3249 	          result = PortToPosition(calls->callinst, ports->portid, &portpos);
3250 	          if (result == True) {
3251 		     points++;
3252 		     if (points == 1) {
3253 		        NEW_POLY(newpoly, cschem);
3254 		        polydefaults(*newpoly, 1, portpos.x, portpos.y);
3255 		        (*newpoly)->style |= UNCLOSED;
3256 		        (*newpoly)->color = RATSNESTCOLOR;
3257 		        addpoly(cschem, *newpoly, (Genericlist *)llist);
3258 		     }
3259 		     else
3260 		        poly_add_point(*newpoly, &portpos);
3261 	          }
3262 	          else {
3263 		    Fprintf(stderr, "Error:  Cannot find pin connection in symbol!\n");
3264 		    /* Deal with error condition? */
3265 	          }
3266 	       }
3267 	    }
3268          }
3269 	 if (++lbus >= llist->subnets) break;
3270       }
3271    }
3272 
3273    /* Now, move all the labels to reconnect to their networks */
3274 
3275    /* (to be done) */
3276 
3277    /* Refresh the screen */
3278    drawarea(NULL, NULL, NULL);
3279 }
3280 
3281 #endif
3282 
3283 /*----------------------------------------------------------------------*/
3284 /* Find a net or netlist in object cschem with the indicated name.  	*/
3285 /*									*/
3286 /* This is the same routine as above, but finds the net with the given	*/
3287 /* name, or all nets which are a subset of the given name, if the name	*/
3288 /* is the name of a bus.  Return NULL if no match was found. 		*/
3289 /*----------------------------------------------------------------------*/
3290 
nametonet(objectptr cschem,objinstptr cinst,char * netname)3291 Genericlist *nametonet(objectptr cschem, objinstptr cinst, char *netname)
3292 {
3293    LabellistPtr seeklabel;
3294    stringpart newstring;
3295 
3296    /* Build a simple xcircuit string from "netname" (nothing malloc'd) */
3297    newstring.type = TEXT_STRING;
3298    newstring.nextpart = NULL;
3299    newstring.data.string = netname;
3300 
3301    /* Check against local networks */
3302 
3303    for (seeklabel = cschem->labels; seeklabel != NULL; seeklabel = seeklabel->next)
3304       if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3305          return (Genericlist *)seeklabel;
3306 
3307    /* Check against global networks */
3308 
3309    for (seeklabel = global_labels; seeklabel != NULL; seeklabel = seeklabel->next)
3310       if (!stringcomprelaxed(seeklabel->label->string, &newstring, cinst))
3311 	 return (Genericlist *)seeklabel;
3312 
3313    return NULL;
3314 }
3315 
3316 /*----------------------------------------------------------------------*/
3317 /* Check if two parts may have the same component index.  This is true	*/
3318 /* for parts with parameterized pins, such as those in the "quadparts"	*/
3319 /* library.  Compare the names of the ports in each instance.  If none	*/
3320 /* match, then these are unique subparts of a single component, and we	*/
3321 /* should return FALSE.  Otherwise, return TRUE.			*/
3322 /*----------------------------------------------------------------------*/
3323 
samepart(CalllistPtr clist1,CalllistPtr clist2)3324 Boolean samepart(CalllistPtr clist1, CalllistPtr clist2)
3325 {
3326    PortlistPtr port;
3327    labelptr plab;
3328    char *pinname1, *pinname2;
3329    Boolean rvalue;
3330 
3331    if (clist1->callobj != clist2->callobj) return FALSE;
3332 
3333    rvalue = FALSE;
3334    for (port = clist1->ports; port != NULL; port = port->next) {
3335       plab = PortToLabel(clist1->callinst, port->portid);
3336       pinname1 = textprint(plab->string, clist1->callinst);
3337       pinname2 = textprint(plab->string, clist2->callinst);
3338       if (!strcmp(pinname1, pinname2)) rvalue = TRUE;
3339       free(pinname1);
3340       free(pinname2);
3341    }
3342    return rvalue;
3343 }
3344 
3345 /*----------------------------------------------------------------------*/
3346 /* Generate an index number for a device in a flat netlist format.	*/
3347 /* We ignore any values given to "index" (this is a to-do item, since	*/
3348 /* many schematics are only a single level of hierarchy, so it makes	*/
3349 /* sense to keep designated indices when possible), but do generate	*/
3350 /* independent index numbering for different device classes.		*/
3351 /*----------------------------------------------------------------------*/
3352 
devflatindex(char * devname)3353 u_int devflatindex(char *devname)
3354 {
3355    flatindex *fp = flatrecord;
3356    while (fp != NULL) {
3357       if (!strcmp(devname, fp->devname)) {
3358 	 return ++fp->index;
3359       }
3360       fp = fp->next;
3361    }
3362    fp = (flatindex *)malloc(sizeof(flatindex));
3363    fp->next = flatrecord;
3364    flatrecord = fp;
3365    fp->index = 1;
3366    fp->devname = devname;
3367    return 1;
3368 }
3369 
3370 /*----------------------------------------------------------------------*/
3371 /* Free the list of per-class flat device indices			*/
3372 /*----------------------------------------------------------------------*/
3373 
freeflatindex()3374 void freeflatindex()
3375 {
3376    flatindex *fpnext, *fp = flatrecord;
3377    while (fp != NULL) {
3378       fpnext = fp->next;
3379       free(fp);
3380       fp = fpnext;
3381    }
3382    flatrecord = NULL;
3383 }
3384 
3385 /*---------------------------------------------------------*/
3386 /* Convert a number (up to 99999) to its base-36 equivalent */
3387 /*---------------------------------------------------------*/
3388 
convert_to_b36(int number)3389 int convert_to_b36(int number)
3390 {
3391    int b36idx, tmpidx;
3392 
3393    tmpidx = number;
3394    b36idx = (tmpidx / 10000) * 1679616;
3395    tmpidx %= 10000;
3396    b36idx += (tmpidx / 1000) * 46656;
3397    tmpidx %= 1000;
3398    b36idx += (tmpidx / 100) * 1296;
3399    tmpidx %= 100;
3400    b36idx += ((tmpidx / 10) * 36) + (tmpidx % 10);
3401 
3402    return b36idx;
3403 }
3404 
3405 /*----------------------------------------------------------------------*/
3406 /* Generate an index number for this device.  Count all devices having	*/
3407 /* the same device name (as printed in the netlist) in the calls of	*/
3408 /* the calling object (cfrom)						*/
3409 /*									*/
3410 /* Update (9/29/04):  Remove any leading whitespace from the device	*/
3411 /* name to prevent treating, for example, "J?" and " J?" as separate	*/
3412 /* device types.							*/
3413 /*									*/
3414 /* Update (7/28/05):  To allow SPICE device indices to include all	*/
3415 /* alphanumerics, we parse in base-36, and count in the subset of	*/
3416 /* base-36 that can be interpreted as decimal (1-9, 36-45, etc.)	*/
3417 /*----------------------------------------------------------------------*/
3418 
devindex(objectptr cfrom,CalllistPtr clist)3419 u_int devindex(objectptr cfrom, CalllistPtr clist)
3420 {
3421    CalllistPtr cptr, listfrom = cfrom->calls;
3422    objectptr devobj = clist->callobj;
3423    u_int *occupied, total, objindex, i, b36idx;
3424    char *devname, *cname;
3425    /* oparamptr ops; (jdk) */
3426 
3427    if (listfrom == NULL) return (u_int)0;
3428    if (clist->devindex >= 0) return clist->devindex;
3429 
3430    devname = (clist->devname == NULL) ? devobj->name : clist->devname;
3431    while (isspace(*devname)) devname++;
3432 
3433    /* Count total number of devices */
3434    for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++);
3435    occupied = (u_int *)malloc(total * sizeof(u_int));
3436    objindex = 1;
3437 
3438    for (cptr = listfrom, total = 0; cptr != NULL; cptr = cptr->next, total++) {
3439       occupied[total] = 0;
3440       if (cptr == clist) continue;
3441       cname = (cptr->devname == NULL) ? cptr->callobj->name : cptr->devname;
3442       while (isspace(*cname)) cname++;
3443       if (!strcmp(cname, devname)) {
3444 	 occupied[total] = cptr->devindex;
3445 	 if (cptr->devindex == objindex) objindex++;
3446       }
3447    }
3448 
3449    b36idx = convert_to_b36(objindex);
3450    for (; objindex <= total; objindex++) {
3451       b36idx = convert_to_b36(objindex);
3452       for (i = 0; i < total; i++)
3453 	 if (occupied[i] == b36idx)
3454 	    break;
3455       if (i == total)
3456 	 break;
3457    }
3458    free(occupied);
3459 
3460    clist->devindex = b36idx;
3461    return objindex;
3462 }
3463 
3464 /*----------------------------------------------------------------------*/
3465 /* Generate and return a list of all info label prefixes in a design	*/
3466 /* This does not depend on the netlist being generated (i.e., does not	*/
3467 /* use CalllistPtr)							*/
3468 /*----------------------------------------------------------------------*/
3469 
genprefixlist(objectptr cschem,slistptr * modelist)3470 void genprefixlist(objectptr cschem, slistptr *modelist)
3471 {
3472    int locpos;
3473    genericptr *pgen;
3474    labelptr plabel;
3475    objinstptr cinst;
3476    stringpart *strptr;
3477    slistptr modeptr;
3478 
3479    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3480       if (IS_LABEL(*pgen)) {
3481 	 plabel = TOLABEL(pgen);
3482          if (plabel->pin == INFO) {
3483 
3484 	    strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3485 	    if (locpos <= 0 || strptr == NULL)
3486 	       continue;  /* null after netlist type designator, don't count */
3487 
3488 	    for (modeptr = *modelist; modeptr; modeptr = modeptr->next)
3489 	       if (!strncmp(modeptr->alias, strptr->data.string, locpos))
3490 		  break;
3491 
3492 	    if (modeptr == NULL) {	/* Mode has not been enumerated */
3493 	       modeptr = (slistptr)malloc(sizeof(stringlist));
3494 	       modeptr->alias = (char *)malloc(locpos + 1);
3495 	       strncpy(modeptr->alias, strptr->data.string, locpos);
3496 	       modeptr->next = *modelist;
3497 	       *modelist = modeptr;
3498  	    }
3499 	 }
3500       }
3501       else if (IS_OBJINST(*pgen)) {
3502 	 cinst = TOOBJINST(pgen);
3503 	 genprefixlist(cinst->thisobject, modelist);
3504 	 if (cinst->thisobject->symschem != NULL)
3505 	    genprefixlist(cinst->thisobject->symschem, modelist);
3506       }
3507    }
3508 }
3509 
3510 /*----------------------------------------------------------------------*/
3511 /* Create and return an ordered list of info labels for the schematic	*/
3512 /* page cschem.  A linked label list is returned, combining info labels	*/
3513 /* from all schematic pages related to cschem (master and slave pages).	*/
3514 /* It is the responsibility of the calling routine to free the linked	*/
3515 /* list.								*/
3516 /*----------------------------------------------------------------------*/
3517 
geninfolist(objectptr cschem,objinstptr cinst,char * mode)3518 LabellistPtr geninfolist(objectptr cschem, objinstptr cinst, char *mode)
3519 {
3520    int locpos, j;
3521    int vmax;
3522    genericptr *pgen;
3523    labelptr plabel;
3524    LabellistPtr newllist, listtop, srchlist;
3525    u_char *strt;
3526    stringpart *strptr;
3527 
3528    listtop = NULL;
3529    vmax = 0;
3530 
3531    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3532       if (IS_LABEL(*pgen)) {
3533 	 plabel = TOLABEL(pgen);
3534          if ((plabel->pin == INFO) && !textncomp(plabel->string, mode, cinst)) {
3535 
3536 	    if (mode[0] == '\0') {
3537 	       strptr = findtextinstring(":", &locpos, plabel->string, cinst);
3538 	       locpos--;
3539 	    }
3540 	    else
3541 	       strptr = findstringpart(strlen(mode), &locpos, plabel->string, cinst);
3542 
3543 	    if (locpos < 0)
3544 	       continue;  /* null after netlist type designator */
3545 
3546 	    strt = strptr->data.string + locpos + 1;
3547 
3548 	    if (*strt != ':') {
3549 	       if (sscanf(strt, "%d", &j) != 1) continue;
3550 
3551 	       /* Consider only positive-valued numbers.  Negative	*/
3552 	       /* indices are handled by "writenet" by merging the	*/
3553 	       /* minus character into the mode name.			*/
3554 
3555 	       if (j < 0) continue;
3556 	       if (j >= vmax) vmax = j + 1;
3557 	    }
3558 	    else
3559 	       j = ++vmax;
3560 
3561 	    newllist = (LabellistPtr)malloc(sizeof(Labellist));
3562 	    newllist->label = plabel;
3563 	    newllist->cschem = cschem;
3564 	    newllist->cinst = cinst;
3565 	    newllist->net.id = j;	/* use this to find the ordering */
3566 	    newllist->subnets = 0;	/* so free() doesn't screw up */
3567 
3568 	    /* Order the linked list */
3569 
3570 	    if ((listtop == NULL) || (listtop->net.id >= j)) {
3571 	       newllist->next = listtop;
3572 	       listtop = newllist;
3573 	    }
3574 	    else {
3575 	       for (srchlist = listtop; srchlist != NULL; srchlist = srchlist->next) {
3576 		  if ((srchlist->next != NULL) && (srchlist->next->net.id >= j)) {
3577 		     newllist->next = srchlist->next;
3578 		     srchlist->next = newllist;
3579 		     break;
3580 		  }
3581 		  else if (srchlist->next == NULL) {
3582 		     srchlist->next = newllist;
3583 		     newllist->next = NULL;
3584 		  }
3585 	       }
3586 	    }
3587          }
3588       }
3589    }
3590    return listtop;
3591 }
3592 
3593 /*----------------------------------------------------------------------*/
3594 /* Trivial default pin name lookup.  Parse an object's pin labels for	*/
3595 /* pin names.  Return the text of the nth pin name, as counted by the	*/
3596 /* position in the object's parts list.  If there are fewer than (n+1)	*/
3597 /* pin labels in the object, return NULL.  Otherwise, return the pin	*/
3598 /* name in a malloc'd character string which the caller must free.	*/
3599 /*----------------------------------------------------------------------*/
3600 
defaultpininfo(objinstptr cinst,int pidx)3601 char *defaultpininfo(objinstptr cinst, int pidx)
3602 {
3603    genericptr *pgen;
3604    labelptr plabel;
3605    objectptr cschem = cinst->thisobject;
3606    int count = 0;
3607    char *stxt;
3608 
3609    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3610       if (IS_LABEL(*pgen)) {
3611 	 plabel = TOLABEL(pgen);
3612          if (plabel->pin == LOCAL) {
3613 	    if (count == pidx) {
3614 	       stxt = textprint(plabel->string, cinst);
3615 	       return stxt;
3616 	    }
3617 	    count++;
3618 	 }
3619       }
3620    }
3621    return NULL;
3622 }
3623 
3624 /*----------------------------------------------------------------------*/
3625 /* Parse an object's info labels for pin names.  This is *not* a	*/
3626 /* netlist function.  The nth pin name is returned, or NULL if not	*/
3627 /* found.  Return value is a malloc'd string which the caller must	*/
3628 /* free.								*/
3629 /*----------------------------------------------------------------------*/
3630 
parsepininfo(objinstptr cinst,char * mode,int pidx)3631 char *parsepininfo(objinstptr cinst, char *mode, int pidx)
3632 {
3633    genericptr *pgen;
3634    labelptr plabel;
3635    u_char *strt, *fnsh;
3636    int slen, i, locpos;
3637    objectptr cschem = cinst->thisobject;
3638    stringpart *strptr;
3639    char *sout;
3640    int count = 0;
3641 
3642    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
3643       if (IS_LABEL(*pgen)) {
3644 	 plabel = TOLABEL(pgen);
3645          if (plabel->pin == INFO) {
3646 	    slen = stringlength(plabel->string, True, cinst);
3647 	    for (i = 1; i < slen; i++) {
3648 	       strptr = findstringpart(i, &locpos, plabel->string, cinst);
3649 	       if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3650 	    }
3651 	    /* Currently we are not checking against "mode". . .*/
3652 	    /* interpret all characters after the colon 	*/
3653 
3654 	    for (++i; i < slen; i++) {
3655 	       strptr = findstringpart(i, &locpos, plabel->string, cinst);
3656 	       if (locpos >= 0) {
3657 
3658 		  /* Do for all text characters */
3659 		  strt = strptr->data.string + locpos;
3660 		  if (*strt == '%') {
3661 		     strt++;
3662 		     i++;
3663 		     if (*strt == 'p') {	/* Pin found! */
3664 			if (count == pidx) {	/* Get pin text */
3665 			   strt++;
3666 			   fnsh = strt + 1;
3667 			   while (*fnsh != ' ' && *fnsh != '\0') fnsh++;
3668 			   sout = malloc(fnsh - strt + 1);
3669 			   strncpy(sout, strt, fnsh - strt);
3670 			   return sout;
3671 			}
3672 			count++;
3673 		     }
3674 		  }
3675 	       }
3676 	    }
3677 	 }
3678       }
3679    }
3680    return NULL;
3681 }
3682 
3683 /*----------------------------------------------------------------------*/
3684 /* Look for information labels in the object parts list.  Parse the	*/
3685 /* information labels and print results to specified output device.	*/
3686 /* (This is not very robust to errors---needs work!)			*/
3687 /* 									*/
3688 /* If "autonumber" is true, then the parsed info label is used to	*/
3689 /* auto-number devices.							*/
3690 /*----------------------------------------------------------------------*/
3691 
parseinfo(objectptr cfrom,objectptr cthis,CalllistPtr clist,char * prefix,char * mode,Boolean autonumber,Boolean no_output)3692 char *parseinfo(objectptr cfrom, objectptr cthis, CalllistPtr clist,
3693 	char *prefix, char *mode, Boolean autonumber, Boolean no_output)
3694 {
3695   /* genericptr *pgen; (jdk) */
3696    stringpart *strptr, *ppin, *optr;
3697    char *snew, *sout = NULL, *pstring;
3698    u_char *strt, *fnsh;
3699    PortlistPtr ports;
3700    oparamptr ops, instops;
3701    int portid, locpos, i, k, subnet, slen; /*  j, (jdk) */
3702    char *key = NULL;
3703    u_int newindex;
3704    Boolean is_flat = False, is_symbol = False, is_iso = False, is_quoted;
3705    Boolean do_update = True, include_once = False;
3706    char *locmode, *b36str, *sptr;
3707    LabellistPtr infolist, infoptr;
3708    FILE *finclude;
3709 
3710    /* For flat netlists, prefix the mode with the word "flat".	*/
3711    /* As of 3/8/07, this includes the ".sim" format, which	*/
3712    /* should be called as mode "flatsim".			*/
3713 
3714    locmode = mode;
3715    if (locmode && (!strncmp(mode, "flat", 4) || !strncmp(mode, "pseu", 4))) {
3716       locmode += 4;
3717       is_flat = True;
3718    }
3719 
3720    /* mode == "" is passed when running resolve_devindex() and indicates */
3721    /* that the routine should be used to assign devindex to calls whose	 */
3722    /* devices have been manually assigned numbers.  The preferred method */
3723    /* is to assign these values through the "index" parameter.  Use of	 */
3724    /* parseinfo() ensures that objects that have no "index" parameter	 */
3725    /* but have valid info-labels for SPICE or PCB output will be	 */
3726    /* assigned the correct device index.				 */
3727    /* Sept. 3, 2006---The use of the prepended "@" character on		 */
3728    /* parameter names means that I can replace the cryptic "idx" with	 */
3729    /* the more obvious "index", which previously conflicted with the	 */
3730    /* PostScript command of the same name.  Parameters "index" and "idx" */
3731    /* are treated interchangeably by the netlister.			 */
3732 
3733    if (locmode[0] == '\0')
3734       do_update = False;
3735 
3736    /* 1st pass: look for valid labels;  see if more than one applies.	*/
3737    /* If so, order them correctly.  Labels may not be numbered in	*/
3738    /* sequence, and may begin with zero.  We allow a lot of flexibility	*/
3739    /* but the general expectation is that sequences start at 0 or 1 and	*/
3740    /* increase by consecutive integers.					*/
3741 
3742    infolist = geninfolist(cthis, clist->callinst, locmode);
3743 
3744    /* Now parse each label in sequence and output the result to */
3745    /* the return string.					*/
3746 
3747    sout = (char *)malloc(1);
3748    *sout = '\0';
3749 
3750    for (infoptr = infolist; infoptr != NULL; infoptr = infoptr->next) {
3751       objectptr pschem, cschem = infoptr->cschem;
3752       labelptr plabel = infoptr->label;
3753       objinstptr cinst = infoptr->cinst;
3754 
3755       /* move to colon character */
3756       slen = stringlength(plabel->string, True, cinst);
3757       for (i = 1; i < slen; i++) {
3758 	 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3759 	 if (locpos >= 0 && *(strptr->data.string + locpos) == ':') break;
3760       }
3761 
3762       /* interpret all characters after the colon */
3763       for (++i; i < slen; i++) {
3764 	 strptr = findstringpart(i, &locpos, plabel->string, cinst);
3765 	 if (locpos >= 0) {
3766 
3767 	    /* Do for all text characters */
3768 	    strt = strptr->data.string + locpos;
3769 	    if (*strt == '%') {
3770 	       is_quoted = False;
3771 	       strt++;
3772 	       i++;
3773 	       switch(*strt) {
3774 		  case '%':
3775 		     sout = (char *)realloc(sout, strlen(sout) + 2);
3776 		     strcat(sout, "%");
3777 		     break;
3778 		  case 'r':
3779 		     sout = (char *)realloc(sout, strlen(sout) + 2);
3780 		     strcat(sout, "\n");
3781 		     break;
3782 		  case 't':
3783 		     sout = (char *)realloc(sout, strlen(sout) + 2);
3784 		     strcat(sout, "\t");
3785 		     break;
3786 		  case 'i':
3787 		     if (clist->devname == NULL)
3788 			clist->devname = strdup(sout);
3789 		     if (do_update) {
3790 		        if (is_flat)
3791 		           newindex = devflatindex(clist->devname);
3792 		        else
3793 		           newindex = devindex(cfrom, clist);
3794 		        b36str = d36a(newindex);
3795 		        sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
3796 		        sprintf(sout + strlen(sout), "%s", b36str);
3797 		     }
3798 		     break;
3799 		  case 'N':
3800 		     sout = (char *)realloc(sout, strlen(sout)
3801 				+ strlen(cschem->name) + 1);
3802 		     strcat(sout, cschem->name);
3803 		     break;
3804 		  case 'n':
3805 		     sptr = strstr(cschem->name, "::");
3806 		     if (sptr == NULL)
3807 			sptr = cschem->name;
3808 		     else
3809 			sptr += 2;
3810 		     sout = (char *)realloc(sout, strlen(sout)
3811 				+ strlen(sptr) + 1);
3812 		     strcat(sout, sptr);
3813 		     break;
3814 		  case 'x':
3815 		     sout = (char *)realloc(sout, strlen(sout) + 7);
3816 		     sprintf(sout + strlen(sout), "%d",
3817 				cinst->position.x);
3818 		     break;
3819 		  case 'y':
3820 		     sout = (char *)realloc(sout, strlen(sout) + 7);
3821 		     sprintf(sout + strlen(sout), "%d",
3822 				cinst->position.y);
3823 		     break;
3824 		  case 'F':
3825 		     if (no_output) break;
3826 		     include_once = True;
3827 		     /* drop through */
3828 		  case 'f':
3829 		     if (no_output) break;
3830 
3831 		     /* Followed by a filename to include verbatim into */
3832 		     /* the output.  Filename either has no spaces or	*/
3833 		     /* is in quotes.					*/
3834 
3835 		     /* Use textprint to catch any embedded parameters	*/
3836 
3837 		     snew = textprint(strptr, cinst);
3838 		     strt = snew;
3839 		     while (*strt != '\0') {
3840 			if (*strt == '%') {
3841 			   if (include_once && *(strt + 1) == 'F')
3842 			      break;
3843 			   else if (!include_once && *(strt + 1) == 'f')
3844 			      break;
3845 			}
3846 		        strt++;
3847 		     }
3848 
3849 		     if (*strt == '\0') {
3850 			/* No filename; print verbatim */
3851 			free(snew);
3852 		        include_once = False;
3853 			break;
3854 		     }
3855 
3856 		     strt += 2;
3857 		     if (*strt == '"') strt++;
3858 		     if (*strt == '"' || *strt == '\0') break;
3859 		     fnsh = strt + 1;
3860 		     while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3861 			fnsh++;
3862 		     strncpy(_STR, strt, (int)(fnsh - strt));
3863 		     _STR[(int)(fnsh - strt)] = '\0';
3864 		     free(snew);
3865 		     i = slen;
3866 
3867 		     /* Do variable and tilde expansion on the filename */
3868 		     xc_tilde_expand(_STR, 149);
3869 		     xc_variable_expand(_STR, 149);
3870 
3871 		     /* Check if this file has been included already */
3872 		     if (include_once) {
3873 			if (check_included(_STR)) {
3874 			   include_once = False;
3875 			   break;
3876 			}
3877 			append_included(_STR);
3878 		     }
3879 
3880 		     /* Open the indicated file and dump to the output */
3881 		     finclude = fopen(_STR, "r");
3882 		     if (finclude != NULL) {
3883 			char *sptr = sout;
3884 			int nlen;
3885 			int stlen = strlen(sout);
3886 			while (fgets(_STR, 149, finclude)) {
3887 			   nlen = strlen(_STR);
3888 			   sout = (char *)realloc(sout, stlen + nlen + 1);
3889 			   sptr = sout + stlen;
3890 	            	   strcpy(sptr, _STR);
3891 			   stlen += nlen;
3892 			}
3893 			fclose(finclude);
3894 		     }
3895 		     else {
3896 			Wprintf("No file named %s found", _STR);
3897 		     }
3898 		     include_once = False;
3899 		     break;
3900 
3901 		  case 'p':
3902 		     /* Pin name either has no spaces or is in quotes */
3903 		     strt++;
3904 		     if (*strt == '"') {
3905 			is_quoted = True;
3906 			strt++;
3907 			i++;
3908 		     }
3909 		     if (*strt == '"' || *strt == '\0') break;
3910 		     fnsh = strt + 1;
3911 		     while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3912 			fnsh++;
3913 		     strncpy(_STR2, strt, (int)(fnsh - strt));
3914 		     _STR2[(int)(fnsh - strt)] = '\0';
3915 		     i += (fnsh - strt);
3916 		     /* if (is_quoted || *fnsh == '\0') i++; */
3917 		     if (is_quoted) i++;
3918 
3919 		     /* Find the port which corresponds to this pin name */
3920 		     /* in the called object (cschem).  If this is a	 */
3921 		     /* linked symbol/schematic, then the port list will */
3922 		     /* be in the schematic view, not in the symbol view */
3923 
3924 		     pschem = cschem;
3925 		     if (cschem->ports == NULL && cschem->symschem != NULL &&
3926 				cschem->symschem->ports != NULL)
3927 			pschem = cschem->symschem;
3928 
3929 		     for (ports = pschem->ports; ports != NULL;
3930 				ports = ports->next) {
3931 
3932 			subnet = getsubnet(ports->netid, pschem);
3933 			ppin = nettopin(ports->netid, pschem, NULL);
3934 			// NOTE:  _STR is used inside textprintsubnet()
3935 			pstring = textprintsubnet(ppin, NULL, subnet);
3936 			if (!strcmp(pstring, _STR2)) {
3937 			   portid = ports->portid;
3938 			   free(pstring);
3939 			   break;
3940 			}
3941 			else
3942 			   free(pstring);
3943 		     }
3944 		     if (ports != NULL) {
3945 
3946 		        /* Find the matching port in the calling object instance */
3947 
3948 			for (ports = clist->ports; ports != NULL;
3949 				ports = ports->next)
3950 			   if (ports->portid == portid) break;
3951 
3952 		        if (ports != NULL) {
3953 
3954 			   ppin = nettopin(ports->netid, cfrom, prefix);
3955 			   subnet = getsubnet(ports->netid, cfrom);
3956 			   snew = textprintsubnet(ppin, cinst, subnet);
3957 			   sout = (char *)realloc(sout, strlen(sout) +
3958 					strlen(snew) + 1);
3959 	            	   strcat(sout, snew);
3960 			   free(snew);
3961 			   break;
3962 			}
3963 			else {
3964 			   Fprintf(stderr, "Error: called non-existant port\n");
3965 			}
3966 		     }
3967 		     else {
3968 			Wprintf("No pin named %s in device %s", _STR, cschem->name);
3969 		     }
3970 		     break;
3971 		  case 'v':
3972 		     /* Parameter name either has no spaces or is in quotes */
3973 		     strt++;
3974 		     if (*strt == '"')  {
3975 			is_quoted = True;
3976 			strt++;
3977 			i++;
3978 		     }
3979 		     if (*strt == '"' || *strt == '\0') break;
3980 		     fnsh = strt + 1;
3981 		     while (*fnsh != '\0' && !isspace(*fnsh) && *fnsh != '"')
3982 			fnsh++;
3983 		     strncpy(_STR, strt, (int)(fnsh - strt));
3984 		     _STR[(int)(fnsh - strt)] = '\0';
3985 		     i += (fnsh - strt);
3986 		     /* if (is_quoted || *fnsh == '\0') i++; */
3987 		     if (is_quoted) i++;
3988 
3989 		     /* Compare this name against the parameter keys */
3990 		     ops = match_param(cschem, _STR);
3991 
3992 		     /* For backwards compatibility, also try matching against	*/
3993 		     /* the parameter default value (method used before 	*/
3994 		     /* implementing parameters as key:value pairs).		*/
3995 
3996 		     if (ops == NULL) {
3997 		        for (ops = cschem->params; ops != NULL; ops = ops->next) {
3998 		           if (ops->type == XC_STRING) {
3999 			      if (!textcomp(ops->parameter.string, _STR, NULL)) {
4000 			         /* substitute the parameter or default */
4001 			         instops = find_param(cinst, ops->key);
4002 			         optr = instops->parameter.string;
4003 			         if (stringlength(optr, True, cinst) > 0) {
4004 				    snew = textprint(optr, cinst);
4005 			            sout = (char *)realloc(sout, strlen(sout)
4006 						+ strlen(snew) + 1);
4007 			            strcat(sout, snew);
4008 				    free(snew);
4009 				 }
4010 			         break;
4011 			      }
4012 			   }
4013 			}
4014 		     }
4015 
4016 		     if (ops == NULL) {
4017 		        Wprintf("No parameter named %s in device %s",
4018 				  _STR, cschem->name);
4019 		     }
4020 		     break;
4021 
4022 		  default:
4023 		     /* Presence of ".ends" statement forces xcircuit	*/
4024 		     /* not to write ".end" at the end of the netlist.	*/
4025 
4026 	             if (*strt == '.')
4027 		        if (!strncmp(strt + 1, "ends", 4))
4028 			   spice_end = FALSE;
4029 
4030 		     sout = (char *)realloc(sout, strlen(sout) + 2);
4031 		     *(sout + strlen(sout) - 1) = *strt;
4032 		     *(sout + strlen(sout)) = '\0';
4033 	       }
4034 	    }
4035 	    else {
4036 	       if (key != NULL)
4037 	          if (clist->devname == NULL)
4038 		     clist->devname = strdup(sout);
4039 
4040 	       /* Parameters with unresolved question marks are treated */
4041 	       /* like a "%i" string.					*/
4042 
4043 	       if ((key != NULL) && !textncomp(strptr, "?", cinst)) {
4044 		  if (do_update || autonumber) {
4045 		     if (is_flat || (cfrom == NULL))
4046 		        newindex = devflatindex(clist->devname);
4047 		     else
4048 		        newindex = devindex(cfrom, clist);
4049 		     k = strlen(sout);
4050 		     b36str = d36a(newindex);
4051 		     sout = (char *)realloc(sout, strlen(sout) + strlen(b36str) + 1);
4052 		     sprintf(sout + k, "%s", b36str);
4053 		  }
4054 
4055 		  /* When called with autonumber = TRUE, generate a parameter	*/
4056 		  /* instance, replacing the question mark with the new index	*/
4057 		  /* number.							*/
4058 
4059 		  if (autonumber) {
4060 		     copyparams(cinst, cinst);
4061 		     ops = match_instance_param(cinst, key);
4062 		     optr = ops->parameter.string;
4063 		     if (!textncomp(optr, "?", cinst)) {
4064 		        optr->data.string = (char *)realloc(optr->data.string,
4065 				strlen(sout + k) + 1);
4066 		        strcpy(optr->data.string, sout + k);
4067 		     }
4068 		     else Wprintf("Error while auto-numbering parameters");
4069 		     resolveparams(cinst);
4070 		  }
4071 	       }
4072 	       else {
4073 		  /* A "?" default parameter that has a (different)	*/
4074 		  /* instance value becomes the device number.		*/
4075 		  int stlen;
4076 		  if ((key != NULL) && (clist->devindex < 0)) {
4077 		     ops = match_param(cschem, key);
4078 		     if (ops->type == XC_STRING) {
4079 			CalllistPtr plist;
4080 			if (!textcomp(ops->parameter.string, "?", NULL)) {
4081 			   if (is_flat) {
4082 			      /* Ignore the value and generate one instead */
4083 			      newindex = devflatindex(clist->devname);
4084 			      b36str = d36a(newindex);
4085 			      k = strlen(sout);
4086 			      sout = (char *)realloc(sout, strlen(sout)
4087 					+ strlen(b36str) + 1);
4088 			      sprintf(sout + k, "%s", b36str);
4089 			      continue;		/* go on to next stringpart */
4090 			   }
4091 			   else if (cfrom != NULL) {
4092 
4093 			      /* Interpret parameter string as a component	*/
4094 			      /* number so we can check for duplicates.  Assume	*/
4095 			      /* a base-36 number, which includes all alphabet	*/
4096 			      /* characters.  This will disambiguate, e.g., "1"	*/
4097 			      /* and "1R", but flag case-sensitivity, e.g.,	*/
4098 			      /* "1R" and "1r".	 Thanks to Carsten Thomas for	*/
4099 			      /* pointing out the problem with assuming		*/
4100 			      /* numerical component values.			*/
4101 
4102 			      char *endptr;
4103 			      newindex = (u_int) strtol(strt, &endptr, 36);
4104 			      if (*endptr != '\0') {
4105 			         Fprintf(stderr, "Warning:  Use of non-alphanumeric"
4106 				   " characters in component number \"%s\"\n", strt);
4107 			      }
4108 			      clist->devindex = newindex;
4109 			      for (plist = cfrom->calls; plist; plist = plist->next) {
4110 			         if ((plist == clist) ||
4111 					(plist->callobj != clist->callobj)) continue;
4112 
4113 			         /* Two parts have been named the same.  Flag	*/
4114 				 /* it, but don't try to do anything about it.  */
4115 				 /* 11/22/06---additionally check part numbers,	*/
4116 				 /* in case the symbol is a component sub-part.	*/
4117 
4118 			         if (plist->devindex == newindex) {
4119 				    if (samepart(plist, clist)) {
4120 				       Fprintf(stderr, "Warning:  duplicate part number"
4121 					   " %s%s and %s%s\n", sout, strt, sout, strt);
4122 				       break;
4123 				    }
4124 				 }
4125 			      }
4126 			   }
4127 			}
4128 		     }
4129 	          }
4130 	          stlen = strlen(sout);
4131 	          sout = (char *)realloc(sout, stlen + 2);
4132 
4133 		  /* By convention, greek "mu" becomes ASCII "u", NOT "m",	*/
4134 		  /* otherwise we get, e.g., millifarads instead of microfarads */
4135 
4136 		  if ((is_symbol && (*strt == 'm')) || (is_iso && (*strt == 0265)))
4137 		     *(sout + stlen) = 'u';
4138 		  else
4139 	             *(sout + stlen) = *strt;
4140 	          *(sout + stlen + 1) = '\0';
4141 	       }
4142 	    }
4143 	 }
4144 
4145 	 /* Some label controls are interpreted.  Most are ignored */
4146 	 else {
4147 	    switch(strptr->type) {
4148 	       case PARAM_START:
4149 		  key = strptr->data.string;
4150 		  break;
4151 	       case PARAM_END:
4152 		  key = NULL;
4153 		  break;
4154 	       case RETURN:
4155 		  sout = (char *)realloc(sout, strlen(sout) + 2);
4156 		  strcat(sout, "\n");
4157 		  break;
4158 	       case TABFORWARD:
4159 		  sout = (char *)realloc(sout, strlen(sout) + 2);
4160 		  strcat(sout, "\t");
4161 		  break;
4162 	       case FONT_NAME:
4163 		  is_symbol = issymbolfont(strptr->data.font);
4164 		  is_iso = isisolatin1(strptr->data.font);
4165 		  break;
4166 	    }
4167 	 }
4168       }
4169 
4170       /* Insert a newline between multiple valid info labels */
4171       if (infoptr->next != NULL) {
4172 	 sout = (char *)realloc(sout, strlen(sout) + 2);
4173 	 strcat(sout, "\n");
4174       }
4175    }
4176    freelabellist(&infoptr);
4177 
4178    if (*sout == '\0') {
4179       free(sout);
4180       return NULL;
4181    }
4182    return sout;
4183 }
4184 
4185 /*----------------------------------------------------------------------*/
4186 /* Write a low-level device						*/
4187 /*----------------------------------------------------------------------*/
4188 
writedevice(FILE * fp,char * mode,objectptr cfrom,CalllistPtr clist,char * prefix)4189 int writedevice(FILE *fp, char *mode, objectptr cfrom, CalllistPtr clist,
4190 	char *prefix)
4191 {
4192    char *sout;
4193    objectptr cthis;
4194 
4195    if (clist == NULL) {
4196       if (fp != NULL) fprintf(fp, "error: null device\n");
4197       return -1;
4198    }
4199 
4200    /* Devices are always symbols.  If the call indicates a schematic	*/
4201    /* and a separate associated symbol, then we want the symbol.	*/
4202    /* If we're writing a flat netlist, then return -1 to indicate that	*/
4203    /* the symbol instance needs to be flattened.			*/
4204 
4205    cthis = clist->callobj;
4206    if (clist->callobj->schemtype == PRIMARY || clist->callobj->schemtype ==
4207 		SECONDARY)
4208       if (clist->callobj->symschem != NULL) {
4209 	 if (!strncmp(mode, "flat", 4))
4210 	    return -1;
4211 	 else
4212 	    cthis = clist->callobj->symschem;
4213       }
4214 
4215    /* Look for information labels in the object parts list. */
4216 
4217    if ((sout = parseinfo(cfrom, cthis, clist, prefix, mode, FALSE, FALSE)) != NULL) {
4218       if (fp != NULL) {
4219 	 fputs(sout, fp);
4220 	 fprintf(fp, "\n");
4221       }
4222       free(sout);
4223       return 0;
4224    }
4225 
4226    /* Information labels do not necessarily exist. */
4227    return -1;
4228 }
4229 
4230 #ifdef TCL_WRAPPER
4231 
4232 /*----------------------------------------------------------------------*/
4233 /* Translate a hierarchical net name into an object and instance.	*/
4234 /* Return TRUE if found, FALSE if not.  If found, object and instance	*/
4235 /* are returned in the argument pointers.				*/
4236 /*----------------------------------------------------------------------*/
4237 
HierNameToObject(objinstptr cinst,char * hiername,pushlistptr * stack)4238 Boolean HierNameToObject(objinstptr cinst, char *hiername, pushlistptr *stack)
4239 {
4240    CalllistPtr calls;
4241    char *nexttoken, *hptr, *pptr;
4242    objectptr cschem, curobj, newobj;
4243    objinstptr newinst;
4244    int devindex, devlen;
4245    /* pushlistptr stackentry; (jdk) */
4246 
4247    cschem = cinst->thisobject;
4248    curobj = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
4249 
4250    if (curobj->calls == NULL) {
4251       if ((updatenets(cinst, FALSE) <= 0) || (curobj->calls == NULL)) {
4252 	 Wprintf("Error in generating netlists!");
4253 	 return False;
4254       }
4255    }
4256 
4257    hptr = hiername;
4258    while (hptr != NULL) {
4259       nexttoken = strchr(hptr, '/');
4260       if (nexttoken != NULL) *nexttoken = '\0';
4261       pptr = strrchr(hptr, '(');
4262       if (pptr != NULL) {
4263 	 if (sscanf(pptr + 1, "%d", &devindex) == 0) {
4264 	    pptr = NULL;
4265 	    devindex = 0;
4266 	 }
4267 	 else
4268 	    *pptr = '\0';
4269       }
4270       else devindex = -1;
4271 
4272       /* check to make sure that the netlist and device indices have been generated */
4273       for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4274 	 if (calls->devindex == -1) {
4275 	    cleartraversed(curobj);
4276 	    resolve_indices(curobj, FALSE);
4277 	 }
4278       }
4279 
4280       /* hptr is now the object name, and devindex is the instance's call index */
4281       /* find the object corresponding to the name.				*/
4282 
4283       newobj = NameToObject(hptr, &newinst, TRUE);
4284 
4285       if (newobj == NULL) {
4286 
4287 	 /* Try device names (for netlist output) instead of object names */
4288 
4289 	 for (calls = curobj->calls; calls != NULL; calls = calls->next) {
4290 	    if (calls->devname != NULL) {
4291 	       devlen = strlen(calls->devname);
4292 	       if (!strncmp(hptr, calls->devname, devlen)) {
4293 		  if (devindex == -1) {
4294 		     if (sscanf(hptr + devlen, "%d", &devindex) == 0)
4295 			devindex = 0;
4296 		  }
4297 		  if (calls->devindex == devindex) {
4298 		     newobj = calls->callinst->thisobject;
4299 		     break;
4300 		  }
4301 	       }
4302 	    }
4303 	 }
4304       }
4305       else {
4306          for (calls = curobj->calls; calls != NULL; calls = calls->next)
4307 	    if ((calls->callobj == newobj) && (calls->devindex == devindex))
4308 	       break;
4309       }
4310       if (newobj == NULL || calls == NULL) {
4311 	 Fprintf(stderr, "object %s in hierarchy not found in schematic.\n", hptr);
4312 	 free_stack(stack);
4313 	 return FALSE;
4314       }
4315 
4316       /* Diagnostic information */
4317       /* fprintf(stderr, "object %s(%d) = %s\n", newobj->name, devindex, hptr); */
4318 
4319       curobj = calls->callobj;
4320       push_stack(stack, calls->callinst, NULL);
4321 
4322       if (pptr != NULL) *pptr = '(';
4323       if (nexttoken == NULL) break;
4324       *nexttoken = '/';
4325       hptr = nexttoken + 1;
4326    }
4327    return TRUE;
4328 }
4329 
4330 #endif
4331 
4332 /*----------------------------------------------------------------------*/
4333 /* Save netlist into a flattened sim or spice file			*/
4334 /*----------------------------------------------------------------------*/
4335 
writeflat(objectptr cschem,CalllistPtr cfrom,char * prefix,FILE * fp,char * mode)4336 void writeflat(objectptr cschem, CalllistPtr cfrom, char *prefix, FILE *fp, char *mode)
4337 {
4338    CalllistPtr calls = cschem->calls;
4339    char *newprefix = (char *)malloc(sizeof(char));
4340 
4341    /* reset device indexes */
4342 
4343    for (calls = cschem->calls; calls != NULL; calls = calls->next)
4344       calls->devindex = -1;
4345 
4346    /* The naming convention for nodes below the top level uses a	*/
4347    /* slash-separated list of hierarchical names.  Thus the call to	*/
4348    /* the hierarchical resolve_indices().  Indices used by bottom-most	*/
4349    /* symbols (e.g., flattened spice) will be generated by a separate	*/
4350    /* routine devflatindex(), unrelated to what is generated here, 	*/
4351    /* which only affects the hierarchical naming of nodes.		*/
4352 
4353    resolve_indices(cschem, FALSE);
4354 
4355    /* write all the subcircuits */
4356 
4357    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4358 
4359       makelocalpins(cschem, calls, prefix);
4360       if (writedevice(fp, mode, cschem, calls, prefix) < 0) {
4361 	 sprintf(_STR, "%s_%u", calls->callobj->name,
4362 		devindex(cschem, calls));
4363 	 newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
4364 		   + strlen(_STR) + 2));
4365 	 sprintf(newprefix, "%s%s/", prefix, _STR);
4366 	 /* Allow cross-referenced parameters between symbols and schematics */
4367 	 /* by substituting into a schematic from its corresponding symbol   */
4368 	 opsubstitute(calls->callobj, calls->callinst);
4369          /* psubstitute(calls->callinst); */
4370          writeflat(calls->callobj, calls, newprefix, fp, mode);
4371       }
4372       clearlocalpins(calls->callobj);
4373    }
4374    free(newprefix);
4375 }
4376 
4377 /*----------------------------------------------------------------------*/
4378 /* Topmost call to write a flattened netlist				*/
4379 /*----------------------------------------------------------------------*/
4380 
topflat(objectptr cschem,objinstptr thisinst,CalllistPtr cfrom,char * prefix,FILE * fp,char * mode)4381 void topflat(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4382 	char *prefix, FILE *fp, char *mode)
4383 {
4384    char *locmode, *stsave = NULL;
4385    int modlen;
4386    Calllist loccalls;
4387 
4388    /* Set up local call list for parsing info labels in the top-level schematic */
4389    loccalls.cschem = NULL;
4390    loccalls.callobj = cschem;
4391    loccalls.callinst = thisinst;
4392    loccalls.devindex = -1;
4393    loccalls.ports = NULL;
4394    loccalls.next = NULL;
4395 
4396    modlen = strlen(mode);
4397    locmode = malloc(2 + modlen);
4398    strcpy(locmode, mode);
4399    locmode[modlen + 1] = '\0';
4400 
4401    /* "<mode>@" lines go in front */
4402 
4403    locmode[modlen] = '@';
4404    if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4405 		FALSE);
4406    if (stsave != NULL) {
4407       fputs(stsave, fp);
4408       fprintf(fp, "\n");
4409       free(stsave);
4410       stsave = NULL;
4411    }
4412 
4413    writeflat(cschem, cfrom, prefix, fp, mode);
4414    freeflatindex();
4415 
4416    /* Check for negative-numbered info labels, indicated output that	*/
4417    /* should be processed after everything else.			*/
4418 
4419    locmode[modlen] = '-';
4420    stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4421    if (stsave != NULL) {
4422       fputs(stsave, fp);
4423       fprintf(fp, "\n");
4424       free(stsave);
4425       stsave = NULL;
4426    }
4427    free(locmode);
4428 }
4429 
4430 /*----------------------------------------------------------------------*/
4431 /* Write out the list of global nets and their pin names		*/
4432 /*----------------------------------------------------------------------*/
4433 
writeglobals(objectptr cschem,FILE * fp)4434 void writeglobals(objectptr cschem, FILE *fp)
4435 {
4436    LabellistPtr llist;
4437    labelptr gpin;
4438    char *snew;
4439 
4440    if (fp == NULL) return;
4441 
4442    for (llist = global_labels; llist != NULL; llist = llist->next) {
4443       gpin = llist->label;
4444       snew = textprint(gpin->string, NULL);
4445       fprintf(fp, ".GLOBAL %s\n", snew);		/* hspice format */
4446       /* fprintf(fp, "%s = %d\n", snew, -gptr->netid); */ /* generic format */
4447       free(snew);
4448    }
4449    fprintf(fp, "\n");
4450 }
4451 
4452 /*----------------------------------------------------------------------*/
4453 /* Write a SPICE subcircuit entry.					*/
4454 /*----------------------------------------------------------------------*/
4455 
writesubcircuit(FILE * fp,objectptr cschem)4456 void writesubcircuit(FILE *fp, objectptr cschem)
4457 {
4458    PortlistPtr ports;
4459    char *pstring;
4460    stringpart *ppin;
4461    int netid, length, plen, subnet; /*  portid, (jdk) */
4462 
4463    /* Objects which have no ports are not written */
4464    if ((cschem->ports != NULL) && (fp != NULL)) {
4465 
4466       fprintf(fp, ".subckt %s", cschem->name);
4467       length = 9 + strlen(cschem->name);
4468 
4469       /* List of parameters in subcircuit.			  */
4470       /* Each parameter connects to a net which may have multiple */
4471       /* names, so find the net associated with the parameter	  */
4472       /* and convert it back into a pin name in the usual manner  */
4473       /* using nettopin().					  */
4474 
4475       for (ports = cschem->ports; ports != NULL;
4476 		ports = ports->next) {
4477          netid = ports->netid;
4478 	 subnet = getsubnet(netid, cschem);
4479 	 ppin = nettopin(netid, cschem, NULL);
4480 	 pstring = textprintsubnet(ppin, NULL, subnet);
4481 	 plen = strlen(pstring) + 1;
4482 	 if (length + plen > 78) {
4483             fprintf(fp, "\n+ ");	/* SPICE line break & continuation */
4484 	    length = 0;
4485 	 }
4486 	 else length += plen;
4487          fprintf(fp, " %s", pstring);
4488 	 free(pstring);
4489       }
4490       fprintf(fp, "\n");
4491    }
4492 }
4493 
4494 /*----------------------------------------------------------------------*/
4495 /* Resolve device names (fill calllist structure member "devname")	*/
4496 /*----------------------------------------------------------------------*/
4497 
resolve_devnames(objectptr cschem)4498 void resolve_devnames(objectptr cschem)
4499 {
4500    CalllistPtr calls;
4501    char *stmp;
4502    oparamptr ops;
4503 
4504    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4505       if (calls->callobj->traversed == False) {
4506          calls->callobj->traversed = True;
4507 	 resolve_devnames(calls->callobj);
4508       }
4509 
4510       if (calls->devname == NULL) {
4511 
4512          /* Check for "class" parameter.  Note that although this	*/
4513          /* parameter may take a different instance value, the		*/
4514          /* device class is determined by the default value.		*/
4515 
4516          ops = find_param(calls->callinst, "class");
4517          if (ops && (ops->type == XC_STRING))
4518 	    calls->devname = textprint(ops->parameter.string, NULL);
4519 	 else {
4520 	    /* The slow way---parse info labels for any device information */
4521             if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4522 			NULL, "", FALSE, TRUE)) != NULL)
4523 	       free(stmp);
4524 	 }
4525       }
4526    }
4527 }
4528 
4529 /*----------------------------------------------------------------------*/
4530 /* Resolve (hierarchical) component numbering.  If "autonumber" is TRUE	*/
4531 /* then this routine does auto-numbering.  Otherwise, it auto-generates	*/
4532 /* device indices internally (in the "calllist" structure) but does not	*/
4533 /* copy them into parameter space.					*/
4534 /*									*/
4535 /* Note: resolve_devindex() only operates on a single object.  Use	*/
4536 /* resolve_indices() to operate on the entire hierarchy.		*/
4537 /*----------------------------------------------------------------------*/
4538 
resolve_devindex(objectptr cschem,Boolean autonumber)4539 void resolve_devindex(objectptr cschem, Boolean autonumber)
4540 {
4541    CalllistPtr calls;
4542    char *stmp, *endptr;
4543    char *idxtype[] = {"index", "idx", NULL};
4544    oparamptr ops, ips;
4545    objinstptr cinst;
4546    stringpart *optr;
4547    int newindex, j;
4548 
4549    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4550 
4551       /* Check if there is an "index" parameter set to "?" in	*/
4552       /* the object with no existing instance value.		*/
4553 
4554       for (j = 0; idxtype[j] != NULL; j++)
4555          if ((ops = match_param(calls->callinst->thisobject, idxtype[j])) != NULL)
4556 	    break;
4557 
4558       if (ops && (ops->type == XC_STRING)) {
4559  	 if (!textcomp(ops->parameter.string, "?", NULL)) {
4560 	    cinst = calls->callinst;
4561 	    ips = match_instance_param(cinst, idxtype[j]);
4562 	    if ((autonumber == TRUE) && (ips == NULL)) {
4563 	       copyparams(cinst, cinst);
4564 	       ops = match_instance_param(cinst, idxtype[j]);
4565 	       optr = ops->parameter.string;
4566 	       newindex = devindex(cschem, calls);
4567 	       stmp = d36a(newindex);
4568 	       optr->data.string = (char *)realloc(optr->data.string, strlen(stmp) + 1);
4569 	       sprintf(optr->data.string, "%s", stmp);
4570 	    }
4571 	    else if (calls->devindex < 0) {
4572 	       if (ips != NULL) {
4573 	          optr = ips->parameter.string;
4574 		  if (optr->type != TEXT_STRING) {
4575 		     /* Not a simple string.  Dump the text. */
4576 		     char *snew = NULL;
4577 		     snew = textprint(optr, NULL),
4578 	             newindex = (u_int) strtol(snew, &endptr, 36);
4579 		     free(snew);
4580 		  }
4581 		  else {
4582 	             newindex = (u_int) strtol(optr->data.string, &endptr, 36);
4583 		  }
4584 	          if (*endptr != '\0') {
4585 		     /* It is possible to get an instance value that is	*/
4586 		     /* the same as the default, although this normally	*/
4587 		     /* doesn't happen.  If it does, quietly remove the	*/
4588 		     /* unneeded instance value.			*/
4589 
4590 		     if (!stringcomp(ops->parameter.string, ips->parameter.string))
4591 			resolveparams(cinst);
4592 		     else
4593 		        Fprintf(stderr, "Warning:  Use of non-alphanumeric"
4594 				" characters in component \"%s%s\" (instance"
4595 				" of %s)\n", (calls->devname) ? calls->devname
4596 				: calls->callobj->name, optr->data.string,
4597 				calls->callobj->name);
4598 	          }
4599 	          else
4600 		     calls->devindex = newindex;
4601 	       }
4602 	       else /* if (autonumber) */
4603 	          devindex(cschem, calls);
4604 	    }
4605 	 }
4606       }
4607       else {
4608 	 /* The slow way---parse info labels for any index information */
4609          if ((stmp = parseinfo(cschem, calls->callinst->thisobject, calls,
4610 		NULL, "", autonumber, TRUE)) != NULL)
4611 	    free(stmp);
4612       }
4613    }
4614 }
4615 
4616 /*----------------------------------------------------------------------*/
4617 /* Recursive call to resolve_devindex() through the circuit hierarchy	*/
4618 /*----------------------------------------------------------------------*/
4619 
resolve_indices(objectptr cschem,Boolean autonumber)4620 void resolve_indices(objectptr cschem, Boolean autonumber) {
4621    CalllistPtr calls;
4622 
4623    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4624       if (calls->callobj->traversed == False) {
4625          calls->callobj->traversed = True;
4626          resolve_indices(calls->callobj, autonumber);
4627       }
4628    }
4629    resolve_devindex(cschem, autonumber);
4630 }
4631 
4632 /*----------------------------------------------------------------------*/
4633 /* Recursive call to clear all generated device numbers.		*/
4634 /*----------------------------------------------------------------------*/
4635 
clear_indices(objectptr cschem)4636 void clear_indices(objectptr cschem) {
4637    CalllistPtr calls;
4638 
4639    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4640       if (calls->callobj->traversed == False) {
4641          calls->callobj->traversed = True;
4642          clear_indices(calls->callobj);
4643       }
4644       calls->devindex = -1;
4645    }
4646 }
4647 
4648 /*----------------------------------------------------------------------*/
4649 /* Recursive call to clear all device indexes (parameter "index")	*/
4650 /*----------------------------------------------------------------------*/
4651 
unnumber(objectptr cschem)4652 void unnumber(objectptr cschem) {
4653    CalllistPtr calls;
4654    oparamptr ops, ips;
4655    char *idxtype[] = {"index", "idx", NULL};
4656    int j;
4657 
4658    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4659       if (calls->callobj->traversed == False) {
4660          calls->callobj->traversed = True;
4661          unnumber(calls->callobj);
4662       }
4663 
4664       for (j = 0; idxtype[j] != NULL; j++)
4665          if ((ops = match_param(calls->callobj, idxtype[j])) != NULL) break;
4666 
4667       if (ops && (ops->type == XC_STRING)) {
4668  	 if (!textcomp(ops->parameter.string, "?", NULL)) {
4669 	    ips = match_instance_param(calls->callinst, idxtype[j]);
4670 	    if (ips != NULL)
4671 	       free_instance_param(calls->callinst, ips);
4672 	 }
4673       }
4674    }
4675 }
4676 
4677 /*----------------------------------------------------------------------*/
4678 /* Save netlist into a hierarchical file				*/
4679 /*----------------------------------------------------------------------*/
4680 
writehierarchy(objectptr cschem,objinstptr thisinst,CalllistPtr cfrom,FILE * fp,char * mode)4681 void writehierarchy(objectptr cschem, objinstptr thisinst, CalllistPtr cfrom,
4682 	FILE *fp, char *mode)
4683 {
4684    CalllistPtr calls = cschem->calls;
4685    PortlistPtr ports, plist;
4686    int pnet, portid, length, plen, subnet, modlen; /* netid, (jdk) */
4687    char *locmode = mode, *stsave = NULL, *pstring;
4688    stringpart *ppin;
4689    Calllist loccalls;
4690 
4691    if (cschem->traversed == True) return;
4692 
4693    /* Set up local call list for parsing info labels in this schematic */
4694    loccalls.cschem = NULL;
4695    loccalls.callobj = cschem;
4696    loccalls.callinst = thisinst;
4697    loccalls.devindex = -1;
4698    loccalls.ports = NULL;
4699    loccalls.next = NULL;
4700 
4701    modlen = strlen(mode);
4702    locmode = malloc(2 + modlen);
4703    strcpy(locmode, mode);
4704    locmode[modlen + 1] = '\0';
4705 
4706    /* "<mode>@" lines go before any subcircuit calls	*/
4707 
4708    locmode[modlen] = '@';
4709    if (fp != NULL) stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE,
4710 		FALSE);
4711    if (stsave != NULL) {
4712       fputs(stsave, fp);
4713       fprintf(fp, "\n");
4714       free(stsave);
4715       stsave = NULL;
4716    }
4717 
4718    /* Subcircuits which make no calls or have no devices do not get written */
4719 
4720    if (calls != NULL) {
4721 
4722       /* Make sure that all the subcircuits have been written first */
4723 
4724       for (; calls != NULL; calls = calls->next) {
4725          if (calls->callobj->traversed == False) {
4726 	    psubstitute(calls->callinst);
4727             writehierarchy(calls->callobj, calls->callinst, calls, fp, mode);
4728             calls->callobj->traversed = True;
4729          }
4730       }
4731       if (cschem->schemtype == FUNDAMENTAL) {
4732 	 free(locmode);
4733 	 return;
4734       }
4735    }
4736 
4737    /* Info-labels on a schematic (if any) get printed out first	*/
4738 
4739    if ((fp != NULL) && (cschem->calls != NULL)) {
4740       stsave = parseinfo(NULL, cschem, &loccalls, NULL, mode, FALSE, FALSE);
4741       if (stsave != NULL) {
4742 
4743 	 /* Check stsave for embedded SPICE subcircuit syntax */
4744 
4745 	 if (!strcmp(mode, "spice"))
4746 	    if (strstr(stsave, ".subckt ") == NULL)
4747 	       writesubcircuit(fp, cschem);
4748 
4749          fputs(stsave, fp);
4750          fprintf(fp, "\n");
4751          free(stsave);
4752          stsave = NULL;
4753       }
4754       else if (cschem->calls != NULL)
4755 	 /* top-level schematics will be ignored because they have no ports */
4756 	 writesubcircuit(fp, cschem);
4757    }
4758 
4759    /* Resolve all fixed part assignments (devindex) */
4760    resolve_devindex(cschem, FALSE);
4761 
4762    /* If the output file is NULL, then we're done */
4763 
4764    if (fp == NULL) {
4765       free(locmode);
4766       return;
4767    }
4768 
4769    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
4770 
4771       /* writedevice() is just another call to parseinfo() */
4772       if (writedevice(fp, mode, cschem, calls, NULL) < 0) {
4773 
4774 	 /* If the device did not produce any output in writedevice(), but	*/
4775 	 /* does not make any calls itself, then it is implicitly a TRIVIAL	*/
4776 	 /* symbol.								*/
4777 
4778 	 if ((calls->callobj->schemtype == TRIVIAL) || (calls->callobj->calls == NULL))
4779 	    continue;
4780 
4781          /* No syntax indicating how to write this call.  Use SPICE "X"		*/
4782 	 /* format and arrange the parameters according to the structure.	*/
4783 
4784 	 calls->devname = strdup(spice_devname);
4785          fprintf(fp, "X%s", d36a(devindex(cschem, calls)));
4786 	 stsave = calls->callobj->name;
4787          length = 6;
4788 
4789 	 /* The object's definition lists calls in the order of the object's	*/
4790 	 /* port list.  Therefore, use this order to find the port list.  	*/
4791 	 /* This will also deal with the fact that not every port has to be	*/
4792 	 /* called by the instance (ports may be left disconnected).		*/
4793 
4794 	 for (ports = calls->callobj->ports; ports != NULL;
4795 			ports = ports->next) {
4796 	    portid = ports->portid;
4797 	    for (plist = calls->ports; plist != NULL; plist = plist->next)
4798 	       if (plist->portid == ports->portid)
4799 		  break;
4800 
4801 	    pnet = (plist == NULL) ?  netmax(cschem) + 1 : plist->netid;
4802 	    subnet = getsubnet(pnet, cschem);
4803 	    ppin = nettopin(pnet, cschem, NULL);
4804 	    pstring = textprintsubnet(ppin, NULL, subnet);
4805 	    plen = strlen(pstring) + 1;
4806 	    if (length + plen > 78) {
4807                fprintf(fp, "\n+ ");	/* SPICE line continuation */
4808 	       length = 0;
4809 	    }
4810 	    else length += plen;
4811 	    fprintf(fp, " %s", pstring);
4812 	    free(pstring);
4813          }
4814 	 plen = 1 + strlen(stsave);
4815 	 if (length + plen > 78) fprintf(fp, "\n+ ");	/* SPICE line cont. */
4816          fprintf(fp, " %s\n", stsave);
4817       }
4818    }
4819 
4820    /* Check for negative-numbered info labels, indicated output that	*/
4821    /* should be processed after everything else.  If we're processing	*/
4822    /* SPICE output but the schematic does not provide a ".ends"		*/
4823    /* statement, then add it to the end of the deck.			*/
4824 
4825    if (cschem->calls != NULL) {
4826       locmode[modlen] = '-';
4827       stsave = parseinfo(NULL, cschem, &loccalls, NULL, locmode, FALSE, FALSE);
4828       if (stsave != NULL) {
4829          fputs(stsave, fp);
4830          fprintf(fp, "\n");
4831          if (cfrom != NULL)
4832 	    if (!strcmp(mode, "spice"))
4833 	       if (strstr(stsave, ".ends") == NULL)
4834 		   fprintf(fp, ".ends\n");
4835          free(stsave);
4836       }
4837       else if (cfrom != NULL)
4838          fprintf(fp, ".ends\n");
4839 
4840       fprintf(fp, "\n");
4841    }
4842 
4843    free(locmode);
4844 }
4845 
4846 /*----------------------------------------------------------------------*/
4847 /* Create generic netlist in the Tcl interpreter variable space		*/
4848 /*----------------------------------------------------------------------*/
4849 
4850 #ifdef TCL_WRAPPER
4851 
tclparseinfo(objectptr cschem)4852 static Tcl_Obj *tclparseinfo(objectptr cschem)
4853 {
4854    genericptr *pgen;
4855    labelptr plabel;
4856 
4857    Tcl_Obj *rlist = Tcl_NewListObj(0, NULL);
4858 
4859    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
4860       if (IS_LABEL(*pgen)) {
4861 	 plabel = TOLABEL(pgen);
4862          if (plabel->pin == INFO) {
4863 	    Tcl_ListObjAppendElement(xcinterp, rlist,
4864 			TclGetStringParts(plabel->string));
4865 	 }
4866       }
4867    }
4868    return rlist;
4869 }
4870 
4871 /*----------------------------------------------------------------------*/
4872 /* Write global variables to Tcl list (in key-value pairs which can be	*/
4873 /* turned into an array variable using the "array set" command)		*/
4874 /*									*/
4875 /* Note: argument "cinst" is unused, but it really should be.  We	*/
4876 /* should not assume that different schematics have the same list of	*/
4877 /* globals!								*/
4878 /*----------------------------------------------------------------------*/
4879 
tclglobals(objinstptr cinst)4880 Tcl_Obj *tclglobals(objinstptr cinst)
4881 {
4882    LabellistPtr llist;
4883    labelptr gpin;
4884    Tcl_Obj *gdict; /*, *netnum; (jdk) */
4885    buslist *sbus;
4886    int netid, lbus;
4887 
4888    gdict = Tcl_NewListObj(0, NULL);
4889    for (llist = global_labels; llist != NULL; llist = llist->next) {
4890       gpin = llist->label;
4891       Tcl_ListObjAppendElement(xcinterp, gdict, TclGetStringParts(gpin->string));
4892       for (lbus = 0;;) {
4893          if (llist->subnets == 0) {
4894 	    netid = llist->net.id;
4895 	 }
4896 	 else {
4897 	    sbus = llist->net.list + lbus;
4898 	    netid = sbus->netid;
4899 	 }
4900          Tcl_ListObjAppendElement(xcinterp, gdict, Tcl_NewIntObj(netid));
4901 	 if (++lbus >= llist->subnets) break;
4902       }
4903    }
4904    return gdict;
4905 }
4906 
4907 /*----------------------------------------------------------------------*/
4908 /* Write a generic hierarchical netlist into Tcl list "subcircuits"	*/
4909 /*----------------------------------------------------------------------*/
4910 
tclhierarchy(objectptr cschem,objinstptr cinst,CalllistPtr cfrom,Tcl_Obj * cktlist)4911 void tclhierarchy(objectptr cschem, objinstptr cinst, CalllistPtr cfrom, Tcl_Obj *cktlist)
4912 {
4913    CalllistPtr calls = cschem->calls;
4914    PortlistPtr ports, plist;
4915    int netid, pnet, portid; /* , length, plen, i; (jdk) */
4916    Tcl_Obj *tclports, *tclcalls, *tclnewcall, *tclnets, *netnum;
4917    Tcl_Obj *tcldevs, *tclparams, *subckt, *newdev, *tcllabel, *portnum;
4918    oparamptr paramlist; /* , locparam; (jdk) */
4919    char *netsdone;
4920 
4921    /* Trivial objects are by definition those that are supposed	*/
4922    /* to be resolved by the netlist generation prior to output	*/
4923    /* and ignored by the output generator.			*/
4924 
4925    if (cschem->schemtype == TRIVIAL) return;
4926 
4927    /* Make sure that all the subcircuits have been written first */
4928 
4929    for (; calls != NULL; calls = calls->next) {
4930       if (calls->callobj->traversed == False) {
4931 	 tclhierarchy(calls->callobj, calls->callinst, calls, cktlist);
4932          calls->callobj->traversed = True;
4933       }
4934    }
4935 
4936    /* Write own subcircuit netlist */
4937    subckt = Tcl_NewListObj(0, NULL);
4938 
4939    /* Prepare the list of network cross-references */
4940    tclnets = Tcl_NewListObj(0, NULL);
4941 
4942    /* Make list of the nets which have been written so we don't redundantly	*/
4943    /* list any entries (use of calloc() initializes all entries to FALSE).	*/
4944    /* (calloc() replaced by malloc() and bzero() because Tcl doesn't	*/
4945    /* have a calloc() function (2/6/04))				*/
4946 
4947    netsdone = (char *)malloc(2 + netmax(cschem));
4948 #ifdef _MSC_VER
4949    memset((void *)netsdone, 0, 2 + netmax(cschem));
4950 #else
4951    bzero((void *)netsdone, 2 + netmax(cschem));
4952 #endif
4953 
4954    /* Write the name (key value pair) */
4955    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("name", 4));
4956    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj(cschem->name,
4957 		strlen(cschem->name)));
4958 
4959    /* Write the handle of the object instance (key value pair) */
4960    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("handle", 6));
4961    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewHandleObj(cinst));
4962 
4963    /* Write the list of ports */
4964    if ((ports = cschem->ports) != NULL) {
4965 
4966       /* List of ports in subcircuit. 			  	  */
4967       /* Each parameter connects to a net which may have multiple */
4968       /* names, so find the net associated with the parameter     */
4969       /* and convert it back into a pin name in the usual manner  */
4970       /* using nettopin().					  */
4971 
4972       tclports = Tcl_NewListObj(0, NULL);
4973       for (; ports != NULL; ports = ports->next) {
4974          netid = ports->netid;
4975 	 portid = ports->portid;
4976 	 netnum = Tcl_NewIntObj(netid);
4977 	 portnum = Tcl_NewIntObj(portid);
4978 	 Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
4979  	 Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
4980 	 if ((netid >= 0) && (netsdone[netid] == (char)0))
4981 	 {
4982  	    Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
4983  	    Tcl_ListObjAppendElement(xcinterp, tclnets,
4984 		TclGetStringParts(nettopin(netid, cschem, NULL)));
4985 	    /* record this net as having been added to the list */
4986 	    netsdone[netid] = (char)1;
4987 	 }
4988       }
4989       Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("ports", 5));
4990       Tcl_ListObjAppendElement(xcinterp, subckt, tclports);
4991    }
4992 
4993    /* Write the list of parameter defaults (key:value pairs) */
4994 
4995    if (cschem->params != NULL) {
4996       tclparams = Tcl_NewListObj(0, NULL);
4997 
4998       for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
4999 	 Tcl_ListObjAppendElement(xcinterp, tclparams,
5000 		Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5001 	 switch(paramlist->type) {
5002 	    case XC_INT:
5003 	       Tcl_ListObjAppendElement(xcinterp, tclparams,
5004 			Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5005 	       break;
5006 	    case XC_FLOAT:
5007 	       Tcl_ListObjAppendElement(xcinterp, tclparams,
5008 			Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5009 	       break;
5010 	    case XC_STRING:
5011 	       tcllabel = TclGetStringParts(paramlist->parameter.string);
5012 	       /* Should get rid of last entry, "End Parameter"; not needed */
5013 	       Tcl_ListObjAppendElement(xcinterp, tclparams, tcllabel);
5014 	       break;
5015 	    case XC_EXPR:
5016 	       Tcl_ListObjAppendElement(xcinterp, tclparams,
5017 			evaluate_raw(cschem, paramlist, cinst, NULL));
5018 	       break;
5019 	 }
5020       }
5021       Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("parameters", 10));
5022       Tcl_ListObjAppendElement(xcinterp, subckt, tclparams);
5023    }
5024 
5025    /* Write the list of calls to subcircuits */
5026 
5027    if ((calls = cschem->calls) != NULL) {
5028       tclcalls = Tcl_NewListObj(0, NULL);
5029       for (; calls != NULL; calls = calls->next) {
5030 
5031          /* Don't write calls to non-functional subcircuits. */
5032 	 if (calls->callobj->schemtype == TRIVIAL) continue;
5033 
5034          tclnewcall = Tcl_NewListObj(0, NULL);
5035 	 Tcl_ListObjAppendElement(xcinterp, tclnewcall, Tcl_NewStringObj("name", 4));
5036 	 Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5037 		Tcl_NewStringObj(calls->callobj->name,
5038 		strlen(calls->callobj->name)));
5039 
5040 
5041          /* Log any local parameter instances */
5042          if (calls->callinst->params != NULL) {
5043 	    tclparams = Tcl_NewListObj(0, NULL);
5044 
5045 	    for (paramlist = calls->callinst->params; paramlist != NULL;
5046 			paramlist = paramlist->next) {
5047 	       Tcl_ListObjAppendElement(xcinterp, tclparams,
5048 			Tcl_NewStringObj(paramlist->key, strlen(paramlist->key)));
5049 	       switch(paramlist->type) {
5050 		  case XC_INT:
5051 	             Tcl_ListObjAppendElement(xcinterp, tclparams,
5052 				Tcl_NewIntObj((int)paramlist->parameter.ivalue));
5053 		     break;
5054 		  case XC_FLOAT:
5055 	             Tcl_ListObjAppendElement(xcinterp, tclparams,
5056 				Tcl_NewDoubleObj((double)paramlist->parameter.fvalue));
5057 		     break;
5058 		  case XC_STRING:
5059 	             Tcl_ListObjAppendElement(xcinterp, tclparams,
5060 				TclGetStringParts(paramlist->parameter.string));
5061 		     break;
5062 		  case XC_EXPR:
5063 		     Tcl_ListObjAppendElement(xcinterp, tclparams,
5064 				evaluate_raw(cschem, paramlist, cinst, NULL));
5065 		     break;
5066 	       }
5067 	    }
5068 	    Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5069 			Tcl_NewStringObj("parameters", 10));
5070 	    Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclparams);
5071          }
5072 
5073 	 /* Called ports are listed by key:value pair (port number: net id) */
5074 
5075          if (calls->callobj->ports != NULL) {
5076             tclports = Tcl_NewListObj(0, NULL);
5077 	    for (ports = calls->callobj->ports; ports != NULL;
5078 			ports = ports->next) {
5079 	       portid = ports->portid;
5080 	       for (plist = calls->ports; plist != NULL; plist = plist->next)
5081 	          if (plist->portid == ports->portid)
5082 		     break;
5083 
5084 	       pnet = (plist == NULL) ?  netmax(cschem) + 1 : plist->netid;
5085 
5086 	       netnum = Tcl_NewIntObj((int)pnet);
5087 	       portnum = Tcl_NewIntObj(portid);
5088 	       Tcl_ListObjAppendElement(xcinterp, tclports, portnum);
5089 	       Tcl_ListObjAppendElement(xcinterp, tclports, netnum);
5090 	       if ((pnet >= 0) && (netsdone[pnet] == (char)0))
5091 	       {
5092 		  Tcl_ListObjAppendElement(xcinterp, tclnets, netnum);
5093 		  Tcl_ListObjAppendElement(xcinterp, tclnets,
5094 			TclGetStringParts(nettopin(pnet, cschem, NULL)));
5095 		  /* record this net as having been added to the list */
5096 		  netsdone[pnet] = (char)1;
5097 	       }
5098             }
5099 	    Tcl_ListObjAppendElement(xcinterp, tclnewcall,
5100 			Tcl_NewStringObj("ports", 5));
5101 	    Tcl_ListObjAppendElement(xcinterp, tclnewcall, tclports);
5102          }
5103 	 Tcl_ListObjAppendElement(xcinterp, tclcalls, tclnewcall);
5104       }
5105       Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("calls", 5));
5106       Tcl_ListObjAppendElement(xcinterp, subckt, tclcalls);
5107    }
5108    free(netsdone);
5109 
5110    /* If the object has info labels, write a device list.	*/
5111    /* Check both the schematic and its symbol for info labels.	*/
5112 
5113    tcldevs = Tcl_NewListObj(0, NULL);
5114    if (cschem->symschem != NULL) {
5115       newdev = tclparseinfo(cschem->symschem);
5116       Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5117    }
5118    newdev = tclparseinfo(cschem);
5119    Tcl_ListObjAppendElement(xcinterp, tcldevs, newdev);
5120    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("devices", 7));
5121    Tcl_ListObjAppendElement(xcinterp, subckt, tcldevs);
5122 
5123    /* Write the network cross-reference dictionary */
5124    Tcl_ListObjAppendElement(xcinterp, subckt, Tcl_NewStringObj("nets", 4));
5125    Tcl_ListObjAppendElement(xcinterp, subckt, tclnets);
5126 
5127    Tcl_ListObjAppendElement(xcinterp, cktlist, subckt);
5128 }
5129 
5130 /*----------------------------------------------------------------------*/
5131 
tcltoplevel(objinstptr cinst)5132 Tcl_Obj *tcltoplevel(objinstptr cinst)
5133 {
5134    Tcl_Obj *cktlist;
5135    objectptr cschem = cinst->thisobject;
5136 
5137    cktlist = Tcl_NewListObj(0, NULL);
5138    cleartraversed(cschem);
5139    tclhierarchy(cschem, cinst, NULL, cktlist);
5140    return cktlist;
5141 }
5142 
5143 #endif	/* TCL_WRAPPER */
5144 
5145 /*----------------------------------------------------------------------*/
5146 /* Create generic netlist in the Python interpreter variable space	*/
5147 /*----------------------------------------------------------------------*/
5148 
5149 #ifdef HAVE_PYTHON
5150 
pyparseinfo(objectptr cschem)5151 static PyObject *pyparseinfo(objectptr cschem)
5152 {
5153    genericptr *pgen;
5154    labelptr plabel;
5155 
5156    PyObject *rlist = PyList_New(0);
5157 
5158    for (pgen = cschem->plist; pgen < cschem->plist + cschem->parts; pgen++) {
5159       if (IS_LABEL(*pgen)) {
5160 	 plabel = TOLABEL(pgen);
5161          if (plabel->pin == INFO)
5162 	    PyList_Append(rlist, PyGetStringParts(plabel->string));
5163       }
5164    }
5165    return rlist;
5166 }
5167 
5168 /*----------------------------------------------------------------------*/
5169 /* Write global variables to Python dictionary				*/
5170 /*----------------------------------------------------------------------*/
5171 
pyglobals(objectptr cschem)5172 PyObject *pyglobals(objectptr cschem)
5173 {
5174    LabellistPtr llist;
5175    labelptr gpin;
5176    PyObject *gdict, *netnum;
5177    int netid, lbus;
5178    buslist *sbus;
5179 
5180    gdict = PyDict_New();
5181    for (llist = global_labels; llist != NULL; llist = llist->next) {
5182       gpin = llist->label;
5183       for (lbus = 0;;) {
5184 	 if (llist->subnets == 0)
5185 	    netid = llist->net.id;
5186 	 else {
5187 	    sbus = llist->net.list + lbus;
5188 	    netid = sbus->netid;
5189 	 }
5190          netnum = PyInt_FromLong((long)(netid));
5191          PyDict_SetItem(gdict, netnum, PyGetStringParts(gpin->string));
5192 	 if (++lbus >= llist->subnets) break;
5193       }
5194    }
5195    return gdict;
5196 }
5197 
5198 /*----------------------------------------------------------------------*/
5199 /* Write a generic hierarchical netlist into Python list "subcircuits"	*/
5200 /*----------------------------------------------------------------------*/
5201 
pyhierarchy(objectptr cschem,CalllistPtr cfrom,PyObject * cktlist)5202 void pyhierarchy(objectptr cschem, CalllistPtr cfrom, PyObject *cktlist)
5203 {
5204    CalllistPtr calls = cschem->calls;
5205    PortlistPtr ports, plist;
5206    int netid, pnet, portid, length, plen, i;
5207    PyObject *pyports, *pycalls, *pynewcall, *pynets, *netnum;
5208    PyObject *pydevs, *pyparams, *subckt, *newdev, *pylabel;
5209    oparamptr paramlist;
5210 
5211    /* Trivial objects are by definition those that are supposed	*/
5212    /* to be resolved by the netlist generation prior to output	*/
5213    /* and ignored by the output generator.			*/
5214 
5215    if (cschem->schemtype == TRIVIAL) return;
5216 
5217    /* Make sure that all the subcircuits have been written first */
5218 
5219    for (; calls != NULL; calls = calls->next) {
5220       if (calls->callobj->traversed == False) {
5221          calls->callobj->traversed = True;
5222 	 pyhierarchy(calls->callobj, calls, cktlist);
5223       }
5224    }
5225 
5226    /* Write own subcircuit netlist */
5227    subckt = PyDict_New();
5228 
5229    /* Prepare the dictionary of network cross-references */
5230    pynets = PyDict_New();
5231 
5232    /* Write the name */
5233    PyDict_SetItem(subckt, PyString_FromString("name"),
5234 		PyString_FromString(cschem->name));
5235 
5236    /* Write the list of ports */
5237    if ((ports = cschem->ports) != NULL) {
5238 
5239       /* List of ports in subcircuit. 			  	  */
5240       /* Each parameter connects to a net which may have multiple */
5241       /* names, so find the net associated with the parameter     */
5242       /* and convert it back into a pin name in the usual manner  */
5243       /* using nettopin().					  */
5244 
5245       pyports = PyList_New(0);
5246       for (; ports != NULL; ports = ports->next) {
5247          netid = ports->netid;
5248 	 netnum = PyInt_FromLong((long)netid);
5249          PyList_Append(pyports, netnum);
5250 	 if (netid >= 0)
5251 	    PyDict_SetItem(pynets, netnum,
5252 		PyGetStringParts(nettopin(netid, cschem, NULL)));
5253       }
5254       PyDict_SetItem(subckt, PyString_FromString("ports"), pyports);
5255    }
5256 
5257    /* Write the list of parameters */
5258 
5259    if (cschem->params != NULL) {
5260       pyparams = PyList_New(0);
5261 
5262       for (paramlist = cschem->params; paramlist != NULL; paramlist = paramlist->next) {
5263 	 if (paramlist->type == XC_INT)
5264 	    PyList_Append(pyparams,
5265 			PyInt_FromLong((long)paramlist->parameter.ivalue));
5266 	 if (paramlist->type == XC_FLOAT)
5267 	    PyList_Append(pyparams,
5268 			PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5269 	 else if (paramlist->type == XC_STRING) {
5270 	    pylabel = PyGetStringParts(paramlist->parameter.string);
5271 	    /* Should get rid of last entry, "End Parameter"; not needed */
5272 	    PyList_Append(pyparams, pylabel);
5273 	 }
5274       }
5275       PyDict_SetItem(subckt, PyString_FromString("parameters"), pyparams);
5276    }
5277 
5278    /* Write the list of calls to subcircuits */
5279 
5280    if ((calls = cschem->calls) != NULL) {
5281       pycalls = PyList_New(0);
5282       for (; calls != NULL; calls = calls->next) {
5283 
5284          /* Don't write calls to non-functional subcircuits. */
5285 	 if (calls->callobj->schemtype == TRIVIAL) continue;
5286 
5287          pynewcall = PyDict_New();
5288          PyDict_SetItem(pynewcall, PyString_FromString("name"),
5289 		PyString_FromString(calls->callobj->name));
5290 
5291          /* Log any local parameter instances */
5292          if (calls->callinst->params != NULL) {
5293 	    pyparams = PyList_New(0);
5294 
5295 	    for (paramlist = cschem->params; paramlist != NULL;
5296 			paramlist = paramlist->next) {
5297 	       if (paramlist->type == XC_INT)
5298 	          PyList_Append(pyparams,
5299 			PyInt_FromLong((long)paramlist->parameter.ivalue));
5300 	       else if (paramlist->type == XC_FLOAT)
5301 	          PyList_Append(pyparams,
5302 			PyFloat_FromDouble((double)paramlist->parameter.fvalue));
5303 	       else if (paramlist->type == XC_STRING)
5304 	          PyList_Append(pyparams,
5305 			PyGetStringParts(paramlist->parameter.string));
5306 	    }
5307 	    PyDict_SetItem(pynewcall, PyString_FromString("parameters"),
5308 			pyparams);
5309          }
5310 
5311          /* The object's definition lists calls in the order of the object's	*/
5312          /* port list.  Therefore, use this order to find the port list.  	*/
5313 	 /* Unconnected ports will be NULL entries in the list (?).		*/
5314 
5315          if (calls->callobj->ports != NULL) {
5316             pyports = PyList_New(0);
5317 	    for (ports = calls->callobj->ports; ports != NULL;
5318 			ports = ports->next) {
5319 	       portid = ports->portid;
5320 	       for (plist = calls->ports; plist != NULL; plist = plist->next)
5321 	          if (plist->portid == ports->portid)
5322 		     break;
5323 
5324 	       pnet = (plist == NULL) ?  netmax(cschem) + 1 : plist->netid;
5325 
5326 	       netnum = PyInt_FromLong((long)pnet);
5327 	       PyList_Append(pyports, netnum);
5328 	       if (pnet >= 0)
5329 	          PyDict_SetItem(pynets, netnum,
5330 			PyGetStringParts(nettopin(pnet, cschem, NULL)));
5331             }
5332 	    PyDict_SetItem(pynewcall, PyString_FromString("ports"), pyports);
5333          }
5334          PyList_Append(pycalls, pynewcall);
5335       }
5336       PyDict_SetItem(subckt, PyString_FromString("calls"), pycalls);
5337    }
5338 
5339    /* If the object has info labels, write a device list.	*/
5340    /* Check both the schematic and its symbol for info labels.	*/
5341 
5342    pydevs = PyList_New(0);
5343    if (cschem->symschem != NULL) {
5344       newdev = pyparseinfo(cschem->symschem);
5345       PyList_Append(pydevs, newdev);
5346    }
5347    newdev = pyparseinfo(cschem);
5348    PyList_Append(pydevs, newdev);
5349    PyDict_SetItem(subckt, PyString_FromString("devices"), pydevs);
5350 
5351    /* Write the network cross-reference dictionary */
5352    PyDict_SetItem(subckt, PyString_FromString("nets"), pynets);
5353 
5354    PyList_Append(cktlist, subckt);
5355 }
5356 
5357 /*----------------------------------------------------------------------*/
5358 
pytoplevel(objectptr cschem)5359 PyObject *pytoplevel(objectptr cschem)
5360 {
5361    PyObject *cktlist;
5362 
5363    cktlist = PyList_New(0);
5364    cleartraversed(cschem);
5365    pyhierarchy(cschem, NULL, cktlist);
5366    return cktlist;
5367 }
5368 
5369 #endif	/* HAVE_PYTHON */
5370 
5371 /*----------------------------------------------------------------------*/
5372 /* Update networks in an object by checking if the network is valid,	*/
5373 /* and destroying and recreating the network if necessary.		*/
5374 /*									*/
5375 /* Run cleartraversed() on all types.  The traversed flag allows a	*/
5376 /* check for infinite recursion when creating calls.			*/
5377 /*									*/
5378 /* Returns -1 on discovery of infinite recursion, 0 on failure to	*/
5379 /* generate a net, and 1 on success.					*/
5380 /*----------------------------------------------------------------------*/
5381 
updatenets(objinstptr uinst,Boolean quiet)5382 int updatenets(objinstptr uinst, Boolean quiet) {
5383    objectptr thisobject;
5384    objinstptr thisinst;
5385    int spage;
5386 
5387    /* Never try to generate a netlist while a file is being read, as	*/
5388    /* can happen if an expression parameter attempts to query a netlist	*/
5389    /* node.								*/
5390 
5391    if (load_in_progress) return 0;
5392 
5393    if (uinst->thisobject->symschem != NULL
5394 		&& uinst->thisobject->schemtype != PRIMARY) {
5395       thisobject = uinst->thisobject->symschem;
5396       if ((spage = is_page(thisobject)) >= 0)
5397          thisinst = xobjs.pagelist[spage]->pageinst;
5398    }
5399    else {
5400       thisobject = uinst->thisobject;
5401       thisinst = uinst;
5402    }
5403 
5404    if (checkvalid(thisobject) == -1) {
5405       uselection *ssave;
5406 
5407       if (cleartraversed(thisobject) == -1) {
5408          Wprintf("Netlist error:  Check for recursion in circuit!");
5409          return -1;
5410       }
5411 
5412       /* Note: destroy/create nets messes up the part list.  Why?? */
5413 
5414       if (areawin->selects > 0)
5415          ssave = remember_selection(areawin->topinstance, areawin->selectlist,
5416 		areawin->selects);
5417       destroynets(thisobject);
5418       createnets(thisinst, quiet);
5419       if (areawin->selects > 0) {
5420          areawin->selectlist = regen_selection(areawin->topinstance, ssave);
5421          free_selection(ssave);
5422       }
5423    }
5424 
5425    if (thisobject->labels == NULL && thisobject->polygons == NULL) {
5426       if (quiet == FALSE)
5427          Wprintf("Netlist error:  No netlist elements in object %s",
5428 		thisobject->name);
5429       return 0;
5430    }
5431    return 1;
5432 }
5433 
5434 /*----------------------------------------------------------------------*/
5435 /* Generate netlist, write information according to mode, and then	*/
5436 /* destroy the netlist (this will be replaced eventually with a dynamic	*/
5437 /* netlist model in which the netlist changes according to editing of	*/
5438 /* individual elements, not created and destroyed wholesale)		*/
5439 /*----------------------------------------------------------------------*/
5440 
writenet(objectptr thisobject,char * mode,char * suffix)5441 void writenet(objectptr thisobject, char *mode, char *suffix)
5442 {
5443    objectptr cschem;
5444    objinstptr thisinst;
5445    char filename[100];
5446    char *prefix, *cpos, *locmode = mode, *stsave = NULL;
5447    FILE *fp;
5448    /* Calllist topcell; (jdk) */
5449    Boolean is_spice = FALSE, sp_end_save = spice_end;
5450 
5451    /* Always use the master schematic, if there is one. */
5452 
5453    if (thisobject->schemtype == SECONDARY)
5454       cschem = thisobject->symschem;
5455    else
5456       cschem = thisobject;
5457 
5458    /* Update the netlist if this has not been done */
5459 
5460    if (NameToPageObject(cschem->name, &thisinst, NULL) == NULL) {
5461       Wprintf("Not a schematic. . . cannot generate output!\n");
5462       return;
5463    }
5464    if (updatenets(thisinst, FALSE) <= 0) {
5465       Wprintf("No file written!");
5466       return;
5467    }
5468 
5469    prefix = (char *)malloc(sizeof(char));
5470    *prefix = '\0';
5471 
5472    if ((cpos = strchr(cschem->name, ':')) != NULL) *cpos = '\0';
5473    sprintf(filename, "%s.%s", cschem->name, suffix);
5474    if (cpos != NULL) *cpos = ':';
5475 
5476    if (!strncmp(mode, "index", 5)) {	/* This mode generates no output */
5477       locmode += 5;
5478       fp = (FILE *)NULL;
5479    }
5480    else if ((fp = fopen(filename, "w")) == NULL) {
5481       Wprintf("Could not open file %s for writing.", filename);
5482       free(prefix);
5483       return;
5484    }
5485 
5486    /* Clear device indices from any previous netlist output */
5487    cleartraversed(cschem);
5488    clear_indices(cschem);
5489 
5490    /* Make sure list of include-once files is empty */
5491 
5492    free_included();
5493 
5494    /* Handle different netlist modes */
5495 
5496    if (!strcmp(mode, "spice")) {
5497 
5498       if (thisobject->schemtype == SYMBOL)
5499 	 cschem = thisobject->symschem;
5500 
5501       is_spice = TRUE;
5502       fprintf(fp, "*SPICE %scircuit <%s> from XCircuit v%s rev %s\n\n",
5503 		(thisobject->schemtype == SYMBOL) ? "sub" : "",
5504 		cschem->name, PROG_VERSION, PROG_REVISION);
5505 
5506       /* writeglobals(cschem, fp); */
5507       cleartraversed(cschem);
5508       writehierarchy(cschem, thisinst, NULL, fp, mode);
5509    }
5510    else if (!strcmp(mode, "flatspice")) {
5511       is_spice = TRUE;
5512       fprintf(fp, "*SPICE (flattened) circuit \"%s\" from XCircuit v%s rev %s\n\n",
5513 		cschem->name, PROG_VERSION, PROG_REVISION);
5514       if (stsave != NULL) {
5515 	 fputs(stsave, fp);
5516 	 fprintf(fp, "\n");
5517       }
5518       topflat(cschem, thisinst, NULL, prefix, fp, mode);
5519    }
5520    else if (!strcmp(mode, "pseuspice")) {
5521       is_spice = TRUE;
5522       fprintf(fp, "*SPICE subcircuit \"%s\" from XCircuit v%s rev %s\n\n",
5523 		cschem->name, PROG_VERSION, PROG_REVISION);
5524       if (stsave != NULL) {
5525 	 fputs(stsave, fp);
5526 	 fprintf(fp, "\n");
5527       }
5528       writeflat(cschem, NULL, prefix, fp, mode);
5529       freeflatindex();
5530    }
5531    else if (!strcmp(mode, "flatsim") || !strcmp(mode, "pseusim")) {
5532       fprintf(fp, "| sim circuit \"%s\" from XCircuit v%s rev %s\n",
5533 		cschem->name, PROG_VERSION, PROG_REVISION);
5534       if (stsave != NULL) {
5535 	 fputs(stsave, fp);
5536 	 fprintf(fp, "\n");
5537       }
5538       topflat(cschem, thisinst, NULL, prefix, fp, mode);
5539    }
5540    else if (!strcmp(locmode, "pcb")) {
5541       struct Ptab *ptable = (struct Ptab *)NULL;
5542       writepcb(&ptable, cschem, NULL, "", mode);
5543       if (stsave != NULL) {
5544 	 fputs(stsave, fp);
5545 	 fprintf(fp, "\n");
5546       }
5547       outputpcb(ptable, fp);
5548       freepcb(ptable);
5549    }
5550    else if (!strncmp(mode, "flat", 4)) {
5551       /* User-defined modes beginning with the word "flat":		*/
5552       /* Assume a flattened netlist, and assume nothing else.		*/
5553 
5554       if (thisobject->schemtype == SYMBOL)
5555 	 cschem = thisobject->symschem;
5556       cleartraversed(cschem);
5557       writeflat(cschem, NULL, prefix, fp, mode);
5558       freeflatindex();
5559    }
5560    else if (!strncmp(mode, "pseu", 4)) {
5561       /* User-defined modes beginning with the word "pseu":		*/
5562       /* Assume a flattened netlist for everything under the top level.	*/
5563 
5564       if (thisobject->schemtype == SYMBOL)
5565 	 cschem = thisobject->symschem;
5566       cleartraversed(cschem);
5567       topflat(cschem, thisinst, NULL, prefix, fp, mode);
5568    }
5569    else {
5570       /* All user-defined modes:  Assume a hierarchical netlist, and	*/
5571       /* assume nothing else.						*/
5572 
5573       if (thisobject->schemtype == SYMBOL)
5574 	 cschem = thisobject->symschem;
5575       cleartraversed(cschem);
5576       writehierarchy(cschem, thisinst, NULL, fp, mode);
5577    }
5578 
5579    /* Finish up SPICE files with a ".end" statement (if requested) */
5580    if (is_spice && (spice_end == True)) fprintf(fp, ".end\n");
5581    spice_end = sp_end_save;
5582 
5583    /* Finish up */
5584 
5585    if (fp != NULL) {
5586       fclose(fp);
5587       Wprintf("%s netlist saved as %s", mode, filename);
5588    }
5589    if (stsave != NULL) free(stsave);
5590    free(prefix);
5591 }
5592 
5593 /*----------------------------------------------------------------------*/
5594 /* Flatten netlist and save into a table of pcb-style nets		*/
5595 /* The routine is recursive.  Each call returns TRUE if some nested	*/
5596 /* call generated output;  this is a reasonable way to tell if we have	*/
5597 /* reached the bottom of the hierarchy.					*/
5598 /*----------------------------------------------------------------------*/
5599 
writepcb(struct Ptab ** ptableptr,objectptr cschem,CalllistPtr cfrom,char * prefix,char * mode)5600 Boolean writepcb(struct Ptab **ptableptr, objectptr cschem, CalllistPtr cfrom,
5601 		char *prefix, char *mode)
5602 {
5603    PolylistPtr plist;
5604    LabellistPtr llist;
5605    CalllistPtr calls;
5606    PortlistPtr ports;
5607    int i, testnet, tmplen, subnet;
5608    char *newprefix = (char *)malloc(sizeof(char));
5609    char *sout, *snew;
5610    struct Ptab *hidx, *htmp;
5611    struct Pstr *tmpstr;
5612    struct Pnet *tmpnet;
5613    objinstptr cinst;
5614    buslist *sbus;
5615    int locnet, lbus;
5616    Boolean outputdone = FALSE;
5617    Boolean outputcall;
5618 
5619    /* Step 1A:  Go through the polygons of this object and add	*/
5620    /* 		any unvisited nets to the table.		*/
5621 
5622    for (plist = cschem->polygons; plist != NULL; plist = plist->next) {
5623       for (lbus = 0;;) {
5624 	 if (plist->subnets == 0)
5625 	    testnet = plist->net.id;
5626 	 else {
5627 	    sbus = plist->net.list + lbus;
5628 	    testnet = sbus->netid;
5629 	 }
5630 
5631          hidx = *ptableptr;
5632          while (hidx != NULL) {
5633             if (hidx->nets != NULL) {
5634 	       for (i = 0; i < hidx->nets->numnets; i++)
5635 	          if (*(hidx->nets->netidx + i) == testnet) break;
5636 	       if (i < hidx->nets->numnets) break;
5637 	    }
5638 	    hidx = hidx->next;
5639          }
5640          if (hidx == NULL) {	/* make new entry for net in table */
5641 	    htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5642 	    tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5643 	    tmpnet->numnets = 1;
5644 	    tmpnet->netidx = (int *)malloc(sizeof(int));
5645 	    *(tmpnet->netidx) = testnet;
5646 	    tmpnet->next = NULL;
5647 	    htmp->cschem = cschem;
5648 	    htmp->nets = tmpnet;
5649 	    htmp->pins = NULL;
5650 	    htmp->next = *ptableptr;
5651 	    *ptableptr = htmp;
5652 	    /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5653          }
5654 	 if (++lbus >= plist->subnets) break;
5655       }
5656    }
5657 
5658    /* Step 1B:  Do the same thing for labels.			*/
5659 
5660    for (llist = cschem->labels; llist != NULL; llist = llist->next) {
5661       for (lbus = 0;;) {
5662 	 if (llist->subnets == 0)
5663 	    testnet = llist->net.id;
5664 	 else {
5665 	    sbus = llist->net.list + lbus;
5666 	    testnet = sbus->netid;
5667 	 }
5668 
5669          hidx = *ptableptr;
5670          while (hidx != NULL) {
5671             if (hidx->nets != NULL) {
5672 	       for (i = 0; i < hidx->nets->numnets; i++)
5673 	          if (*(hidx->nets->netidx + i) == testnet) break;
5674 	       if (i < hidx->nets->numnets) break;
5675 	    }
5676 	    hidx = hidx->next;
5677          }
5678          if (hidx == NULL) {	/* make new entry for net in table */
5679 	    htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5680 	    tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5681 	    tmpnet->numnets = 1;
5682 	    tmpnet->netidx = (int *)malloc(sizeof(int));
5683 	    *(tmpnet->netidx) = testnet;
5684 	    tmpnet->next = NULL;
5685 	    htmp->cschem = cschem;
5686 	    htmp->nets = tmpnet;
5687 	    htmp->pins = NULL;
5688 	    htmp->next = *ptableptr;
5689 	    *ptableptr = htmp;
5690 	    /* Fprintf(stdout, "Added new index entry for net %d\n", testnet); */
5691          }
5692 	 if (++lbus >= llist->subnets) break;
5693       }
5694    }
5695 
5696    /* Step 2:  Resolve fixed device indices */
5697    resolve_devindex(cschem, FALSE);
5698 
5699    /* Step 3:  Go through the list of calls to search for endpoints */
5700 
5701    for (calls = cschem->calls; calls != NULL; calls = calls->next) {
5702       objectptr cthis;
5703 
5704       cinst = calls->callinst;
5705 
5706       /* Trivial objects should have been dealt with already.		*/
5707       /* If we don't continue the loop here, you get netlist output for	*/
5708       /* objects like dots and circles.					*/
5709       if (calls->callobj->schemtype == TRIVIAL) continue;
5710 
5711       /* Step 4:  If call is to a bottom-most schematic, output the device connections */
5712       /* Info-label can provide an alternate name or specify the instance number */
5713 
5714       cthis = calls->callobj;
5715       if (calls->callobj->schemtype == PRIMARY || calls->callobj->schemtype ==
5716 		SECONDARY)
5717 	 if (calls->callobj->symschem != NULL)
5718 	    cthis = calls->callobj->symschem;
5719 
5720       if ((sout = parseinfo(cschem, cthis, calls, prefix, mode, FALSE, TRUE)) == NULL) {
5721 	 if (calls->devname == NULL)
5722 	    calls->devname = strdup(calls->callinst->thisobject->name);
5723          sprintf(_STR, "%s_%s", calls->devname, d36a(devindex(cschem, calls)));
5724       }
5725       else {
5726 	 sscanf(sout, "%s", _STR);	/* Copy the first word out of sout */
5727       }
5728       newprefix = (char *)realloc(newprefix, sizeof(char) * (strlen(prefix)
5729 		+ strlen(_STR) + 2));
5730       sprintf(newprefix, "%s%s/", prefix, _STR);
5731 
5732       /*----------------------------------------------------------------*/
5733       /* Parsing of the "pcb:" info label---				*/
5734       /* Find all <net>=<name> strings and create the appropriate nets	*/
5735       /*----------------------------------------------------------------*/
5736 
5737       if (sout) {
5738 	 char rsave, *lhs, *rhs, *rend, *sptr = sout, *tmppinname;
5739 
5740 	 while ((rhs = strchr(sptr, '=')) != NULL) {
5741 	    Genericlist *implicit, newlist;
5742 	    struct Pstr *psrch = NULL;
5743 
5744 	    lhs = rhs - 1;
5745 	    while ((lhs >= sptr) && isspace(*lhs)) lhs--;
5746 	    while ((lhs >= sptr) && !isspace(*lhs)) lhs--;
5747 	    *rhs = '\0';
5748 	    rhs++;
5749 	    if (isspace(*lhs)) lhs++;
5750 	    while ((*rhs) && isspace(*rhs)) rhs++;
5751 	    for (rend = rhs; (*rend) && (!isspace(*rend)); rend++);
5752 	    rsave = *rend;
5753 	    *rend = '\0';
5754 
5755 	    /* Get the net from the equation RHS.  If none exists, assume  */
5756 	    /* the name is a global net and add it to the list of globals. */
5757 
5758 	    implicit = nametonet(cschem, cinst, rhs);
5759 	    if (implicit == NULL) {
5760 	       stringpart *strptr;
5761 	       label templabel;
5762 
5763 	       labeldefaults(&templabel, GLOBAL, 0, 0);
5764 	       strptr = templabel.string;
5765 	       strptr->type = TEXT_STRING;
5766 	       strptr->data.string = strdup(rhs);
5767 	       newlist.subnets = 0;
5768 	       newlist.net.id = globalmax() - 1;
5769 	       addglobalpin(cschem, cinst, &templabel, &newlist);
5770 	       implicit = &newlist;
5771 	       free(strptr->data.string);
5772 	    }
5773 
5774 	    /* Get the name of the pin */
5775 	    tmppinname = (char *)malloc(strlen(newprefix) + strlen(lhs) + 2);
5776 	    strcpy(tmppinname, newprefix);
5777 	    if ((tmplen = strlen(newprefix)) > 0) tmppinname[tmplen - 1] = '-';
5778 	    strcat(tmppinname, lhs);
5779 
5780 	    /* Find the net in the ptable, or create a new entry for it */
5781 
5782 	    hidx = *ptableptr;
5783 	    while (hidx != NULL) {
5784 	       if (hidx->nets != NULL) {
5785 		  for (i = 0; i < hidx->nets->numnets; i++)
5786 		     if (*(hidx->nets->netidx + i) == implicit->net.id)
5787 			 break;
5788 		  if (i < hidx->nets->numnets) break;
5789 	       }
5790 	       hidx = hidx->next;
5791 	    }
5792 	    if (hidx == NULL) {
5793 	       htmp = (struct Ptab *)malloc(sizeof(struct Ptab));
5794 	       tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5795 	       tmpnet->numnets = 1;
5796 	       tmpnet->netidx = (int *)malloc(sizeof(int));
5797 	       *(tmpnet->netidx) = implicit->net.id;
5798 	       tmpnet->next = NULL;
5799 	       htmp->cschem = cschem;
5800 	       htmp->nets = tmpnet;
5801 	       htmp->pins = NULL;
5802 	       htmp->next = *ptableptr;
5803 	       *ptableptr = htmp;
5804 	       hidx = htmp;
5805 	    }
5806 	    else {
5807 	       /* Check if any entries are the same as tmppinname */
5808 	       for (psrch = hidx->pins; psrch != NULL; psrch = psrch->next) {
5809 		  if (!strcmp(psrch->string->data.string, tmppinname))
5810 		     break;
5811 	       }
5812 	    }
5813 
5814 	    /* Get the pin name from the equation LHS */
5815 	    if (psrch == NULL) {
5816 	       tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5817 	       tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5818 	       tmpstr->string->type = TEXT_STRING;
5819 	       tmpstr->string->nextpart = NULL;
5820 	       tmpstr->string->data.string = tmppinname;
5821 	       tmpstr->next = hidx->pins;
5822 	       hidx->pins = tmpstr;
5823 	    }
5824 	    else {
5825 	       free(tmppinname);
5826 	    }
5827 
5828 	    /* Proceed to the next LHS=RHS pair */
5829 	    *rend = rsave;
5830 	    sptr = rend;
5831 	 }
5832 	 free(sout);
5833       }
5834 
5835       outputcall = FALSE;
5836       if (calls->callobj->calls != NULL) {
5837 
5838          /* Step 4A: Push current net translations */
5839 	 /* (Don't push or pop global net numbers:  no translation needed!) */
5840 
5841          hidx = *ptableptr;
5842          while (hidx != NULL) {
5843             if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5844 			(*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5845                tmpnet = (struct Pnet *)malloc(sizeof(struct Pnet));
5846 	       tmpnet->numnets = 0;
5847                tmpnet->netidx = NULL;
5848                tmpnet->next = hidx->nets;
5849                hidx->nets = tmpnet;
5850             }
5851             hidx = hidx->next;
5852          }
5853 
5854 	 /* Step 4B: Generate net translation table for each subcircuit */
5855 
5856          for (ports = calls->ports; ports != NULL; ports = ports->next) {
5857 	    for (hidx = *ptableptr; hidx != NULL; hidx = hidx->next) {
5858                if (hidx->nets != NULL) {
5859 	          if (hidx->nets->next != NULL) {
5860 		     for (i = 0; i < hidx->nets->next->numnets; i++)
5861 		        if (*(hidx->nets->next->netidx + i) == ports->netid)
5862 			   break;
5863 		     if (i < hidx->nets->next->numnets) break;
5864 		  }
5865 		  else if (ports->netid < 0) {
5866 		     if (hidx->nets->netidx != NULL)
5867 			if (*(hidx->nets->netidx) == ports->netid)
5868 			   break;
5869 		  }
5870 	       }
5871             }
5872 	    if (hidx != NULL) {
5873 	       hidx->nets->numnets++;
5874 	       if (hidx->nets->numnets == 1)
5875 		  hidx->nets->netidx = (int *)malloc(sizeof(int));
5876 	       else
5877 		  hidx->nets->netidx = (int *)realloc(hidx->nets->netidx,
5878 			hidx->nets->numnets * sizeof(int));
5879 
5880 	       /* Translate net value */
5881 	       locnet = translatedown(ports->netid, ports->portid,
5882 				calls->callobj);
5883 	       *(hidx->nets->netidx + hidx->nets->numnets - 1) = locnet;
5884 
5885 	       /* Fprintf(stdout, "Translation: net %d in object %s is net "
5886 			"%d in object %s\n", ports->netid, cschem->name,
5887 			locnet, calls->callobj->name); */
5888 	    }
5889          }
5890 
5891          /* Step 4C: Run routine recursively on the subcircuit */
5892 	 /* If it had a "pcb:" info label that was handled, then ignore */
5893 
5894 	 /* Fprintf(stdout, "Recursive call of writepcb() to %s\n",
5895 		calls->callobj->name); */
5896 
5897 	 outputcall = writepcb(ptableptr, calls->callobj, calls, newprefix, mode);
5898 
5899          /* Step 4D: Pop the translation table */
5900 	 /* (Don't pop global nets (designated by negative net number)) */
5901 
5902          hidx = *ptableptr;
5903          while (hidx != NULL) {
5904             if ((hidx->nets != NULL) && (((hidx->nets->numnets > 0) &&
5905 			(*(hidx->nets->netidx) >= 0)) || (hidx->nets->numnets == 0))) {
5906 	       tmpnet = hidx->nets->next;
5907 	       if (hidx->nets->numnets > 0) free(hidx->nets->netidx);
5908 	       free(hidx->nets);
5909 	       hidx->nets = tmpnet;
5910             }
5911             hidx = hidx->next;
5912          }
5913       }
5914 
5915       if (!outputcall) {
5916 	 stringpart *ppin;
5917 
5918          /* Fprintf(stdout, "Reached lowest-level schematic:  Finding connections\n"); */
5919 	 for (ports = calls->ports; ports != NULL; ports = ports->next) {
5920 	    locnet = translatedown(ports->netid, ports->portid, calls->callobj);
5921 	    /* Global pin names should probably be ignored, not just	*/
5922 	    /* when an object is declared to be "trivial".  Not sure if	*/
5923 	    /* this breaks certain netlists. . .			*/
5924 	    if (locnet < 0) continue;
5925 
5926 	    /* Get the name of the pin in the called object with no prefix. */
5927 	    subnet = getsubnet(locnet, calls->callobj);
5928 	    ppin = nettopin(locnet, calls->callobj, NULL);
5929 	    hidx = *ptableptr;
5930 	    while (hidx != NULL) {
5931 	       if ((hidx->nets != NULL) && (hidx->nets->numnets > 0)) {
5932 		  /* Global nets were not translated, do not iterate through list */
5933 		  if (*(hidx->nets->netidx) >= 0) {
5934 		     for (i = 0; i < hidx->nets->numnets; i++)
5935 	                if (*(hidx->nets->netidx + i) == ports->netid)
5936 		           break;
5937 		     if (i < hidx->nets->numnets) break;
5938 		  } else {
5939 	             if (*(hidx->nets->netidx) == ports->netid) break;
5940 		  }
5941 	       }
5942 	       hidx = hidx->next;
5943 	    }
5944 	    if (hidx == NULL) {
5945 	       snew = textprintsubnet(ppin, cinst, subnet);
5946 	       Fprintf(stdout, "Warning:  Unconnected pin %s%s\n", newprefix, snew);
5947 	       free(snew);
5948 	    }
5949 	    else {
5950 	       outputcall = TRUE;
5951 	       outputdone = TRUE;
5952 	       tmpstr = (struct Pstr *)malloc(sizeof(struct Pstr));
5953 	       tmpstr->string = (stringpart *)malloc(sizeof(stringpart));
5954 	       tmpstr->string->type = TEXT_STRING;
5955 	       tmpstr->string->nextpart = NULL;
5956 	       snew = textprintsubnet(ppin, cinst, subnet);
5957 	       tmpstr->string->data.string = (char *)malloc(strlen(newprefix)
5958 			+ strlen(snew) + 2);
5959 	       strcpy(tmpstr->string->data.string, newprefix);
5960 	       /* Replace slash '/' with dash '-' at bottommost level */
5961 	       if ((tmplen = strlen(newprefix)) > 0)
5962 	          tmpstr->string->data.string[tmplen - 1] = '-';
5963 	       strcat(tmpstr->string->data.string, snew);
5964 	       free(snew);
5965 	       tmpstr->next = hidx->pins;
5966 	       hidx->pins = tmpstr;
5967 
5968 	       /* diagnostic information */
5969 	       {
5970 		  struct Pnet *locnet = hidx->nets;
5971 		  int ctr = 0;
5972 		  while (locnet->next != NULL) {
5973 		     locnet = locnet->next;
5974 		     ctr++;
5975 		  }
5976 	          /* Fprintf(stdout, "Logged level-%d net %d (local net %d) pin %s\n",
5977 			ctr, *(locnet->netidx), *(hidx->nets->netidx + i),
5978 			tmpstr->string); */
5979 	       }
5980             }
5981          }
5982       }
5983    }
5984 
5985    /* Step 5: Cleanup */
5986 
5987    free(newprefix);
5988    return outputdone;
5989 }
5990 
5991 /*----------------------------------------------------------------------*/
5992 /* Save PCB table into pcb-style file				   	*/
5993 /*----------------------------------------------------------------------*/
5994 
outputpcb(struct Ptab * ptable,FILE * fp)5995 void outputpcb(struct Ptab *ptable, FILE *fp)
5996 {
5997    int netidx = 1, ccol, subnet;
5998    struct Ptab *pseek;
5999    struct Pstr *sseek;
6000    char *snew;
6001    stringpart *ppin;
6002 
6003    if (fp == NULL) return;
6004 
6005    for (pseek = ptable; pseek != NULL; pseek = pseek->next) {
6006       if (pseek->pins != NULL) {
6007 	 if ((pseek->nets != NULL) && (pseek->nets->numnets > 0)) {
6008 	     subnet = getsubnet(*(pseek->nets->netidx), pseek->cschem);
6009 	     ppin = nettopin(*(pseek->nets->netidx), pseek->cschem, "");
6010 	     snew = textprintsubnet(ppin, NULL, subnet);
6011 	     strcpy(_STR, snew);
6012 	     free(snew);
6013 	 }
6014 	 else
6015              sprintf(_STR, "NET%d ", netidx++);
6016          fprintf(fp, "%-11s ", _STR);
6017          ccol = 12;
6018          for (sseek = pseek->pins; sseek != NULL; sseek = sseek->next) {
6019 	    ccol += stringlength(sseek->string, False, NULL) + 3;
6020 	    if (ccol > 78) {
6021 	       fprintf(fp, "\\\n              ");
6022       	       ccol = 18 + stringlength(sseek->string, False, NULL);
6023 	    }
6024 	    snew = textprint(sseek->string, NULL);
6025 	    fprintf(fp, "%s   ", snew);
6026 	    free(snew);
6027          }
6028          fprintf(fp, "\n");
6029       }
6030       /* else fprintf(fp, "NET%d	*UNCONNECTED*\n", netidx++);	*/
6031    }
6032 }
6033 
6034 /*----------------------------------------------*/
6035 /* free memory allocated to PCB net tables	*/
6036 /*----------------------------------------------*/
6037 
freepcb(struct Ptab * ptable)6038 void freepcb(struct Ptab *ptable)
6039 {
6040    struct Ptab *pseek, *pseek2;
6041    struct Pstr *sseek, *sseek2;
6042    struct Pnet *nseek, *nseek2;
6043 
6044    pseek = ptable;
6045    pseek2 = pseek;
6046 
6047    while (pseek2 != NULL) {
6048       pseek = pseek->next;
6049 
6050       sseek = pseek2->pins;
6051       sseek2 = sseek;
6052       while (sseek2 != NULL) {
6053 	 sseek = sseek->next;
6054 	 freelabel(sseek2->string);
6055 	 free(sseek2);
6056 	 sseek2 = sseek;
6057       }
6058 
6059       nseek = pseek2->nets;
6060       nseek2 = nseek;
6061       while (nseek2 != NULL) {
6062 	 nseek = nseek->next;
6063 	 if (nseek2->numnets > 0) free(nseek2->netidx);
6064 	 free(nseek2);
6065 	 nseek2 = nseek;
6066       }
6067 
6068       free(pseek2);
6069       pseek2 = pseek;
6070    }
6071 }
6072 
6073 /*----------------------------------------------------------------------*/
6074 /* Remove an element from the netlist.  This is necessary when an	*/
6075 /* element is deleted from the object, so that the netlist does not	*/
6076 /* refer to a nonexistant element, or try to perform netlist function	*/
6077 /* on it.								*/
6078 /*									*/
6079 /* Return True or False depending on whether a pin was "orphaned" on	*/
6080 /* the corresponding schematic or symbol (if any), as this may cause	*/
6081 /* the class of the object (FUNDAMENTAL, TRIVIAL, etc.) to change.	*/
6082 /*----------------------------------------------------------------------*/
6083 
RemoveFromNetlist(objectptr thisobject,genericptr thiselem)6084 Boolean RemoveFromNetlist(objectptr thisobject, genericptr thiselem)
6085 {
6086    Boolean pinchanged = False;
6087    labelptr nlab;
6088    polyptr npoly;
6089    objectptr pschem;
6090    objinstptr ninst;
6091 
6092    PolylistPtr plist, plast;
6093    LabellistPtr llist, llast;
6094    CalllistPtr clist, clast;
6095 
6096    pschem = (thisobject->schemtype == SECONDARY) ? thisobject->symschem
6097 	: thisobject;
6098 
6099    switch (thiselem->type) {
6100       case OBJINST:
6101 	 ninst = (objinstptr)thiselem;
6102 	 /* If this is an object instance, remove it from the calls */
6103 	 clast = NULL;
6104 	 for (clist = pschem->calls; clist != NULL; clist = clist->next) {
6105 	    if (clist->callinst == ninst) {
6106 	       if (clast == NULL)
6107 		  pschem->calls = clist->next;
6108 	       else
6109 		  clast->next = clist->next;
6110 	       freecalls(clist);
6111 	       clist = (clast) ? clast : pschem->calls;
6112 	       break;
6113 	    }
6114 	    else
6115 	       clast = clist;
6116 	 }
6117 	 break;
6118 
6119       case POLYGON:
6120 	 npoly = (polyptr)thiselem;
6121 	 if (nonnetwork(npoly)) break;
6122 
6123 	 /* If this is a polygon, remove it from the netlist */
6124 	 plast = NULL;
6125 	 for (plist = pschem->polygons; plist != NULL; plist = plist->next) {
6126 	    if (plist->poly == npoly) {
6127 	       if (plast == NULL)
6128 		  pschem->polygons = plist->next;
6129 	       else
6130 		  plast->next = plist->next;
6131 	       if (plist->subnets > 0)
6132 		  free(plist->net.list);
6133 	       break;
6134 	    }
6135 	    else
6136 	       plast = plist;
6137 	 }
6138 	 break;
6139 
6140       case LABEL:
6141 	 nlab = (labelptr)thiselem;
6142 	 if ((nlab->pin != LOCAL) && (nlab->pin != GLOBAL)) break;
6143 
6144 	 /* If this is a label, remove it from the netlist */
6145 	 llast = NULL;
6146 	 for (llist = pschem->labels; llist != NULL; llist = llist->next) {
6147 	    if (llist->label == nlab) {
6148 	       if (llast == NULL)
6149 		  pschem->labels = llist->next;
6150 	       else
6151 		  llast->next = llist->next;
6152 	       if (llist->subnets > 0)
6153 		  free(llist->net.list);
6154 	       break;
6155 	    }
6156 	    else
6157 	       llast = llist;
6158 	 }
6159 
6160 	 /* Mark pin label in corresponding schematic/symbol as "orphaned" */
6161 	 /* by changing designation from type "pin" to type "label".       */
6162 
6163 	 if (findlabelcopy(nlab, nlab->string) == NULL) {
6164 	    changeotherpins(NULL, nlab->string);
6165 	    if (nlab->pin == INFO) pinchanged = True;
6166 	 }
6167    }
6168    return pinchanged;
6169 }
6170 
6171 /*----------------------------------------------------------------------*/
6172 /* Remove one element from the netlist					*/
6173 /*----------------------------------------------------------------------*/
6174 
remove_netlist_element(objectptr cschem,genericptr genelem)6175 void remove_netlist_element(objectptr cschem, genericptr genelem) {
6176 
6177    objectptr pschem;
6178    CalllistPtr clist, clast, cnext;
6179    LabellistPtr llist, llast, lnext;
6180    PolylistPtr plist, plast, pnext;
6181    Boolean found = FALSE;
6182 
6183    /* Always call on the primary schematic */
6184    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6185 
6186    switch (ELEMENTTYPE(genelem)) {
6187       case POLYGON:
6188 	 /* remove polygon from polygon list */
6189 	 plast = NULL;
6190 	 for (plist = pschem->polygons; plist != NULL; ) {
6191 	    pnext = plist->next;
6192 	    if ((genericptr)plist->poly == genelem) {
6193 	       found = TRUE;
6194 	       if (plist->subnets > 0)
6195 	          free(plist->net.list);
6196 	       free(plist);
6197 	       if (plast != NULL)
6198 	          plast->next = pnext;
6199 	       else
6200 		  pschem->polygons = pnext;
6201 	       break;
6202 	    }
6203 	    else
6204 	       plast = plist;
6205 	    plist = pnext;
6206 	 }
6207 	 break;
6208 
6209       case LABEL:
6210 
6211 	 /* remove label from label list */
6212 	 llast = NULL;
6213 	 for (llist = pschem->labels; llist != NULL; ) {
6214 	    lnext = llist->next;
6215 	    if ((genericptr)llist->label == genelem) {
6216 	       found = TRUE;
6217 	       if (llist->subnets > 0)
6218 	          free(llist->net.list);
6219 	       free(llist);
6220 	       if (llast != NULL)
6221 	          llast->next = lnext;
6222 	       else
6223 		  pschem->labels = lnext;
6224 	       break;
6225 	    }
6226 	    else
6227 	       llast = llist;
6228 	    llist = lnext;
6229 	 }
6230 
6231 	 /* also check the globals */
6232 	 llast = NULL;
6233 	 for (llist = global_labels; llist != NULL; ) {
6234 	    lnext = llist->next;
6235 	    if ((genericptr)llist->label == genelem) {
6236 	       found = TRUE;
6237 	       if (llist->subnets > 0)
6238 	          free(llist->net.list);
6239 	       free(llist);
6240 	       if (llast != NULL)
6241 	          llast->next = lnext;
6242 	       else
6243 		  global_labels = lnext;
6244 	       break;
6245 	    }
6246 	    else
6247 	       llast = llist;
6248 	    llist = lnext;
6249          }
6250 	 break;
6251 
6252       case OBJINST:
6253 
6254 	 /* remove instance from call list */
6255 	 clast = NULL;
6256 	 for (clist = pschem->calls; clist != NULL; ) {
6257 	    cnext = clist->next;
6258 	    if ((genericptr)clist->callinst == genelem) {
6259 	       found = TRUE;
6260 	       freecalls(clist);
6261 	       if (clast != NULL)
6262 	          clast->next = cnext;
6263 	       else
6264 		  pschem->calls = cnext;
6265 	    }
6266 	    else
6267 	       clast = clist;
6268 	    clist = cnext;
6269 	 }
6270 	 break;
6271    }
6272    if (found)
6273       pschem->valid = FALSE;
6274 }
6275 
6276 /*----------------------------------------------------------------------*/
6277 /* Free memory allocated for the ports in a calls.			*/
6278 /*----------------------------------------------------------------------*/
6279 
freecalls(CalllistPtr calls)6280 void freecalls(CalllistPtr calls)
6281 {
6282    PortlistPtr ports, pptr;
6283 
6284    for (ports = calls->ports; ports != NULL;) {
6285       pptr = ports->next;
6286       free(ports);
6287       ports = pptr;
6288    }
6289    if (calls->devname != NULL) free(calls->devname);
6290    free(calls);
6291 }
6292 
6293 /*----------------------------------------------------------------------*/
6294 /* Free memory for a Genericlist structure (may also be a Labellist or	*/
6295 /* Polylist structure).							*/
6296 /*----------------------------------------------------------------------*/
6297 
freegenlist(Genericlist * nets)6298 void freegenlist(Genericlist *nets)
6299 {
6300    if (nets == NULL) return;
6301    if (nets->subnets > 0)
6302       free(nets->net.list);
6303    free(nets);
6304 }
6305 
6306 /*----------------------------------------------------------------------*/
6307 /* Free memory allocated for the label list in a netlist.		*/
6308 /*----------------------------------------------------------------------*/
6309 
freelabellist(LabellistPtr * listtop)6310 void freelabellist(LabellistPtr *listtop)
6311 {
6312    LabellistPtr labellist, llist;
6313 
6314    for (labellist = *listtop; labellist != NULL;) {
6315       llist = labellist->next;
6316       freegenlist((Genericlist *)labellist);
6317       labellist = llist;
6318    }
6319    *listtop = NULL;
6320 }
6321 
6322 /*----------------------------------------------------------------------*/
6323 /* Free memory allocated for the polygon list in a netlist.		*/
6324 /*----------------------------------------------------------------------*/
6325 
freepolylist(PolylistPtr * listtop)6326 void freepolylist(PolylistPtr *listtop)
6327 {
6328    PolylistPtr polylist, plist;
6329 
6330    for (polylist = *listtop; polylist != NULL;) {
6331       plist = polylist->next;
6332       freegenlist((Genericlist *)polylist);
6333       polylist = plist;
6334    }
6335    *listtop = NULL;
6336 }
6337 
6338 /*----------------------------------------------------------------------*/
6339 /* Free memory allocated for netlist 					*/
6340 /*----------------------------------------------------------------------*/
6341 
freenetlist(objectptr cschem)6342 void freenetlist(objectptr cschem)
6343 {
6344    PolylistPtr *plist;
6345    LabellistPtr *llist;
6346 
6347    plist = &cschem->polygons;
6348    freepolylist(plist);
6349    llist = &cschem->labels;
6350    freelabellist(llist);
6351 }
6352 
6353 /*----------------------------------------------------------------------*/
6354 /* Clear the "traversed" flag in all objects of the hierarchy.		*/
6355 /*----------------------------------------------------------------------*/
6356 
cleartraversed_level(objectptr cschem,int level)6357 int cleartraversed_level(objectptr cschem, int level)
6358 {
6359    genericptr *cgen;
6360    objinstptr cinst;
6361    objectptr callobj, pschem;
6362 
6363    /* Always call on the primary schematic */
6364    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6365 
6366    /* Recursively call cleartraversed() on all subobjects, a la gennetlist()  */
6367    /* Use the parts list of the object, not the calls, because calls */
6368    /* may have been removed.						*/
6369 
6370    if (level == HIERARCHY_LIMIT) return -1;
6371 
6372    for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6373       if (IS_OBJINST(*cgen)) {
6374  	 cinst = TOOBJINST(cgen);
6375 
6376 	 if (cinst->thisobject->symschem != NULL)
6377 	    callobj = cinst->thisobject->symschem;
6378 	 else
6379 	    callobj = cinst->thisobject;
6380 
6381 	 /* Don't infinitely recurse if object is on its own schematic	  */
6382 	 /* However, we need to take a stab at more subtle recursion, too */
6383 
6384 	 if (callobj != pschem)
6385 	    if (cleartraversed_level(callobj, level + 1) == -1)
6386 	       return -1;
6387       }
6388    }
6389    pschem->traversed = False;
6390 
6391    return 0;
6392 }
6393 
6394 /*----------------------------------------------------------------------*/
6395 /* This is the routine normally called, as it hides the "level"		*/
6396 /* argument tagging the level of recursion.				*/
6397 /*----------------------------------------------------------------------*/
6398 
cleartraversed(objectptr cschem)6399 int cleartraversed(objectptr cschem) {
6400    return cleartraversed_level(cschem, 0);
6401 }
6402 
6403 /*----------------------------------------------------------------------*/
6404 /* If any part of the netlist is invalid, destroy the entire netlist 	*/
6405 /*----------------------------------------------------------------------*/
6406 
checkvalid(objectptr cschem)6407 int checkvalid(objectptr cschem)
6408 {
6409    genericptr *cgen;
6410    objinstptr cinst;
6411    objectptr callobj, pschem;
6412 
6413    /* If the object has been declared a non-network object, ignore it */
6414    if (cschem->schemtype == NONETWORK) return 0;
6415 
6416    /* Always operate on the master schematic */
6417    pschem = (cschem->schemtype == SECONDARY) ? cschem->symschem : cschem;
6418 
6419    /* Stop immediately if the netlist is invalid */
6420    if (pschem->valid == False) return -1;
6421 
6422    /* Otherwise, recursively call checkvalid() on all subobjects.	*/
6423    /* Use the parts list of the object, not the calls, because calls */
6424    /* may have been removed.						*/
6425 
6426    for (cgen = pschem->plist; cgen < pschem->plist + pschem->parts; cgen++) {
6427       if (IS_OBJINST(*cgen)) {
6428  	 cinst = TOOBJINST(cgen);
6429 
6430 	 if (cinst->thisobject->symschem != NULL)
6431 	    callobj = cinst->thisobject->symschem;
6432 	 else
6433 	    callobj = cinst->thisobject;
6434 
6435 	 /* Don't infinitely recurse if object is on its own schematic	  */
6436 
6437 	 if (callobj == pschem) continue;
6438 
6439 	 /* If there is a symbol, don't check its parts, but check if 	*/
6440 	 /* its netlist has been checkvalid.				*/
6441 
6442 	 if ((cinst->thisobject->symschem != NULL) &&
6443 		(cinst->thisobject->labels == NULL) &&
6444 		(cinst->thisobject->polygons == NULL) &&
6445 		(cinst->thisobject->valid == False))
6446 	    return -1;
6447 
6448 	 /* Recursive call on subschematic */
6449 	 if (checkvalid(callobj) == -1)
6450 	    return -1;
6451       }
6452    }
6453    return 0;	/* All subnetlists and own netlist are valid */
6454 }
6455 
6456 /*----------------------------------------------------------------------*/
6457 /* Free memory allocated to temporary labels generated for the netlist	*/
6458 /*----------------------------------------------------------------------*/
6459 
freetemplabels(objectptr cschem)6460 void freetemplabels(objectptr cschem)
6461 {
6462    genericptr *cgen;
6463    objinstptr cinst;
6464    objectptr callobj;
6465 
6466    /* Recursively call freetemplabels() on all subobjects, a la gennetlist()  */
6467    /* Use the parts list of the object, not the calls, because calls */
6468    /* may have been removed.						*/
6469 
6470    for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6471       if (IS_OBJINST(*cgen)) {
6472 
6473  	 cinst = TOOBJINST(cgen);
6474 	 if (cinst->thisobject->symschem != NULL)
6475 	    callobj = cinst->thisobject->symschem;
6476 	 else
6477 	    callobj = cinst->thisobject;
6478 
6479 	 /* Don't infinitely recurse if object is on its own schematic */
6480 	 if (callobj != cschem) freetemplabels(callobj);
6481 
6482 	 /* Also free the temp labels of any associated symbol */
6483 	 if (cinst->thisobject->symschem != NULL) freetemplabels(cinst->thisobject);
6484       }
6485 
6486       /* Free any temporary labels which have been created */
6487 
6488       else if (IS_LABEL(*cgen)) {
6489 	 labelptr clab = TOLABEL(cgen);
6490 	 /* int tmpval = (int)(cgen - cschem->plist); (jdk) */
6491 	 if (clab->string->type != FONT_NAME) {
6492 	    genericptr *tgen;
6493 
6494 	    clab = TOLABEL(cgen);
6495 	    freelabel(clab->string);
6496 	    free(clab);
6497 	    for (tgen = cgen + 1; tgen < cschem->plist + cschem->parts; tgen++)
6498 	       *(tgen - 1) = *tgen;
6499 	    cschem->parts--;
6500 	    cgen--;
6501 	 }
6502       }
6503    }
6504 }
6505 
6506 /*----------------------------------------------------------------------*/
6507 /* Free memory allocated for netlists, ports, and calls			*/
6508 /*----------------------------------------------------------------------*/
6509 
freenets(objectptr cschem)6510 void freenets(objectptr cschem)
6511 {
6512    CalllistPtr calls, cptr;
6513    PortlistPtr ports, pptr;
6514    genericptr *cgen;
6515    objinstptr cinst;
6516    objectptr callobj;
6517 
6518    /* Recursively call freenets() on all subobjects, a la gennetlist()  */
6519    /* Use the parts list of the object, not the calls, because calls */
6520    /* may have been removed.						*/
6521 
6522    if (cschem->schemtype == PRIMARY || cschem->schemtype == SECONDARY ||
6523 		(cschem->schemtype == SYMBOL && cschem->symschem == NULL)) {
6524       for (cgen = cschem->plist; cgen < cschem->plist + cschem->parts; cgen++) {
6525          if (IS_OBJINST(*cgen)) {
6526 
6527  	    cinst = TOOBJINST(cgen);
6528 	    if (cinst->thisobject->symschem != NULL)
6529 	       callobj = cinst->thisobject->symschem;
6530 	    else
6531 	       callobj = cinst->thisobject;
6532 
6533 	    /* Don't infinitely recurse if object is on its own schematic */
6534 	    if (callobj != cschem) freenets(callobj);
6535 
6536 	    /* Also free the netlist of any associated symbol */
6537 	    if (cinst->thisobject->symschem != NULL) freenets(cinst->thisobject);
6538 	 }
6539       }
6540    }
6541 
6542    /* Free the allocated structures for this object */
6543 
6544    for (calls = cschem->calls; calls != NULL;) {
6545       cptr = calls->next;
6546       freecalls(calls);
6547       calls = cptr;
6548    }
6549    cschem->calls = NULL;
6550 
6551    for (ports = cschem->ports; ports != NULL;) {
6552       pptr = ports->next;
6553       free(ports);
6554       ports = pptr;
6555    }
6556    cschem->ports = NULL;
6557 
6558    freenetlist(cschem);
6559 
6560    cschem->traversed = False;
6561    cschem->valid = False;
6562    freegenlist(cschem->highlight.netlist);
6563    cschem->highlight.netlist = NULL;
6564    cschem->highlight.thisinst = NULL;
6565 }
6566 
6567 /*----------------------------------------------------------------------*/
6568 /* Free the global pin list 					   	*/
6569 /*----------------------------------------------------------------------*/
6570 
freeglobals()6571 void freeglobals()
6572 {
6573    LabellistPtr labellist, llist;
6574 
6575    for (labellist = global_labels; labellist != NULL;) {
6576       llist = labellist->next;
6577 
6578       /* Labels in the global list are temporary and must be deallocated */
6579       freelabel(labellist->label->string);
6580       free(labellist->label);
6581 
6582       freegenlist((Genericlist *)labellist);
6583       labellist = llist;
6584    }
6585    global_labels = NULL;
6586 }
6587 
6588 /*----------------------------------------------------------------------*/
6589 /* Get rid of locally-defined pin names	 				*/
6590 /*----------------------------------------------------------------------*/
6591 
clearlocalpins(objectptr cschem)6592 void clearlocalpins(objectptr cschem)
6593 {
6594    NetnamePtr netnames, nextname;
6595 
6596    for (netnames = cschem->netnames; netnames != NULL; ) {
6597       nextname = netnames->next;
6598       if (netnames->localpin != NULL) {
6599          freelabel(netnames->localpin);
6600       }
6601       free(netnames);
6602       netnames = nextname;
6603    }
6604    cschem->netnames = NULL;
6605 }
6606 
6607 /*----------------------------------------------------------------------*/
6608 /* Handle lists of included files					*/
6609 /*----------------------------------------------------------------------*/
6610 
6611 /*----------------------------------------------------------------------*/
6612 /* check_included() --- check if a file has already been included.	*/
6613 /*	Check by inode instead of filename so that we cannot trick the	*/
6614 /*	program by giving, e.g., one relative path and one full path.	*/
6615 /*----------------------------------------------------------------------*/
6616 
check_included(char * filename)6617 Boolean check_included(char *filename)
6618 {
6619    struct stat filestatus;
6620    int numi;
6621 
6622    if (stat(filename, &filestatus) == 0) {
6623       if (included_files == NULL) return FALSE;
6624       for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++) {
6625 	 if (*(included_files + numi) == filestatus.st_ino) return TRUE;
6626       }
6627    }
6628    return FALSE;
6629 }
6630 
6631 /*----------------------------------------------------------------------*/
6632 /* append_included() --- update the list of included files by adding	*/
6633 /*	the inode of filename to the list.				*/
6634 /*----------------------------------------------------------------------*/
6635 
append_included(char * filename)6636 void append_included(char *filename)
6637 {
6638    struct stat filestatus;
6639    int numi;
6640 
6641    if (stat(filename, &filestatus) == 0) {
6642 
6643       if (included_files == NULL) {
6644          included_files = (ino_t *)malloc(2 * sizeof(ino_t));
6645          *included_files = filestatus.st_ino;
6646          *(included_files + 1) = (ino_t)NULL;
6647       }
6648       else {
6649 	 for (numi = 0; *(included_files + numi) != (ino_t)NULL; numi++);
6650 
6651 	 included_files = (ino_t *)realloc(included_files,
6652 		(++numi + 1) * sizeof(ino_t));
6653 
6654 	 *(included_files + numi - 1) = filestatus.st_ino;
6655 	 *(included_files + numi) = (ino_t)NULL;
6656       }
6657    }
6658    else {
6659       Wprintf("Error: Cannot stat include file \"%s\"\n", filename);
6660    }
6661 }
6662 
6663 /*----------------------------------------------------------------------*/
6664 
free_included()6665 void free_included()
6666 {
6667    if (included_files != NULL) {
6668       free(included_files);
6669       included_files = NULL;
6670    }
6671 }
6672 
6673 /*-------------------------------------------------------------------------*/
6674