1 /*-----------------------------------------------------------------------*/
2 /* files.c --- file handling routines for xcircuit			 */
3 /* Copyright (c) 2002  Tim Edwards, Johns Hopkins University        	 */
4 /*-----------------------------------------------------------------------*/
5 
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <time.h>
10 #ifndef XC_WIN32
11 #include <pwd.h>
12 #endif
13 #include <ctype.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #ifndef XC_WIN32
18 #include <unistd.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #else
22 #include <winsock2.h>
23 #endif
24 
25 #ifdef TCL_WRAPPER
26 #include <tk.h>
27 #else
28 #ifndef XC_WIN32
29 #include "Xw/TextEdit.h"   /* for XwTextCopyBuffer() */
30 #endif
31 #endif
32 
33 /*------------------------------------------------------------------------*/
34 /* Local includes                                                         */
35 /*------------------------------------------------------------------------*/
36 
37 #include "colordefs.h"
38 #include "xcircuit.h"
39 
40 /*----------------------------------------------------------------------*/
41 /* Function prototype declarations                                      */
42 /*----------------------------------------------------------------------*/
43 #include "prototypes.h"
44 
45 #ifdef ASG
46 extern void Route(XCWindowData *, Boolean);
47 extern int ReadSpice(FILE *);
48 #endif
49 
50 /*------------------------------------------------------------------------*/
51 /* Useful (local) defines						  */
52 /*------------------------------------------------------------------------*/
53 
54 #define OUTPUTWIDTH 80	/* maximum text width of output */
55 
56 #define S_OBLIQUE   13	/* position of Symbol-Oblique in font array */
57 
58 /*------------------------------------------------------------------------*/
59 /* External Variable definitions                                          */
60 /*------------------------------------------------------------------------*/
61 
62 #ifdef TCL_WRAPPER
63 extern Tcl_Interp *xcinterp;
64 #endif
65 
66 extern char _STR2[250], _STR[150];
67 extern Globaldata xobjs;
68 extern XCWindowData *areawin;
69 extern fontinfo *fonts;
70 extern short fontcount;
71 extern Cursor appcursors[NUM_CURSORS];
72 extern XtAppContext app;
73 extern Display *dpy;
74 extern Window win;
75 extern short beeper;
76 extern int number_colors;
77 extern colorindex *colorlist;
78 
79 
80 /*------------------------------------------------------*/
81 /* Global variable definitions				*/
82 /*------------------------------------------------------*/
83 
84 Boolean load_in_progress = False;
85 char version[20];
86 
87 /* Structure for remembering what names refer to the same object */
88 
89 aliasptr aliastop;
90 
91 /*------------------------------------------------------*/
92 /* Utility routine---compare version numbers		*/
93 /* Given version strings v1 and v2, return -1 if	*/
94 /* version v1 < v2, +1 if version v1 > v2, and 0 if	*/
95 /* they are equal.					*/
96 /*------------------------------------------------------*/
97 
compare_version(char * v1,char * v2)98 int compare_version(char *v1, char *v2)
99 {
100    int vers1, subvers1, vers2, subvers2;
101 
102    sscanf(v1, "%d.%d", &vers1, &subvers1);
103    sscanf(v2, "%d.%d", &vers2, &subvers2);
104 
105    if (vers1 < vers2) return -1;
106    else if (vers1 > vers2) return 1;
107    else {
108       if (subvers1 < subvers2) return -1;
109       if (subvers1 > subvers2) return 1;
110       else return 0;
111    }
112 }
113 
114 /*------------------------------------------------------*/
115 /* Simple utility---get rid of newline character	*/
116 /*------------------------------------------------------*/
117 
ridnewline(char * sptr)118 char *ridnewline(char *sptr)
119 {
120    char *tstrp;
121 
122    for (tstrp = sptr; *tstrp != '\0' && *tstrp != '\n'; tstrp++);
123    if (*tstrp == '\n') *tstrp = '\0';
124    return tstrp;
125 }
126 
127 /*----------------------------------------------------------------------*/
128 /* Check if two filenames are equivalent.  This requires separately	*/
129 /* checking any absolute or relative pathnames in front of the filename	*/
130 /* as well as the filename itself.					*/
131 /*----------------------------------------------------------------------*/
132 
133 #define PATHSEP '/'
134 
filecmp(char * filename1,char * filename2)135 int filecmp(char *filename1, char *filename2)
136 {
137    char *root1, *root2, *path1, *path2, *end1, *end2;
138    int rval;
139    struct stat statbuf;
140    ino_t inode1;
141    const char *cwdname = ".";
142 
143    if (filename1 == NULL || filename2 == NULL) return 1;
144 
145    if (!strcmp(filename1, filename2)) return 0;	/* exact match */
146 
147    root1 = strrchr(filename1, PATHSEP);
148    root2 = strrchr(filename2, PATHSEP);
149 
150    if (root1 == NULL) {
151       path1 = (char *)cwdname;
152       end1 = NULL;
153       root1 = filename1;
154    }
155    else {
156       path1 = filename1;
157       end1 = root1;
158       root1++;
159    }
160 
161    if (root2 == NULL) {
162       path2 = (char *)cwdname;
163       end2 = NULL;
164       root2 = filename2;
165    }
166    else {
167       path2 = filename2;
168       end2 = root2;
169       root2++;
170    }
171 
172    if (strcmp(root1, root2)) return 1;	/* root names don't match */
173 
174    /* If we got here, one or both filenames specify a directory	*/
175    /* path, and the directory paths are different strings.	*/
176    /* However, one may be an absolute path and the other a	*/
177    /* relative path, so we check the inodes of the paths for	*/
178    /* equivalence.  Note that the file itself is not assumed to	*/
179    /* exist.							*/
180 
181    rval = 1;
182    if (end1 != NULL) *end1 = '\0';
183    if (stat(path1, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
184       inode1 = statbuf.st_ino;
185       if (end2 != NULL) *end2 = '\0';
186       if (stat(path2, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
187 	 if (inode1 == statbuf.st_ino)
188 	    rval = 0;
189       }
190       if (end2 != NULL) *end2 = PATHSEP;
191    }
192    if (end1 != NULL) *end1 = PATHSEP;
193    return rval;
194 }
195 
196 /*--------------------------------------------------------------*/
197 /* Make sure that a string (object or parameter name) is a	*/
198 /* valid PostScript name.  We do this by converting illegal	*/
199 /* characters to the PostScript \ooo (octal value) form.	*/
200 /*								*/
201 /* This routine does not consider whether the name might be a	*/
202 /* PostScript numeric value.  This problem is taken care of by	*/
203 /* having the load/save routines prepend '@' to parameters and	*/
204 /* a technology namespace to object names.			*/
205 /*								*/
206 /* If "need_prefix" is TRUE, then prepend "@" to the result	*/
207 /* string, unless teststring is a numerical parameter name	*/
208 /* (p_...).							*/
209 /*--------------------------------------------------------------*/
210 
create_valid_psname(char * teststring,Boolean need_prefix)211 char *create_valid_psname(char *teststring, Boolean need_prefix)
212 {
213    int i, isize, ssize;
214    static char *optr = NULL;
215    char *sptr, *pptr;
216    Boolean prepend = need_prefix;
217    char illegalchars[] = {'/', '}', '{', ']', '[', ')', '(', '<', '>', ' ', '%'};
218 
219    /* Check for illegal characters which have syntactical meaning in */
220    /* PostScript, and the presence of nonprintable characters or     */
221    /* whitespace.		     				     */
222 
223    ssize = strlen(teststring);
224    isize = ssize;
225 
226    if (need_prefix && !strncmp(teststring, "p_", 2))
227       prepend = FALSE;
228    else
229       isize++;
230 
231    for (sptr = teststring; *sptr != '\0'; sptr++) {
232       if ((!isprint(*sptr)) || isspace(*sptr))
233 	 isize += 3;
234       else {
235          for (i = 0; i < sizeof(illegalchars); i++) {
236 	    if (*sptr == illegalchars[i]) {
237 	       isize += 3;
238 	       break;
239 	    }
240 	 }
241       }
242    }
243    if (isize == ssize) return teststring;
244    isize++;
245 
246    if (optr == NULL)
247       optr = (char *)malloc(isize);
248    else
249       optr = (char *)realloc(optr, isize);
250 
251    pptr = optr;
252 
253    if (prepend) *pptr++ = '@';
254 
255    for (sptr = teststring; *sptr != '\0'; sptr++) {
256       if ((!isprint(*sptr)) || isspace(*sptr)) {
257 	 sprintf(pptr, "\\%03o", *sptr);
258 	 pptr += 4;
259       }
260       else {
261          for (i = 0; i < sizeof(illegalchars); i++) {
262 	    if (*sptr == illegalchars[i]) {
263 	       sprintf(pptr, "\\%03o", *sptr);
264 	       pptr += 4;
265 	       break;
266 	    }
267          }
268 	 if (i == sizeof(illegalchars))
269 	    *pptr++ = *sptr;
270       }
271    }
272    *pptr++ = '\0';
273    return optr;
274 }
275 
276 /*------------------------------------------------------*/
277 /* Turn a PostScript string with possible backslash	*/
278 /* escapes into a normal character string.		*/
279 /*							*/
280 /* if "spacelegal" is TRUE, we are parsing a PostScript	*/
281 /* string in parentheses () where whitespace is legal.	*/
282 /* If FALSE, we are parsing a PostScript name where	*/
283 /* whitespace is illegal, and any whitespace should be	*/
284 /* considered the end of the name.			*/
285 /*							*/
286 /* "dest" is ASSUMED to be large enough to hold the	*/
287 /* result.  "dest" is always equal to or smaller than	*/
288 /* "src" in length.  "size" should be the maximum size	*/
289 /* of the string, or 1 less that the allocated memory,	*/
290 /* allowing for a final NULL byte to be added.		*/
291 /*							*/
292 /* The fact that "dest" is always smaller than or equal	*/
293 /* to "src" means that parse_ps_string(a, a, ...) is	*/
294 /* legal.						*/
295 /*							*/
296 /* Return 0 if the result is empty, 1 otherwise.	*/
297 /*------------------------------------------------------*/
298 
parse_ps_string(char * src,char * dest,int size,Boolean spacelegal,Boolean strip)299 int parse_ps_string(char *src, char *dest, int size, Boolean spacelegal, Boolean strip)
300 {
301    char *sptr = src;
302    char *tptr = dest;
303    int tmpdig, rval = 0;
304 
305    /* Strip leading "@", inserted by XCircuit to	*/
306    /* prevent conflicts with PostScript reserved	*/
307    /* keywords or numbers.				*/
308 
309    if (strip && (*sptr == '@')) sptr++;
310 
311    for (;; sptr++) {
312       if ((*sptr == '\0') || (isspace(*sptr) && !spacelegal)) {
313 	 *tptr = '\0';
314 	 break;
315       }
316       else {
317          if (*sptr == '\\') {
318             sptr++;
319 	    if (*sptr >= '0' && *sptr < '8') {
320 	       sscanf(sptr, "%3o", &tmpdig);
321 	       *tptr++ = (u_char)tmpdig;
322 	       sptr += 2;
323 	    }
324             else
325 	       *tptr++ = *sptr;
326          }
327 	 else
328 	    *tptr++ = *sptr;
329 	 rval = 1;
330       }
331       if ((int)(tptr - dest) > size) {
332 	 Wprintf("Warning:  Name \"%s\" in input exceeded buffer length!\n", src);
333 	 *tptr = '\0';
334 	 return rval;
335       }
336    }
337    return rval;
338 }
339 
340 /*------------------------------------------------------*/
341 /* Free memory allocated to a label string		*/
342 /*------------------------------------------------------*/
343 
freelabel(stringpart * string)344 void freelabel(stringpart *string)
345 {
346    stringpart *strptr = string, *tmpptr;
347 
348    while (strptr != NULL) {
349       if (strptr->type == TEXT_STRING || strptr->type == PARAM_START)
350          free(strptr->data.string);
351       tmpptr = strptr->nextpart;
352       free(strptr);
353       strptr = tmpptr;
354    }
355 }
356 
357 /*------------------------------------------------------*/
358 /* Free memory for a single element			*/
359 /*------------------------------------------------------*/
360 
free_single(genericptr genobj)361 void free_single(genericptr genobj)
362 {
363    objinstptr geninst;
364    oparamptr ops, fops;
365 
366    if (IS_POLYGON(genobj)) free(((polyptr)(genobj))->points);
367    else if (IS_LABEL(genobj)) freelabel(((labelptr)(genobj))->string);
368    else if (IS_GRAPHIC(genobj)) freegraphic((graphicptr)(genobj));
369    else if (IS_PATH(genobj)) free(((pathptr)(genobj))->plist);
370    else if (IS_OBJINST(genobj)) {
371       geninst = (objinstptr)genobj;
372       ops = geninst->params;
373       while (ops != NULL) {
374 	 /* Don't try to free data from indirect parameters */
375 	 /* (That's not true---all data are copied by epsubstitute) */
376 	 /* if (find_indirect_param(geninst, ops->key) == NULL) { */
377 	    switch(ops->type) {
378 	       case XC_STRING:
379 	          freelabel(ops->parameter.string);
380 	          break;
381 	       case XC_EXPR:
382 	          free(ops->parameter.expr);
383 	          break;
384 	    }
385 	 /* } */
386 	 free(ops->key);
387 	 fops = ops;
388 	 ops = ops->next;
389 	 free(fops);
390       }
391    }
392    free_all_eparams(genobj);
393 }
394 
395 /*---------------------------------------------------------*/
396 /* Reset an object structure by freeing all alloc'd memory */
397 /*---------------------------------------------------------*/
398 
reset(objectptr localdata,short mode)399 void reset(objectptr localdata, short mode)
400 {
401   /* short i; (jdk) */
402 
403    if (localdata->polygons != NULL || localdata->labels != NULL)
404       destroynets(localdata);
405 
406    localdata->valid = False;
407 
408    if (localdata->parts > 0) {
409       genericptr *genobj;
410 
411       if (mode != SAVE) {
412 
413 	 for (genobj = localdata->plist; genobj < localdata->plist
414 	        + localdata->parts; genobj++)
415 
416 	    /* (*genobj == NULL) only on library pages		*/
417 	    /* where the instances are kept in the library	*/
418 	    /* definition, and are only referenced on the page.	*/
419 
420 	    if (*genobj != NULL) {
421 	       free_single(*genobj);
422 	       free(*genobj);
423 	    }
424       }
425       free(localdata->plist);
426 
427       removeparams(localdata);
428 
429       initmem(localdata);
430       if (mode == DESTROY)
431 	 free(localdata->plist);
432    }
433 }
434 
435 /*---------------------------------------------------------*/
436 
pagereset(short rpage)437 void pagereset(short rpage)
438 {
439    /* free alloc'd filename */
440 
441    if (xobjs.pagelist[rpage]->filename != NULL)
442       free(xobjs.pagelist[rpage]->filename);
443    xobjs.pagelist[rpage]->filename = (char *)NULL;
444 
445    if (xobjs.pagelist[rpage]->background.name != NULL)
446       free(xobjs.pagelist[rpage]->background.name);
447    xobjs.pagelist[rpage]->background.name = (char *)NULL;
448 
449    clearselects();
450 
451    /* New pages pick up their properties from page 0, which can be changed */
452    /* from the .xcircuitrc file on startup (or loaded from a script).	   */
453    /* Thanks to Norman Werner (norman.werner@student.uni-magdeburg.de) for */
454    /* pointing out this more obvious way of doing the reset, and providing */
455    /* a patch.								   */
456 
457    xobjs.pagelist[rpage]->wirewidth = xobjs.pagelist[0]->wirewidth;
458    xobjs.pagelist[rpage]->orient = xobjs.pagelist[0]->orient;
459    xobjs.pagelist[rpage]->pmode = xobjs.pagelist[0]->pmode;
460    xobjs.pagelist[rpage]->outscale = xobjs.pagelist[0]->outscale;
461    xobjs.pagelist[rpage]->drawingscale.x = xobjs.pagelist[0]->drawingscale.x;
462    xobjs.pagelist[rpage]->drawingscale.y = xobjs.pagelist[0]->drawingscale.y;
463    xobjs.pagelist[rpage]->gridspace = xobjs.pagelist[0]->gridspace;
464    xobjs.pagelist[rpage]->snapspace = xobjs.pagelist[0]->snapspace;
465    xobjs.pagelist[rpage]->coordstyle = xobjs.pagelist[0]->coordstyle;
466    xobjs.pagelist[rpage]->margins = xobjs.pagelist[0]->margins;
467 
468    if (xobjs.pagelist[rpage]->coordstyle == CM) {
469       xobjs.pagelist[rpage]->pagesize.x = 595;
470       xobjs.pagelist[rpage]->pagesize.y = 842;  /* A4 */
471    }
472    else {
473       xobjs.pagelist[rpage]->pagesize.x = 612;	/* letter */
474       xobjs.pagelist[rpage]->pagesize.y = 792;
475    }
476 }
477 
478 /*---------------------------------------------------------*/
479 
initmem(objectptr localdata)480 void initmem(objectptr localdata)
481 {
482    localdata->parts = 0;
483    localdata->plist = (genericptr *)malloc(sizeof(genericptr));
484    localdata->hidden = False;
485    localdata->changes = 0;
486    localdata->params = NULL;
487 
488    localdata->viewscale = 0.5;
489 
490    /* Object should not reference the window:  this needs to be rethunk! */
491    if (areawin != NULL) {
492       localdata->pcorner.x = -areawin->width;
493       localdata->pcorner.y = -areawin->height;
494    }
495    localdata->bbox.width = 0;
496    localdata->bbox.height = 0;
497    localdata->bbox.lowerleft.x = 0;
498    localdata->bbox.lowerleft.y = 0;
499 
500    localdata->highlight.netlist = NULL;
501    localdata->highlight.thisinst = NULL;
502    localdata->schemtype = PRIMARY;
503    localdata->symschem = NULL;
504    localdata->netnames = NULL;
505    localdata->polygons = NULL;
506    localdata->labels = NULL;
507    localdata->ports = NULL;
508    localdata->calls = NULL;
509    localdata->valid = False;
510    localdata->infolabels = False;
511    localdata->traversed = False;
512 }
513 
514 /*--------------------------------------------------------------*/
515 /* Exhaustively compare the contents of two objects and return  */
516 /* true if equivalent, false if not.				*/
517 /*--------------------------------------------------------------*/
518 
elemcompare(genericptr * compgen,genericptr * gchk)519 Boolean elemcompare(genericptr *compgen, genericptr *gchk)
520 {
521    Boolean bres;
522    switch(ELEMENTTYPE(*compgen)) {
523       case(ARC):
524 	 bres = (TOARC(compgen)->position.x == TOARC(gchk)->position.x &&
525             TOARC(compgen)->position.y == TOARC(gchk)->position.y &&
526 	    TOARC(compgen)->style == TOARC(gchk)->style &&
527 	    TOARC(compgen)->width == TOARC(gchk)->width &&
528 	    abs(TOARC(compgen)->radius) == abs(TOARC(gchk)->radius) &&
529 	    TOARC(compgen)->yaxis  == TOARC(gchk)->yaxis &&
530 	    TOARC(compgen)->angle1 == TOARC(gchk)->angle1 &&
531 	    TOARC(compgen)->angle2 == TOARC(gchk)->angle2);
532 	 break;
533       case(SPLINE):
534 	 bres = (TOSPLINE(compgen)->style == TOSPLINE(gchk)->style &&
535 	    TOSPLINE(compgen)->width == TOSPLINE(gchk)->width &&
536 	    TOSPLINE(compgen)->ctrl[0].x == TOSPLINE(gchk)->ctrl[0].x &&
537 	    TOSPLINE(compgen)->ctrl[0].y == TOSPLINE(gchk)->ctrl[0].y &&
538 	    TOSPLINE(compgen)->ctrl[1].x == TOSPLINE(gchk)->ctrl[1].x &&
539 	    TOSPLINE(compgen)->ctrl[1].y == TOSPLINE(gchk)->ctrl[1].y &&
540 	    TOSPLINE(compgen)->ctrl[2].x == TOSPLINE(gchk)->ctrl[2].x &&
541 	    TOSPLINE(compgen)->ctrl[2].y == TOSPLINE(gchk)->ctrl[2].y &&
542 	    TOSPLINE(compgen)->ctrl[3].x == TOSPLINE(gchk)->ctrl[3].x &&
543 	    TOSPLINE(compgen)->ctrl[3].y == TOSPLINE(gchk)->ctrl[3].y);
544 	 break;
545       case(POLYGON): {
546 	 int i;
547 	 if (TOPOLY(compgen)->style == TOPOLY(gchk)->style &&
548 	       TOPOLY(compgen)->width == TOPOLY(gchk)->width &&
549 	       TOPOLY(compgen)->number == TOPOLY(gchk)->number) {
550 	    for (i = 0; i < TOPOLY(compgen)->number; i++) {
551 	       if (TOPOLY(compgen)->points[i].x != TOPOLY(gchk)->points[i].x
552 		     || TOPOLY(compgen)->points[i].y != TOPOLY(gchk)->points[i].y)
553 		  break;
554 	    }
555 	    bres = (i == TOPOLY(compgen)->number);
556 	 }
557 	 else bres = False;
558 	 }break;
559    }
560    return bres;
561 }
562 
563 /*--------------------------------------------------------------*/
564 /* Compare any element with any other element.			*/
565 /*--------------------------------------------------------------*/
566 
compare_single(genericptr * compgen,genericptr * gchk)567 Boolean compare_single(genericptr *compgen, genericptr *gchk)
568 {
569    Boolean bres = False;
570 
571    if ((*gchk)->type == (*compgen)->type) {
572       switch(ELEMENTTYPE(*compgen)) {
573 	 case(OBJINST):{
574 	    objinst *newobj = TOOBJINST(compgen);
575 	    objinst *oldobj = TOOBJINST(gchk);
576 	    bres = (newobj->position.x == oldobj->position.x &&
577 	    		newobj->position.y == oldobj->position.y &&
578 	     		newobj->rotation == oldobj->rotation &&
579 	     		newobj->scale == oldobj->scale &&
580 	     		newobj->style == oldobj->style &&
581 	     		newobj->thisobject == oldobj->thisobject);
582 	    } break;
583 	 case(LABEL):
584 	    bres = (TOLABEL(compgen)->position.x == TOLABEL(gchk)->position.x &&
585 	    		TOLABEL(compgen)->position.y == TOLABEL(gchk)->position.y &&
586 	     		TOLABEL(compgen)->rotation == TOLABEL(gchk)->rotation &&
587 	     		TOLABEL(compgen)->scale == TOLABEL(gchk)->scale &&
588 	     		TOLABEL(compgen)->anchor == TOLABEL(gchk)->anchor &&
589 			TOLABEL(compgen)->pin == TOLABEL(gchk)->pin &&
590 	     		!stringcomp(TOLABEL(compgen)->string, TOLABEL(gchk)->string));
591 	    break;
592 	 case(PATH): /* elements *must* be in same order for a path */
593 	    bres = (TOPATH(compgen)->parts == TOPATH(gchk)->parts &&
594 			TOPATH(compgen)->style == TOPATH(gchk)->style &&
595 			TOPATH(compgen)->width == TOPATH(gchk)->width);
596 	    if (bres) {
597 	       genericptr *pathchk, *gpath;
598 	       for (pathchk = TOPATH(compgen)->plist, gpath =
599 			   TOPATH(gchk)->plist; pathchk < TOPATH(compgen)->plist
600 			   + TOPATH(compgen)->parts; pathchk++, gpath++) {
601 		  if (!elemcompare(pathchk, gpath)) bres = False;
602 	       }
603 	    }
604 	    break;
605 	 case(ARC): case(SPLINE): case(POLYGON):
606 	    bres = elemcompare(compgen, gchk);
607 	    break;
608       }
609    }
610    return bres;
611 }
612 
613 /*--------------------------------------------------------------------*/
614 
objcompare(objectptr obja,objectptr objb)615 short objcompare(objectptr obja, objectptr objb)
616 {
617    genericptr *compgen, *glist, *gchk, *remg;
618    short      csize;
619    Boolean    bres;
620 
621    /* quick check on equivalence of number of objects */
622 
623    if (obja->parts != objb->parts) return False;
624 
625    /* check equivalence of parameters.  Parameters need not be in any	*/
626    /* order; they must only match by key and value.			*/
627 
628    if (obja->params == NULL && objb->params != NULL) return False;
629    else if (obja->params != NULL && objb->params == NULL) return False;
630    else if (obja->params != NULL || objb->params != NULL) {
631      oparamptr opsa, opsb;
632      for (opsa = obja->params; opsa != NULL; opsa = opsa->next) {
633 	 opsb = match_param(objb, opsa->key);
634 	 if (opsb == NULL) return False;
635 	 else if (opsa->type != opsb->type) return False;
636 	 switch (opsa->type) {
637 	    case XC_STRING:
638 	       if (stringcomp(opsa->parameter.string, opsb->parameter.string))
639 		  return False;
640 	       break;
641 	    case XC_EXPR:
642 	       if (strcmp(opsa->parameter.expr, opsb->parameter.expr))
643 		  return False;
644 	       break;
645 	    case XC_INT: case XC_FLOAT:
646 	       if (opsa->parameter.ivalue != opsb->parameter.ivalue)
647 		  return False;
648 	       break;
649 	 }
650       }
651    }
652 
653    /* For the exhaustive check we must match component for component. */
654    /* Best not to assume that elements are in same order for both.    */
655 
656    csize = obja->parts;
657 
658    glist = (genericptr *)malloc(csize * sizeof(genericptr));
659    for (compgen = objb->plist; compgen < objb->plist + csize; compgen++)
660       (*(glist + (int)(compgen - objb->plist))) = *compgen;
661    for (compgen = obja->plist; compgen < obja->plist + obja->parts;
662 	 compgen++) {
663       bres = False;
664       for (gchk = glist; gchk < glist + csize; gchk++) {
665 	 if ((*compgen)->color == (*gchk)->color)
666 	    bres = compare_single(compgen, gchk);
667 	 if (bres) {
668 	   csize--;
669 	   for (remg = gchk; remg < glist + csize; remg++)
670                *remg = *(remg + 1);
671            break;
672 	 }
673       }
674    }
675    free(glist);
676    if (csize != 0) return False;
677 
678    /* Both objects cannot attempt to set an associated schematic/symbol to  */
679    /* separate objects, although it is okay for one to make the association */
680    /* and the other not to.						    */
681 
682    if (obja->symschem != NULL && objb->symschem != NULL)
683       if (obja->symschem != objb->symschem)
684 	 return False;
685 
686    return(True);
687 }
688 
689 /*------------------------*/
690 /* scale renormalization  */
691 /*------------------------*/
692 
getpsscale(float value,short page)693 float getpsscale(float value, short page)
694 {
695    if (xobjs.pagelist[page]->coordstyle != CM)
696       return (value * INCHSCALE);
697    else
698       return (value * CMSCALE);
699 }
700 
701 /*---------------------------------------------------------------*/
702 /* Keep track of columns of output and split lines when too long */
703 /*---------------------------------------------------------------*/
704 
dostcount(FILE * ps,short * count,short addlength)705 void dostcount(FILE *ps, short *count, short addlength)
706 {
707    *count += addlength;
708    if (*count > OUTPUTWIDTH) {
709       *count = addlength;
710       fprintf(ps, "\n");
711    }
712 }
713 
714 /*----------------------------------------------------------------------*/
715 /* Write a numerical value as a string to _STR, making a parameter	*/
716 /* substitution if appropriate.						*/
717 /* Return 1 if a parameter substitution was made, 0 if not.		*/
718 /*----------------------------------------------------------------------*/
719 
varpcheck(FILE * ps,short value,objectptr localdata,int pointno,short * stptr,genericptr thiselem,u_char which)720 Boolean varpcheck(FILE *ps, short value, objectptr localdata, int pointno,
721 	short *stptr, genericptr thiselem, u_char which)
722 {
723    oparamptr ops;
724    eparamptr epp;
725    Boolean done = False;
726 
727    for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
728       if ((epp->pdata.pointno != -1) && (epp->pdata.pointno != pointno)) continue;
729       ops = match_param(localdata, epp->key);
730       if (ops != NULL && (ops->which == which)) {
731 	 sprintf(_STR, "%s ", epp->key);
732 	 done = True;
733 	 break;
734       }
735    }
736 
737    if (!done) {
738       if (pointno == -1) return done;
739       sprintf(_STR, "%d ", (int)value);
740    }
741    else if ((epp->pdata.pointno == -1) && (pointno >= 0)) {
742       sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
743    }
744 
745    dostcount (ps, stptr, strlen(_STR));
746    fputs(_STR, ps);
747    return done;
748 }
749 
750 /*----------------------------------------------------------------------*/
751 /* like varpcheck(), but without pointnumber				*/
752 /*----------------------------------------------------------------------*/
753 
varcheck(FILE * ps,short value,objectptr localdata,short * stptr,genericptr thiselem,u_char which)754 void varcheck(FILE *ps, short value, objectptr localdata,
755 	short *stptr, genericptr thiselem, u_char which)
756 {
757    varpcheck(ps, value, localdata, 0, stptr, thiselem, which);
758 }
759 
760 /*----------------------------------------------------------------------*/
761 /* like varcheck(), but for floating-point values			*/
762 /*----------------------------------------------------------------------*/
763 
varfcheck(FILE * ps,float value,objectptr localdata,short * stptr,genericptr thiselem,u_char which)764 void varfcheck(FILE *ps, float value, objectptr localdata, short *stptr,
765 	genericptr thiselem, u_char which)
766 {
767    oparamptr ops;
768    eparamptr epp;
769    Boolean done = False;
770 
771    for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
772       ops = match_param(localdata, epp->key);
773       if (ops != NULL && (ops->which == which)) {
774 	 sprintf(_STR, "%s ", epp->key);
775 	 done = True;
776 	 break;
777       }
778    }
779 
780    if (!done)
781       sprintf(_STR, "%3.3f ", value);
782 
783    dostcount (ps, stptr, strlen(_STR));
784    fputs(_STR, ps);
785 }
786 
787 /*----------------------------------------------------------------------*/
788 /* Like varpcheck(), for path types only.				*/
789 /*----------------------------------------------------------------------*/
790 
varpathcheck(FILE * ps,short value,objectptr localdata,int pointno,short * stptr,genericptr * thiselem,pathptr thispath,u_char which)791 Boolean varpathcheck(FILE *ps, short value, objectptr localdata, int pointno,
792 	short *stptr, genericptr *thiselem, pathptr thispath, u_char which)
793 {
794    oparamptr ops;
795    eparamptr epp;
796    Boolean done = False;
797 
798    for (epp = thispath->passed; epp != NULL; epp = epp->next) {
799       if ((epp->pdata.pathpt[0] != -1) && (epp->pdata.pathpt[1] != pointno)) continue;
800       if ((epp->pdata.pathpt[0] != -1) && (epp->pdata.pathpt[0] !=
801 		(short)(thiselem - thispath->plist))) continue;
802       ops = match_param(localdata, epp->key);
803       if (ops != NULL && (ops->which == which)) {
804 	 sprintf(_STR, "%s ", epp->key);
805 	 done = True;
806 	 break;
807       }
808    }
809 
810    if (!done) {
811       if (pointno == -1) return done;
812       sprintf(_STR, "%d ", (int)value);
813    }
814    else if ((epp->pdata.pathpt[0] == -1) && (pointno >= 0)) {
815       sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
816    }
817    dostcount (ps, stptr, strlen(_STR));
818    fputs(_STR, ps);
819    return done;
820 }
821 
822 /* Structure used to hold data specific to each load mode.  See	*/
823 /* xcircuit.h for the list of load modes (enum loadmodes)	*/
824 
825 typedef struct _loaddata {
826     void (*func)();		/* Routine to run to load the file */
827     char *prompt;		/* Substring name of action, for prompting */
828     char *filext;		/* Default extention of file to load */
829 } loaddata;
830 
831 /*-------------------------------------------------------*/
832 /* Load a PostScript or Python (interpreter script) file */
833 /*-------------------------------------------------------*/
834 
getfile(xcWidget button,pointertype mode,caddr_t nulldata)835 void getfile(xcWidget button, pointertype mode, caddr_t nulldata)
836 {
837    static loaddata loadmodes[LOAD_MODES] = {
838 	{normalloadfile,  "load",    "ps"},	/* mode NORMAL */
839 	{importfile,     "import",  "ps"},	/* mode IMPORT */
840 	{loadbackground, "render",  "ps"},	/* mode PSBKGROUND */
841 #ifdef HAVE_PYTHON
842 	{execscript,	"execute",  "py"},
843 #else
844 	{execscript,	"execute",    ""},	/* mode SCRIPT */
845 #endif
846 	{crashrecover,	"recover",  "ps"},	/* mode RECOVER */
847 #ifdef ASG
848 	{importspice,	"import", "spice"},	/* mode IMPORTSPICE */
849 #endif
850 #ifdef HAVE_CAIRO
851 	{importgraphic,	"import", "ppm"},	/* mode IMPORTGRAPHIC */
852 #endif
853    };
854 
855    buttonsave *savebutton = NULL;
856    char *promptstr = NULL;
857    /* char strext[10]; (jdk) */
858    int idx = (int)mode;
859 
860    if (is_page(topobject) == -1) {
861       Wprintf("Can only read file into top-level page!");
862       return;
863    }
864    else if (idx >= LOAD_MODES) {
865       Wprintf("Unknown mode passed to routine getfile()\n");
866       return;
867    }
868 #ifndef TCL_WRAPPER
869    savebutton = getgeneric(button, getfile, (void *)mode);
870 #endif
871    if (idx == RECOVER) {
872       char *cfile = getcrashfilename();
873       promptstr = (char *)malloc(18 + ((cfile == NULL) ? 9 : strlen(cfile)));
874       sprintf(promptstr, "Recover file \'%s\'?", (cfile == NULL) ? "(unknown)" : cfile);
875       popupprompt(button, promptstr, NULL, loadmodes[idx].func, savebutton, NULL);
876       if (cfile) free(cfile);
877    }
878    else {
879       promptstr = (char *)malloc(18 + strlen(loadmodes[idx].prompt));
880       sprintf(promptstr, "Select file to %s:", loadmodes[idx].prompt);
881       popupprompt(button, promptstr, "\0", loadmodes[idx].func,
882 		savebutton, loadmodes[idx].filext);
883    }
884    free(promptstr);
885 }
886 
887 /*--------------------------------------------------------------*/
888 /* Tilde ('~') expansion in file name.  Assumes that filename	*/
889 /* is a static character array of size "nchars".		*/
890 /*--------------------------------------------------------------*/
891 
xc_tilde_expand(char * filename,int nchars)892 Boolean xc_tilde_expand(char *filename, int nchars)
893 {
894 #ifndef _MSC_VER
895    struct passwd *passwd;
896    char *username = NULL, *expanded, *sptr;
897 
898    if (*filename == '~') {
899       sptr = filename + 1;
900       if (*sptr == '/' || *sptr == ' ' || *sptr == '\0')
901 	 username = getenv("HOME");
902       else {
903 	 for (; *sptr != '/' && *sptr != '\0'; sptr++);
904 	 if (*sptr == '\0') *(sptr + 1) = '\0';
905 	 *sptr = '\0';
906 
907 	 passwd = getpwnam(filename + 1);
908 	 if (passwd != NULL)
909 	    username = passwd->pw_dir;
910 
911 	 *sptr = '/';
912       }
913       if (username != NULL) {
914 	 expanded = (char *)malloc(strlen(username) +
915 		strlen(filename));
916 	 strcpy(expanded, username);
917          strcat(expanded, sptr);
918 	 strncpy(filename, expanded, nchars);
919          free(expanded);
920       }
921       return True;
922    }
923    return False;
924 #else
925    return False;
926 #endif
927 }
928 
929 /*--------------------------------------------------------------*/
930 /* Variable ('$') expansion in file name 			*/
931 /*--------------------------------------------------------------*/
932 
xc_variable_expand(char * filename,int nchars)933 Boolean xc_variable_expand(char *filename, int nchars)
934 {
935    char *expanded, *sptr, tmpchar, *varpos, *varsub;
936 
937    if ((varpos = strchr(filename, '$')) != NULL) {
938       for (sptr = varpos; *sptr != '/' && *sptr != '\0'; sptr++);
939       if (*sptr == '\0') *(sptr + 1) = '\0';
940       tmpchar = *sptr;
941       *sptr = '\0';
942 
943 #ifdef TCL_WRAPPER
944       /* Interpret as a Tcl variable */
945       varsub = (char *)Tcl_GetVar(xcinterp, varpos + 1, TCL_NAMESPACE_ONLY);
946 #else
947       /* Interpret as an environment variable */
948       varsub = (char *)getenv((const char *)(varpos + 1));
949 #endif
950 
951       if (varsub != NULL) {
952 
953 	 *varpos = '\0';
954 	 expanded = (char *)malloc(strlen(varsub) + strlen(filename) +
955 		strlen(sptr + 1) + 2);
956 	 strcpy(expanded, filename);
957          strcat(expanded, varsub);
958 	 *sptr = tmpchar;
959 	 strcat(expanded, sptr);
960 	 strncpy(filename, expanded, nchars);
961          free(expanded);
962       }
963       else
964          *sptr = tmpchar;
965       return True;
966    }
967    return False;
968 }
969 
970 /*--------------------------------------------------------------*/
971 /* Attempt to find a file and open it.				*/
972 /*--------------------------------------------------------------*/
973 
fileopen(char * filename,char * suffix,char * name_return,int nchars)974 FILE *fileopen(char *filename, char *suffix, char *name_return, int nchars)
975 {
976    FILE *file = NULL;
977    char inname[250], expname[250], *sptr, *cptr, *iptr, *froot;
978    int slen;
979 
980    sscanf(filename, "%249s", expname);
981    xc_tilde_expand(expname, 249);
982    while (xc_variable_expand(expname, 249));
983 
984    sptr = xobjs.filesearchpath;
985    while (1) {
986       if ((xobjs.filesearchpath == NULL) || (expname[0] == '/')) {
987 	 strcpy(inname, expname);
988 	 iptr = inname;
989       }
990       else {
991 	 strcpy(inname, sptr);
992 	 cptr = strchr(sptr, ':');
993 	 slen = (cptr == NULL) ? strlen(sptr) : (int)(cptr - sptr);
994 	 sptr += (slen + ((cptr == NULL) ? 0 : 1));
995 	 iptr = inname + slen;
996 	 if (*(iptr - 1) != '/') strcpy(iptr++, "/");
997 	 strcpy(iptr, expname);
998       }
999 
1000       /* Attempt to open the filename with a suffix */
1001 
1002       if ((froot = strrchr(iptr, '/')) == NULL) froot = iptr;
1003       if (strrchr(froot, '.') == NULL) {
1004          if (suffix) {
1005 	    if (suffix[0] != '.')
1006 	       strncat(inname, ".", 249);
1007             strncat(inname, suffix, 249);
1008 	 }
1009          file = fopen(inname, "r");
1010       }
1011 
1012       /* Attempt to open the filename as given, without a suffix */
1013 
1014       if (file == NULL) {
1015          strcpy(iptr, expname);
1016          file = fopen(inname, "r");
1017       }
1018 
1019       if (file != NULL) break;
1020       else if (sptr == NULL) break;
1021       else if (*sptr == '\0') break;
1022    }
1023 
1024    if (name_return) strncpy(name_return, inname, nchars);
1025    return file;
1026 }
1027 
1028 /*---------------------------------------------------------*/
1029 
nextfilename()1030 Boolean nextfilename()	/* extract next filename from _STR2 into _STR */
1031 {
1032    char *cptr, *slptr;
1033 
1034    sprintf(_STR, "%.149s", _STR2);
1035    if ((cptr = strrchr(_STR2, ',')) != NULL) {
1036       slptr = strrchr(_STR, '/');
1037       if (slptr == NULL || ((slptr - _STR) > (cptr - _STR2))) slptr = _STR - 1;
1038       sprintf(slptr + 1, "%s", cptr + 1);
1039       *cptr = '\0';
1040       return True;
1041    }
1042    else return False;
1043 }
1044 
1045 /*---------------------------------------------------------*/
1046 
loadfontlib()1047 void loadfontlib()
1048 {
1049    loadlibrary(FONTLIB);
1050 }
1051 
1052 /*------------------------------------------------------*/
1053 /* Handle library loading and refresh current page if 	*/
1054 /* it is a library page that just changed.		*/
1055 /*------------------------------------------------------*/
1056 
loadglib(Boolean lflag,short ilib,short tlib)1057 void loadglib(Boolean lflag, short ilib, short tlib)
1058 {
1059    while (nextfilename()) {
1060       if (lflag)
1061 	 lflag = False;
1062       else
1063          ilib = createlibrary(False);
1064       loadlibrary(ilib);
1065       /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
1066    }
1067    if (lflag)
1068       lflag = False;
1069    else
1070       ilib = createlibrary(False);
1071    loadlibrary(ilib);
1072    /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
1073 }
1074 
1075 /*------------------------------------------------------*/
1076 /* Load new library:  Create new library page and load	*/
1077 /* to it.						*/
1078 /*------------------------------------------------------*/
1079 
loadulib()1080 void loadulib()
1081 {
1082    loadglib(False, (short)0, (short)is_library(topobject) + LIBRARY);
1083 }
1084 
1085 /*-----------------------------------------------------------*/
1086 /* Add to library:  If on library page, add to that library. */
1087 /* Otherwise, create a new library page and load to it.	     */
1088 /*-----------------------------------------------------------*/
1089 
loadblib()1090 void loadblib()
1091 {
1092    short ilib, tlib;
1093    Boolean lflag = True;
1094 
1095    /* Flag whether current page is a library page or not */
1096 
1097    if ((tlib = is_library(topobject)) < 0) {
1098       ilib = LIBRARY;
1099       lflag = False;
1100    }
1101    else
1102       ilib = tlib + LIBRARY;
1103 
1104    loadglib(lflag, ilib, tlib + LIBRARY);
1105 }
1106 
1107 /*---------------------------------------------------------*/
1108 
getlib(xcWidget button,caddr_t clientdata,caddr_t nulldata)1109 void getlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
1110 {
1111    buttonsave *savebutton;
1112 #ifndef TCL_WRAPPER
1113    savebutton = getgeneric(button, getlib, NULL);
1114 #endif
1115    popupprompt(button, "Enter library to load:", "\0", loadblib, savebutton,
1116 	"lps");
1117 }
1118 
1119 /*---------------------------------------------------------*/
1120 
getuserlib(xcWidget button,caddr_t clientdata,caddr_t nulldata)1121 void getuserlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
1122 {
1123    buttonsave *savebutton;
1124 
1125 #ifndef TCL_WRAPPER
1126    savebutton = getgeneric(button, getuserlib, NULL);
1127 #endif
1128    popupprompt(button, "Enter library to load:", "\0", loadulib, savebutton,
1129 	"lps");
1130 }
1131 
1132 /*------------------------------------------------------*/
1133 /* Add a new name to the list of aliases for an object	*/
1134 /*------------------------------------------------------*/
1135 
addalias(objectptr thisobj,char * newname)1136 Boolean addalias(objectptr thisobj, char *newname)
1137 {
1138    aliasptr aref;
1139    slistptr sref;
1140    /* Boolean retval = False; (jdk) */
1141    char *origname = thisobj->name;
1142 
1143    for (aref = aliastop; aref != NULL; aref = aref->next)
1144       if (aref->baseobj == thisobj)
1145 	 break;
1146 
1147    /* An equivalence, not an alias */
1148    if (!strcmp(origname, newname)) return True;
1149 
1150    if (aref == NULL) {	/* entry does not exist;  add new baseobj */
1151       aref = (aliasptr)malloc(sizeof(alias));
1152       aref->baseobj = thisobj;
1153       aref->aliases = NULL;
1154       aref->next = aliastop;
1155       aliastop = aref;
1156    }
1157 
1158    for (sref = aref->aliases; sref != NULL; sref = sref->next)
1159       if (!strcmp(sref->alias, newname))
1160 	 break;
1161 
1162    if (sref == NULL) {		/* needs new entry */
1163       sref = (slistptr)malloc(sizeof(stringlist));
1164       sref->alias = strdup(newname);
1165       sref->next = aref->aliases;
1166       aref->aliases = sref;
1167       return False;
1168    }
1169    else return True;		/* alias already exists! */
1170 }
1171 
1172 /*------------------------------------------------------*/
1173 /* Remove all object name aliases			*/
1174 /*------------------------------------------------------*/
1175 
cleanupaliases(short mode)1176 void cleanupaliases(short mode)
1177 {
1178    aliasptr aref;
1179    slistptr sref;
1180    objectptr baseobj;
1181    char *sptr; /*  *basename, (jdk) */
1182    int i, j;
1183 
1184    if (aliastop == NULL) return;
1185 
1186    for (aref = aliastop; aref != NULL; aref = aref->next) {
1187       baseobj = aref->baseobj;
1188       for (sref = aref->aliases; sref != NULL; sref = sref->next)
1189 	 free(sref->alias);
1190    }
1191 
1192    for (; (aref = aliastop->next); aliastop = aref)
1193       free(aliastop);
1194    free(aliastop);
1195    aliastop = NULL;
1196 
1197    /* Get rid of propagating underscores in names */
1198 
1199    for (i = 0; i < ((mode == FONTLIB) ? 1 : xobjs.numlibs); i++) {
1200       for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number :
1201 		xobjs.userlibs[i].number); j++) {
1202 	 baseobj = (mode == FONTLIB) ? *(xobjs.fontlib.library + j) :
1203 		*(xobjs.userlibs[i].library + j);
1204 
1205 	 sptr = baseobj->name;
1206 	 while (*sptr == '_') sptr++;
1207 	 /* need memmove to avoid overwriting? */
1208 	 memmove((void *)baseobj->name, (const void *)sptr, strlen(sptr) + 1);
1209 	 checkname(baseobj);
1210       }
1211    }
1212 }
1213 
1214 /*------------------------------------------------------*/
1215 /* Open a library file by name and return a pointer to	*/
1216 /* the file structure, or NULL if an error occurred.	*/
1217 /*------------------------------------------------------*/
1218 
libopen(char * libname,short mode,char * name_return,int nchars)1219 FILE *libopen(char *libname, short mode, char *name_return, int nchars)
1220 {
1221    FILE *file = NULL;
1222    char inname[150], expname[150], *sptr, *cptr, *iptr;
1223    int slen;
1224    char *suffix = (mode == FONTENCODING) ? ".xfe" : ".lps";
1225 
1226    sscanf(libname, "%149s", expname);
1227    xc_tilde_expand(expname, 149);
1228    while(xc_variable_expand(expname, 149));
1229 
1230    sptr = xobjs.libsearchpath;
1231    while (1) {
1232 
1233       if ((xobjs.libsearchpath == NULL) || (expname[0] == '/')) {
1234 	 strcpy(inname, expname);
1235 	 iptr = inname;
1236       }
1237       else {
1238 	 strcpy(inname, sptr);
1239 	 cptr = strchr(sptr, ':');
1240 	 slen = (cptr == NULL) ? strlen(sptr) : (int)(cptr - sptr);
1241 	 sptr += (slen + ((cptr == NULL) ? 0 : 1));
1242 	 iptr = inname + slen;
1243 	 if (*(iptr - 1) != '/') strcpy(iptr++, "/");
1244 	 strcpy(iptr, expname);
1245       }
1246 
1247       /* Try to open the filename with a suffix if it doesn't have one */
1248 
1249       if (strrchr(iptr, '.') == NULL) {
1250 	 strncat(inname, suffix, 149);
1251          file = fopen(inname, "r");
1252       }
1253 
1254       /* Try to open the filename as given, without a suffix */
1255 
1256       if (file == NULL) {
1257 	 strcpy(iptr, expname);
1258 	 file = fopen(inname, "r");
1259       }
1260 
1261       if (file != NULL) break;
1262       else if (sptr == NULL) break;
1263       else if (*sptr == '\0') break;
1264    }
1265 
1266    if ((file == NULL) && (xobjs.libsearchpath == NULL)) {
1267 
1268       /* if not found in cwd and there is no library search	  */
1269       /* path, look for environment variable "XCIRCUIT_LIB_DIR"	  */
1270       /* defined (Thanks to Ali Moini, U. Adelaide, S. Australia) */
1271 
1272       char *tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");
1273 
1274       if (tmp_s != NULL) {
1275 	 sprintf(inname, "%s/%s", tmp_s, expname);
1276       	 file = fopen(inname, "r");
1277       	 if (file == NULL) {
1278 	    sprintf(inname, "%s/%s%s", tmp_s, expname, suffix);
1279 	    file = fopen(inname, "r");
1280 	 }
1281       }
1282 
1283       /* last resort:  hard-coded directory BUILTINS_DIR */
1284 
1285       if (file == NULL) {
1286 	 sprintf(inname, "%s/%s", BUILTINS_DIR, expname);
1287       	 file = fopen(inname, "r");
1288       	 if (file == NULL) {
1289 	    sprintf(inname, "%s/%s%s", BUILTINS_DIR, expname, suffix);
1290 	    file = fopen(inname, "r");
1291 	 }
1292       }
1293    }
1294 
1295    if (name_return) strncpy(name_return, inname, nchars);
1296    return file;
1297 }
1298 
1299 /*--------------------------------------------------------------*/
1300 /* Add a record to the instlist list of the library indexed by  */
1301 /* mode, create a new instance, and add it to the record.	*/
1302 /*								*/
1303 /* objname is the name of the library object to be instanced,	*/
1304 /* and buffer is the line containing the instance parameters	*/
1305 /* of the new instance, optionally preceded by scale and	*/
1306 /* rotation values.						*/
1307 /*								*/
1308 /*--------------------------------------------------------------*/
1309 
new_library_instance(short mode,char * objname,char * buffer,TechPtr defaulttech)1310 objinstptr new_library_instance(short mode, char *objname, char *buffer,
1311 	TechPtr defaulttech)
1312 {
1313    char *lineptr;
1314    objectptr libobj, localdata;
1315    objinstptr newobjinst;
1316    int j;
1317    char *nsptr, *fullname = objname;
1318 
1319    localdata = xobjs.libtop[mode + LIBRARY]->thisobject;
1320 
1321    /* For (older) libraries that do not use technologies, give the      */
1322    /* object a technology name in the form <library>::<object>          */
1323 
1324    if ((nsptr = strstr(objname, "::")) == NULL) {
1325       int deftechlen = (defaulttech == NULL) ? 0 : strlen(defaulttech->technology);
1326       fullname = (char *)malloc(deftechlen + strlen(objname) + 3);
1327       if (defaulttech == NULL)
1328          sprintf(fullname, "::%s", objname);
1329       else
1330          sprintf(fullname, "%s::%s", defaulttech->technology, objname);
1331    }
1332 
1333    for (j = 0; j < xobjs.userlibs[mode].number; j++) {
1334       libobj = *(xobjs.userlibs[mode].library + j);
1335       if (!strcmp(fullname, libobj->name)) {
1336 	 newobjinst = addtoinstlist(mode, libobj, TRUE);
1337 
1338 	 lineptr = buffer;
1339 	 while (isspace(*lineptr)) lineptr++;
1340 	 if (*lineptr != '<') {
1341 	    /* May declare instanced scale and rotation first */
1342 	    lineptr = varfscan(localdata, lineptr, &newobjinst->scale,
1343 			(genericptr)newobjinst, P_SCALE);
1344 	    lineptr = varfscan(localdata, lineptr, &newobjinst->rotation,
1345 			(genericptr)newobjinst, P_ROTATION);
1346 	 }
1347 	 readparams(NULL, newobjinst, libobj, lineptr);
1348 	 if (fullname != objname) free(fullname);
1349 	 return newobjinst;
1350       }
1351    }
1352    if (fullname != objname) free(fullname);
1353    return NULL;		/* error finding the library object */
1354 }
1355 
1356 /*------------------------------------------------------*/
1357 /* Import a single object from a library file.  "mode"	*/
1358 /* is the number of the library into which the object	*/
1359 /* will be placed.  This function allows new libraries	*/
1360 /* to be generated by "cutting and pasting" from	*/
1361 /* existing libraries.  It also allows better library	*/
1362 /* management when the number of objects becomes very	*/
1363 /* large, such as when using "diplib" (7400 series	*/
1364 /* chips, created from the PCB library).		*/
1365 /*------------------------------------------------------*/
1366 
importfromlibrary(short mode,char * libname,char * objname)1367 void importfromlibrary(short mode, char *libname, char *objname)
1368 {
1369    FILE *ps;
1370    char temp[150], keyword[100];
1371    char inname[150], *tptr;
1372    objectptr *newobject;
1373    objlistptr redef;
1374    char saveversion[20];
1375    Boolean dependencies = False;
1376    TechPtr nsptr = NULL;
1377 
1378    ps = libopen(libname, mode, inname, 149);
1379    if (ps == NULL) {
1380       Fprintf(stderr, "Cannot open library %s for import.\n", libname);
1381       return;
1382    }
1383 
1384    strcpy(version, "2.0"); /* Assume version is 2.0 unless found in header */
1385 
1386    for(;;) {
1387       if (fgets(temp, 149, ps) == NULL) {
1388          Wprintf("Error in library.");
1389 	 goto endload;
1390       }
1391       else if (temp[0] == '/') {
1392 	 int s = 1;
1393 	 if (temp[1] == '@') s = 2;
1394 	 sscanf(&temp[s], "%s", keyword);
1395 	 if (!strcmp(keyword, objname))
1396 	    break;
1397       }
1398       else if (*temp == '%') {
1399 	 char *tptr = temp + 1;
1400 	 while (isspace(*tptr)) tptr++;
1401 	 if (!strncmp(tptr, "Version:", 8)) {
1402 	    tptr += 9;
1403 	    sscanf(tptr, "%20s", version);
1404 	    ridnewline(version);
1405 	 }
1406 	 else if (!strncmp(tptr, "Library", 7)) {
1407 	    char *techname = strstr(tptr, ":");
1408 	    if (techname != NULL) {
1409 	       techname++;	/* skip over colon */
1410 	       while(isspace(*techname)) techname++;
1411 	       ridnewline(techname);	/* Remove newline character */
1412 	       if ((tptr = strrchr(techname, '/')) != NULL)
1413 		  techname = tptr + 1;
1414 	       if ((tptr = strrchr(techname, '.')) != NULL)
1415 		  if (!strncmp(tptr, ".lps", 4))
1416 		     *tptr = '\0';
1417 	       nsptr = AddNewTechnology(techname, inname);
1418 	       if (nsptr) {
1419 		  // Set the IMPORTED flag, to help prevent overwriting
1420 		  // the techfile with a truncated version of it, unless
1421 		  // the filename of the techfile has been changed, in
1422 		  // which case the technology should be considered to
1423 		  // stand on its own, and is not considered a partially
1424 		  // complete imported version of the original techfile.
1425 
1426 		  if (!strcmp(inname, nsptr->filename))
1427 		     nsptr->flags |= TECH_IMPORTED;
1428 	       }
1429 	    }
1430 	 }
1431          else if (!strncmp(tptr, "Depend", 6)) {
1432 	    dependencies = TRUE;
1433 	    tptr += 7;
1434 	    sscanf(tptr, "%s", keyword);
1435 	    if (!strcmp(keyword, objname)) {
1436 	       /* Load dependencies */
1437 	       while (1) {
1438 	          tptr += strlen(keyword) + 1;
1439 	          if (sscanf(tptr, "%s", keyword) != 1) break;
1440 	          if (keyword[0] == '\n' || keyword[0] == '\0') break;
1441 	          /* Recursive import */
1442 		  strcpy(saveversion, version);
1443 	          importfromlibrary(mode, libname, keyword);
1444 		  strcpy(version, saveversion);
1445 	       }
1446 	    }
1447 	 }
1448       }
1449    }
1450 
1451    if ((compare_version(version, "3.2") < 0) && (!dependencies)) {
1452       Fprintf(stderr, "Library does not have dependency list and cannot "
1453 		"be trusted.\nLoad and rewrite library to update.\n");
1454       goto endload;
1455    }
1456 
1457    newobject = new_library_object(mode, keyword, &redef, nsptr);
1458 
1459    load_in_progress = True;
1460    if (objectread(ps, *newobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr) == False) {
1461 
1462       if (library_object_unique(mode, *newobject, redef)) {
1463 	 add_object_to_library(mode, *newobject);
1464 	 cleanupaliases(mode);
1465 
1466 	 /* pull in any instances of this object that	*/
1467 	 /* are defined in the library			*/
1468 
1469 	 for(;;) {
1470 	    if (fgets(temp, 149, ps) == NULL) {
1471 	       Wprintf("Error in library.");
1472 	       goto endload;
1473 	    }
1474 	    else if (!strncmp(temp, "% EndLib", 8))
1475 	       break;
1476 	    else if (strstr(temp, "libinst") != NULL) {
1477 	       if ((tptr = strstr(temp, objname)) != NULL) {
1478 	          if (*(tptr - 1) == '/') {
1479 		     char *eptr = tptr;
1480 		     while (!isspace(*++eptr));
1481 		     *eptr = '\0';
1482 		     new_library_instance(mode - LIBRARY, tptr, temp, nsptr);
1483 		  }
1484 	       }
1485 	    }
1486 	 }
1487 
1488 	 if (mode != FONTLIB) {
1489 	    composelib(mode);
1490 	    centerview(xobjs.libtop[mode]);
1491 	 }
1492       }
1493    }
1494 
1495 endload:
1496    fclose(ps);
1497    strcpy(version, PROG_VERSION);
1498    load_in_progress = False;
1499 }
1500 
1501 /*------------------------------------------------------*/
1502 /* Copy all technologies' replace flags into temp flag.	*/
1503 /* Then clear the replace flags (replace none)		*/
1504 /*------------------------------------------------------*/
1505 
TechReplaceSave()1506 void TechReplaceSave()
1507 {
1508    TechPtr nsp;
1509 
1510    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1511    {
1512       if (nsp->flags & TECH_REPLACE)
1513 	 nsp->flags |= TECH_REPLACE_TEMP;
1514       else
1515          nsp->flags &= ~TECH_REPLACE_TEMP;
1516       nsp->flags &= ~TECH_REPLACE;
1517    }
1518 }
1519 
1520 /*------------------------------------------------------*/
1521 /* Restore all technologies' replace flags 		*/
1522 /*------------------------------------------------------*/
1523 
TechReplaceRestore()1524 void TechReplaceRestore()
1525 {
1526    TechPtr nsp;
1527 
1528    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1529    {
1530       if (nsp->flags & TECH_REPLACE_TEMP)
1531 	 nsp->flags |= TECH_REPLACE;
1532       else
1533          nsp->flags &= ~TECH_REPLACE;
1534    }
1535 }
1536 
1537 /*------------------------------------------------------*/
1538 /* Set all technologies' replace flags (replace all)	*/
1539 /*------------------------------------------------------*/
1540 
TechReplaceAll()1541 void TechReplaceAll()
1542 {
1543    TechPtr nsp;
1544 
1545    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1546       nsp->flags |= TECH_REPLACE;
1547 }
1548 
1549 /*------------------------------------------------------*/
1550 /* Clear all technologies' replace flags (replace none)	*/
1551 /*------------------------------------------------------*/
1552 
TechReplaceNone()1553 void TechReplaceNone()
1554 {
1555    TechPtr nsp;
1556 
1557    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1558       nsp->flags &= ~TECH_REPLACE;
1559 }
1560 
1561 
1562 /*------------------------------------------------------*/
1563 /* Compare an object's name with a specific technology	*/
1564 /*							*/
1565 /* A missing "::" prefix separator or an empty prefix	*/
1566 /* both match a NULL technology or a technology name	*/
1567 /* that is an empty string ("").  All of these		*/
1568 /* conditions indicate the default "user" technology.	*/
1569 /*------------------------------------------------------*/
1570 
CompareTechnology(objectptr cobj,char * technology)1571 Boolean CompareTechnology(objectptr cobj, char *technology)
1572 {
1573    char *cptr;
1574    Boolean result = FALSE;
1575 
1576    if ((cptr = strstr(cobj->name, "::")) != NULL) {
1577       if (technology == NULL)
1578 	 result = (cobj->name == cptr) ? TRUE : FALSE;
1579       else {
1580          *cptr = '\0';
1581          if (!strcmp(cobj->name, technology)) result = TRUE;
1582          *cptr = ':';
1583       }
1584    }
1585    else if (technology == NULL)
1586       result = TRUE;
1587 
1588    return result;
1589 }
1590 
1591 /*------------------------------------------------------*/
1592 /* Find a technology record				*/
1593 /*------------------------------------------------------*/
1594 
LookupTechnology(char * technology)1595 TechPtr LookupTechnology(char *technology)
1596 {
1597    TechPtr nsp;
1598    Boolean usertech = FALSE;
1599 
1600    // A NULL technology is allowed as equivalent to a
1601    // technology name of "" (null string)
1602 
1603    if (technology == NULL)
1604       usertech = TRUE;
1605    else if (*technology == '\0')
1606       usertech = TRUE;
1607    else if (!strcmp(technology, "(user)"))
1608       usertech = TRUE;
1609 
1610    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1611       if (usertech == TRUE) {
1612 	 if (*nsp->technology == '\0')
1613 	    return nsp;
1614       }
1615       if ((technology != NULL) && !strcmp(technology, nsp->technology))
1616 	 return nsp;
1617    }
1618    return NULL;
1619 }
1620 
1621 /*------------------------------------------------------*/
1622 /* Find a technology according to the filename		*/
1623 /*------------------------------------------------------*/
1624 
GetFilenameTechnology(char * filename)1625 TechPtr GetFilenameTechnology(char *filename)
1626 {
1627    TechPtr nsp;
1628 
1629    if (filename == NULL) return NULL;
1630 
1631    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1632       if (!filecmp(filename, nsp->filename))
1633 	 return nsp;
1634 
1635    return NULL;
1636 }
1637 
1638 /*------------------------------------------------------*/
1639 /* Find a technology record corresponding to the	*/
1640 /* indicated object's technology.			*/
1641 /*------------------------------------------------------*/
1642 
GetObjectTechnology(objectptr thisobj)1643 TechPtr GetObjectTechnology(objectptr thisobj)
1644 {
1645    TechPtr nsp;
1646    char *cptr;
1647    /* int nlen; (jdk) */
1648 
1649    cptr = strstr(thisobj->name, "::");
1650    if (cptr == NULL) return NULL;
1651    else *cptr = '\0';
1652 
1653    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1654       if (!strcmp(thisobj->name, nsp->technology))
1655 	 break;
1656 
1657    *cptr = ':';
1658    return nsp;
1659 }
1660 
1661 /*------------------------------------------------------*/
1662 /* Add a new technology name to the list		*/
1663 /*------------------------------------------------------*/
1664 
AddNewTechnology(char * technology,char * filename)1665 TechPtr AddNewTechnology(char *technology, char *filename)
1666 {
1667    TechPtr nsp;
1668    char usertech[] = "";
1669    char *localtech = technology;
1670 
1671    // In the case where somebody has saved the contents of the user
1672    // technology to a file, create a technology->filename mapping
1673    // using a null string ("") for the technology name.  If we are
1674    // only checking if a technology name exists (filename is NULL),
1675    // then ignore an reference to the user technology.
1676 
1677    if (technology == NULL) {
1678       if (filename == NULL) return NULL;
1679       else localtech = usertech;
1680    }
1681 
1682    for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1683       if (!strcmp(localtech, nsp->technology)) {
1684 
1685 	 /* A namespace may be created for an object that is a dependency */
1686 	 /* in a different technology.  If so, it will have a NULL	  */
1687 	 /* filename, and the filename should be replaced if we ever load */
1688 	 /* the file that properly defines the technology.		  */
1689 
1690 	 if ((nsp->filename == NULL) && (filename != NULL))
1691 	    nsp->filename = strdup(filename);
1692 
1693 	 return nsp;	/* Namespace already exists */
1694       }
1695    }
1696 
1697    nsp = (TechPtr)malloc(sizeof(Technology));
1698    nsp->next = xobjs.technologies;
1699    if (filename == NULL)
1700       nsp->filename = NULL;
1701    else
1702       nsp->filename = strdup(filename);
1703    nsp->technology = strdup(localtech);
1704    nsp->flags = (u_char)0;
1705    xobjs.technologies = nsp;
1706 
1707    return nsp;
1708 }
1709 
1710 /*------------------------------------------------------*/
1711 /* Check an object's name for a technology, and add it	*/
1712 /* to the list of technologies if it has one.		*/
1713 /*------------------------------------------------------*/
1714 
AddObjectTechnology(objectptr thisobj)1715 void AddObjectTechnology(objectptr thisobj)
1716 {
1717    char *cptr;
1718 
1719    cptr = strstr(thisobj->name, "::");
1720    if (cptr != NULL) {
1721       *cptr = '\0';
1722       AddNewTechnology(thisobj->name, NULL);
1723       *cptr = ':';
1724    }
1725 }
1726 
1727 /*------------------------------------------------------*/
1728 /* Load a library page (given in parameter "mode") and	*/
1729 /* rename the library page to match the library name as */
1730 /* found in the file header.				*/
1731 /*------------------------------------------------------*/
1732 
loadlibrary(short mode)1733 Boolean loadlibrary(short mode)
1734 {
1735    FILE *ps;
1736    objinstptr saveinst;
1737    char temp[150], keyword[30], percentc, inname[150];
1738    TechPtr nsptr = NULL;
1739 
1740    ps = libopen(_STR, mode, inname, 149);
1741 
1742    if ((ps == NULL) && (mode == FONTLIB)) {
1743       /* We automatically try looking in all the usual places plus a	*/
1744       /* subdirectory named "fonts".					*/
1745 
1746       sprintf(temp, "fonts/%s", _STR);
1747       ps = libopen(temp, mode, inname, 149);
1748    }
1749    if (ps == NULL) {
1750       Wprintf("Library not found.");
1751       return False;
1752    }
1753 
1754    /* current version is PROG_VERSION;  however, all libraries newer than */
1755    /* version 2.0 require "Version" in the header.  So unnumbered	  */
1756    /* libraries may be assumed to be version 1.9 or earlier.		  */
1757 
1758    strcpy(version, "1.9");
1759    for(;;) {
1760       if (fgets(temp, 149, ps) == NULL) {
1761          Wprintf("Error in library.");
1762 	 fclose(ps);
1763          return False;
1764       }
1765       sscanf(temp, "%c %29s", &percentc, keyword);
1766 
1767       /* Commands in header are PostScript comments (%) */
1768       if (percentc == '%') {
1769 
1770 	 /* The library name in the header corresponds to the object	*/
1771 	 /* technology defined by the library.  This no longer has 	*/
1772 	 /* anything to do with the name of the library page where we	*/
1773 	 /* are loading this file.					*/
1774 
1775 	 /* Save the technology, filename, and flags in the technology list. */
1776 
1777          if ((mode != FONTLIB) && !strcmp(keyword, "Library")) {
1778 	    char *cptr, *nptr;
1779 	    cptr = strchr(temp, ':');
1780 	    if (cptr != NULL) {
1781 	       cptr += 2;
1782 
1783 	       /* Don't write terminating newline to the object's name string */
1784 	       ridnewline(cptr);
1785 
1786 	       /* The default user technology is written to the output	*/
1787 	       /* as "(user)".  If this is found, replace it with a	*/
1788 	       /* null string.						*/
1789 	       if (!strcmp(cptr, "(user)")) cptr += 6;
1790 
1791 	       /* Removing any leading pathname from the library name */
1792 	       if ((nptr = strrchr(cptr, '/')) != NULL) cptr = nptr + 1;
1793 
1794 	       /* Remove any ".lps" extension from the library name */
1795 
1796 	       nptr = strrchr(cptr, '.');
1797 	       if ((nptr != NULL) && !strcmp(nptr, ".lps")) *nptr = '\0';
1798 
1799 	       nsptr = AddNewTechnology(cptr, inname);
1800 
1801 	       if (nsptr) {
1802 		  // If anything was previously imported from this file
1803 		  // using importfromlibrary(), then the IMPORTED flag
1804 		  // will be set and needs to be cleared.
1805 	          nsptr->flags &= ~TECH_IMPORTED;
1806 	       }
1807 	    }
1808          }
1809 
1810          /* This comment gives the Xcircuit version number */
1811 	 else if (!strcmp(keyword, "Version:")) {
1812 	    char tmpv[20];
1813 	    if (sscanf(temp, "%*c %*s %s", tmpv) > 0) strcpy(version, tmpv);
1814 	 }
1815 
1816          /* This PostScript comment marks the end of the file header */
1817          else if (!strcmp(keyword, "XCircuitLib")) break;
1818       }
1819    }
1820 
1821    /* Set the current top object to the library page so that any	*/
1822    /* expression parameters are computed with respect to the library,	*/
1823    /* not a page.  Revert back to the page after loading the library.	*/
1824 
1825    saveinst = areawin->topinstance;
1826    areawin->topinstance = xobjs.libtop[mode];
1827 
1828    load_in_progress = True;
1829    objectread(ps, topobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr);
1830    load_in_progress = False;
1831    cleanupaliases(mode);
1832 
1833    areawin->topinstance = saveinst;
1834 
1835    if (mode != FONTLIB) {
1836       composelib(mode);
1837       centerview(xobjs.libtop[mode]);
1838       if (nsptr == NULL) nsptr = GetFilenameTechnology(inname);
1839       if (nsptr != NULL)
1840          Wprintf("Loaded library file %s", inname);
1841       else
1842          Wprintf("Loaded library file %s (technology %s)", inname,
1843 		nsptr->technology);
1844    }
1845    else
1846         Wprintf("Loaded font file %s", inname);
1847 
1848    strcpy(version, PROG_VERSION);
1849    fclose(ps);
1850 
1851    /* Check if the library is read-only by opening for append */
1852 
1853    if ((mode != FONTLIB) && (nsptr != NULL)) {
1854       ps = fopen(inname, "a");
1855       if (ps == NULL)
1856          nsptr->flags |= TECH_READONLY;
1857       else
1858          fclose(ps);
1859    }
1860 
1861    return True;
1862 }
1863 
1864 /*---------------------------------------------------------*/
1865 
startloadfile(int libnum)1866 void startloadfile(int libnum)
1867 {
1868    int savemode;
1869    short firstpage = areawin->page;
1870 
1871    while (nextfilename()) {
1872       loadfile(0, libnum);
1873 
1874       /* find next undefined page */
1875 
1876       while(areawin->page < xobjs.pages &&
1877 	   xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
1878       changepage(areawin->page);
1879    }
1880    loadfile(0, libnum);
1881 
1882 
1883    /* Prevent page change from being registered as an undoable action */
1884    savemode = eventmode;
1885    eventmode = UNDO_MODE;
1886 
1887    /* Display the first page loaded */
1888    newpage(firstpage);
1889    eventmode = savemode;
1890 
1891    setsymschem();
1892 }
1893 
1894 /*------------------------------------------------------*/
1895 /* normalloadfile() is a call to startloadfile(-1)	*/
1896 /* meaning load symbols to the User Library 		*/
1897 /*------------------------------------------------------*/
1898 
normalloadfile()1899 void normalloadfile()
1900 {
1901    startloadfile(-1);
1902 }
1903 
1904 /*------------------------------------------------------*/
1905 /* Import an xcircuit file onto the current page	*/
1906 /*------------------------------------------------------*/
1907 
importfile()1908 void importfile()
1909 {
1910    while (nextfilename()) loadfile(1, -1);
1911    loadfile(1, -1);
1912 }
1913 
1914 /*------------------------------------------------------*/
1915 /* Import an PPM graphic file onto the current page	*/
1916 /*------------------------------------------------------*/
1917 
1918 #ifdef HAVE_CAIRO
importgraphic(void)1919 void importgraphic(void)
1920 {
1921    char inname[250];
1922    FILE *spcfile;
1923 
1924    if (eventmode == CATALOG_MODE) {
1925       Wprintf("Cannot import a graphic while in the library window.");
1926       return;
1927    }
1928 
1929    if (!nextfilename()) {
1930       xc_tilde_expand(_STR, 149);
1931       sscanf(_STR, "%149s", inname);
1932       if (!new_graphic(NULL, inname, 0, 0)) {
1933          Wprintf("Error:  Graphic file not found.");
1934 	 return;
1935       }
1936    }
1937    else {
1938       Wprintf("Error:  No graphic file to read.");
1939       return;
1940    }
1941 }
1942 #endif /* HAVE_CAIRO */
1943 
1944 /*--------------------------------------------------------------*/
1945 /* Skip forward in the input file to the next comment line	*/
1946 /*--------------------------------------------------------------*/
1947 
skiptocomment(char * temp,int length,FILE * ps)1948 void skiptocomment(char *temp, int length, FILE *ps)
1949 {
1950    int pch;
1951 
1952    do {
1953       pch = getc(ps);
1954    } while (pch == '\n');
1955 
1956    ungetc(pch, ps);
1957    if (pch == '%') fgets(temp, length, ps);
1958 }
1959 
1960 /*--------------------------------------------------------------*/
1961 /* ASG file import functions:					*/
1962 /* This function loads a SPICE deck (hspice format)		*/
1963 /*--------------------------------------------------------------*/
1964 
1965 #ifdef ASG
1966 
importspice()1967 void importspice()
1968 {
1969    char inname[250];
1970    FILE *spcfile;
1971 
1972    if (eventmode == CATALOG_MODE) {
1973       Wprintf("Cannot import a netlist while in the library window.");
1974       return;
1975    }
1976 
1977    if (!nextfilename()) {
1978       xc_tilde_expand(_STR, 149);
1979       sscanf(_STR, "%149s", inname);
1980       spcfile = fopen(inname, "r");
1981       if (spcfile != NULL) {
1982          ReadSpice(spcfile);
1983          Route(areawin, False);
1984          fclose(spcfile);
1985       }
1986       else {
1987          Wprintf("Error:  Spice file not found.");
1988 	 return;
1989       }
1990    }
1991    else {
1992       Wprintf("Error:  No spice file to read.");
1993       return;
1994    }
1995 }
1996 
1997 #endif
1998 
1999 /*--------------------------------------------------------------*/
2000 /* Load an xcircuit file into xcircuit				*/
2001 /*								*/
2002 /*    mode = 0 is a "load" function.  Behavior:  load on 	*/
2003 /*	current page if empty.  Otherwise, load on first empty	*/
2004 /*	page found.  If no empty pages exist, create a new page	*/
2005 /*	and load there.						*/
2006 /*    mode = 1 is an "import" function.  Behavior:  add		*/
2007 /*	contents of file to the current page.			*/
2008 /*    mode = 2 is a "library load" function.  Behavior:  add	*/
2009 /*	objects in file to the user library but ignore the	*/
2010 /*	page contents.						*/
2011 /*								*/
2012 /* Return value:  True if file was successfully loaded, False	*/
2013 /*	if not.							*/
2014 /*--------------------------------------------------------------*/
2015 
2016 typedef struct _connects *connectptr;
2017 
2018 typedef struct _connects {
2019    short page;
2020    char *master;
2021    connectptr next;
2022 } connects;
2023 
loadfile(short mode,int libnum)2024 Boolean loadfile(short mode, int libnum)
2025 {
2026    FILE *ps;
2027    char inname[150], temp[150], keyword[30], percentc, *pdchar;
2028    char teststr[50], teststr2[20], pagestr[100];
2029    short offx, offy, multipage, page, temppmode = 0;
2030    float tmpfl;
2031    XPoint pagesize;
2032    connects *connlist = NULL;
2033    struct stat statbuf;
2034    int loclibnum = (libnum == -1) ? USERLIB : libnum;
2035 
2036    /* First, if we're in catalog mode, return with error */
2037 
2038    if (eventmode == CATALOG_MODE) {
2039       Wprintf("Cannot load file from library window");
2040       return False;
2041    }
2042 
2043    /* Do tilde/variable expansions on filename and open */
2044    ps = fileopen(_STR, "ps", inname, 149);
2045 
2046    /* Could possibly be a library file?				*/
2047    /* (Note---loadfile() has no problems loading libraries	*/
2048    /* except for setting technology names and setting the	*/
2049    /* library page view at the end.  The loadlibrary() routine	*/
2050    /* should probably be merged into this one.)			*/
2051 
2052    if (ps == NULL) {
2053       ps = fileopen(_STR, "lps", NULL, 0);
2054       if (ps != NULL) {
2055 	 fclose(ps);
2056 	 loadlibrary(loclibnum);
2057 	 return True;
2058       }
2059    }
2060    else if (!strcmp(inname + strlen(inname) - 4, ".lps")) {
2061       fclose(ps);
2062       loadlibrary(loclibnum);
2063       return True;
2064    }
2065 
2066 
2067 #ifdef LGF
2068    /* Could possibly be an LGF file? */
2069    if (ps == NULL) {
2070       ps = fileopen(_STR, "lgf", NULL, 0);
2071       if (ps != NULL) {
2072 	 fclose(ps);
2073 	 loadlgf(mode);
2074 	 return True;
2075       }
2076    }
2077 
2078    /* Could possibly be an LGF backup (.lfo) file? */
2079    if (ps == NULL) {
2080       ps = fileopen(_STR, "lfo", NULL, 0);
2081       if (ps != NULL) {
2082 	 fclose(ps);
2083 	 loadlgf(mode);
2084 	 return True;
2085       }
2086    }
2087 #endif /* LGF */
2088 
2089    /* Check for empty file---don't attempt to read empty files */
2090    if (ps != NULL) {
2091       if (fstat(fileno(ps), &statbuf) == 0 && (statbuf.st_size == (off_t)0)) {
2092          fclose(ps);
2093 	 ps = NULL;
2094       }
2095    }
2096 
2097    /* What to do if no file was found. . . */
2098 
2099    if (ps == NULL) {
2100       if (topobject->parts == 0 && (mode == 0)) {
2101 
2102          /* Check for file extension, and remove if "ps". */
2103 
2104          if ((pdchar = strchr(_STR, '.')) != NULL)
2105 	    if (!strcmp(pdchar + 1, "ps")) *pdchar = '\0';
2106 
2107          free(xobjs.pagelist[areawin->page]->filename);
2108          xobjs.pagelist[areawin->page]->filename = strdup(_STR);
2109 
2110          /* If the name has a path component, use only the root		*/
2111          /* for the object name, but the full path for the filename.	*/
2112 
2113          if ((pdchar = strrchr(_STR, '/')) != NULL)
2114             sprintf(topobject->name, "%s", pdchar + 1);
2115          else
2116 	    sprintf(topobject->name, "%s", _STR);
2117 
2118          renamepage(areawin->page);
2119          printname(topobject);
2120          Wprintf("Starting new drawing");
2121       }
2122       else {
2123          Wprintf("Can't find file %s, won't overwrite current page", _STR);
2124       }
2125       return False;
2126    }
2127 
2128    strcpy(version, "1.0");
2129    multipage = 1;
2130    pagesize.x = 612;
2131    pagesize.y = 792;
2132 
2133    for(;;) {
2134       if (fgets(temp, 149, ps) == NULL) {
2135 	 Wprintf("Error: EOF in or before prolog.");
2136 	 return False;
2137       }
2138       sscanf(temp, "%c%29s", &percentc, keyword);
2139       for (pdchar = keyword; isspace(*pdchar); pdchar++);
2140       if (percentc == '%') {
2141 	 if (!strcmp(pdchar, "XCircuit")) break;
2142 	 if (!strcmp(pdchar, "XCircuitLib")) {
2143 	    /* version control in libraries is post version 1.9 */
2144 	    if (compare_version(version, "1.0") == 0) strcpy(version, "1.9");
2145 	    break;
2146 	 }
2147 	 if (!strcmp(pdchar, "%Page:")) break;
2148 	 if (strstr(pdchar, "PS-Adobe") != NULL)
2149 	    temppmode = (strstr(temp, "EPSF") != NULL) ? 0 : 1;
2150          else if (!strcmp(pdchar, "Version:"))
2151 	    sscanf(temp, "%*c%*s %20s", version);
2152 	 else if (!strcmp(pdchar, "%Pages:")) {
2153 	    pdchar = advancetoken(temp);
2154 	    multipage = atoi(pdchar);
2155 	 }
2156 	 /* Crash files get renamed back to their original filename */
2157 	 else if (!strcmp(pdchar, "%Title:")) {
2158 	    if (xobjs.tempfile != NULL)
2159 	       if (!strcmp(inname, xobjs.tempfile))
2160 	          sscanf(temp, "%*c%*s %s", inname);
2161 	 }
2162 	 else if ((temppmode == 1) && !strcmp(pdchar, "%BoundingBox:")) {
2163 	    short botx, boty;
2164 	    sscanf(temp, "%*s %hd %hd %hd %hd", &botx, &boty,
2165 		&(pagesize.x), &(pagesize.y));
2166 	    pagesize.x += botx;
2167 	    pagesize.y += boty;
2168 	 }
2169 
2170       }
2171 #ifdef LGF
2172       else if (percentc == '-' && !strcmp(keyword, "5")) {
2173 	 fclose(ps);
2174 	 loadlgf(mode);
2175 	 return True;
2176       }
2177 #endif
2178    }
2179 
2180    /* Look for old-style files (no %%Page; maximum one page in file) */
2181 
2182    if (!strcmp(pdchar, "XCircuit"))
2183       skiptocomment(temp, 149, ps);
2184 
2185    for (page = 0; page < multipage; page++) {
2186       sprintf(pagestr, "%d", page + 1);
2187 
2188       /* read out-of-page library definitions */
2189 
2190       if (strstr(temp, "%%Page:") == NULL && strstr(temp, "offsets") == NULL) {
2191          load_in_progress = True;
2192 	 objectread(ps, topobject, 0, 0, loclibnum, temp, DEFAULTCOLOR, NULL);
2193          load_in_progress = False;
2194       }
2195 
2196       if (strstr(temp, "%%Page:") != NULL) {
2197 	 sscanf(temp + 8, "%99s", pagestr);
2198 
2199 	 /* Read the next line so any keywords in the Page name don't	*/
2200 	 /* confuse the parser.						*/
2201 	 if (fgets(temp, 149, ps) == NULL) {
2202             Wprintf("Error: bad page header.");
2203             return False;
2204 	 }
2205 
2206 	 /* Library load mode:  Ignore all pages, just load objects */
2207 	 if (mode == 2) {
2208 	    while (strstr(temp, "showpage") == NULL) {
2209 	       if (fgets(temp, 149, ps) == NULL) {
2210 		  Wprintf("Error: bad page definition.");
2211 		  return False;
2212 	       }
2213 	    }
2214 	    skiptocomment(temp, 149, ps);
2215 	    continue;
2216 	 }
2217       }
2218 
2219       /* go to new page if necessary */
2220 
2221       if (page > 0) {
2222 
2223 	 /* find next undefined page */
2224 
2225 	 while(areawin->page < xobjs.pages &&
2226 		xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
2227 	 changepage(areawin->page);
2228       }
2229 
2230       /* If this file was a library file then there is no page to load */
2231 
2232       if (strstr(temp, "EndLib") != NULL) {
2233    	 composelib(loclibnum);
2234 	 centerview(xobjs.libtop[mode]);
2235 	 Wprintf("Loaded library.");
2236 	 return True;
2237       }
2238 
2239       /* good so far;  let's clear out the old data structure */
2240 
2241       if (mode == 0) {
2242          reset(topobject, NORMAL);
2243          pagereset(areawin->page);
2244 	 xobjs.pagelist[areawin->page]->pmode = temppmode;
2245 	 if (temppmode == 1) {
2246 	    xobjs.pagelist[areawin->page]->pagesize.x = pagesize.x;
2247 	    xobjs.pagelist[areawin->page]->pagesize.y = pagesize.y;
2248 	 }
2249       }
2250       else {
2251 	 invalidate_netlist(topobject);
2252 	 /* ensure that the netlist for topobject is destroyed */
2253 	 freenetlist(topobject);
2254       }
2255 
2256       /* clear the undo record */
2257       flush_undo_stack();
2258 
2259       /* read to the "scale" line, picking up inch/cm type, drawing */
2260       /* scale, and grid/snapspace along the way		    */
2261 
2262       offx = offy = 0;
2263       for(;;) {
2264          if (strstr(temp, "offsets") != NULL) {
2265 	    /* Prior to version 3.1.28 only. . . */
2266             sscanf(temp, "%c %hd %hd %*s", &percentc, &offx, &offy);
2267             if(percentc != '%') {
2268                Wprintf("Something wrong in offsets line.");
2269                offx = offy = 0;
2270             }
2271 	 }
2272 
2273 	 if ((temppmode == 1) && strstr(temp, "%%PageBoundingBox:") != NULL) {
2274 	    /* Recast the individual page size if specified per page */
2275 	    sscanf(temp, "%*s %*d %*d %hd %hd",
2276 		&xobjs.pagelist[areawin->page]->pagesize.x,
2277 		&xobjs.pagelist[areawin->page]->pagesize.y);
2278 	 }
2279 	 else if (strstr(temp, "drawingscale") != NULL)
2280 	    sscanf(temp, "%*c %hd:%hd %*s",
2281 		&xobjs.pagelist[areawin->page]->drawingscale.x,
2282 		&xobjs.pagelist[areawin->page]->drawingscale.y);
2283 
2284 	 else if (strstr(temp, "hidden") != NULL)
2285 	    topobject->hidden = True;
2286 
2287 	 else if (strstr(temp, "is_symbol") != NULL) {
2288 	    sscanf(temp, "%*c %49s", teststr);
2289 	    checkschem(topobject, teststr);
2290 	 }
2291          else if (strstr(temp, "is_primary") != NULL) {
2292 	   /* objectptr master; (jdk) */
2293 	    connects *newconn;
2294 
2295 	    /* Save information about master schematic and link at end of load */
2296 	    sscanf(temp, "%*c %49s", teststr);
2297 	    newconn = (connects *)malloc(sizeof(connects));
2298 	    newconn->next = connlist;
2299 	    connlist = newconn;
2300 	    newconn->page = areawin->page;
2301 	    newconn->master = strdup(teststr);
2302          }
2303 
2304 	 else if (strstr(temp, "gridspace"))
2305 	    sscanf(temp, "%*c %f %f %*s", &xobjs.pagelist[areawin->page]->gridspace,
2306 		 &xobjs.pagelist[areawin->page]->snapspace);
2307          else if (strstr(temp, "scale") != NULL || strstr(temp, "rotate") != NULL) {
2308             /* rotation (landscape mode) is optional; parse accordingly */
2309 
2310             sscanf(temp, "%f %49s", &tmpfl, teststr);
2311             if (strstr(teststr, "scale") != NULL) {
2312 #ifndef TCL_WRAPPER
2313 	       setgridtype(teststr);
2314 #else
2315 	       if (strstr(teststr, "inch"))
2316 		  Tcl_Eval(xcinterp, "xcircuit::coordstyle inches");
2317 	       else
2318 		  Tcl_Eval(xcinterp, "xcircuit::coordstyle centimeters");
2319 #endif
2320                xobjs.pagelist[areawin->page]->outscale = tmpfl;
2321             }
2322             else if (!strcmp(teststr, "rotate")) {
2323                xobjs.pagelist[areawin->page]->orient = (short)tmpfl;
2324                fgets(temp, 149, ps);
2325                sscanf(temp, "%f %19s", &tmpfl, teststr2);
2326 	       if (strstr(teststr2, "scale") != NULL) {
2327 #ifndef TCL_WRAPPER
2328 	          setgridtype(teststr2);
2329 #else
2330 	          if (strstr(teststr2, "inch"))
2331 		     Tcl_Eval(xcinterp, "xcircuit::coordstyle inches");
2332 	          else
2333 		     Tcl_Eval(xcinterp, "xcircuit::coordstyle centimeters");
2334 #endif
2335 	          xobjs.pagelist[areawin->page]->outscale = tmpfl;
2336 	       }
2337 	       else {
2338 	          sscanf(temp, "%*f %*f %19s", teststr2);
2339 	          if (!strcmp(teststr2, "scale"))
2340 	             xobjs.pagelist[areawin->page]->outscale = tmpfl /
2341 		          getpsscale(1.0, areawin->page);
2342 	          else {
2343 	             Wprintf("Error in scale/rotate constructs.");
2344 	             return False;
2345 	          }
2346 	       }
2347             }
2348             else {     /* old style scale? */
2349                sscanf(temp, "%*f %*s %19s", teststr2);
2350 	       if ((teststr2 != NULL) && (!strcmp(teststr2, "scale")))
2351                   xobjs.pagelist[areawin->page]->outscale = tmpfl /
2352 		        getpsscale(1.0, areawin->page);
2353 	       else {
2354                   Wprintf("Error in scale/rotate constructs.");
2355                   return False;
2356 	       }
2357             }
2358 	 }
2359          else if (strstr(temp, "setlinewidth") != NULL) {
2360             sscanf(temp, "%f %*s", &xobjs.pagelist[areawin->page]->wirewidth);
2361 	    xobjs.pagelist[areawin->page]->wirewidth /= 1.3;
2362 	    break;
2363 	 }
2364 	 else if (strstr(temp, "insertion") != NULL) {
2365 	    /* read in an included background image */
2366 	    readbackground(ps);
2367 	 }
2368 	 else if (strstr(temp, "<<") != NULL) {
2369 	    char *buffer = temp, *endptr;
2370 	    /* Make sure we have the whole dictionary before calling */
2371 	    while (strstr(buffer, ">>") == NULL) {
2372 	       if (buffer == temp) {
2373 		  buffer = (char *)malloc(strlen(buffer) + 150);
2374 		  strcpy(buffer, temp);
2375 	       }
2376 	       else
2377 	          buffer = (char *)realloc(buffer, strlen(buffer) + 150);
2378 	       endptr = ridnewline(buffer);
2379 	       *endptr++ = ' ';
2380                fgets(endptr, 149, ps);
2381 	    }
2382 	    /* read top-level parameter dictionary */
2383 	    readparams(NULL, NULL, topobject, buffer);
2384 	    if (buffer != temp) free(buffer);
2385 	 }
2386 
2387 	 if (fgets(temp, 149, ps) == NULL) {
2388             Wprintf("Error: Problems encountered in page header.");
2389             return False;
2390          }
2391       }
2392 
2393       load_in_progress = True;
2394       objectread(ps, topobject, offx, offy, LIBRARY, temp, DEFAULTCOLOR, NULL);
2395       load_in_progress = False;
2396 
2397       /* skip to next page boundary or file trailer */
2398 
2399       if (strstr(temp, "showpage") != NULL && multipage != 1) {
2400 	 char *fstop;
2401 
2402 	 skiptocomment(temp, 149, ps);
2403 
2404 	 /* check for new filename if this is a crash recovery file */
2405          if ((fstop = strstr(temp, "is_filename")) != NULL) {
2406 	    strncpy(inname, temp + 2, (int)(fstop - temp - 3));
2407 	    *(inname + (int)(fstop - temp) - 3) = '\0';
2408 	    fgets(temp, 149, ps);
2409 	    skiptocomment(temp, 149, ps);
2410          }
2411       }
2412 
2413       /* Finally: set the filename and pagename for this page */
2414 
2415       if (mode == 0) {
2416 	 char tpstr[6], *rootptr;
2417 
2418 	 /* Set filename and page title.	      */
2419 
2420 	 if (xobjs.pagelist[areawin->page]->filename != NULL)
2421 	    free(xobjs.pagelist[areawin->page]->filename);
2422 	 xobjs.pagelist[areawin->page]->filename = strdup(inname);
2423 
2424 	 /* Get the root name (after all path components) */
2425 
2426 	 rootptr = strrchr(xobjs.pagelist[areawin->page]->filename, '/');
2427 	 if (rootptr == NULL) rootptr = xobjs.pagelist[areawin->page]->filename;
2428 	 else rootptr++;
2429 
2430 	 /* If we didn't read in a page name from the %%Page: header line, then */
2431 	 /* set the page name to the root name of the file.			*/
2432 
2433 	 sprintf(tpstr, "%d", page + 1);
2434 	 if (!strcmp(pagestr, tpstr)) {
2435 	    if (rootptr == NULL)
2436 	      sprintf(topobject->name, "Page %d", page + 1);
2437 	    else
2438 	      sprintf(topobject->name, "%.79s", rootptr);
2439 
2440               /* Delete filename extensions ".ps" or ".eps" from the page name */
2441               if ((pdchar = strrchr(topobject->name, '.')) != NULL) {
2442 	         if (!strcmp(pdchar + 1, "ps") || !strcmp(pdchar + 1, "eps"))
2443 		    *pdchar = '\0';
2444 	      }
2445 	 }
2446 	 else
2447 	    sprintf(topobject->name, "%.79s", pagestr);
2448 
2449          renamepage(areawin->page);
2450       }
2451 
2452       /* set object position to fit to window separately for each page */
2453       calcbbox(areawin->topinstance);
2454       centerview(areawin->topinstance);
2455    }
2456 
2457    /* Crash file recovery: read any out-of-page library definitions tacked */
2458    /* onto the end of the file into the user library.			   */
2459    if (strncmp(temp, "%%Trailer", 9)) {
2460       load_in_progress = True;
2461       objectread(ps, topobject, 0, 0, USERLIB, temp, DEFAULTCOLOR, NULL);
2462       load_in_progress = False;
2463    }
2464 
2465    cleanupaliases(USERLIB);
2466 
2467    /* Connect master->slave schematics */
2468    while (connlist != NULL) {
2469       connects *thisconn = connlist;
2470       objectptr master = NameToPageObject(thisconn->master, NULL, NULL);
2471       if (master) {
2472 	 xobjs.pagelist[thisconn->page]->pageinst->thisobject->symschem = master;
2473 	 xobjs.pagelist[thisconn->page]->pageinst->thisobject->schemtype = SECONDARY;
2474       }
2475       else {
2476 	 Fprintf(stderr, "Error:  Cannot find primary schematic for %s\n",
2477 		xobjs.pagelist[thisconn->page]->pageinst->thisobject->name);
2478       }
2479       connlist = thisconn->next;
2480       free(thisconn->master);
2481       free(thisconn);
2482    }
2483 
2484    Wprintf("Loaded file: %s (%d page%s)", inname, multipage,
2485 	(multipage > 1 ? "s" : ""));
2486 
2487    composelib(loclibnum);
2488    centerview(xobjs.libtop[loclibnum]);
2489    composelib(PAGELIB);
2490 
2491    if (compare_version(version, PROG_VERSION) == 1) {
2492       Wprintf("WARNING: file %s is version %s vs. executable version %s",
2493 		inname, version, PROG_VERSION);
2494    }
2495 
2496    strcpy(version, PROG_VERSION);
2497    fclose(ps);
2498    return True;
2499 }
2500 
2501 /*------------------------------------------------------*/
2502 /* Object name comparison:  True if names are equal,    */
2503 /* not counting leading underscores.			*/
2504 /*------------------------------------------------------*/
2505 
objnamecmp(char * name1,char * name2)2506 int objnamecmp(char *name1, char *name2)
2507 {
2508    char *n1ptr = name1;
2509    char *n2ptr = name2;
2510 
2511    while (*n1ptr == '_') n1ptr++;
2512    while (*n2ptr == '_') n2ptr++;
2513 
2514    return (strcmp(n1ptr, n2ptr));
2515 }
2516 
2517 /*------------------------------------------------------*/
2518 /* Standard delimiter matching character.		*/
2519 /*------------------------------------------------------*/
2520 
standard_delimiter_end(char source)2521 char standard_delimiter_end(char source)
2522 {
2523    char target;
2524    switch(source) {
2525       case '(':  target = ')'; break;
2526       case '[':  target = ']'; break;
2527       case '{':  target = '}'; break;
2528       case '<':  target = '>'; break;
2529       default:   target = source;
2530    }
2531    return target;
2532 }
2533 
2534 /*------------------------------------------------------*/
2535 /* Find matching parenthesis, bracket, brace, or tag	*/
2536 /* Don't count when backslash character '\' is in front */
2537 /*------------------------------------------------------*/
2538 
find_delimiter(u_char * fstring)2539 u_char *find_delimiter(u_char *fstring)
2540 {
2541    int count = 1;
2542 
2543    u_char *search = fstring;
2544    u_char source = *fstring;
2545    u_char target;
2546 
2547    target = (u_char)standard_delimiter_end((char)source);
2548    while (*++search != '\0') {
2549       if (*search == source && *(search - 1) != '\\') count++;
2550       else if (*search == target && *(search - 1) != '\\') count--;
2551       if (count == 0) break;
2552    }
2553    return search;
2554 }
2555 
2556 /*----------------------------------------------------------------------*/
2557 /* Remove unnecessary font change information from a label		*/
2558 /*----------------------------------------------------------------------*/
2559 
cleanuplabel(stringpart ** strhead)2560 void cleanuplabel(stringpart **strhead)
2561 {
2562    stringpart *curpart = *strhead;
2563    int oldfont, curfont;
2564    Boolean fline = False;
2565 
2566    oldfont = curfont = -1;
2567 
2568    while (curpart != NULL) {
2569       switch (curpart->type) {
2570 	 case FONT_NAME:
2571 	    if (curfont == curpart->data.font) {
2572 	       /* Font change is redundant:  remove it */
2573 	       /* Careful!  font changes remove overline/underline; if	*/
2574 	       /* either one is in effect, replace it with "noline"	*/
2575 	       if (fline)
2576 		  curpart->type = NOLINE;
2577 	       else
2578 	          curpart = deletestring(curpart, strhead, NULL);
2579 	    }
2580 	    else {
2581 	       curfont = curpart->data.font;
2582 	    }
2583 	    break;
2584 
2585 	 case FONT_SCALE:
2586 	    /* Old style font scale is always written absolute, not relative.	*/
2587 	    /* Changes in scale were not allowed, so just get rid of them.	*/
2588 	    if (compare_version(version, "2.3") < 0)
2589 	       curpart = deletestring(curpart, strhead, areawin->topinstance);
2590 	    break;
2591 
2592 	 /* A font change may occur inside a parameter, so any font	*/
2593 	 /* declaration after a parameter must be considered to be	*/
2594 	 /* intentional.						*/
2595 
2596 	 case PARAM_END:
2597 	    curfont = oldfont = -1;
2598 	    break;
2599 
2600 	 case OVERLINE: case UNDERLINE:
2601 	    fline = True;
2602 	    break;
2603 
2604 	 case NOLINE:
2605 	    fline = False;
2606 	    break;
2607 
2608 	 case NORMALSCRIPT: case RETURN:
2609 	    if (oldfont != -1) {
2610 	       curfont = oldfont;
2611 	       oldfont = -1;
2612 	    }
2613 	    break;
2614 
2615 	 case SUBSCRIPT: case SUPERSCRIPT:
2616 	    if (oldfont == -1)
2617 	       oldfont = curfont;
2618 	    break;
2619       }
2620       if (curpart != NULL)
2621 	 curpart = curpart->nextpart;
2622    }
2623 }
2624 
2625 /*----------------------------------------------------------------------*/
2626 /* Read label segments							*/
2627 /*----------------------------------------------------------------------*/
2628 
readlabel(objectptr localdata,char * lineptr,stringpart ** strhead)2629 void readlabel(objectptr localdata, char *lineptr, stringpart **strhead)
2630 {
2631    Boolean fline = False;
2632    /* char *sptr; (jdk) */
2633    short j;
2634    char *endptr, *segptr = lineptr;
2635    char key[100];
2636    stringpart *newpart;
2637    oparamptr ops;
2638 
2639    while (*segptr != '\0') {	/* Look through all segments */
2640 
2641       while (isspace(*segptr) && (*segptr != '\0')) segptr++;
2642 
2643       if (*segptr == '(' || *segptr == '{') {
2644          endptr = find_delimiter(segptr);
2645          *endptr++ = '\0';
2646 	 /* null string (e.g., null parameter substitution) */
2647 	 if ((*segptr == '(') && (*(segptr + 1) == '\0')) {
2648 	    segptr = endptr;
2649 	    continue;
2650 	 }
2651       }
2652       else if (*segptr == '\0' || *segptr == '}') break;
2653 
2654       makesegment(strhead, *strhead);
2655       newpart = *strhead;
2656 
2657       /* Embedded command is in braces: {} */
2658 
2659       if (*segptr == '{') {
2660 
2661          /* Find the command for this PostScript procedure */
2662 	 char *cmdptr = endptr - 2;
2663          while (isspace(*cmdptr)) cmdptr--;
2664          while (!isspace(*cmdptr) && (cmdptr > segptr)) cmdptr--;
2665 	 cmdptr++;
2666 	 segptr++;
2667 
2668          if (!strncmp(cmdptr, "Ss", 2))
2669 	    newpart->type = SUPERSCRIPT;
2670          else if (!strncmp(cmdptr, "ss", 2))
2671             newpart->type = SUBSCRIPT;
2672          else if (!strncmp(cmdptr, "ns", 2))
2673             newpart->type = NORMALSCRIPT;
2674          else if (!strncmp(cmdptr, "hS", 2))
2675             newpart->type = HALFSPACE;
2676          else if (!strncmp(cmdptr, "qS", 2))
2677             newpart->type = QTRSPACE;
2678          else if (!strncmp(cmdptr, "CR", 2)) {
2679 	    newpart->type = RETURN;
2680 	    newpart->data.flags = 0;
2681 	 }
2682          else if (!strcmp(cmdptr, "Ts")) /* "Tab set" command */
2683 	    newpart->type = TABSTOP;
2684          else if (!strcmp(cmdptr, "Tf")) /* "Tab forward" command */
2685 	    newpart->type = TABFORWARD;
2686          else if (!strcmp(cmdptr, "Tb")) /* "Tab backward" command */
2687 	    newpart->type = TABBACKWARD;
2688          else if (!strncmp(cmdptr, "ol", 2)) {
2689             newpart->type = OVERLINE;
2690             fline = True;
2691          }
2692          else if (!strncmp(cmdptr, "ul", 2)) {
2693             newpart->type = UNDERLINE;
2694             fline = True;
2695          }
2696          else if (!strncmp(cmdptr, "sce", 3)) {  /* revert to default color */
2697             newpart->type = FONT_COLOR;
2698 	    newpart->data.color = DEFAULTCOLOR;
2699          }
2700          else if (cmdptr == segptr) {   /* cancel over- or underline */
2701             newpart->type = NOLINE;
2702             fline = False;
2703          }
2704 	 /* To-do:  replace old-style backspace with tab stop */
2705          else if (!strcmp(cmdptr, "bs")) {
2706 	    Wprintf("Warning:  Obsolete backspace command ommitted in text");
2707          }
2708          else if (!strcmp(cmdptr, "Kn")) {  /* "Kern" command */
2709 	    int kx, ky;
2710 	    sscanf(segptr, "%d %d", &kx, &ky);
2711 	    newpart->type = KERN;
2712 	    newpart->data.kern[0] = kx;
2713 	    newpart->data.kern[1] = ky;
2714          }
2715 	 else if (!strcmp(cmdptr, "MR")) { /* "Margin stop" command */
2716 	    int width;
2717 	    sscanf(segptr, "%d", &width);
2718 	    newpart->type = MARGINSTOP;
2719 	    newpart->data.width = width;
2720 	 }
2721          else if (!strcmp(cmdptr, "scb")) {  /* change color command */
2722 	    float cr, cg, cb;
2723 	    int cval, cindex;
2724 	    sscanf(segptr, "%f %f %f", &cr, &cg, &cb);
2725             newpart->type = FONT_COLOR;
2726 	    cindex = rgb_alloccolor((int)(cr * 65535), (int)(cg * 65535),
2727 		   (int)(cb * 65535));
2728 	    newpart->data.color = cindex;
2729          }
2730          else if (!strcmp(cmdptr, "cf")) {  /* change font or scale command */
2731 	    char *nextptr, *newptr = segptr;
2732 
2733 	    /* Set newptr to the fontname and nextptr to the next token. */
2734 	    while (*newptr != '/' && *newptr != '\0') newptr++;
2735 	    if (*newptr++ == '\0') {
2736 	       Wprintf("Error:  Bad change-font command");
2737 	       newpart->type = NOLINE;	/* placeholder */
2738 	    }
2739 	    for (nextptr = newptr; !isspace(*nextptr); nextptr++);
2740 	    *(nextptr++) = '\0';
2741 	    while (isspace(*nextptr)) nextptr++;
2742 
2743             for (j = 0; j < fontcount; j++)
2744                if (!strcmp(newptr, fonts[j].psname))
2745 	          break;
2746 
2747             if (j == fontcount) 		/* this is a non-loaded font */
2748                if (loadfontfile(newptr) < 0) {
2749 		  if (fontcount > 0) {
2750 		     Wprintf("Error:  Font \"%s\" not found---using default.", newptr);
2751 		     j = 0;
2752 		  }
2753 		  else {
2754 		     Wprintf("Error:  No fonts!");
2755 	    	     newpart->type = NOLINE;	/* placeholder */
2756 		  }
2757 	       }
2758 
2759             if (isdigit(*nextptr)) { /* second form of "cf" command---includes scale */
2760 	       float locscale;
2761 	       sscanf(nextptr, "%f", &locscale);
2762 	       newpart->type = FONT_SCALE;
2763 	       newpart->data.scale = locscale;
2764 	       makesegment(strhead, *strhead);
2765 	       newpart = *strhead;
2766             }
2767 	    newpart->type = FONT_NAME;
2768 	    newpart->data.font = j;
2769          }
2770          else {   /* This exec isn't a known label function */
2771 	    Wprintf("Error:  unknown substring function");
2772 	    newpart->type = NOLINE;	/* placeholder */
2773          }
2774       }
2775 
2776       /* Text substring is in parentheses: () */
2777 
2778       else if (*segptr == '(') {
2779          if (fline == True) {
2780 	    newpart->type = NOLINE;
2781             makesegment(strhead, *strhead);
2782 	    newpart = *strhead;
2783 	    fline = False;
2784          }
2785          newpart->type = TEXT_STRING;
2786          newpart->data.string = (u_char *)malloc(1 + strlen(++segptr));
2787 
2788          /* Copy string, translating octal codes into 8-bit characters */
2789 	 parse_ps_string(segptr, newpart->data.string, strlen(segptr), TRUE, TRUE);
2790       }
2791 
2792       /* Parameterized substrings are denoted by parameter key.	*/
2793       /* The parameter (default and/or substitution value) is 	*/
2794       /* assumed to exist.					*/
2795 
2796       else {
2797 
2798 	 parse_ps_string(segptr, key, 99, FALSE, TRUE);
2799 	 if (strlen(key) > 0) {
2800 	    newpart->type = PARAM_START;
2801 	    newpart->data.string = (char *)malloc(1 + strlen(key));
2802 	    strcpy(newpart->data.string, key);
2803 
2804 	    /* check for compatibility between the parameter value and	*/
2805 	    /* the number of parameters and parameter type.		*/
2806 
2807 	    ops = match_param(localdata, key);
2808 	    if (ops == NULL) {
2809 	       Fprintf(stderr, "readlabel() error:  No such parameter %s!\n", key);
2810 	       deletestring(newpart, strhead, areawin->topinstance);
2811 	    }
2812 
2813             /* Fprintf(stdout, "Parameter %s called from object %s\n",	*/
2814 	    /* key,	localdata->name);				*/
2815          }
2816 	 endptr = segptr + 1;
2817 	 while (!isspace(*endptr) && (*endptr != '\0')) endptr++;
2818       }
2819       segptr = endptr;
2820    }
2821 }
2822 
2823 /*--------------------------------------*/
2824 /* skip over whitespace in string input */
2825 /*--------------------------------------*/
2826 
skipwhitespace(char * lineptr)2827 char *skipwhitespace(char *lineptr)
2828 {
2829    char *locptr = lineptr;
2830 
2831    while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2832    return locptr;
2833 }
2834 
2835 /*-------------------------------------------*/
2836 /* advance to the next token in string input */
2837 /*-------------------------------------------*/
2838 
advancetoken(char * lineptr)2839 char *advancetoken(char *lineptr)
2840 {
2841    char *locptr = lineptr;
2842 
2843    while (!isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2844    while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2845    return locptr;
2846 }
2847 
2848 /*------------------------------------------------------*/
2849 /* Read a parameter list for an object instance call.	*/
2850 /* This uses the key-value dictionary method but also	*/
2851 /* allows the "old style" in which each parameter	*/
2852 /* was automatically assigned the key v1, v2, etc.	*/
2853 /*------------------------------------------------------*/
2854 
readparams(objectptr localdata,objinstptr newinst,objectptr libobj,char * buffer)2855 void readparams(objectptr localdata, objinstptr newinst, objectptr libobj,
2856 		char *buffer)
2857 {
2858    oparamptr newops, objops, fops;
2859    char *arrayptr, *endptr, *arraynext;
2860    int paramno = 0;
2861    char paramkey[100];
2862 
2863    if ((arrayptr = strstr(buffer, "<<")) == NULL)
2864       if ((arrayptr = strchr(buffer, '[')) == NULL)
2865 	 return;
2866 
2867    endptr = find_delimiter(arrayptr);
2868    if (*arrayptr == '<') {
2869       arrayptr++;	/* move to second '<' in "<<"	*/
2870       endptr--;		/* back up to first '>' in ">>"	*/
2871    }
2872 
2873    /* move to next non-space token after opening bracket */
2874    arrayptr++;
2875    while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++;
2876 
2877    while ((*arrayptr != '\0') && (arrayptr < endptr)) {
2878 
2879       newops = (oparamptr)malloc(sizeof(oparam));
2880 
2881       /* Arrays contain values only.  Dictionaries contain key:value pairs */
2882       if (*endptr == '>') {	/* dictionary type */
2883 	 if (*arrayptr != '/') {
2884 	    Fprintf(stdout, "Error: Dictionary key is a literal, not a name\n");
2885 	 }
2886 	 else arrayptr++;	/* Skip PostScript name delimiter */
2887 	 parse_ps_string(arrayptr, paramkey, 99, FALSE, TRUE);
2888 	 newops->key = (char *)malloc(1 + strlen(paramkey));
2889 	 strcpy(newops->key, paramkey);
2890 	 arrayptr = advancetoken(arrayptr);
2891       }
2892       else {		/* array type; keys are "v1", "v2", etc. */
2893 	 paramno++;
2894 	 newops->key = (char *)malloc(6);
2895 	 sprintf(newops->key, "v%d", paramno);
2896       }
2897 
2898       /* Find matching parameter in object definition */
2899       if (newinst) {
2900 	 objops = match_param(libobj, newops->key);
2901 	 if (objops == NULL) {
2902 	    Fprintf(stdout, "Error: parameter %s does not exist in object %s!\n",
2903 			newops->key, libobj->name);
2904 	    free(newops->key);
2905 	    free(newops);
2906 	    return;
2907 	 }
2908       }
2909 
2910       /* Add to instance's parameter list */
2911       /* If newinst is null, then the parameters are added to libobj */
2912       newops->next = NULL;
2913       if (newinst) {
2914 
2915 	 /* Delete any parameters with duplicate names.  This	*/
2916 	 /* This may indicate an expression parameter that was	*/
2917 	 /* precomputed while determining the bounding box.	*/
2918 
2919 	 for (fops = newinst->params; fops != NULL; fops = fops->next)
2920 	    if (!strcmp(fops->key, newops->key))
2921 	       if ((fops = free_instance_param(newinst, fops)) == NULL)
2922 		  break;
2923 
2924 	 if (newinst->params == NULL)
2925 	    newinst->params = newops;
2926 	 else {
2927 	    for (fops = newinst->params; fops->next != NULL; fops = fops->next);
2928 	    fops->next = newops;
2929 	 }
2930       }
2931       else {
2932 	 if (libobj->params == NULL)
2933 	    libobj->params = newops;
2934 	 else {
2935 	    for (fops = libobj->params; fops->next != NULL; fops = fops->next);
2936 	    fops->next = newops;
2937 	 }
2938       }
2939 
2940       /* Fill in "which" entry from the object default */
2941       newops->which = (newinst) ? objops->which : 0;
2942 
2943       /* Check next token.  If not either end-of-dictionary or	*/
2944       /* the next parameter key, then value is an expression.	*/
2945       /* Expressions are written as two strings, the first the	*/
2946       /* result of evaluting the expression, and the second the	*/
2947       /* expression itself, followed by "pop" to prevent the	*/
2948       /* PostScript interpreter from trying to evaluate the	*/
2949       /* expression (which is not in PostScript).		*/
2950 
2951       if (*arrayptr == '(' || *arrayptr == '{')
2952 	 arraynext = find_delimiter(arrayptr);
2953       else
2954 	 arraynext = arrayptr;
2955       arraynext = advancetoken(arraynext);
2956 
2957       if ((*endptr == '>') && (arraynext < endptr) && (*arraynext != '/')) {
2958 	 char *substrend, *arraysave;
2959 
2960 	 if (*arraynext == '(' || *arraynext == '{') {
2961 
2962 	    substrend = find_delimiter(arraynext);
2963 	    arraysave = arraynext + 1;
2964 	    arraynext = advancetoken(substrend);
2965 
2966 	    newops->type = (u_char)XC_EXPR;
2967 	    newops->which = P_EXPRESSION;	/* placeholder */
2968 	 }
2969 
2970 	 if (strncmp(arraynext, "pop ", 4)) {
2971 	    Wprintf("Error:  bad expression parameter!\n");
2972 #ifdef TCL_WRAPPER
2973 	    newops->parameter.expr = strdup("expr 0");
2974 #else
2975 	    newops->parameter.expr = strdup("0");
2976 #endif
2977 	    arrayptr = advancetoken(arrayptr);
2978 	 } else {
2979 	    *substrend = '\0';
2980 	    newops->parameter.expr = strdup(arraysave);
2981 	    arrayptr = advancetoken(arraynext);
2982 	 }
2983       }
2984 
2985       else if (*arrayptr == '(' || *arrayptr == '{') {
2986 	 float r, g, b;
2987 	 char *substrend, csave;
2988 	 stringpart *endpart;
2989 
2990 	 /* type XC_STRING */
2991 
2992 	 substrend = find_delimiter(arrayptr);
2993 	 csave = *(++substrend);
2994 	 *substrend = '\0';
2995 	 if (*arrayptr == '{') arrayptr++;
2996 
2997 	 /* A numerical value immediately following the opening */
2998 	 /* brace indicates a color parameter.			*/
2999 	 if (sscanf(arrayptr, "%f %f %f", &r, &g, &b) == 3) {
3000 	    newops->type = (u_char)XC_INT;
3001 	    newops->which = P_COLOR;
3002 	    newops->parameter.ivalue = rgb_alloccolor((int)(r * 65535),
3003 		(int)(g * 65535), (int)(b * 65535));
3004 	    *substrend = csave;
3005 	 }
3006 	 else {
3007 	    char *arraytmp = arrayptr;
3008 	    char linkdefault[5] = "(%n)";
3009 
3010 	    newops->type = (u_char)XC_STRING;
3011 	    newops->which = P_SUBSTRING;
3012 	    newops->parameter.string = NULL;
3013 
3014 	    /* Quick check for "link" parameter:  make object name into "%n" */
3015 	    if (!strcmp(newops->key, "link"))
3016 	       if (!strncmp(arrayptr + 1, libobj->name, strlen(libobj->name)) &&
3017 			!strcmp(arrayptr + strlen(libobj->name) + 1, ")"))
3018 		  arraytmp = linkdefault;
3019 
3020 	    readlabel(libobj, arraytmp, &(newops->parameter.string));
3021 	    *substrend = csave;
3022 
3023 	    /* Append a PARAM_END to the parameter string */
3024 
3025 	    endpart = makesegment(&(newops->parameter.string), NULL);
3026 	    endpart->type = PARAM_END;
3027 	    endpart->data.string = (u_char *)NULL;
3028 	 }
3029 	 arrayptr = substrend;
3030 	 while (isspace(*arrayptr) && *arrayptr != '\0')
3031 	    arrayptr++;
3032       }
3033       else {
3034 	/* char *token; (jdk) */
3035 	 int scanned = 0;
3036 
3037 	 /* type XC_FLOAT or XC_INT, or an indirect reference */
3038 
3039 	 newops->type = (newinst) ? objops->type : (u_char)XC_FLOAT;
3040 
3041 	 if (newops->type == XC_FLOAT) {
3042 	    scanned = sscanf(arrayptr, "%f", &(newops->parameter.fvalue));
3043 	    /* Fprintf(stdout, "Object %s called with parameter "
3044 			"%s value %g\n", libobj->name,
3045 			newops->key, newops->parameter.fvalue); */
3046 	 }
3047 	 else if (newops->type == XC_INT) {
3048 	    scanned = sscanf(arrayptr, "%d", &(newops->parameter.ivalue));
3049 	    /* Fprintf(stdout, "Object %s called with parameter "
3050 			"%s value %d\n", libobj->name,
3051 			newops->key, newops->parameter.ivalue); */
3052 	 }
3053 	 else if (newops->type == XC_EXPR) {
3054 	    /* Instance values of parameters hold the last evaluated	*/
3055 	    /* result and will be regenerated, so we can ignore them	*/
3056 	    /* here.  By ignoring it, we don't have to deal with issues	*/
3057 	    /* like type promotion.					*/
3058 	    free_instance_param(newinst, newops);
3059 	    scanned = 1;	/* avoid treating as an indirect ref */
3060 	 }
3061 	 else if (newops->type == XC_STRING) {
3062 	    /* Fill string record, so we have a valid record.  This will */
3063 	    /* be blown away and replaced by opsubstitute(), but it must */
3064 	    /* have an initial valid entry.				 */
3065 	    stringpart *tmpptr;
3066 	    newops->parameter.string = NULL;
3067 	    tmpptr = makesegment(&newops->parameter.string, NULL);
3068 	    tmpptr->type = TEXT_STRING;
3069 	    tmpptr = makesegment(&newops->parameter.string, NULL);
3070 	    tmpptr->type = PARAM_END;
3071 	 }
3072 	 else {
3073 	    Fprintf(stderr, "Error: unknown parameter type!\n");
3074 	 }
3075 
3076 	 if (scanned == 0) {
3077 	    /* Indirect reference --- create an eparam in the instance */
3078 	    parse_ps_string(arrayptr, paramkey, 99, FALSE, TRUE);
3079 
3080 	    if (!newinst || !localdata) {
3081 		/* Only object instances can use indirect references */
3082 		Fprintf(stderr, "Error: parameter default %s cannot "
3083 			"be parsed!\n", paramkey);
3084 	    }
3085 	    else if (match_param(localdata, paramkey) == NULL) {
3086 		/* Reference key must exist in the calling object */
3087 		Fprintf(stderr, "Error: parameter value %s cannot be parsed!\n",
3088 			paramkey);
3089 	    }
3090 	    else {
3091 	       /* Create an eparam record in the instance */
3092 	       eparamptr newepp = make_new_eparam(paramkey);
3093 	       newepp->flags |= P_INDIRECT;
3094 	       newepp->pdata.refkey = strdup(newops->key);
3095 	       newepp->next = newinst->passed;
3096 	       newinst->passed = newepp;
3097 	    }
3098 
3099 	 }
3100 	 arrayptr = advancetoken(arrayptr);
3101       }
3102    }
3103 
3104    /* Calculate the unique bounding box for the instance */
3105 
3106    if (newinst && (newinst->params != NULL)) {
3107       opsubstitute(libobj, newinst);
3108       calcbboxinst(newinst);
3109    }
3110 }
3111 
3112 /*--------------------------------------------------------------*/
3113 /* Read a value which might be a short integer or a parameter.	*/
3114 /* If the value is a parameter, check the parameter list to see */
3115 /* if it needs to be re-typecast.  Return the position to the	*/
3116 /* next token in "lineptr".					*/
3117 /*--------------------------------------------------------------*/
3118 
varpscan(objectptr localdata,char * lineptr,short * hvalue,genericptr thiselem,int pointno,int offset,u_char which)3119 char *varpscan(objectptr localdata, char *lineptr, short *hvalue,
3120 	genericptr thiselem, int pointno, int offset, u_char which)
3121 {
3122    oparamptr ops = NULL;
3123    char key[100];
3124    /* char *nexttok; (jdk) */
3125    eparamptr newepp;
3126 
3127    if (sscanf(lineptr, "%hd", hvalue) != 1) {
3128       parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3129 
3130       ops = match_param(localdata, key);
3131       newepp = make_new_eparam(key);
3132 
3133       /* Add parameter to the linked list */
3134       newepp->next = thiselem->passed;
3135       thiselem->passed = newepp;
3136       newepp->pdata.pointno = pointno;
3137 
3138       if (ops != NULL) {
3139 
3140 	 /* It cannot be known whether a parameter value is a float or int */
3141 	 /* until we see how the parameter is used.  So we always read the */
3142 	 /* parameter default as a float, and re-typecast it if necessary. */
3143 
3144 	 if (ops->type == XC_FLOAT) {
3145 	    ops->type = XC_INT;
3146 	    /* (add 0.1 to avoid roundoff error in conversion to integer) */
3147 	    ops->parameter.ivalue = (int)(ops->parameter.fvalue +
3148 			((ops->parameter.fvalue < 0) ? -0.1 : 0.1));
3149 	 }
3150 	 ops->which = which;
3151 	 *hvalue = (short)ops->parameter.ivalue;
3152       }
3153       else {
3154 	 *hvalue = 0; /* okay; will get filled in later */
3155 	 Fprintf(stderr, "Error:  parameter %s was used but not defined!\n", key);
3156       }
3157    }
3158 
3159    *hvalue -= (short)offset;
3160 
3161    return advancetoken(skipwhitespace(lineptr));
3162 }
3163 
3164 /*--------------------------------------------------------------*/
3165 /* Read a value which might be a short integer or a parameter,	*/
3166 /* but which is not a point in a pointlist.			*/
3167 /*--------------------------------------------------------------*/
3168 
varscan(objectptr localdata,char * lineptr,short * hvalue,genericptr thiselem,u_char which)3169 char *varscan(objectptr localdata, char *lineptr, short *hvalue,
3170 	 genericptr thiselem, u_char which)
3171 {
3172    return varpscan(localdata, lineptr, hvalue, thiselem, 0, 0, which);
3173 }
3174 
3175 /*--------------------------------------------------------------*/
3176 /* Read a value which might be a float or a parameter.		*/
3177 /* Return the position to the next token in "lineptr".		*/
3178 /*--------------------------------------------------------------*/
3179 
varfscan(objectptr localdata,char * lineptr,float * fvalue,genericptr thiselem,u_char which)3180 char *varfscan(objectptr localdata, char *lineptr, float *fvalue,
3181 	genericptr thiselem, u_char which)
3182 {
3183    oparamptr ops = NULL;
3184    eparamptr newepp;
3185    char key[100];
3186 
3187    if (sscanf(lineptr, "%f", fvalue) != 1) {
3188       parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3189 
3190       /* This bit of a hack takes care of scale-variant	*/
3191       /* linewidth specifiers for object instances.	*/
3192 
3193       if (!strncmp(key, "/sv", 3)) {
3194 	 ((objinstptr)thiselem)->style &= ~LINE_INVARIANT;
3195 	 return varfscan(localdata, advancetoken(skipwhitespace(lineptr)),
3196 			fvalue, thiselem, which);
3197       }
3198 
3199       ops = match_param(localdata, key);
3200       newepp = make_new_eparam(key);
3201 
3202       /* Add parameter to the linked list */
3203       newepp->next = thiselem->passed;
3204       thiselem->passed = newepp;
3205 
3206       if (ops != NULL) {
3207 	 ops->which = which;
3208 	 *fvalue = ops->parameter.fvalue;
3209       }
3210       else
3211 	 Fprintf(stderr, "Error: no parameter \"%s\" defined!\n", key);
3212    }
3213 
3214    /* advance to next token */
3215    return advancetoken(skipwhitespace(lineptr));
3216 }
3217 
3218 /*--------------------------------------------------------------*/
3219 /* Same as varpscan(), but for path types only.			*/
3220 /*--------------------------------------------------------------*/
3221 
varpathscan(objectptr localdata,char * lineptr,short * hvalue,genericptr * thiselem,pathptr thispath,int pointno,int offset,u_char which,eparamptr * nepptr)3222 char *varpathscan(objectptr localdata, char *lineptr, short *hvalue,
3223 	genericptr *thiselem, pathptr thispath, int pointno, int offset,
3224 	u_char which, eparamptr *nepptr)
3225 {
3226    oparamptr ops = NULL;
3227    char key[100];
3228    eparamptr newepp;
3229 
3230    if (nepptr != NULL) *nepptr = NULL;
3231 
3232    if (sscanf(lineptr, "%hd", hvalue) != 1) {
3233       parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3234       ops = match_param(localdata, key);
3235       newepp = make_new_eparam(key);
3236       newepp->pdata.pathpt[1] = pointno;
3237 
3238       if (thiselem == NULL)
3239          newepp->pdata.pathpt[0] = (short)0;
3240       else {
3241 	 short elemidx = (short)(thiselem - thispath->plist);
3242 	 if (elemidx >= 0 && elemidx < thispath->parts)
3243             newepp->pdata.pathpt[0] = (short)(thiselem - thispath->plist);
3244 	 else {
3245 	    Fprintf(stderr, "Error:  Bad parameterized path point!\n");
3246 	    free(newepp);
3247 	    goto pathdone;
3248 	 }
3249       }
3250       if (nepptr != NULL) *nepptr = newepp;
3251 
3252       /* Add parameter to the linked list. */
3253 
3254       newepp->next = thispath->passed;
3255       thispath->passed = newepp;
3256 
3257       if (ops != NULL) {
3258 
3259 	 /* It cannot be known whether a parameter value is a float or int */
3260 	 /* until we see how the parameter is used.  So we always read the */
3261 	 /* parameter default as a float, and re-typecast it if necessary. */
3262 
3263 	 if (ops->type == XC_FLOAT) {
3264 	    ops->type = XC_INT;
3265 	    /* (add 0.1 to avoid roundoff error in conversion to integer) */
3266 	    ops->parameter.ivalue = (int)(ops->parameter.fvalue +
3267 			((ops->parameter.fvalue < 0) ? -0.1 : 0.1));
3268 	 }
3269 	 ops->which = which;
3270 	 *hvalue = (short)ops->parameter.ivalue;
3271       }
3272       else {
3273 	 *hvalue = 0; /* okay; will get filled in later */
3274 	 Fprintf(stderr, "Error:  parameter %s was used but not defined!\n", key);
3275       }
3276    }
3277 
3278 pathdone:
3279    *hvalue -= (short)offset;
3280    return advancetoken(skipwhitespace(lineptr));
3281 }
3282 
3283 /*--------------------------------------------------------------*/
3284 /* Create a new instance of an object in the library's list of	*/
3285 /* instances.  This instance will be used on the library page	*/
3286 /* when doing "composelib()".					*/
3287 /*--------------------------------------------------------------*/
3288 
addtoinstlist(int libnum,objectptr libobj,Boolean virtual)3289 objinstptr addtoinstlist(int libnum, objectptr libobj, Boolean virtual)
3290 {
3291    objinstptr newinst = (objinstptr) malloc(sizeof(objinst));
3292    liblistptr spec = (liblistptr) malloc(sizeof(liblist));
3293    liblistptr srch;
3294 
3295    newinst->type = OBJINST;
3296    instancedefaults(newinst, libobj, 0, 0);
3297 
3298    spec->virtual = (u_char)virtual;
3299    spec->thisinst = newinst;
3300    spec->next = NULL;
3301 
3302    /* Add to end, so that duplicate, parameterized instances	*/
3303    /* always come after the original instance with the default	*/
3304    /* parameters.						*/
3305 
3306    if ((srch = xobjs.userlibs[libnum].instlist) == NULL)
3307       xobjs.userlibs[libnum].instlist = spec;
3308    else {
3309       while (srch->next != NULL) srch = srch->next;
3310       srch->next = spec;
3311    }
3312 
3313    /* Calculate the instance-specific bounding box */
3314    calcbboxinst(newinst);
3315 
3316    return newinst;
3317 }
3318 
3319 /*--------------------------------------------------------------*/
3320 /* Deal with object reads:  Create a new object and prepare for	*/
3321 /* reading.  The library number is passed as "mode".		*/
3322 /*--------------------------------------------------------------*/
3323 
new_library_object(short mode,char * name,objlistptr * retlist,TechPtr defaulttech)3324 objectptr *new_library_object(short mode, char *name, objlistptr *retlist,
3325 		TechPtr defaulttech)
3326 {
3327    objlistptr newdef, redef = NULL;
3328    objectptr *newobject, *libobj;
3329    objectptr *curlib = (mode == FONTLIB) ?
3330 		xobjs.fontlib.library : xobjs.userlibs[mode - LIBRARY].library;
3331    short *libobjects = (mode == FONTLIB) ?
3332 		&xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;
3333    int i, j;
3334    char *nsptr, *fullname = name;
3335 
3336    curlib = (objectptr *) realloc(curlib, (*libobjects + 1)
3337 		      * sizeof(objectptr));
3338    if (mode == FONTLIB) xobjs.fontlib.library = curlib;
3339    else xobjs.userlibs[mode - LIBRARY].library = curlib;
3340 
3341    /* For (older) libraries that do not use technologies, give the	*/
3342    /* object a technology name in the form <library>::<object>		*/
3343 
3344    if ((nsptr = strstr(name, "::")) == NULL) {
3345       int deftechlen = (defaulttech == NULL) ? 0 : strlen(defaulttech->technology);
3346       fullname = (char *)malloc(deftechlen + strlen(name) + 3);
3347       if (defaulttech == NULL)
3348          sprintf(fullname, "::%s", name);
3349       else
3350          sprintf(fullname, "%s::%s", defaulttech->technology, name);
3351    }
3352 
3353    /* initial 1-pointer allocations */
3354 
3355    newobject = curlib + (*libobjects);
3356    *newobject = (objectptr) malloc(sizeof(object));
3357    initmem(*newobject);
3358 
3359    /* check that this object is not already in list of objects */
3360 
3361    if (mode == FONTLIB) {
3362       for (libobj = xobjs.fontlib.library; libobj != xobjs.fontlib.library +
3363             xobjs.fontlib.number; libobj++) {
3364 	 /* This font character may be a redefinition of another */
3365 	 if (!objnamecmp(fullname, (*libobj)->name)) {
3366 	    newdef = (objlistptr) malloc(sizeof(objlist));
3367 	    newdef->libno = FONTLIB;
3368 	    newdef->thisobject = *libobj;
3369 	    newdef->next = redef;
3370 	    redef = newdef;
3371 	 }
3372       }
3373    }
3374    else {
3375       for (i = 0; i < xobjs.numlibs; i++) {
3376 	 for (j = 0; j < xobjs.userlibs[i].number; j++) {
3377 	    libobj = xobjs.userlibs[i].library + j;
3378 	    /* This object may be a redefinition of another object */
3379 	    if (!objnamecmp(fullname, (*libobj)->name)) {
3380 	       newdef = (objlistptr) malloc(sizeof(objlist));
3381 	       newdef->libno = i + LIBRARY;
3382 	       newdef->thisobject = *libobj;
3383 	       newdef->next = redef;
3384 	       redef = newdef;
3385 	    }
3386 	 }
3387       }
3388    }
3389 
3390    (*libobjects)++;
3391    sprintf((*newobject)->name, "%s", fullname);
3392    if (fullname != name) free(fullname);
3393 
3394    /* initmem() initialized schemtype to PRIMARY;  change it. */
3395    (*newobject)->schemtype = (mode == FONTLIB) ? GLYPH : SYMBOL;
3396 
3397    /* If the object declares a technology name that is different from the */
3398    /* default, then add the technology name to the list of technologies,  */
3399    /* with a NULL filename.						  */
3400 
3401    if (mode != FONTLIB) AddObjectTechnology(*newobject);
3402 
3403    *retlist = redef;
3404    return newobject;
3405 }
3406 
3407 /*--------------------------------------------------------------*/
3408 /* do an exhaustive comparison between a new object and any	*/
3409 /* object having the same name. If they are the same, destroy	*/
3410 /* the duplicate.  If different, rename the original one.	*/
3411 /*--------------------------------------------------------------*/
3412 
library_object_unique(short mode,objectptr newobject,objlistptr redef)3413 Boolean library_object_unique(short mode, objectptr newobject, objlistptr redef)
3414 {
3415    Boolean is_unique = True;
3416    objlistptr newdef;
3417    short *libobjects = (mode == FONTLIB) ?
3418 		&xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;
3419 
3420    if (redef == NULL)
3421       return is_unique;	/* No name conflicts; object is okay as-is */
3422 
3423    for (newdef = redef; newdef != NULL; newdef = newdef->next) {
3424 
3425       /* Must make sure that default parameter values are */
3426       /* plugged into both objects!		  	  */
3427       opsubstitute(newdef->thisobject, NULL);
3428       opsubstitute(newobject, NULL);
3429 
3430       if (objcompare(newobject, newdef->thisobject) == True) {
3431 	  addalias(newdef->thisobject, newobject->name);
3432 
3433 	  /* If the new object has declared an association to a */
3434 	  /* schematic, transfer it to the original, and make   */
3435 	  /* sure that the page points to the object which will */
3436 	  /* be left, not the one which will be destroyed.	*/
3437 
3438 	  if (newobject->symschem != NULL) {
3439 	     newdef->thisobject->symschem = newobject->symschem;
3440 	     newdef->thisobject->symschem->symschem = newdef->thisobject;
3441 	  }
3442 
3443 	  reset(newobject, DESTROY);
3444 	  (*libobjects)--;
3445 	  is_unique = False;
3446 	  break;
3447        }
3448 
3449        /* Not the same object, but has the same name.  This can't	*/
3450        /* happen within the same input file, so the name of the		*/
3451        /* original object can safely be altered.			*/
3452 
3453        else if (!strcmp(newobject->name, newdef->thisobject->name)) {
3454 
3455 	  /* Replacement---for project management, allow the technology	*/
3456 	  /* master version to take precedence over a local version.	*/
3457 
3458 	  TechPtr nsptr = GetObjectTechnology(newobject);
3459 
3460 	  if (nsptr && (nsptr->flags & TECH_REPLACE)) {
3461 	     reset(newobject, DESTROY);
3462 	     (*libobjects)--;
3463 	     is_unique = False;
3464 	  }
3465 	  else
3466 	     checkname(newdef->thisobject);
3467 	  break;
3468        }
3469     }
3470     for (; (newdef = redef->next); redef = newdef)
3471        free(redef);
3472     free(redef);
3473 
3474     return is_unique;
3475 }
3476 
3477 /*--------------------------------------------------------------*/
3478 /* Add an instance of the object to the library's instance list */
3479 /*--------------------------------------------------------------*/
3480 
add_object_to_library(short mode,objectptr newobject)3481 void add_object_to_library(short mode, objectptr newobject)
3482 {
3483    objinstptr libinst;
3484 
3485    if (mode == FONTLIB) return;
3486 
3487    libinst = addtoinstlist(mode - LIBRARY, newobject, False);
3488    calcbboxvalues(libinst, (genericptr *)NULL);
3489 
3490    /* Center the view of the object in its instance */
3491    centerview(libinst);
3492 }
3493 
3494 /*--------------------------------------------------------------*/
3495 /* Continuation Line --- add memory to "buffer" as necessary.	*/
3496 /* Add a space character to the current text in "buffer" and	*/
3497 /* return a pointer to the new end-of-text.			*/
3498 /*--------------------------------------------------------------*/
3499 
continueline(char ** buffer)3500 char *continueline(char **buffer)
3501 {
3502    char *lineptr;
3503    int bufsize;
3504 
3505    for (lineptr = *buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++);
3506    /* Repair Windoze-mangled files */
3507    if ((lineptr > *buffer) && (*lineptr == '\n') && (*(lineptr - 1) == '\r'))
3508       *(lineptr - 1) = ' ';
3509    if (*lineptr == '\n') *lineptr++ = ' ';
3510 
3511    bufsize = (int)(lineptr - (*buffer)) + 256;
3512    *buffer = (char *)realloc((*buffer), bufsize * sizeof(char));
3513 
3514    return ((*buffer) + (bufsize - 256));
3515 }
3516 
3517 /*--------------------------------------------------------------*/
3518 /* Read image data out of the Setup block of the input		*/
3519 /* We assume that width and height have been parsed from the	*/
3520 /* "imagedata" line and the file pointer is at the next line.	*/
3521 /*--------------------------------------------------------------*/
3522 
readimagedata(FILE * ps,int width,int height)3523 void readimagedata(FILE *ps, int width, int height)
3524 {
3525    char temp[150], ascbuf[6];
3526    int x, y, p, q, r, g, b, ilen;
3527    char *pptr;
3528    Imagedata *iptr;
3529    Boolean do_flate = False, do_ascii = False;
3530    u_char *filtbuf, *flatebuf;
3531    union {
3532       u_char b[4];
3533       u_long i;
3534    } pixel;
3535 
3536    iptr = addnewimage(NULL, width, height);
3537 
3538    /* Read the image data */
3539 
3540    fgets(temp, 149, ps);
3541    if (strstr(temp, "ASCII85Decode") != NULL) do_ascii = TRUE;
3542 #ifdef HAVE_LIBZ
3543    if (strstr(temp, "FlateDecode") != NULL) do_flate = TRUE;
3544 #else
3545    if (strstr(temp, "FlateDecode") != NULL)
3546       Fprintf(stderr, "Error:  Don't know how to Flate decode!"
3547 		"  Get zlib and recompile xcircuit!\n");
3548 #endif
3549    while (strstr(temp, "ReusableStreamDecode") == NULL)
3550       fgets(temp, 149, ps);  /* Additional piped filter lines */
3551 
3552    fgets(temp, 149, ps);  /* Initial data line */
3553    q = 0;
3554    pptr = temp;
3555    ilen = 3 * width * height;
3556    filtbuf = (u_char *)malloc(ilen + 4);
3557 
3558    if (!do_ascii) {	/* ASCIIHexDecode algorithm */
3559       q = 0;
3560       for (y = 0; y < height; y++) {
3561          for (x = 0; x < width; x++) {
3562             sscanf(pptr, "%02x%02x%02x", &r, &g, &b);
3563             filtbuf[q++] = (u_char)r;
3564             filtbuf[q++] = (u_char)g;
3565             filtbuf[q++] = (u_char)b;
3566             pptr += 6;
3567             if (*pptr == '\n') {
3568                fgets(temp, 149, ps);
3569                pptr = temp;
3570             }
3571          }
3572       }
3573    }
3574    else {		/* ASCII85Decode algorithm */
3575       p = 0;
3576       while (1) {
3577          ascbuf[0] = *pptr;
3578          pptr++;
3579          if (ascbuf[0] == '~')
3580 	    break;
3581          else if (ascbuf[0] == 'z') {
3582 	    for (y = 0; y < 5; y++) ascbuf[y] = '\0';
3583          }
3584          else {
3585 	    for (y = 1; y < 5; y++) {
3586 	       if (*pptr == '\n') {
3587 	          fgets(temp, 149, ps);
3588 	          pptr = temp;
3589 	       }
3590 	       ascbuf[y] = *pptr;
3591 	       if (ascbuf[y] == '~') {
3592 	          for (; y < 5; y++) {
3593 		     ascbuf[y] = '!';
3594 		     p++;
3595 		  }
3596 	          break;
3597 	       }
3598 	       else pptr++;
3599 	    }
3600 	    for (y = 0; y < 5; y++) ascbuf[y] -= '!';
3601          }
3602 
3603          if (*pptr == '\n') {
3604 	    fgets(temp, 149, ps);
3605 	    pptr = temp;
3606          }
3607 
3608          /* Decode from ASCII85 to binary */
3609 
3610          pixel.i = ascbuf[4] + ascbuf[3] * 85 + ascbuf[2] * 7225 +
3611 		ascbuf[1] * 614125 + ascbuf[0] * 52200625;
3612 
3613 	 /* Add in roundoff for final bytes */
3614 	 if (p > 0) {
3615 	    switch (p) {
3616 	       case 3:
3617 		  pixel.i += 0xff0000;
3618 	       case 2:
3619 		  pixel.i += 0xff00;
3620 	       case 1:
3621 		  pixel.i += 0xff;
3622 	    }
3623 	 }
3624 
3625          for (y = 0; y < (4 - p); y++) {
3626 	    filtbuf[q + y] = pixel.b[3 - y];
3627          }
3628          q += (4 - p);
3629          if (q >= ilen) break;
3630       }
3631    }
3632 
3633    /* Extra decoding goes here */
3634 
3635 #ifdef HAVE_LIBZ
3636    if (do_flate) {
3637       flatebuf = (char *)malloc(ilen);
3638       large_inflate(filtbuf, q, &flatebuf, ilen);
3639       free(filtbuf);
3640    }
3641    else
3642 #endif
3643 
3644    flatebuf = filtbuf;
3645 
3646    q = 0;
3647    for (y = 0; y < height; y++)
3648       for (x = 0; x < width; x++) {
3649 	 u_char r, g, b;
3650 	 r = flatebuf[q++];
3651 	 g = flatebuf[q++];
3652 	 b = flatebuf[q++];
3653 	 xcImagePutPixel(iptr->image, x, y, r, g, b);
3654       }
3655 
3656    free(flatebuf);
3657 
3658    fgets(temp, 149, ps);	/* definition line */
3659    fgets(temp, 149, ps);	/* pick up name of image from here */
3660    for (pptr = temp; !isspace(*pptr); pptr++);
3661    *pptr = '\0';
3662    iptr->filename = strdup(temp + 1);
3663    for (x = 0; x < 5; x++) fgets(temp, 149, ps);  /* skip image dictionary */
3664 }
3665 
3666 /*--------------------------------------------------------------*/
3667 /* Read an object (page) from a file into xcircuit		*/
3668 /*--------------------------------------------------------------*/
3669 
objectread(FILE * ps,objectptr localdata,short offx,short offy,short mode,char * retstr,int ccolor,TechPtr defaulttech)3670 Boolean objectread(FILE *ps, objectptr localdata, short offx, short offy,
3671 	short mode, char *retstr, int ccolor, TechPtr defaulttech)
3672 {
3673    char *temp, *buffer, keyword[80];
3674    short tmpfont = -1;
3675    float tmpscale = 0.0;
3676    objectptr	*libobj;
3677    int curcolor = ccolor;
3678    char *colorkey = NULL;
3679    char *widthkey = NULL;
3680    int i, j, k;
3681    short px, py;
3682    objinstptr *newinst;
3683    eparamptr epptrx, epptry;	/* used for paths only */
3684 
3685    /* path-handling variables */
3686    pathptr *newpath;
3687    XPoint startpoint;
3688 
3689    keyword[0] = '\0';
3690 
3691    buffer = (char *)malloc(256 * sizeof(char));
3692    temp = buffer;
3693 
3694    for(;;) {
3695       char *lineptr, *keyptr, *saveptr;
3696 
3697       if (fgets(temp, 255, ps) == NULL) {
3698 	 if (strcmp(keyword, "restore")) {
3699             Wprintf("Error: end of file.");
3700 	    *retstr = '\0';
3701 	 }
3702 	 break;
3703       }
3704       temp = buffer;
3705 
3706       /* because PostScript is a stack language, we will scan from the end */
3707       for (lineptr = buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++);
3708       /* Avoid CR-LF at EOL added by stupid Windoze programs */
3709       if ((lineptr > buffer) && *(lineptr - 1) == '\r') lineptr--;
3710       if (lineptr != buffer) {  /* ignore any blank lines */
3711          for (keyptr = lineptr - 1; isspace(*keyptr) && keyptr != buffer; keyptr--);
3712          for (; !isspace(*keyptr) && keyptr != buffer; keyptr--);
3713          sscanf(keyptr, "%79s", keyword);
3714 
3715          if (!strcmp(keyword, "showpage")) {
3716             strncpy(retstr, buffer, 150);
3717             retstr[149] = '\0';
3718 	    free(buffer);
3719 
3720 	    /* If we have just read a schematic that is attached	*/
3721 	    /* to a symbol, check all of the pin labels in the symbol	*/
3722 	    /* to see if they correspond to pin names in the schematic.	*/
3723 	    /* The other way around (pin in schematic with no		*/
3724 	    /* corresponding name in the symbol) is not an error.	*/
3725 
3726 	    if (localdata->symschem != NULL) {
3727 	       genericptr *pgen, *lgen;
3728 	       labelptr plab, lcmp;
3729 	       for (pgen = localdata->symschem->plist; pgen < localdata->
3730 			symschem->plist + localdata->symschem->parts; pgen++) {
3731 		  if (IS_LABEL(*pgen)) {
3732 		     plab = TOLABEL(pgen);
3733 		     if (plab->pin == LOCAL) {
3734 			for (lgen = localdata->plist; lgen < localdata->plist +
3735 					localdata->parts; lgen++) {
3736 			   if (IS_LABEL(*lgen)) {
3737 			      lcmp = TOLABEL(lgen);
3738 			      if (lcmp->pin == LOCAL)
3739 				 if (!stringcomprelaxed(lcmp->string, plab->string,
3740 						areawin->topinstance))
3741 				    break;
3742 			   }
3743 			}
3744 			if (lgen == localdata->plist + localdata->parts) {
3745 			   char *cptr, *d1ptr, *d2ptr;
3746 			   char *pch = textprint(plab->string, areawin->topinstance);
3747 
3748 			   /* Check for likely delimiters before applying warning */
3749 
3750 			   if ((cptr = strchr(pch, ':')) != NULL) {
3751 			      d1ptr = strchr(pch, '[');
3752 			      d2ptr = strchr(pch, ']');
3753 			      if (d1ptr != NULL && d2ptr != NULL &&
3754 					d1ptr < cptr && d2ptr > cptr) {
3755 				 if (areawin->buschar != '[') {
3756 				    areawin->buschar = '[';
3757 				    Fprintf(stderr, "Warning:  Bus character \'[\'"
3758 					" apparently used but not declared.\n");
3759 				 }
3760 			      }
3761 			      d1ptr = strchr(pch, '{');
3762 			      d2ptr = strchr(pch, '}');
3763 			      if (d1ptr != NULL && d2ptr != NULL &&
3764 					d1ptr < cptr && d2ptr > cptr) {
3765 				 if (areawin->buschar != '{') {
3766 				    areawin->buschar = '{';
3767 				    Fprintf(stderr, "Warning:  Bus character \'{\'"
3768 					" apparently used in pin \"%s\""
3769 					" but not declared.\n", pch);
3770 				 }
3771 			      }
3772 			      d1ptr = strchr(pch, '<');
3773 			      d2ptr = strchr(pch, '>');
3774 			      if (d1ptr != NULL && d2ptr != NULL &&
3775 					d1ptr < cptr && d2ptr > cptr) {
3776 				 if (areawin->buschar != '<') {
3777 				    areawin->buschar = '<';
3778 				    Fprintf(stderr, "Warning:  Bus character \'<\'"
3779 					" apparently used in pin \"%s\""
3780 					" but not declared.\n", pch);
3781 				 }
3782 			      }
3783 			      d1ptr = strchr(pch, '(');
3784 			      d2ptr = strchr(pch, ')');
3785 			      if (d1ptr != NULL && d2ptr != NULL &&
3786 					d1ptr < cptr && d2ptr > cptr) {
3787 				 if (areawin->buschar != '(') {
3788 				    areawin->buschar = '(';
3789 				    Fprintf(stderr, "Warning:  Bus character \'(\'"
3790 					" apparently used in pin \"%s\""
3791 					" but not declared.\n", pch);
3792 				 }
3793 			      }
3794 			   }
3795 			   else
3796 			       Fprintf(stderr, "Warning:  Unattached pin \"%s\" in "
3797 					"symbol %s\n", pch,
3798 					localdata->symschem->name);
3799 			   free(pch);
3800 			}
3801 		     }
3802 		  }
3803 	       }
3804 	    }
3805 	    return False;  /* end of page */
3806 	 }
3807 
3808 	 /* make a color change, adding the color if necessary */
3809 
3810 	 else if (!strcmp(keyword, "scb")) {
3811 	    float red, green, blue;
3812 	    if (sscanf(buffer, "%f %f %f", &red, &green, &blue) == 3) {
3813 	       curcolor = rgb_alloccolor((int)(red * 65535), (int)(green * 65535),
3814 		   	(int)(blue * 65535));
3815 	       colorkey = NULL;
3816 	    }
3817 	    else {
3818 	       char tmpkey[30];
3819 	       oparamptr ops;
3820 
3821 	       parse_ps_string(buffer, tmpkey, 29, FALSE, TRUE);
3822 	       ops = match_param(localdata, tmpkey);
3823 	       if (ops != NULL) {
3824 		  /* Recast expression parameter, if necessary */
3825 		  if (ops->which == P_EXPRESSION) ops->which = P_COLOR;
3826 		  if (ops->which == P_COLOR) {
3827 		     colorkey = ops->key;
3828 		     switch (ops->type) {
3829 			case XC_INT:
3830 		           curcolor = ops->parameter.ivalue;
3831 			   break;
3832 			default:
3833 		           curcolor = DEFAULTCOLOR;	/* placeholder */
3834 			   break;
3835 		     }
3836 		  }
3837 	       }
3838 	    }
3839 	 }
3840 
3841 	 /* end the color change, returning to default */
3842 
3843 	 else if (!strcmp(keyword, "sce")) {
3844 	    curcolor = ccolor;
3845 	    colorkey = NULL;
3846 	 }
3847 
3848 	 /* begin a path constructor */
3849 
3850 	 else if (!strcmp(keyword, "beginpath")) {
3851 	    px = py = 0;
3852 
3853 	    NEW_PATH(newpath, localdata);
3854 	    (*newpath)->plist = (genericptr *)malloc(sizeof(genericptr));
3855 	    (*newpath)->parts = 0;
3856 	    (*newpath)->color = curcolor;
3857 	    (*newpath)->passed = NULL;
3858 
3859 	    for (--keyptr; *keyptr == ' '; keyptr--);
3860 	    for (; *keyptr != ' '; keyptr--);
3861 
3862 	    /* check for "addtox" and "addtoy" parameter specification */
3863 	    while (!strncmp(keyptr + 1, "addto", 5)) {
3864 	       saveptr = keyptr + 1;
3865 
3866 	       for (--keyptr; *keyptr == ' '; keyptr--);
3867 	       for (; *keyptr != ' '; keyptr--);
3868 
3869 	       /* Get parameter and its value */
3870 	       if (*(saveptr + 5) == 'x')
3871 	          varpscan(localdata, keyptr + 1, &px, (genericptr)*newpath,
3872 				-1, offx, P_POSITION_X);
3873 	       else
3874 	          varpscan(localdata, keyptr + 1, &py, (genericptr)*newpath,
3875 				-1, offy, P_POSITION_Y);
3876 
3877 	       for (--keyptr; *keyptr == ' '; keyptr--);
3878 	       for (; *keyptr != ' '; keyptr--);
3879 	    }
3880 
3881 	    lineptr = varpathscan(localdata, buffer, &startpoint.x,
3882 			(genericptr *)NULL, *newpath, 0, offx + px, P_POSITION_X,
3883 			&epptrx);
3884 	    lineptr = varpathscan(localdata, lineptr, &startpoint.y,
3885 			(genericptr *)NULL, *newpath, 0, offy + py, P_POSITION_Y,
3886 			&epptry);
3887 
3888 	    std_eparam((genericptr)(*newpath), colorkey);
3889 	 }
3890 
3891 	 /* end the path constructor */
3892 
3893 	 else if (!strcmp(keyword, "endpath")) {
3894 
3895 	    lineptr = varscan(localdata, buffer, &(*newpath)->style,
3896 			(genericptr)*newpath, P_STYLE);
3897 	    lineptr = varfscan(localdata, lineptr, &(*newpath)->width,
3898 			(genericptr)*newpath, P_LINEWIDTH);
3899 
3900 	    if ((*newpath)->parts <= 0) {	/* in case of an empty path */
3901 	       free((*newpath)->plist);
3902 	       free(*newpath);
3903 	       localdata->parts--;
3904 	    }
3905 	    newpath = NULL;
3906 	 }
3907 
3908 	 /* read path parts */
3909 
3910 	 else if (!strcmp(keyword, "polyc")) {
3911 	    polyptr *newpoly;
3912 	    pointlist newpoints;
3913 	    short tmpnum;
3914 
3915 	    px = py = 0;
3916 
3917 	    NEW_POLY(newpoly, (*newpath));
3918 
3919 	    for (--keyptr; *keyptr == ' '; keyptr--);
3920 	    for (; *keyptr != ' '; keyptr--);
3921 
3922 	    /* check for "addtox" and "addtoy" parameter specification */
3923 	    while (!strncmp(keyptr + 1, "addto", 5)) {
3924 	       saveptr = keyptr + 1;
3925 
3926 	       for (--keyptr; *keyptr == ' '; keyptr--);
3927 	       for (; *keyptr != ' '; keyptr--);
3928 
3929 	       /* Get parameter and its value */
3930 	       if (*(saveptr + 5) == 'x')
3931 	          varpscan(localdata, keyptr + 1, &px, (genericptr)*newpoly,
3932 				-1, offx, P_POSITION_X);
3933 	       else
3934 	          varpscan(localdata, keyptr + 1, &py, (genericptr)*newpoly,
3935 				-1, offy, P_POSITION_Y);
3936 
3937 	       for (--keyptr; *keyptr == ' '; keyptr--);
3938 	       for (; *keyptr != ' '; keyptr--);
3939 	    }
3940 
3941 	    sscanf(keyptr, "%hd", &tmpnum);
3942 	    (*newpoly)->number = tmpnum + 1;
3943 	    (*newpoly)->width = 1.0;
3944 	    (*newpoly)->style = UNCLOSED;
3945 	    (*newpoly)->color = curcolor;
3946 	    (*newpoly)->passed = NULL;
3947 	    (*newpoly)->cycle = NULL;
3948 
3949             (*newpoly)->points = (pointlist) malloc((*newpoly)->number *
3950 		   sizeof(XPoint));
3951 
3952 	    /* If the last point on the last path part was parameterized, then	*/
3953 	    /* the first point of the spline must be, too.			*/
3954 
3955 	    if (epptrx != NULL) {
3956 	       eparamptr newepp = copyeparam(epptrx, (genericptr)(*newpath));
3957 	       newepp->next = (*newpath)->passed;
3958 	       (*newpath)->passed = newepp;
3959 	       newepp->pdata.pathpt[1] = 0;
3960 	       newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
3961 	    }
3962 	    if (epptry != NULL) {
3963 	       eparamptr newepp = copyeparam(epptry, (genericptr)(*newpath));
3964 	       newepp->next = (*newpath)->passed;
3965 	       (*newpath)->passed = newepp;
3966 	       newepp->pdata.pathpt[1] = 0;
3967 	       newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
3968 	    }
3969 
3970 	    lineptr = buffer;
3971 
3972             newpoints = (*newpoly)->points + (*newpoly)->number - 1;
3973 	    lineptr = varpathscan(localdata, lineptr, &newpoints->x,
3974 			(genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3975 			 offx + px, P_POSITION_X, &epptrx);
3976 	    lineptr = varpathscan(localdata, lineptr, &newpoints->y,
3977 			(genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3978 			offy + py, P_POSITION_Y, &epptry);
3979 
3980             for (--newpoints; newpoints > (*newpoly)->points; newpoints--) {
3981 
3982 	       lineptr = varpathscan(localdata, lineptr, &newpoints->x,
3983 			(genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3984 			 offx + px, P_POSITION_X, NULL);
3985 	       lineptr = varpathscan(localdata, lineptr, &newpoints->y,
3986 			(genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3987 			offy + py, P_POSITION_Y, NULL);
3988 	    }
3989 	    newpoints->x = startpoint.x;
3990 	    newpoints->y = startpoint.y;
3991 	    startpoint.x = (newpoints + (*newpoly)->number - 1)->x;
3992 	    startpoint.y = (newpoints + (*newpoly)->number - 1)->y;
3993 	 }
3994 
3995 	 else if (!strcmp(keyword, "arc") || !strcmp(keyword, "arcn")) {
3996 	    XPoint temppoint;
3997 	    arcptr *newarc;
3998 	    NEW_ARC(newarc, (*newpath));
3999 	    (*newarc)->width = 1.0;
4000 	    (*newarc)->style = UNCLOSED;
4001 	    (*newarc)->color = curcolor;
4002 	    (*newarc)->passed = NULL;
4003 	    (*newarc)->cycle = NULL;
4004 
4005 	    lineptr = varpscan(localdata, buffer, &(*newarc)->position.x,
4006 			(genericptr)*newarc, 0, offx, P_POSITION_X);
4007 	    lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4008 			(genericptr)*newarc, 0, offy, P_POSITION_Y);
4009 	    lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4010 			(genericptr)*newarc, P_RADIUS);
4011 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4012 			(genericptr)*newarc, P_ANGLE1);
4013 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4014 			(genericptr)*newarc, P_ANGLE2);
4015 
4016 	    (*newarc)->yaxis = (*newarc)->radius;
4017 	    if (!strcmp(keyword, "arcn")) {
4018 	       float tmpang = (*newarc)->angle1;
4019 	       (*newarc)->radius = -((*newarc)->radius);
4020 	       (*newarc)->angle1 = (*newarc)->angle2;
4021 	       (*newarc)->angle2 = tmpang;
4022 	    }
4023 
4024 	    calcarc(*newarc);
4025 	    temppoint.x = startpoint.x;
4026 	    temppoint.y = startpoint.y;
4027 	    startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x;
4028 	    startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y;
4029 	    decomposearc(*newpath, &temppoint);
4030 	 }
4031 
4032 	 else if (!strcmp(keyword, "pellip") || !strcmp(keyword, "nellip")) {
4033 	    XPoint temppoint;
4034 	    arcptr *newarc;
4035 	    NEW_ARC(newarc, (*newpath));
4036 	    (*newarc)->width = 1.0;
4037 	    (*newarc)->style = UNCLOSED;
4038 	    (*newarc)->color = curcolor;
4039 	    (*newarc)->passed = NULL;
4040 	    (*newarc)->cycle = NULL;
4041 
4042 	    lineptr = varpscan(localdata, buffer, &(*newarc)->position.x,
4043 			(genericptr)*newarc, 0, offx, P_POSITION_X);
4044 	    lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4045 			(genericptr)*newarc, 0, offy, P_POSITION_Y);
4046 	    lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4047 			(genericptr)*newarc, P_RADIUS);
4048 	    lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis,
4049 			(genericptr)*newarc, P_MINOR_AXIS);
4050 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4051 			(genericptr)*newarc, P_ANGLE1);
4052 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4053 			(genericptr)*newarc, P_ANGLE2);
4054 
4055 	    if (!strcmp(keyword, "nellip")) {
4056 	       float tmpang = (*newarc)->angle1;
4057 	       (*newarc)->radius = -((*newarc)->radius);
4058 	       (*newarc)->angle1 = (*newarc)->angle2;
4059 	       (*newarc)->angle2 = tmpang;
4060 
4061 	    }
4062 	    calcarc(*newarc);
4063 	    temppoint.x = startpoint.x;
4064 	    temppoint.y = startpoint.y;
4065 	    startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x;
4066 	    startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y;
4067 	    decomposearc(*newpath, &temppoint);
4068 	 }
4069 
4070 	 else if (!strcmp(keyword, "curveto")) {
4071 	    splineptr *newspline;
4072 	    px = py = 0;
4073 
4074 	    NEW_SPLINE(newspline, (*newpath));
4075 	    (*newspline)->passed = NULL;
4076 	    (*newspline)->cycle = NULL;
4077 	    (*newspline)->width = 1.0;
4078 	    (*newspline)->style = UNCLOSED;
4079 	    (*newspline)->color = curcolor;
4080 
4081 	    /* If the last point on the last path part was parameterized, then	*/
4082 	    /* the first point of the spline must be, too.			*/
4083 
4084 	    if (epptrx != NULL) {
4085 	       eparamptr newepp = copyeparam(epptrx, (genericptr)(*newpath));
4086 	       newepp->next = (*newpath)->passed;
4087 	       (*newpath)->passed = newepp;
4088 	       newepp->pdata.pathpt[1] = 0;
4089 	       newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
4090 	    }
4091 	    if (epptry != NULL) {
4092 	       eparamptr newepp = copyeparam(epptry, (genericptr)(*newpath));
4093 	       newepp->next = (*newpath)->passed;
4094 	       (*newpath)->passed = newepp;
4095 	       newepp->pdata.pathpt[1] = 0;
4096 	       newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
4097 	    }
4098 
4099 	    for (--keyptr; *keyptr == ' '; keyptr--);
4100 	    for (; *keyptr != ' '; keyptr--);
4101 
4102 	    /* check for "addtox" and "addtoy" parameter specification */
4103 	    while (!strncmp(keyptr + 1, "addto", 5)) {
4104 	       saveptr = keyptr + 1;
4105 
4106 	       for (--keyptr; *keyptr == ' '; keyptr--);
4107 	       for (; *keyptr != ' '; keyptr--);
4108 
4109 	       /* Get parameter and its value */
4110 	       if (*(saveptr + 5) == 'x')
4111 	          varpscan(localdata, keyptr + 1, &px, (genericptr)*newspline,
4112 				-1, offx, P_POSITION_X);
4113 	       else
4114 	          varpscan(localdata, keyptr + 1, &py, (genericptr)*newspline,
4115 				-1, offy, P_POSITION_Y);
4116 
4117 	       for (--keyptr; *keyptr == ' '; keyptr--);
4118 	       for (; *keyptr != ' '; keyptr--);
4119 	    }
4120 
4121 
4122 	    lineptr = varpathscan(localdata, buffer, &(*newspline)->ctrl[1].x,
4123 			(genericptr *)newspline, *newpath, 1, offx + px, P_POSITION_X,
4124 			NULL);
4125 	    lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[1].y,
4126 			(genericptr *)newspline, *newpath, 1, offy + py, P_POSITION_Y,
4127 			NULL);
4128 	    lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[2].x,
4129 			(genericptr *)newspline, *newpath, 2, offx + px, P_POSITION_X,
4130 			NULL);
4131 	    lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[2].y,
4132 			(genericptr *)newspline, *newpath, 2, offy + py, P_POSITION_Y,
4133 			NULL);
4134 	    lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[3].x,
4135 			(genericptr *)newspline, *newpath, 3, offx + px, P_POSITION_X,
4136 			&epptrx);
4137 	    lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[3].y,
4138 			(genericptr *)newspline, *newpath, 3, offy + py, P_POSITION_Y,
4139 			&epptry);
4140 
4141 	    (*newspline)->ctrl[0].x = startpoint.x;
4142 	    (*newspline)->ctrl[0].y = startpoint.y;
4143 
4144 	    calcspline(*newspline);
4145 	    startpoint.x = (*newspline)->ctrl[3].x;
4146 	    startpoint.y = (*newspline)->ctrl[3].y;
4147 	 }
4148 
4149          /* read arcs */
4150 
4151          else if (!strcmp(keyword, "xcarc")) {
4152 	    arcptr *newarc;
4153 
4154 	    NEW_ARC(newarc, localdata);
4155 	    (*newarc)->color = curcolor;
4156 	    (*newarc)->passed = NULL;
4157 	    (*newarc)->cycle = NULL;
4158 
4159 	    /* backward compatibility */
4160 	    if (compare_version(version, "1.5") < 0) {
4161 	       sscanf(buffer, "%hd %hd %hd %f %f %f %hd", &(*newarc)->position.x,
4162 	          &(*newarc)->position.y, &(*newarc)->radius, &(*newarc)->angle1,
4163 	          &(*newarc)->angle2, &(*newarc)->width, &(*newarc)->style);
4164 	       (*newarc)->position.x -= offx;
4165 	       (*newarc)->position.y -= offy;
4166 	    }
4167 	    else {
4168 	       lineptr = varscan(localdata, buffer, &(*newarc)->style,
4169 				(genericptr)*newarc, P_STYLE);
4170 	       lineptr = varfscan(localdata, lineptr, &(*newarc)->width,
4171 				(genericptr)*newarc, P_LINEWIDTH);
4172 	       lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x,
4173 				(genericptr)*newarc, 0, offx, P_POSITION_X);
4174 	       lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4175 				(genericptr)*newarc, 0, offy, P_POSITION_Y);
4176 	       lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4177 				(genericptr)*newarc, P_RADIUS);
4178 	       lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4179 				(genericptr)*newarc, P_ANGLE1);
4180 	       lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4181 				(genericptr)*newarc, P_ANGLE2);
4182 	    }
4183 
4184 	    (*newarc)->yaxis = (*newarc)->radius;
4185 	    calcarc(*newarc);
4186 	    std_eparam((genericptr)(*newarc), colorkey);
4187          }
4188 
4189 	 /* read ellipses */
4190 
4191          else if (!strcmp(keyword, "ellipse")) {
4192 	    arcptr *newarc;
4193 
4194 	    NEW_ARC(newarc, localdata);
4195 
4196 	    (*newarc)->color = curcolor;
4197 	    (*newarc)->passed = NULL;
4198 	    (*newarc)->cycle = NULL;
4199 
4200 	    lineptr = varscan(localdata, buffer, &(*newarc)->style,
4201 			(genericptr)*newarc, P_STYLE);
4202 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->width,
4203 			(genericptr)*newarc, P_LINEWIDTH);
4204 	    lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x,
4205 			(genericptr)*newarc, 0, offx, P_POSITION_X);
4206 	    lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4207 			(genericptr)*newarc, 0, offy, P_POSITION_Y);
4208 	    lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4209 			(genericptr)*newarc, P_RADIUS);
4210 	    lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis,
4211 			(genericptr)*newarc, P_MINOR_AXIS);
4212 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4213 			(genericptr)*newarc, P_ANGLE1);
4214 	    lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4215 			(genericptr)*newarc, P_ANGLE2);
4216 
4217 	    calcarc(*newarc);
4218 	    std_eparam((genericptr)(*newarc), colorkey);
4219          }
4220 
4221          /* read polygons */
4222 	 /* (and wires---backward compatibility for v1.5 and earlier) */
4223 
4224          else if (!strcmp(keyword, "polygon") || !strcmp(keyword, "wire")) {
4225 	    polyptr *newpoly;
4226 	    pointlist newpoints;
4227 	    px = py = 0;
4228 
4229 	    NEW_POLY(newpoly, localdata);
4230 	    lineptr = buffer;
4231 
4232 	    (*newpoly)->passed = NULL;
4233 	    (*newpoly)->cycle = NULL;
4234 
4235 	    if (!strcmp(keyword, "wire")) {
4236 	       (*newpoly)->number = 2;
4237 	       (*newpoly)->width = 1.0;
4238 	       (*newpoly)->style = UNCLOSED;
4239 	    }
4240 	    else {
4241 	       /* backward compatibility */
4242 	       if (compare_version(version, "1.5") < 0) {
4243 	          for (--keyptr; *keyptr == ' '; keyptr--);
4244 	          for (; *keyptr != ' '; keyptr--);
4245 	          sscanf(keyptr, "%hd", &(*newpoly)->style);
4246 	          for (--keyptr; *keyptr == ' '; keyptr--);
4247 	          for (; *keyptr != ' '; keyptr--);
4248 	          sscanf(keyptr, "%f", &(*newpoly)->width);
4249 	       }
4250 	       for (--keyptr; *keyptr == ' '; keyptr--);
4251 	       for (; *keyptr != ' '; keyptr--);
4252 	       /* check for "addtox" and "addtoy" parameter specification */
4253 	       while (!strncmp(keyptr + 1, "addto", 5)) {
4254 		  saveptr = keyptr + 1;
4255 
4256 	          for (--keyptr; *keyptr == ' '; keyptr--);
4257 	          for (; *keyptr != ' '; keyptr--);
4258 
4259 		  /* Get parameter and its value */
4260 		  if (*(saveptr + 5) == 'x')
4261 	             varpscan(localdata, keyptr + 1, &px, (genericptr)*newpoly,
4262 				-1, offx, P_POSITION_X);
4263 		  else
4264 	             varpscan(localdata, keyptr + 1, &py, (genericptr)*newpoly,
4265 				-1, offy, P_POSITION_Y);
4266 
4267 	          for (--keyptr; *keyptr == ' '; keyptr--);
4268 	          for (; *keyptr != ' '; keyptr--);
4269 	       }
4270 	       sscanf(keyptr, "%hd", &(*newpoly)->number);
4271 
4272 	       if (compare_version(version, "1.5") >= 0) {
4273 	          lineptr = varscan(localdata, lineptr, &(*newpoly)->style,
4274 				(genericptr)*newpoly, P_STYLE);
4275 	          lineptr = varfscan(localdata, lineptr, &(*newpoly)->width,
4276 				(genericptr)*newpoly, P_LINEWIDTH);
4277 	       }
4278 	    }
4279 
4280 	    if ((*newpoly)->style & BBOX)
4281 	       (*newpoly)->color = BBOXCOLOR;
4282 	    else
4283 	       (*newpoly)->color = curcolor;
4284             (*newpoly)->points = (pointlist) malloc((*newpoly)->number *
4285 		   sizeof(XPoint));
4286 
4287             for (newpoints = (*newpoly)->points; newpoints < (*newpoly)->points
4288 		+ (*newpoly)->number; newpoints++) {
4289 	       lineptr = varpscan(localdata, lineptr, &newpoints->x,
4290 			(genericptr)*newpoly, newpoints - (*newpoly)->points,
4291 			offx + px, P_POSITION_X);
4292 	       lineptr = varpscan(localdata, lineptr, &newpoints->y,
4293 			(genericptr)*newpoly, newpoints - (*newpoly)->points,
4294 			offy + py, P_POSITION_Y);
4295 	    }
4296 	    std_eparam((genericptr)(*newpoly), colorkey);
4297          }
4298 
4299 	 /* read spline curves */
4300 
4301          else if (!strcmp(keyword, "spline")) {
4302             splineptr *newspline;
4303 	    px = py = 0;
4304 
4305 	    NEW_SPLINE(newspline, localdata);
4306 	    (*newspline)->color = curcolor;
4307 	    (*newspline)->passed = NULL;
4308 	    (*newspline)->cycle = NULL;
4309 
4310 	    /* backward compatibility */
4311 	    if (compare_version(version, "1.5") < 0) {
4312                sscanf(buffer, "%f %hd %hd %hd %hd %hd %hd %hd %hd %hd",
4313 	          &(*newspline)->width, &(*newspline)->ctrl[1].x,
4314 	          &(*newspline)->ctrl[1].y, &(*newspline)->ctrl[2].x,
4315 	          &(*newspline)->ctrl[2].y, &(*newspline)->ctrl[3].x,
4316 	          &(*newspline)->ctrl[3].y, &(*newspline)->ctrl[0].x,
4317 	          &(*newspline)->ctrl[0].y, &(*newspline)->style);
4318 	       (*newspline)->ctrl[1].x -= offx; (*newspline)->ctrl[2].x -= offx;
4319 	       (*newspline)->ctrl[0].x -= offx;
4320 	       (*newspline)->ctrl[3].x -= offx;
4321 	       (*newspline)->ctrl[1].y -= offy; (*newspline)->ctrl[2].y -= offy;
4322 	       (*newspline)->ctrl[3].y -= offy;
4323 	       (*newspline)->ctrl[0].y -= offy;
4324 	    }
4325 	    else {
4326 
4327 	       for (--keyptr; *keyptr == ' '; keyptr--);
4328 	       for (; *keyptr != ' '; keyptr--);
4329 	       /* check for "addtox" and "addtoy" parameter specification */
4330 	       while (!strncmp(keyptr + 1, "addto", 5)) {
4331 		  saveptr = keyptr + 1;
4332 
4333 	          for (--keyptr; *keyptr == ' '; keyptr--);
4334 	          for (; *keyptr != ' '; keyptr--);
4335 
4336 		  /* Get parameter and its value */
4337 		  if (*(saveptr + 5) == 'x')
4338 	             varpscan(localdata, keyptr + 1, &px, (genericptr)*newspline,
4339 				-1, offx, P_POSITION_X);
4340 		  else
4341 	             varpscan(localdata, keyptr + 1, &py, (genericptr)*newspline,
4342 				-1, offy, P_POSITION_Y);
4343 
4344 	          for (--keyptr; *keyptr == ' '; keyptr--);
4345 	          for (; *keyptr != ' '; keyptr--);
4346 	       }
4347 
4348 	       lineptr = varscan(localdata, buffer, &(*newspline)->style,
4349 				(genericptr)*newspline, P_STYLE);
4350 	       lineptr = varfscan(localdata, lineptr, &(*newspline)->width,
4351 				(genericptr)*newspline, P_LINEWIDTH);
4352 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].x,
4353 				(genericptr)*newspline, 1, offx + px, P_POSITION_X);
4354 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].y,
4355 				(genericptr)*newspline, 1, offy + py, P_POSITION_Y);
4356 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].x,
4357 				(genericptr)*newspline, 2, offx + px, P_POSITION_X);
4358 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].y,
4359 				(genericptr)*newspline, 2, offy + py, P_POSITION_Y);
4360 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].x,
4361 				(genericptr)*newspline, 3, offx + px, P_POSITION_X);
4362 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].y,
4363 				(genericptr)*newspline, 3, offy + py, P_POSITION_Y);
4364 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].x,
4365 				(genericptr)*newspline, 0, offx + px, P_POSITION_X);
4366 	       lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].y,
4367 				(genericptr)*newspline, 0, offy + py, P_POSITION_Y);
4368 
4369 	       /* check for "addtox" and "addtoy" parameter specification */
4370 	    }
4371 
4372 	    calcspline(*newspline);
4373 	    std_eparam((genericptr)(*newspline), colorkey);
4374          }
4375 
4376          /* read graphics image instances */
4377 
4378 	 else if (!strcmp(keyword, "graphic")) {
4379             graphicptr *newgp;
4380 	    Imagedata *img;
4381 
4382 	    lineptr = buffer + 1;
4383 	    for (i = 0; i < xobjs.images; i++) {
4384 	       img = xobjs.imagelist + i;
4385 	       if (!strncmp(img->filename, lineptr, strlen(img->filename))) {
4386 		  NEW_GRAPHIC(newgp, localdata);
4387 		  (*newgp)->color = curcolor;
4388 		  (*newgp)->passed = NULL;
4389 #ifndef HAVE_CAIRO
4390 		  (*newgp)->clipmask = (Pixmap)NULL;
4391 		  (*newgp)->target = NULL;
4392 		  (*newgp)->valid = False;
4393 #endif /* HAVE_CAIRO */
4394 		  (*newgp)->source = img->image;
4395 		  img->refcount++;
4396 		  lineptr += strlen(img->filename) + 1;
4397 		  break;
4398 	       }
4399 	    }
4400 	    if (i == xobjs.images) {
4401 	       /* Error:  Line points to a non-existant image (no data) */
4402 	       /* See if we can load the image name as a filename, and	*/
4403 	       /* if that fails, then we must throw an error and ignore	*/
4404 	       /* the image element.					*/
4405 
4406 	       graphicptr locgp;
4407 	       char *sptr = strchr(lineptr, ' ');
4408 
4409 	       if (sptr != NULL)
4410 		  *sptr = '\0';
4411 	       else
4412 		  sptr = lineptr;
4413 	       locgp = new_graphic(NULL, lineptr, 0, 0);
4414 
4415 	       if (locgp == NULL) {
4416 	          Fprintf(stderr, "Error:  No graphic data for \"%s\".\n",
4417 			lineptr);
4418 		  newgp = NULL;
4419 	       }
4420 	       else {
4421 		  lineptr = sptr;
4422 		  newgp = &locgp;
4423 	       }
4424 	    }
4425 
4426 	    if ((newgp != NULL) && (*newgp != NULL)) {
4427 	       lineptr = varfscan(localdata, lineptr, &(*newgp)->scale,
4428 				(genericptr)*newgp, P_SCALE);
4429 	       lineptr = varfscan(localdata, lineptr, &(*newgp)->rotation,
4430 				(genericptr)*newgp, P_ROTATION);
4431 	       lineptr = varpscan(localdata, lineptr, &(*newgp)->position.x,
4432 				(genericptr)*newgp, 0, offx, P_POSITION_X);
4433 	       lineptr = varpscan(localdata, lineptr, &(*newgp)->position.y,
4434 				(genericptr)*newgp, 0, offy, P_POSITION_Y);
4435 	       std_eparam((genericptr)(*newgp), colorkey);
4436 	    }
4437 	 }
4438 
4439          /* read labels */
4440 
4441          else if (!strcmp(keyword, "fontset")) { 	/* old style */
4442             char tmpstring[100];
4443             int i;
4444             sscanf(buffer, "%f %*c%99s", &tmpscale, tmpstring);
4445             for (i = 0; i < fontcount; i++)
4446                if (!strcmp(tmpstring, fonts[i].psname)) {
4447 		  tmpfont = i;
4448 		  break;
4449 	       }
4450 	    if (i == fontcount) i = 0;	/* Why bother with anything fancy? */
4451          }
4452 
4453          else if (!strcmp(keyword, "label") || !strcmp(keyword, "pinlabel")
4454 		|| !strcmp(keyword, "pinglobal") || !strcmp(keyword, "infolabel")) {
4455 
4456 	    labelptr *newlabel;
4457 	    stringpart *firstscale, *firstfont;
4458 
4459 	    NEW_LABEL(newlabel, localdata);
4460 	    (*newlabel)->color = curcolor;
4461 	    (*newlabel)->string = NULL;
4462 	    (*newlabel)->passed = NULL;
4463 	    (*newlabel)->cycle = NULL;
4464 
4465 	    /* scan backwards to get the number of substrings */
4466 	    lineptr = keyptr - 1;
4467 	    for (i = 0; i < ((compare_version(version, "2.3") < 0) ? 5 : 6); i++) {
4468 	       for (; *lineptr == ' '; lineptr--);
4469 	       for (; *lineptr != ' '; lineptr--);
4470 	    }
4471 	    if ((strchr(lineptr, '.') != NULL) && (compare_version(version, "2.3") < 0)) {
4472 	       Fprintf(stderr, "Error:  File version claims to be %s,"
4473 			" but has version %s labels\n", version, PROG_VERSION);
4474 	       Fprintf(stderr, "Attempting to resolve problem by updating version.\n");
4475 	       strcpy(version, PROG_VERSION);
4476 	       for (; *lineptr == ' '; lineptr--);
4477 	       for (; *lineptr != ' '; lineptr--);
4478 	    }
4479 	    /* no. segments is ignored---may be a derived quantity, anyway */
4480 	    if (compare_version(version, "2.3") < 0) {
4481 	       sscanf(lineptr, "%*s %hd %hf %hd %hd", &(*newlabel)->anchor,
4482 		   &(*newlabel)->rotation, &(*newlabel)->position.x,
4483 		   &(*newlabel)->position.y);
4484 	       (*newlabel)->position.x -= offx; (*newlabel)->position.y -= offy;
4485 	       *lineptr = '\0';
4486 	    }
4487 	    else {
4488 	       *lineptr++ = '\0';
4489 	       lineptr = advancetoken(lineptr);  /* skip string token */
4490 	       lineptr = varscan(localdata, lineptr, &(*newlabel)->anchor,
4491 				(genericptr)*newlabel, P_ANCHOR);
4492 	       lineptr = varfscan(localdata, lineptr, &(*newlabel)->rotation,
4493 				(genericptr)*newlabel, P_ROTATION);
4494 	       lineptr = varfscan(localdata, lineptr, &(*newlabel)->scale,
4495 				(genericptr)*newlabel, P_SCALE);
4496 	       lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.x,
4497 				(genericptr)*newlabel, 0, offx, P_POSITION_X);
4498 	       lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.y,
4499 				(genericptr)*newlabel, 0, offy, P_POSITION_Y);
4500 	    }
4501 	    if (compare_version(version, "2.4") < 0)
4502 	       (*newlabel)->rotation = -(*newlabel)->rotation;
4503 	    while ((*newlabel)->rotation < 0.0) (*newlabel)->rotation += 360.0;
4504 
4505 	    (*newlabel)->pin = False;
4506 	    if (strcmp(keyword, "label")) {	/* all the schematic types */
4507 	       /* enable schematic capture if it is not already on. */
4508 	       if (!strcmp(keyword, "pinlabel"))
4509 		  (*newlabel)->pin = LOCAL;
4510 	       else if (!strcmp(keyword, "pinglobal"))
4511 		  (*newlabel)->pin = GLOBAL;
4512 	       else if (!strcmp(keyword, "infolabel")) {
4513 		  /* Do not turn top-level pages into symbols! */
4514 		  /* Info labels on schematics are treated differently. */
4515 		  if (localdata != topobject)
4516 		     localdata->schemtype = FUNDAMENTAL;
4517 		  (*newlabel)->pin = INFO;
4518 		  if (curcolor == DEFAULTCOLOR)
4519 		     (*newlabel)->color = INFOLABELCOLOR;
4520 	       }
4521 	    }
4522 
4523 	    lineptr = buffer;	/* back to beginning of string */
4524 	    if (!strncmp(lineptr, "mark", 4)) lineptr += 4;
4525 
4526 	    readlabel(localdata, lineptr, &(*newlabel)->string);
4527 	    CheckMarginStop(*newlabel, areawin->topinstance, FALSE);
4528 
4529 	    if (compare_version(version, "2.3") < 0) {
4530 	       /* Switch 1st scale designator to overall font scale */
4531 
4532 	       firstscale = (*newlabel)->string->nextpart;
4533 	       if (firstscale->type != FONT_SCALE) {
4534 	          if (tmpscale != 0.0)
4535 	             (*newlabel)->scale = 0.0;
4536 	          else
4537 	             (*newlabel)->scale = 1.0;
4538 	       }
4539 	       else {
4540 	          (*newlabel)->scale = firstscale->data.scale;
4541 	          deletestring(firstscale, &((*newlabel)->string),
4542 				areawin->topinstance);
4543 	       }
4544 	    }
4545 
4546 	    firstfont = (*newlabel)->string;
4547 	    if ((firstfont == NULL) || (firstfont->type != FONT_NAME)) {
4548 	       if (tmpfont == -1) {
4549 	          Fprintf(stderr, "Error:  Label with no font designator?\n");
4550 		  tmpfont = 0;
4551 	       }
4552 	       firstfont = makesegment(&((*newlabel)->string), (*newlabel)->string);
4553 	       firstfont->type = FONT_NAME;
4554 	       firstfont->data.font = tmpfont;
4555 	    }
4556 	    cleanuplabel(&(*newlabel)->string);
4557 
4558 	    std_eparam((genericptr)(*newlabel), colorkey);
4559          }
4560 
4561 	 /* read symbol-to-schematic connection */
4562 
4563 	 else if (!strcmp(keyword, "is_schematic")) {
4564 	    char tempstr[50];
4565 	    for (lineptr = buffer; *lineptr == ' '; lineptr++);
4566 	    parse_ps_string(++lineptr, tempstr, 49, FALSE, FALSE);
4567 	    checksym(localdata, tempstr);
4568 	 }
4569 
4570          /* read bounding box (font files only)	*/
4571 
4572          else if (!strcmp(keyword, "bbox")) {
4573 	    for (lineptr = buffer; *lineptr == ' '; lineptr++);
4574             if (*lineptr != '%') {
4575 	       Wprintf("Illegal bbox.");
4576 	       free(buffer);
4577                *retstr = '\0';
4578 	       return True;
4579 	    }
4580 	    sscanf(++lineptr, "%hd %hd %hd %hd",
4581 		&localdata->bbox.lowerleft.x, &localdata->bbox.lowerleft.y,
4582 		&localdata->bbox.width, &localdata->bbox.height);
4583 
4584 	    // If this is *not* a FONTLIB, then the font character symbols
4585 	    // are being edited as a normal library, so copy the bounding
4586 	    // box into a FIXEDBBOX-type box.
4587 
4588 	    if (mode != FONTLIB) {
4589 		polyptr *newpoly;
4590 		pointlist newpoints;
4591 
4592 		NEW_POLY(newpoly, localdata);
4593 		(*newpoly)->passed = NULL;
4594 		(*newpoly)->cycle = NULL;
4595 		(*newpoly)->number = 4;
4596 		(*newpoly)->width = 1.0;
4597 		(*newpoly)->style = FIXEDBBOX;
4598 		(*newpoly)->color = FIXEDBBOXCOLOR;
4599        		(*newpoly)->points = (pointlist) malloc(4 * sizeof(XPoint));
4600 		newpoints = (*newpoly)->points;
4601 		newpoints->x = localdata->bbox.lowerleft.x;
4602 		newpoints->y = localdata->bbox.lowerleft.y;
4603 		newpoints++;
4604 		newpoints->x = localdata->bbox.lowerleft.x + localdata->bbox.width;
4605 		newpoints->y = localdata->bbox.lowerleft.y;
4606 		newpoints++;
4607 		newpoints->x = localdata->bbox.lowerleft.x + localdata->bbox.width;
4608 		newpoints->y = localdata->bbox.lowerleft.y + localdata->bbox.height;
4609 		newpoints++;
4610 		newpoints->x = localdata->bbox.lowerleft.x;
4611 		newpoints->y = localdata->bbox.lowerleft.y + localdata->bbox.height;
4612 		std_eparam((genericptr)(*newpoly), colorkey);
4613 	    }
4614          }
4615 
4616 	 /* read "hidden" attribute */
4617 
4618 	 else if (!strcmp(keyword, "hidden")) {
4619 	    localdata->hidden = True;
4620 	 }
4621 
4622 	 /* read "libinst" special instance of a library part */
4623 
4624 	 else if (!strcmp(keyword, "libinst")) {
4625 
4626 	    /* Read backwards from keyword to find name of object instanced. */
4627 	    for (lineptr = keyptr; *lineptr != '/' && lineptr > buffer;
4628 			lineptr--);
4629 	    parse_ps_string(++lineptr, keyword, 79, FALSE, FALSE);
4630 	    new_library_instance(mode - LIBRARY, keyword, buffer, defaulttech);
4631 	 }
4632 
4633 	 /* read objects */
4634 
4635          else if (!strcmp(keyword, "{")) {  /* This is an object definition */
4636 	    objlistptr redef;
4637 	    objectptr *newobject;
4638 
4639 	    for (lineptr = buffer; *lineptr == ' '; lineptr++);
4640 	    if (*lineptr++ != '/') {
4641 	       /* This may be part of a label. . . treat as a continuation line */
4642 	       temp = continueline(&buffer);
4643 	       continue;
4644 	    }
4645 	    parse_ps_string(lineptr, keyword, 79, FALSE, FALSE);
4646 
4647 	    newobject = new_library_object(mode, keyword, &redef, defaulttech);
4648 
4649 	    if (objectread(ps, *newobject, 0, 0, mode, retstr, curcolor,
4650 			defaulttech) == True) {
4651                strncpy(retstr, buffer, 150);
4652                retstr[149] = '\0';
4653 	       free(buffer);
4654 	       return True;
4655             }
4656 	    else {
4657 	       if (library_object_unique(mode, *newobject, redef))
4658 	          add_object_to_library(mode, *newobject);
4659 	    }
4660          }
4661          else if (!strcmp(keyword, "def")) {
4662             strncpy(retstr, buffer, 150);
4663             retstr[149] = '\0';
4664 	    free (buffer);
4665 	    return False; /* end of object def or end of object library */
4666 	 }
4667 
4668 	 else if (!strcmp(keyword, "loadfontencoding")) {
4669 	    /* Deprecated, but retained for backward compatibility. */
4670 	    /* Load from script, .xcircuitrc, or command line instead. */
4671 	    for (lineptr = buffer; *lineptr != '%'; lineptr++);
4672 	    sscanf (lineptr + 1, "%149s", _STR);
4673 	    if (*(lineptr + 1) != '%') loadfontfile(_STR);
4674 	 }
4675 	 else if (!strcmp(keyword, "loadlibrary")) {
4676 	    /* Deprecated, but retained for backward compatibility */
4677 	    /* Load from script, .xcircuitrc, or command line instead. */
4678 	    int ilib, tlib;
4679 
4680 	    for (lineptr = buffer; *lineptr != '%'; lineptr++);
4681 	    sscanf (++lineptr, "%149s", _STR);
4682 	    while (isspace(*lineptr)) lineptr++;
4683 	    while (!isspace(*++lineptr));
4684 	    while (isspace(*++lineptr));
4685 	    if (sscanf (lineptr, "%d", &ilib) > 0) {
4686 	       while ((ilib - 2 + LIBRARY) > xobjs.numlibs) {
4687 		  tlib = createlibrary(False);
4688 		  if (tlib != xobjs.numlibs - 2 + LIBRARY) {
4689 		     ilib = tlib;
4690 		     break;
4691 		  }
4692 	       }
4693 	       mode = ilib - 1 + LIBRARY;
4694 	    }
4695 	    loadlibrary(mode);
4696 	 }
4697 	 else if (!strcmp(keyword, "beginparm")) { /* parameterized object */
4698 	    short tmpnum, i;
4699 	    for (--keyptr; *keyptr == ' '; keyptr--);
4700 	    for (; isdigit(*keyptr) && (keyptr >= buffer); keyptr--);
4701 	    sscanf(keyptr, "%hd", &tmpnum);
4702 	    lineptr = buffer;
4703 	    while (isspace(*lineptr)) lineptr++;
4704 
4705 	    if (tmpnum < 256) {		/* read parameter defaults in order */
4706 	       stringpart *newpart;
4707 	       /* oparamptr newops; (jdk) */
4708 
4709 	       for (i = 0; i < tmpnum; i++) {
4710 		  oparamptr newops;
4711 
4712 	          newops = (oparamptr)malloc(sizeof(oparam));
4713 		  newops->next = localdata->params;
4714 		  localdata->params = newops;
4715 		  newops->key = (char *)malloc(6);
4716 		  sprintf(newops->key, "v%d", i + 1);
4717 
4718 		  if (*lineptr == '(' || *lineptr == '{') {  /* type is XC_STRING */
4719 		     char *linetmp, csave;
4720 
4721 		     newops->parameter.string = NULL;
4722 
4723 		     /* get simple substring or set of substrings and commands */
4724 		     linetmp = find_delimiter(lineptr);
4725 		     csave = *(++linetmp);
4726 		     *linetmp = '\0';
4727 		     if (*lineptr == '{') lineptr++;
4728 		     readlabel(localdata, lineptr, &(newops->parameter.string));
4729 
4730 		     /* Add the ending part to the parameter string */
4731 		     newpart = makesegment(&(newops->parameter.string), NULL);
4732 		     newpart->type = PARAM_END;
4733 		     newpart->data.string = (u_char *)NULL;
4734 
4735 		     newops->type = (u_char)XC_STRING;
4736 		     newops->which = P_SUBSTRING;
4737 		     /* Fprintf(stdout, "Parameter %d to object %s defaults "
4738 				"to string \"%s\"\n", i + 1, localdata->name,
4739 				ops->parameter.string); */
4740 		     *linetmp = csave;
4741 		     lineptr = linetmp;
4742 		     while (isspace(*lineptr)) lineptr++;
4743 		  }
4744 		  else {	/* type is assumed to be XC_FLOAT */
4745 		     newops->type = (u_char)XC_FLOAT;
4746 	             sscanf(lineptr, "%g", &newops->parameter.fvalue);
4747 		     /* Fprintf(stdout, "Parameter %s to object %s defaults to "
4748 				"value %g\n", newops->key, localdata->name,
4749 				newops->parameter.fvalue); */
4750 		     lineptr = advancetoken(lineptr);
4751 		  }
4752 	       }
4753 	    }
4754 	 }
4755 	 else if (!strcmp(keyword, "nonetwork")) {
4756 	    localdata->valid = True;
4757 	    localdata->schemtype = NONETWORK;
4758 	 }
4759 	 else if (!strcmp(keyword, "trivial")) {
4760 	    localdata->schemtype = TRIVIAL;
4761 	 }
4762          else if (!strcmp(keyword, "begingate")) {
4763 	    localdata->params = NULL;
4764 	    /* read dictionary of parameter key:value pairs */
4765 	    readparams(NULL, NULL, localdata, buffer);
4766 	 }
4767 
4768          else if (!strcmp(keyword, "%%Trailer")) break;
4769          else if (!strcmp(keyword, "EndLib")) break;
4770 	 else if (!strcmp(keyword, "restore"));    /* handled at top */
4771 	 else if (!strcmp(keyword, "grestore"));   /* ignore */
4772          else if (!strcmp(keyword, "endgate"));    /* also ignore */
4773 	 else if (!strcmp(keyword, "xyarray"));	   /* ignore for now */
4774          else {
4775 	    char *tmpptr, *libobjname, *objnamestart;
4776 	    Boolean matchtech, found = False;
4777 
4778 	    /* First, make sure this is not a general comment line */
4779 	    /* Return if we have a page boundary	   	   */
4780 	    /* Read an image if this is an imagedata line.	   */
4781 
4782 	    for (tmpptr = buffer; isspace(*tmpptr); tmpptr++);
4783 	    if (*tmpptr == '%') {
4784 	       if (strstr(buffer, "%%Page:") == tmpptr) {
4785                   strncpy(retstr, buffer, 150);
4786                   retstr[149] = '\0';
4787 		  free (buffer);
4788 		  return True;
4789 	       }
4790 	       else if (strstr(buffer, "%imagedata") == tmpptr) {
4791 		  int width, height;
4792 		  sscanf(buffer, "%*s %d %d", &width, &height);
4793 		  readimagedata(ps, width, height);
4794 	       }
4795 	       continue;
4796 	    }
4797 
4798 	    parse_ps_string(keyword, keyword, 79, FALSE, FALSE);
4799 	    matchtech =  (strstr(keyword, "::") == NULL) ? FALSE : TRUE;
4800 
4801 	    /* If the file contains a reference to a bare-word device	*/
4802 	    /* without a technology prefix, then it is probably an	*/
4803 	    /* older-version file.  If this is the case, then the file	*/
4804 	    /* should define an unprefixed object, which will be given	*/
4805 	    /* a null prefix (just "::" by itself).  Look for that	*/
4806 	    /* match.							*/
4807 
4808 	    /* (Assume that this line calls an object instance) */
4809 	    /* Double loop through user libraries 		*/
4810 
4811             for (k = 0; k < ((mode == FONTLIB) ? 1 : xobjs.numlibs); k++) {
4812 	       for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number :
4813 			xobjs.userlibs[k].number); j++) {
4814 
4815 		  libobj = (mode == FONTLIB) ? xobjs.fontlib.library + j :
4816 			xobjs.userlibs[k].library + j;
4817 
4818 		  /* Objects which have a technology ("<lib>::<obj>")	*/
4819 		  /* must compare exactly.  Objects which don't	can 	*/
4820 		  /* only match an object of the same name with a null	*/
4821 		  /* technology prefix.					*/
4822 
4823 		  libobjname = (*libobj)->name;
4824 		  if (!matchtech) {
4825 		     objnamestart = strstr(libobjname, "::");
4826 		     if (objnamestart != NULL) libobjname = objnamestart + 2;
4827 		  }
4828 	          if (!objnamecmp(keyword, libobjname)) {
4829 
4830 		     /* If the name is not exactly the same (appended underscores) */
4831 		     /* check if the name is on the list of aliases. */
4832 
4833 		     if (strcmp(keyword, libobjname)) {
4834 			Boolean is_alias = False;
4835 			aliasptr ckalias = aliastop;
4836 			slistptr sref;
4837 
4838 			for (; ckalias != NULL; ckalias = ckalias->next) {
4839 			   if (ckalias->baseobj == (*libobj)) {
4840 			      sref = ckalias->aliases;
4841 			      for (; sref != NULL; sref = sref->next) {
4842 			         if (!strcmp(keyword, sref->alias)) {
4843 				    is_alias = True;
4844 				    break;
4845 				 }
4846 			      }
4847 			      if (is_alias) break;
4848 			   }
4849 			}
4850 			if (!is_alias) continue;
4851 		     }
4852 
4853 		     if (!matchtech && ((*libobj)->name != objnamestart))
4854 			if (compare_version(version, "3.7") > 0)
4855 			   continue;	// no prefix in file must match
4856 					// null prefix in library object.
4857 
4858 		     found = True;
4859 		     NEW_OBJINST(newinst, localdata);
4860 		     (*newinst)->thisobject = *libobj;
4861 		     (*newinst)->color = curcolor;
4862 		     (*newinst)->params = NULL;
4863 		     (*newinst)->passed = NULL;
4864 		     (*newinst)->bbox.lowerleft.x = (*libobj)->bbox.lowerleft.x;
4865 		     (*newinst)->bbox.lowerleft.y = (*libobj)->bbox.lowerleft.y;
4866 		     (*newinst)->bbox.width = (*libobj)->bbox.width;
4867 		     (*newinst)->bbox.height = (*libobj)->bbox.height;
4868 		     (*newinst)->schembbox = NULL;
4869 
4870 	             lineptr = varfscan(localdata, buffer, &(*newinst)->scale,
4871 				(genericptr)*newinst, P_SCALE);
4872 
4873 		     /* Format prior to xcircuit 3.7 did not have scale-	*/
4874 		     /* invariant linewidth.  If scale is 1, though, keep it	*/
4875 		     /* invariant so as not to overuse the scale-variance	*/
4876 		     /* flag in the output file.				*/
4877 
4878 		     if ((compare_version(version, "3.7") < 0) && ((*newinst)->scale != 1.0))
4879 		        (*newinst)->style = NORMAL;
4880 		     else
4881 		        (*newinst)->style = LINE_INVARIANT;
4882 
4883 	       	     lineptr = varfscan(localdata, lineptr, &(*newinst)->rotation,
4884 				(genericptr)*newinst, P_ROTATION);
4885 	             lineptr = varpscan(localdata, lineptr, &(*newinst)->position.x,
4886 				(genericptr)*newinst, 0, offx, P_POSITION_X);
4887 	             lineptr = varpscan(localdata, lineptr, &(*newinst)->position.y,
4888 				(genericptr)*newinst, 0, offy, P_POSITION_Y);
4889 
4890 		     /* Look for the "not netlistable" flag */
4891 		     if (*lineptr == '/') {
4892 			if (!strncmp(lineptr, "/nn", 3))
4893 			   (*newinst)->style |= INST_NONETLIST;
4894 		     }
4895 
4896 		     /* Negative rotations = flip in x in version 2.3.6 and    */
4897 		     /* earlier.  Later versions don't allow negative rotation */
4898 
4899 	    	     if (compare_version(version, "2.4") < 0) {
4900                         if ((*newinst)->rotation < 0.0) {
4901 			   (*newinst)->scale = -((*newinst)->scale);
4902 			   (*newinst)->rotation += 1.0;
4903 		        }
4904 		        (*newinst)->rotation = -(*newinst)->rotation;
4905 		     }
4906 
4907                      while ((*newinst)->rotation > 360.0)
4908 			(*newinst)->rotation -= 360.0;
4909                      while ((*newinst)->rotation < 0.0)
4910 			(*newinst)->rotation += 360.0;
4911 
4912 		     std_eparam((genericptr)(*newinst), colorkey);
4913 
4914 		     /* Does this instance contain parameters? */
4915 		     readparams(localdata, *newinst, *libobj, buffer);
4916 
4917 		     calcbboxinst(*newinst);
4918 
4919 		     break;
4920 
4921 	          } /* if !strcmp */
4922 	       } /* for j loop */
4923 	       if (found) break;
4924 	    } /* for k loop */
4925 	    if (!found)		/* will assume that we have a continuation line */
4926 	       temp = continueline(&buffer);
4927          }
4928       }
4929    }
4930    strncpy(retstr, buffer, 150);
4931    retstr[149] = '\0';
4932    free(buffer);
4933    return True;
4934 }
4935 
4936 /*------------------------*/
4937 /* Save a PostScript file */
4938 /*------------------------*/
4939 
4940 #ifdef TCL_WRAPPER
4941 
setfile(char * filename,int mode)4942 void setfile(char *filename, int mode)
4943 {
4944    if ((filename == NULL) || (xobjs.pagelist[areawin->page]->filename == NULL)) {
4945       Wprintf("Error: No filename for schematic.");
4946       if (areawin->area && beeper) XBell(dpy, 100);
4947       return;
4948    }
4949 
4950    /* see if name has been changed in the buffer */
4951 
4952    if (strcmp(xobjs.pagelist[areawin->page]->filename, filename)) {
4953       Wprintf("Changing name of edit file.");
4954       free(xobjs.pagelist[areawin->page]->filename);
4955       xobjs.pagelist[areawin->page]->filename = strdup(filename);
4956    }
4957 
4958    if (strstr(xobjs.pagelist[areawin->page]->filename, "Page ") != NULL) {
4959       Wprintf("Warning: Enter a new name.");
4960       if (areawin->area && beeper) XBell(dpy, 100);
4961    }
4962    else {
4963       savefile(mode);
4964       if (areawin->area && beeper) XBell(dpy, 100);
4965    }
4966 }
4967 
4968 #else  /* !TCL_WRAPPER */
4969 
setfile(xcWidget button,xcWidget fnamewidget,caddr_t clientdata)4970 void setfile(xcWidget button, xcWidget fnamewidget, caddr_t clientdata)
4971 {
4972    /* see if name has been changed in the buffer */
4973 
4974    sprintf(_STR2, "%.249s", (char *)XwTextCopyBuffer(fnamewidget));
4975    if (xobjs.pagelist[areawin->page]->filename == NULL) {
4976       xobjs.pagelist[areawin->page]->filename = strdup(_STR2);
4977    }
4978    else if (strcmp(xobjs.pagelist[areawin->page]->filename, _STR2)) {
4979       Wprintf("Changing name of edit file.");
4980       free(xobjs.pagelist[areawin->page]->filename);
4981       xobjs.pagelist[areawin->page]->filename = strdup(_STR2);
4982    }
4983    if (strstr(xobjs.pagelist[areawin->page]->filename, "Page ") != NULL) {
4984       Wprintf("Warning: Enter a new name.");
4985       if (areawin->area && beeper) XBell(dpy, 100);
4986    }
4987    else {
4988 
4989       Arg wargs[1];
4990       xcWidget db, di;
4991 
4992       savefile(CURRENT_PAGE);
4993 
4994       /* Change "close" button to read "done" */
4995 
4996       di = xcParent(button);
4997       db = XtNameToWidget(di, "Close");
4998       XtSetArg(wargs[0], XtNlabel, "  Done  ");
4999       XtSetValues(db, wargs, 1);
5000       if (areawin->area && beeper) XBell(dpy, 100);
5001    }
5002 }
5003 
5004 #endif  /* TCL_WRAPPER */
5005 
5006 /*--------------------------------------------------------------*/
5007 /* Update number of changes for an object and initiate a temp	*/
5008 /* file save if total number of unsaved changes exceeds 20.	*/
5009 /*--------------------------------------------------------------*/
5010 
incr_changes(objectptr thisobj)5011 void incr_changes(objectptr thisobj)
5012 {
5013    /* It is assumed that empty pages are meant to be that way */
5014    /* and are not to be saved, so changes are marked as zero. */
5015 
5016    if (thisobj->parts == 0) {
5017       thisobj->changes = 0;
5018       return;
5019    }
5020 
5021    /* Remove any pending timeout */
5022 
5023    if (xobjs.timeout_id != (xcIntervalId)NULL) {
5024       xcRemoveTimeOut(xobjs.timeout_id);
5025       xobjs.timeout_id = (xcIntervalId)NULL;
5026    }
5027 
5028    thisobj->changes++;
5029 
5030    /* When in "suspend" mode, we assume that we are reading commands from
5031     * a script, and therefore we should not continuously increment changes
5032     * and keep saving the backup file.
5033     */
5034 
5035    if (xobjs.suspend < 0)
5036       xobjs.new_changes++;
5037 
5038    if (xobjs.new_changes > MAXCHANGES) {
5039 #ifdef TCL_WRAPPER
5040       savetemp(NULL);
5041 #else
5042       savetemp(NULL, NULL);
5043 #endif
5044    }
5045 
5046    /* Generate a new timeout */
5047 
5048    if (areawin->area)
5049 	xobjs.timeout_id = xcAddTimeOut(app, 60000 * xobjs.save_interval,
5050  		savetemp, NULL);
5051 }
5052 
5053 /*--------------------------------------------------------------*/
5054 /* tempfile save						*/
5055 /*--------------------------------------------------------------*/
5056 
5057 #ifdef TCL_WRAPPER
5058 
savetemp(ClientData clientdata)5059 void savetemp(ClientData clientdata)
5060 {
5061 
5062 #else
5063 
5064 void savetemp(XtPointer clientdata, xcIntervalId *id)
5065 {
5066 
5067 #endif
5068    /* If areawin->area is NULL then xcircuit is running in	*/
5069    /* batch mode and should not make backups.			*/
5070    if (areawin->area == NULL) return;
5071 
5072    /* Remove the timeout ID.  If this routine is called from	*/
5073    /* incr_changes() instead of from the timeout interrupt	*/
5074    /* service routine, then this value will already be NULL.	*/
5075 
5076    xobjs.timeout_id = (xcIntervalId)NULL;
5077 
5078    /* First see if there are any unsaved changes in the file.	*/
5079    /* If not, then just reset the counter and continue.  	*/
5080 
5081    if (xobjs.new_changes > 0) {
5082       if (xobjs.tempfile == NULL)
5083       {
5084          int fd;
5085          char *template = (char *)malloc(20 + strlen(xobjs.tempdir));
5086 	 int pid = (int)getpid();
5087 
5088          sprintf(template, "%s/XC%d.XXXXXX", xobjs.tempdir, pid);
5089 
5090 #ifdef _MSC_VER
5091          fd = mktemp(template);
5092 #else
5093          fd = mkstemp(template);
5094 #endif
5095          if (fd == -1) {
5096 	    Fprintf(stderr, "Error generating file for savetemp\n");
5097 	    free(template);
5098          }
5099          close(fd);
5100          xobjs.tempfile = strdup(template);
5101          free(template);
5102       }
5103       /* Show the "wristwatch" cursor, as graphic data may cause this	*/
5104       /* step to be inordinately long.					*/
5105 
5106       XDefineCursor(dpy, areawin->window, WAITFOR);
5107       savefile(ALL_PAGES);
5108       XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5109       xobjs.new_changes = 0;	/* reset the count */
5110    }
5111 }
5112 
5113 /*----------------------------------------------------------------------*/
5114 /* Set all objects in the list "wroteobjs" as having no unsaved changes */
5115 /*----------------------------------------------------------------------*/
5116 
5117 void setassaved(objectptr *wroteobjs, short written)
5118 {
5119    int i;
5120 
5121    for (i = 0; i < written; i++)
5122       (*(wroteobjs + i))->changes = 0;
5123 }
5124 
5125 /*---------------------------------------------------------------*/
5126 /* Save indicated library.  If libno is 0, save current page if	 */
5127 /* the current page is a library.  If not, save the user library */
5128 /*---------------------------------------------------------------*/
5129 
5130 void savelibpopup(xcWidget button, char *technology, caddr_t nulldata)
5131 {
5132    TechPtr nsptr;
5133 
5134 #ifndef TCL_WRAPPER
5135    buttonsave *savebutton;
5136 #endif
5137 
5138    nsptr = LookupTechnology(technology);
5139 
5140    if (nsptr != NULL) {
5141       if ((nsptr->flags & TECH_READONLY) != 0) {
5142          Wprintf("Library technology \"%s\" is read-only.", technology);
5143          return;
5144       }
5145    }
5146 
5147 #ifndef TCL_WRAPPER
5148    savebutton = getgeneric(button, savelibpopup, technology);
5149    popupprompt(button, "Enter technology:", "\0", savelibrary,
5150 	savebutton, NULL);
5151 #endif
5152 }
5153 
5154 /*---------------------------------------------------------*/
5155 
5156 #ifndef TCL_WRAPPER
5157 
5158 void savelibrary(xcWidget w, char *technology)
5159 {
5160    char outname[250];
5161    sscanf(_STR2, "%249s", outname);
5162    savetechnology(technology, outname);
5163 }
5164 
5165 #endif
5166 
5167 /*---------------------------------------------------------*/
5168 
5169 void savetechnology(char *technology, char *outname)
5170 {
5171    FILE *ps;
5172    char *outptr, *validname, outfile[150];
5173    objectptr *wroteobjs, libobjptr, *optr, depobj;
5174    genericptr *gptr;
5175    liblistptr spec;
5176    short written;
5177    short *glist;
5178    char *uname = NULL;
5179    char *hostname = NULL;
5180    struct passwd *mypwentry = NULL;
5181    int i, j, ilib;
5182    TechPtr nsptr;
5183    char *loctechnology;
5184 
5185    // Don't use the string "(user)" as a technology name;  this is just
5186    // a GUI placeholder for a null string ("").  This shouldn't happen,
5187    // though, as technology should be NULL if the user technology is
5188    // selected.
5189 
5190    if (technology && (!strcmp(technology, "(user)")))
5191       nsptr = LookupTechnology(NULL);
5192    else
5193       nsptr = LookupTechnology(technology);
5194 
5195    if ((outptr = strrchr(outname, '/')) == NULL)
5196       outptr = outname;
5197    else
5198       outptr++;
5199    strcpy(outfile, outname);
5200    if (strchr(outptr, '.') == NULL) strcat(outfile, ".lps");
5201 
5202    xc_tilde_expand(outfile, 149);
5203    while(xc_variable_expand(outfile, 149));
5204 
5205    if (nsptr != NULL && nsptr->filename != NULL) {
5206       // To be pedantic, we should probably check that the inodes of the
5207       // files are different, to be sure we are avoiding an unintentional
5208       // over-write.
5209 
5210       if (!strcmp(outfile, nsptr->filename)) {
5211 
5212 	 if ((nsptr->flags & TECH_READONLY) != 0) {
5213 	    Wprintf("Technology file \"%s\" is read-only.", technology);
5214 	    return;
5215 	 }
5216 
5217 	 if ((nsptr->flags & TECH_IMPORTED) != 0) {
5218 	    Wprintf("Attempt to write a truncated technology file!");
5219 	    return;
5220 	 }
5221       }
5222    }
5223 
5224    ps = fopen(outfile, "wb");
5225    if (ps == NULL) {
5226       Wprintf("Can't open PS file.");
5227       if (nsptr && nsptr->filename && (!strcmp(nsptr->filename, outfile))) {
5228 	 Wprintf("Marking technology \"%s\" as read-only.", technology);
5229 	 nsptr->flags |= TECH_READONLY;
5230       }
5231       return;
5232    }
5233 
5234    /* Did the technology name change?  If so, register the new name.	*/
5235    /* Clear any "IMPORTED" or "READONLY" flags.				*/
5236 
5237    if (nsptr && nsptr->filename && strcmp(outfile, nsptr->filename)) {
5238       Wprintf("Technology filename changed from \"%s\" to \"%s\".",
5239 		nsptr->filename, outfile);
5240       free(nsptr->filename);
5241       nsptr->filename = strdup(outfile);
5242       nsptr->flags &= ~(TECH_READONLY | TECH_IMPORTED);
5243    }
5244    else if (nsptr && !nsptr->filename) {
5245       nsptr->filename = strdup(outfile);
5246       nsptr->flags &= ~(TECH_READONLY | TECH_IMPORTED);
5247    }
5248 
5249    fprintf(ps, "%%! PostScript set of library objects for XCircuit\n");
5250    fprintf(ps, "%%  Version: %s\n", version);
5251    fprintf(ps, "%%  Library name is: %s\n",
5252 		(technology == NULL) ? "(user)" : technology);
5253 #ifdef _MSC_VER
5254    uname = getenv((const char *)"USERNAME");
5255 #else
5256    uname = getenv((const char *)"USER");
5257    if (uname != NULL) mypwentry = getpwnam(uname);
5258 #endif
5259 
5260    /* Check for both $HOST and $HOSTNAME environment variables.  Thanks */
5261    /* to frankie liu <frankliu@Stanford.EDU> for this fix.		*/
5262 
5263    if ((hostname = getenv((const char *)"HOSTNAME")) == NULL)
5264       if ((hostname = getenv((const char *)"HOST")) == NULL) {
5265 	 if (gethostname(_STR, 149) != 0)
5266 	    hostname = uname;
5267 	 else
5268 	    hostname = _STR;
5269       }
5270 
5271 #ifndef _MSC_VER
5272    if (mypwentry != NULL)
5273       fprintf(ps, "%%  Author: %s <%s@%s>\n", mypwentry->pw_gecos, uname,
5274 		hostname);
5275 #endif
5276 
5277    fprintf(ps, "%%\n\n");
5278 
5279    /* Print lists of object dependencies, where they exist.		*/
5280    /* Note that objects can depend on objects in other technologies;	*/
5281    /* this is allowed.							*/
5282 
5283    wroteobjs = (objectptr *) malloc(sizeof(objectptr));
5284    for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5285       for (j = 0; j < xobjs.userlibs[ilib].number; j++) {
5286 
5287 	 libobjptr = *(xobjs.userlibs[ilib].library + j);
5288          if (CompareTechnology(libobjptr, technology)) {
5289 	    written = 0;
5290 
5291 	    /* Search for all object definitions instantiated in this object, */
5292 	    /* and add them to the dependency list (non-recursive).	   */
5293 
5294 	    for (gptr = libobjptr->plist; gptr < libobjptr->plist
5295 			+ libobjptr->parts; gptr++) {
5296 	       if (IS_OBJINST(*gptr)) {
5297 	          depobj = TOOBJINST(gptr)->thisobject;
5298 
5299 	          /* Search among the list of objects already collected.	*/
5300 	          /* If this object has been previously, then ignore it.	*/
5301 	          /* Otherwise, update the list of object dependencies	*/
5302 
5303 	          for (optr = wroteobjs; optr < wroteobjs + written; optr++)
5304 		     if (*optr == depobj)
5305 		        break;
5306 
5307 	          if (optr == wroteobjs + written) {
5308 		     wroteobjs = (objectptr *)realloc(wroteobjs, (written + 1) *
5309 				sizeof(objectptr));
5310 		     *(wroteobjs + written) = depobj;
5311 		     written++;
5312 	          }
5313 	       }
5314 	    }
5315 	    if (written > 0) {
5316 	       fprintf(ps, "%% Depend %s", libobjptr->name);
5317 	       for (i = 0; i < written; i++) {
5318 	          depobj = *(wroteobjs + i);
5319 	          fprintf(ps, " %s", depobj->name);
5320 	       }
5321 	       fprintf(ps, "\n");
5322 	    }
5323          }
5324       }
5325    }
5326 
5327    fprintf(ps, "\n%% XCircuitLib library objects\n");
5328 
5329    /* Start by looking for any graphic images in the library objects	*/
5330    /* and saving the graphic image data at the top of the file.		*/
5331 
5332    glist = (short *)malloc(xobjs.images * sizeof(short));
5333    for (i = 0; i < xobjs.images; i++) glist[i] = 0;
5334 
5335    for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5336       for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) {
5337 	 libobjptr = spec->thisinst->thisobject;
5338          if (CompareTechnology(libobjptr, technology))
5339 	    count_graphics(spec->thisinst->thisobject, glist);
5340       }
5341    }
5342    output_graphic_data(ps, glist);
5343    free(glist);
5344 
5345    /* list of library objects already written */
5346 
5347    wroteobjs = (objectptr *)realloc(wroteobjs, sizeof(objectptr));
5348    written = 0;
5349 
5350    /* write all of the object definitions used, bottom up, with virtual	*/
5351    /* instances in the correct placement.  The need to find virtual	*/
5352    /* instances is why we do a look through the library pages and not	*/
5353    /* the libraries themselves when looking for objects matching the	*/
5354    /* given technology.							*/
5355 
5356    for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5357       for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) {
5358 	 libobjptr = spec->thisinst->thisobject;
5359          if (CompareTechnology(libobjptr, technology)) {
5360             if (!spec->virtual) {
5361                printobjects(ps, spec->thisinst->thisobject, &wroteobjs,
5362 			&written, DEFAULTCOLOR);
5363       	    }
5364             else {
5365 	       if ((spec->thisinst->scale != 1.0) ||
5366 			(spec->thisinst->rotation != 0.0)) {
5367 	    	  fprintf(ps, "%3.3f %3.3f ", spec->thisinst->scale,
5368 				spec->thisinst->rotation);
5369 	       }
5370                printparams(ps, spec->thisinst, 0);
5371 	       validname = create_valid_psname(spec->thisinst->thisobject->name, FALSE);
5372 	       /* Names without technologies get '::' string (blank technology) */
5373 	       if (technology == NULL)
5374                   fprintf(ps, "/::%s libinst\n", validname);
5375 	       else
5376                   fprintf(ps, "/%s libinst\n", validname);
5377 	       if ((spec->next != NULL) && (!(spec->next->virtual)))
5378 	          fprintf(ps, "\n");
5379             }
5380          }
5381       }
5382    }
5383 
5384    setassaved(wroteobjs, written);
5385    if (nsptr) nsptr->flags &= (~TECH_CHANGED);
5386    xobjs.new_changes = countchanges(NULL);
5387 
5388    /* and the postlog */
5389 
5390    fprintf(ps, "\n%% EndLib\n");
5391    fclose(ps);
5392    if (technology != NULL)
5393       Wprintf("Library technology \"%s\" saved as file %s.",technology, outname);
5394    else
5395       Wprintf("Library technology saved as file %s.", outname);
5396 
5397    free(wroteobjs);
5398 }
5399 
5400 /*----------------------------------------------------------------------*/
5401 /* Recursive routine to search the object hierarchy for fonts used	*/
5402 /*----------------------------------------------------------------------*/
5403 
5404 void findfonts(objectptr writepage, short *fontsused) {
5405    genericptr *dfp;
5406    stringpart *chp;
5407    int findex;
5408 
5409    for (dfp = writepage->plist; dfp < writepage->plist + writepage->parts; dfp++) {
5410       if (IS_LABEL(*dfp)) {
5411          for (chp = TOLABEL(dfp)->string; chp != NULL; chp = chp->nextpart) {
5412 	    if (chp->type == FONT_NAME) {
5413 	       findex = chp->data.font;
5414 	       if (fontsused[findex] == 0) {
5415 	          fontsused[findex] = 0x8000 | fonts[findex].flags;
5416 	       }
5417 	    }
5418 	 }
5419       }
5420       else if (IS_OBJINST(*dfp)) {
5421 	 findfonts(TOOBJINST(dfp)->thisobject, fontsused);
5422       }
5423    }
5424 }
5425 
5426 /*------------------------------------------------------*/
5427 /* Write graphics image data to file "ps".  "glist" is	*/
5428 /* a pointer to a vector of short integers, each one	*/
5429 /* being an index into xobjs.images for an image that	*/
5430 /* is to be output.					*/
5431 /*------------------------------------------------------*/
5432 
5433 void output_graphic_data(FILE *ps, short *glist)
5434 {
5435    char *fptr, ascbuf[6];
5436    int i, j;
5437    for (i = 0; i < xobjs.images; i++) {
5438       Imagedata *img = xobjs.imagelist + i;
5439       int ilen, flen, k, m = 0, n, q = 0;
5440       u_char *filtbuf, *flatebuf;
5441       Boolean lastpix = False;
5442       union {
5443 	u_long i;
5444 	u_char b[4];
5445       } pixel;
5446       int width = xcImageGetWidth(img->image);
5447       int height = xcImageGetHeight(img->image);
5448 
5449       if (glist[i] == 0) continue;
5450 
5451       fprintf(ps, "%%imagedata %d %d\n", width, height);
5452       fprintf(ps, "currentfile /ASCII85Decode filter ");
5453 
5454 #ifdef HAVE_LIBZ
5455       fprintf(ps, "/FlateDecode filter\n");
5456 #endif
5457 
5458       fprintf(ps, "/ReusableStreamDecode filter\n");
5459 
5460       /* creating a stream buffer is wasteful if we're just using ASCII85	*/
5461       /* decoding but is a must for compression filters. 			*/
5462 
5463       ilen = 3 * width * height;
5464       filtbuf = (u_char *)malloc(ilen + 4);
5465       q = 0;
5466 
5467       for (j = 0; j < height; j++)
5468 	 for (k = 0; k < width; k++) {
5469 	    unsigned char r, g, b;
5470 	    xcImageGetPixel(img->image, k, j, &r, &g, &b);
5471 	    filtbuf[q++] = r;
5472 	    filtbuf[q++] = g;
5473 	    filtbuf[q++] = b;
5474 	 }
5475 
5476       /* Extra encoding goes here */
5477 #ifdef HAVE_LIBZ
5478       flen = ilen * 2;
5479       flatebuf = (char *)malloc(flen);
5480       ilen = large_deflate(flatebuf, flen, filtbuf, ilen);
5481       free(filtbuf);
5482 #else
5483       flatebuf = filtbuf;
5484 #endif
5485 
5486       ascbuf[5] = '\0';
5487       pixel.i = 0;
5488       for (j = 0; j < ilen; j += 4) {
5489 	 if ((j + 4) > ilen) lastpix = TRUE;
5490 	 if (!lastpix && (flatebuf[j] + flatebuf[j + 1] + flatebuf[j + 2]
5491 			+ flatebuf[j + 3] == 0)) {
5492 	    fprintf(ps, "z");
5493 	    m++;
5494 	 }
5495 	 else {
5496 	    for (n = 0; n < 4; n++)
5497 	       pixel.b[3 - n] = flatebuf[j + n];
5498 
5499 	    ascbuf[0] = '!' + (pixel.i / 52200625);
5500 	    pixel.i %= 52200625;
5501 	    ascbuf[1] = '!' + (pixel.i / 614125);
5502 	    pixel.i %= 614125;
5503 	    ascbuf[2] = '!' + (pixel.i / 7225);
5504 	    pixel.i %= 7225;
5505 	    ascbuf[3] = '!' + (pixel.i / 85);
5506 	    pixel.i %= 85;
5507 	    ascbuf[4] = '!' + pixel.i;
5508 	    if (lastpix)
5509 	       for (n = 0; n < ilen + 1 - j; n++)
5510 	          fprintf(ps, "%c", ascbuf[n]);
5511 	    else
5512 	       fprintf(ps, "%5s", ascbuf);
5513 	    m += 5;
5514 	 }
5515 	 if (m > 75) {
5516 	    fprintf(ps, "\n");
5517 	    m = 0;
5518 	 }
5519       }
5520       fprintf(ps, "~>\n");
5521       free(flatebuf);
5522 
5523       /* Remove any filesystem path information from the image name.	*/
5524       /* Otherwise, the slashes will cause PostScript to err.		*/
5525 
5526       fptr = strrchr(img->filename, '/');
5527       if (fptr == NULL)
5528 	 fptr = img->filename;
5529       else
5530 	 fptr++;
5531       fprintf(ps, "/%sdata exch def\n", fptr);
5532       fprintf(ps, "/%s <<\n", fptr);
5533       fprintf(ps, "  /ImageType 1 /Width %d /Height %d /BitsPerComponent 8\n",
5534 	    width, height);
5535       fprintf(ps, "  /MultipleDataSources false\n");
5536       fprintf(ps, "  /Decode [0 1 0 1 0 1]\n");
5537       fprintf(ps, "  /ImageMatrix [1 0 0 -1 %d %d]\n",
5538 	    width >> 1, height >> 1);
5539       fprintf(ps, "  /DataSource %sdata >> def\n\n", fptr);
5540    }
5541 }
5542 
5543 /*----------------------------------------------------------------------*/
5544 /* Main file saving routine						*/
5545 /*----------------------------------------------------------------------*/
5546 /*	mode 		description					*/
5547 /*----------------------------------------------------------------------*/
5548 /*	ALL_PAGES	saves a crash recovery backup file		*/
5549 /*	CURRENT_PAGE	saves all pages associated with the same	*/
5550 /*			filename as the current page, and all		*/
5551 /*			dependent schematics (which have their		*/
5552 /*			filenames changed to match).			*/
5553 /*	NO_SUBCIRCUITS	saves all pages associated with the same	*/
5554 /*			filename as the current page, only.		*/
5555 /*----------------------------------------------------------------------*/
5556 
5557 void savefile(short mode)
5558 {
5559    FILE *ps, *pro, *enc;
5560    char outname[150], temp[150], prologue[150], *fname, *fptr, ascbuf[6];
5561    /* u_char decodebuf[6]; (jdk */
5562    short written, fontsused[256], i, page, curpage, multipage;
5563    short savepage, stcount, *pagelist, *glist;
5564    objectptr *wroteobjs;
5565    objinstptr writepage;
5566    int findex, j;
5567    time_t tdate;
5568    char *tmp_s;
5569 
5570    if (mode != ALL_PAGES) {
5571       /* doubly-protected file write: protect against errors during file write */
5572       fname = xobjs.pagelist[areawin->page]->filename;
5573       sprintf(outname, "%s~", fname);
5574       rename(fname, outname);
5575    }
5576    else {
5577       /* doubly-protected backup: protect against errors during file write */
5578       sprintf(outname, "%sB", xobjs.tempfile);
5579       rename(xobjs.tempfile, outname);
5580       fname = xobjs.tempfile;
5581    }
5582 
5583    if ((fptr = strrchr(fname, '/')) != NULL)
5584       fptr++;
5585    else
5586       fptr = fname;
5587 
5588    if ((mode != ALL_PAGES) && (strchr(fptr, '.') == NULL))
5589       sprintf(outname, "%s.ps", fname);
5590    else sprintf(outname, "%s", fname);
5591 
5592    xc_tilde_expand(outname, 149);
5593    while(xc_variable_expand(outname, 149));
5594 
5595    ps = fopen(outname, "wb");
5596    if (ps == NULL) {
5597       Wprintf("Can't open file %s for writing.", outname);
5598       return;
5599    }
5600 
5601    if ((mode != NO_SUBCIRCUITS) && (mode != ALL_PAGES))
5602       collectsubschems(areawin->page);
5603 
5604    /* Check for multiple-page output: get the number of pages;	*/
5605    /* ignore empty pages.					*/
5606 
5607    multipage = 0;
5608 
5609    if (mode == NO_SUBCIRCUITS)
5610       pagelist = pagetotals(areawin->page, INDEPENDENT);
5611    else if (mode == ALL_PAGES)
5612       pagelist = pagetotals(areawin->page, ALL_PAGES);
5613    else
5614       pagelist = pagetotals(areawin->page, TOTAL_PAGES);
5615 
5616    for (page = 0; page < xobjs.pages; page++)
5617       if (pagelist[page] > 0)
5618 	  multipage++;
5619 
5620    if (multipage == 0) {
5621       Wprintf("Panic:  could not find this page in page list!");
5622       free (pagelist);
5623       fclose(ps);
5624       return;
5625    }
5626 
5627    /* Print the PostScript DSC Document Header */
5628 
5629    fprintf(ps, "%%!PS-Adobe-3.0");
5630    if (multipage == 1 && !(xobjs.pagelist[areawin->page]->pmode & 1))
5631       fprintf(ps, " EPSF-3.0\n");
5632    else
5633       fprintf(ps, "\n");
5634    fprintf(ps, "%%%%Title: %s\n", fptr);
5635    fprintf(ps, "%%%%Creator: XCircuit v%2.1f rev%d\n", PROG_VERSION, PROG_REVISION);
5636    tdate = time(NULL);
5637    fprintf(ps, "%%%%CreationDate: %s", asctime(localtime(&tdate)));
5638    fprintf(ps, "%%%%Pages: %d\n", multipage);
5639 
5640    /* This is just a default value; each bounding box is declared per	*/
5641    /* page by the DSC "PageBoundingBox" keyword.			*/
5642    /* However, encapsulated files adjust the bounding box to center on	*/
5643    /* the object, instead of centering the object on the page.		*/
5644 
5645    if (multipage == 1 && !(xobjs.pagelist[areawin->page]->pmode & 1)) {
5646       objectptr thisobj = xobjs.pagelist[areawin->page]->pageinst->thisobject;
5647       float psscale = getpsscale(xobjs.pagelist[areawin->page]->outscale,
5648 			areawin->page);
5649 
5650       /* The top-level bounding box determines the size of an encapsulated */
5651       /* drawing, regardless of the PageBoundingBox numbers.  Therefore,   */
5652       /* we size this to bound just the picture by closing up the 1" (72   */
5653       /* PostScript units) margins, except for a tiny sliver of a margin   */
5654       /* (4 PostScript units) which covers a bit of sloppiness in the font */
5655       /* measurements.							   */
5656 
5657       fprintf(ps, "%%%%BoundingBox: 68 68 %d %d\n",
5658 	 (int)((float)thisobj->bbox.width * psscale)
5659 			+ xobjs.pagelist[areawin->page]->margins.x + 4,
5660 	 (int)((float)thisobj->bbox.height * psscale)
5661 			+ xobjs.pagelist[areawin->page]->margins.y + 4);
5662    }
5663    else if (xobjs.pagelist[0]->coordstyle == CM)
5664       fprintf(ps, "%%%%BoundingBox: 0 0 595 842\n");  /* A4 default (fixed by jdk) */
5665    else
5666       fprintf(ps, "%%%%BoundingBox: 0 0 612 792\n");  /* letter default */
5667 
5668    for (i = 0; i < fontcount; i++) fontsused[i] = 0;
5669    fprintf(ps, "%%%%DocumentNeededResources: font ");
5670    stcount = 32;
5671 
5672    /* find all of the fonts used in this document */
5673    /* log all fonts which are native PostScript   */
5674 
5675    for (curpage = 0; curpage < xobjs.pages; curpage++)
5676       if (pagelist[curpage] > 0) {
5677          writepage = xobjs.pagelist[curpage]->pageinst;
5678          findfonts(writepage->thisobject, fontsused);
5679       }
5680 
5681    for (i = 0; i < fontcount; i++) {
5682       if (fontsused[i] & 0x8000)
5683 	 if ((fonts[i].flags & 0x8018) == 0x0) {
5684 	    stcount += strlen(fonts[i].psname) + 1;
5685 	    if (stcount > OUTPUTWIDTH) {
5686 	       stcount = strlen(fonts[i].psname) + 11;
5687 	       fprintf(ps, "\n%%%%+ font ");
5688 	    }
5689 	    fprintf(ps, "%s ", fonts[i].psname);
5690 	 }
5691    }
5692 
5693    fprintf(ps, "\n%%%%EndComments\n");
5694 
5695    tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");
5696    if (tmp_s != NULL) {
5697       sprintf(prologue, "%s/%s", tmp_s, PROLOGUE_FILE);
5698       pro = fopen(prologue, "r");
5699    }
5700    else
5701       pro = NULL;
5702 
5703    if (pro == NULL) {
5704       sprintf(prologue, "%s/%s", PROLOGUE_DIR, PROLOGUE_FILE);
5705       pro = fopen(prologue, "r");
5706       if (pro == NULL) {
5707          sprintf(prologue, "%s", PROLOGUE_FILE);
5708          pro = fopen(prologue, "r");
5709          if (pro == NULL) {
5710             Wprintf("Can't open prolog.");
5711 	    free(pagelist);
5712 	    fclose(ps);
5713             return;
5714 	 }
5715       }
5716    }
5717 
5718    /* write the prolog to the output */
5719 
5720    for(;;) {
5721       if (fgets(temp, 149, pro) == NULL) break;
5722       if (!strncmp(temp, "%%EndProlog", 11)) break;
5723       fputs(temp, ps);
5724    }
5725    fclose(pro);
5726 
5727    /* Special font encodings not known to PostScript	*/
5728    /* (anything other than Standard and ISOLatin-1)	*/
5729 
5730    for (findex = 0; findex < fontcount; findex++) {
5731       if ((fontsused[findex] & 0xf80) == 0x400) {
5732 	 /* Cyrillic (ISO8859-5) */
5733 	 sprintf(prologue, "%s/%s", PROLOGUE_DIR, CYRILLIC_ENC_FILE);
5734 	 pro = fopen(prologue, "r");
5735          if (pro == NULL) {
5736             sprintf(prologue, "%s", CYRILLIC_ENC_FILE);
5737             pro = fopen(prologue, "r");
5738             if (pro == NULL) {
5739                Wprintf("Warning:  Missing font encoding vectors.");
5740                Wprintf("Output may not print properly.");
5741 	    }
5742          }
5743 	 if (pro != NULL) {
5744             for(;;) {
5745                if (fgets(temp, 149, pro) == NULL) break;
5746                fputs(temp, ps);
5747             }
5748             fclose(pro);
5749 	 }
5750       }
5751       else if ((fontsused[findex] & 0xf80) == 0x180) {
5752 	 /* Eastern European (ISOLatin2) */
5753 	 sprintf(prologue, "%s/%s", PROLOGUE_DIR, ISOLATIN2_ENC_FILE);
5754 	 pro = fopen(prologue, "r");
5755          if (pro == NULL) {
5756             sprintf(prologue, "%s", ISOLATIN2_ENC_FILE);
5757             pro = fopen(prologue, "r");
5758             if (pro == NULL) {
5759                Wprintf("Warning:  Missing font encoding vectors.");
5760                Wprintf("Output may not print properly.");
5761 	    }
5762          }
5763 	 if (pro != NULL) {
5764             for(;;) {
5765                if (fgets(temp, 149, pro) == NULL) break;
5766                fputs(temp, ps);
5767             }
5768             fclose(pro);
5769 	 }
5770       }
5771       else if ((fontsused[findex] & 0xf80) == 0x300) {
5772 	 /* Turkish (ISOLatin5) */
5773 	 sprintf(prologue, "%s/%s", PROLOGUE_DIR, ISOLATIN5_ENC_FILE);
5774 	 pro = fopen(prologue, "r");
5775          if (pro == NULL) {
5776             sprintf(prologue, "%s", ISOLATIN5_ENC_FILE);
5777             pro = fopen(prologue, "r");
5778             if (pro == NULL) {
5779                Wprintf("Warning:  Missing font encoding vectors.");
5780                Wprintf("Output may not print properly.");
5781 	    }
5782          }
5783 	 if (pro != NULL) {
5784             for(;;) {
5785                if (fgets(temp, 149, pro) == NULL) break;
5786                fputs(temp, ps);
5787             }
5788             fclose(pro);
5789 	 }
5790       }
5791    }
5792 
5793    /* Finish off prolog */
5794    fputs("%%EndProlog\n", ps);
5795 
5796    /* Special font handling */
5797 
5798    for (findex = 0; findex < fontcount; findex++) {
5799 
5800       /* Derived font slant */
5801 
5802       if ((fontsused[findex] & 0x032) == 0x032)
5803          fprintf(ps, "/%s /%s .167 fontslant\n\n",
5804 		fonts[findex].psname, fonts[findex].family);
5805 
5806       /* Derived ISO-Latin1 encoding */
5807 
5808       if ((fontsused[findex] & 0xf80) == 0x100) {
5809 	 char *fontorig = NULL;
5810 	 short i;
5811 	 /* find the original standard-encoded font (can be itself) */
5812 	 for (i = 0; i < fontcount; i++) {
5813 	    if (i == findex) continue;
5814 	    if (!strcmp(fonts[i].family, fonts[findex].family) &&
5815 		 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5816 	       fontorig = fonts[i].psname;
5817 	       break;
5818 	    }
5819 	 }
5820 	 if (fontorig == NULL) fontorig = fonts[findex].psname;
5821 	 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5822 	 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5823 	 fprintf(ps, "/Encoding ISOLatin1Encoding def currentdict end\n");
5824 	 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5825       }
5826 
5827       /* Derived Cyrillic (ISO8859-5) encoding */
5828 
5829       if ((fontsused[findex] & 0xf80) == 0x400) {
5830 	 char *fontorig = NULL;
5831 	 short i;
5832 	 /* find the original standard-encoded font (can be itself) */
5833 	 for (i = 0; i < fontcount; i++) {
5834 	    if (i == findex) continue;
5835 	    if (!strcmp(fonts[i].family, fonts[findex].family) &&
5836 		 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5837 	       fontorig = fonts[i].psname;
5838 	       break;
5839 	    }
5840 	 }
5841 	 if (fontorig == NULL) fontorig = fonts[findex].psname;
5842 	 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5843 	 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5844 	 fprintf(ps, "/Encoding ISO8859_5Encoding def currentdict end\n");
5845 	 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5846       }
5847 
5848       /* ISO-Latin2 encoding */
5849 
5850       if ((fontsused[findex] & 0xf80) == 0x180) {
5851 	 char *fontorig = NULL;
5852 	 short i;
5853 	 /* find the original standard-encoded font (can be itself) */
5854 	 for (i = 0; i < fontcount; i++) {
5855 	    if (i == findex) continue;
5856 	    if (!strcmp(fonts[i].family, fonts[findex].family) &&
5857 		 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5858 	       fontorig = fonts[i].psname;
5859 	       break;
5860 	    }
5861 	 }
5862 	 if (fontorig == NULL) fontorig = fonts[findex].psname;
5863 	 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5864 	 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5865 	 fprintf(ps, "/Encoding ISOLatin2Encoding def currentdict end\n");
5866 	 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5867       }
5868 
5869       /* ISO-Latin5 encoding */
5870 
5871       if ((fontsused[findex] & 0xf80) == 0x300) {
5872 	 char *fontorig = NULL;
5873 	 short i;
5874 	 /* find the original standard-encoded font (can be itself) */
5875 	 for (i = 0; i < fontcount; i++) {
5876 	    if (i == findex) continue;
5877 	    if (!strcmp(fonts[i].family, fonts[findex].family) &&
5878 		 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5879 	       fontorig = fonts[i].psname;
5880 	       break;
5881 	    }
5882 	 }
5883 	 if (fontorig == NULL) fontorig = fonts[findex].psname;
5884 	 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5885 	 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5886 	 fprintf(ps, "/Encoding ISOLatin5Encoding def currentdict end\n");
5887 	 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5888       }
5889 
5890       /* To do:  Special encoding */
5891 
5892       if ((fontsused[findex] & 0xf80) == 0x80) {
5893       }
5894 
5895       /* To do:  Vectored (drawn) font */
5896 
5897       if (fontsused[findex] & 0x8) {
5898       }
5899    }
5900 
5901    /* List of objects already written */
5902    wroteobjs = (objectptr *) malloc (sizeof(objectptr));
5903    written = 0;
5904 
5905    fprintf(ps, "%% XCircuit output starts here.\n\n");
5906    fprintf(ps, "%%%%BeginSetup\n\n");
5907 
5908    /* Write out all of the images used */
5909 
5910    glist = collect_graphics(pagelist);
5911    output_graphic_data(ps, glist);
5912    free(glist);
5913 
5914    for (curpage = 0; curpage < xobjs.pages; curpage++) {
5915       if (pagelist[curpage] == 0) continue;
5916 
5917       /* Write all of the object definitions used, bottom up */
5918       printrefobjects(ps, xobjs.pagelist[curpage]->pageinst->thisobject,
5919 		&wroteobjs, &written);
5920    }
5921 
5922    fprintf(ps, "\n%%%%EndSetup\n\n");
5923 
5924    page = 0;
5925    for (curpage = 0; curpage < xobjs.pages; curpage++) {
5926       if (pagelist[curpage] == 0) continue;
5927 
5928       /* Print the page header, all elements in the page, and page trailer */
5929       savepage = areawin->page;
5930       /* Set the current page for the duration of printing so that any	*/
5931       /* page parameters will be printed correctly.			*/
5932       areawin->page = curpage;
5933       printpageobject(ps, xobjs.pagelist[curpage]->pageinst->thisobject,
5934 		++page, curpage);
5935       areawin->page = savepage;
5936 
5937       /* For crash recovery, log the filename for each page */
5938       if (mode == ALL_PAGES) {
5939 	 fprintf(ps, "%% %s is_filename\n",
5940 		(xobjs.pagelist[curpage]->filename == NULL) ?
5941 		xobjs.pagelist[curpage]->pageinst->thisobject->name :
5942 		xobjs.pagelist[curpage]->filename);
5943       }
5944 
5945       fprintf(ps, "\n");
5946       fflush(ps);
5947    }
5948 
5949    /* For crash recovery, save all objects that have been edited but are */
5950    /* not in the list of objects already saved.				 */
5951 
5952    if (mode == ALL_PAGES)
5953    {
5954       int i, j, k;
5955       objectptr thisobj;
5956 
5957       for (i = 0; i < xobjs.numlibs; i++) {
5958 	 for (j = 0; j < xobjs.userlibs[i].number; j++) {
5959 	    thisobj = *(xobjs.userlibs[i].library + j);
5960 	    if (thisobj->changes > 0 ) {
5961 	       for (k = 0; k < written; k++)
5962 	          if (thisobj == *(wroteobjs + k)) break;
5963 	       if (k == written)
5964       	          printobjects(ps, thisobj, &wroteobjs, &written, DEFAULTCOLOR);
5965 	    }
5966 	 }
5967       }
5968    }
5969    else {	/* No unsaved changes in these objects */
5970       setassaved(wroteobjs, written);
5971       for (i = 0; i < xobjs.pages; i++)
5972 	 if (pagelist[i] > 0)
5973 	    xobjs.pagelist[i]->pageinst->thisobject->changes = 0;
5974       xobjs.new_changes = countchanges(NULL);
5975    }
5976 
5977    /* Free allocated memory */
5978    free((char *)pagelist);
5979    free((char *)wroteobjs);
5980 
5981    /* Done! */
5982 
5983    fprintf(ps, "%%%%Trailer\n");
5984    fprintf(ps, "XCIRCsave restore\n");
5985    fprintf(ps, "%%%%EOF\n");
5986    fclose(ps);
5987 
5988    Wprintf("File %s saved (%d page%s).", fname, multipage,
5989 		(multipage > 1 ? "s" : ""));
5990 
5991    if (mode == ALL_PAGES) {
5992       /* Remove the temporary redundant backup */
5993       sprintf(outname, "%sB", xobjs.tempfile);
5994       unlink(outname);
5995    }
5996    else if (!xobjs.retain_backup) {
5997       /* Remove the backup file */
5998       sprintf(outname, "%s~", fname);
5999       unlink(outname);
6000    }
6001 
6002    /* Write LATEX strings, if any are present */
6003    TopDoLatex();
6004 }
6005 
6006 /*----------------------------------------------------------------------*/
6007 /* Given a color index, print the R, G, B values			*/
6008 /*----------------------------------------------------------------------*/
6009 
6010 int printRGBvalues(char *tstr, int index, const char *postfix)
6011 {
6012    int i;
6013    if (index >= 0 && index < number_colors) {
6014       sprintf(tstr, "%4.3f %4.3f %4.3f %s",
6015 		(float)colorlist[index].color.red   / 65535,
6016 		(float)colorlist[index].color.green / 65535,
6017 		(float)colorlist[index].color.blue  / 65535,
6018 		postfix);
6019       return 0;
6020    }
6021 
6022    /* The program can reach this point for any color which is	*/
6023    /* not listed in the table.  This can happen when parameters	*/
6024    /* printed from printobjectparams object contain the string	*/
6025    /* "@p_color".  Therefore print the default top-level	*/
6026    /* default color, which is black.				*/
6027 
6028    /* If the index is *not* DEFAULTCOLOR (-1), return an error	*/
6029    /* code.							*/
6030 
6031    sprintf(tstr, "0 0 0 %s", postfix);
6032    return (index == DEFAULTCOLOR) ? 0 : -1;
6033 }
6034 
6035 /*----------------------------------------------------*/
6036 /* Write string to PostScript string, ignoring NO_OPs */
6037 /*----------------------------------------------------*/
6038 
6039 char *nosprint(char *baseptr, int *margin, int *extsegs)
6040 {
6041    int qtmp, slen = 100;
6042    char *sptr, *lptr = NULL, lsave, *sptr2;
6043    u_char *pptr, *qptr, *bptr;
6044 
6045    bptr = (u_char *)malloc(slen);	/* initial length 100 */
6046    qptr = bptr;
6047 
6048    while(1) {	/* loop for breaking up margin-limited text into words */
6049 
6050       if (*margin > 0) {
6051 	 sptr = strrchr(baseptr, ' ');
6052 	 if (sptr == NULL)
6053 	    sptr = baseptr;
6054 	 else {
6055 	    if (*(sptr + 1) == '\0') {
6056 	       while (*sptr == ' ') sptr--;
6057 	       *(sptr + 1) = '\0';
6058 	       sptr2 = strrchr(baseptr, ' ');
6059 	       *(sptr + 1) = ' ';
6060 	       if (sptr2 == NULL)
6061 		  sptr = baseptr;
6062 	       else
6063 		  sptr = sptr2 + 1;
6064 	    }
6065 	    else
6066 	       sptr++;
6067 	 }
6068       }
6069       else
6070 	 sptr = baseptr;
6071 
6072       *qptr++ = '(';
6073 
6074       /* Includes extended character set (non-ASCII) */
6075 
6076       for (pptr = sptr; pptr && *pptr != '\0'; pptr++) {
6077          /* Ensure enough space for the string, including everything */
6078          /* following the "for" loop */
6079          qtmp = qptr - bptr;
6080          if (qtmp + 7 >= slen) {
6081 	    slen += 7;
6082 	    bptr = (char *)realloc(bptr, slen);
6083 	    qptr = bptr + qtmp;
6084          }
6085 
6086          /* Deal with non-printable characters and parentheses */
6087          if (*pptr > (char)126) {
6088 	    sprintf(qptr, "\\%3o", (int)(*pptr));
6089 	    qptr += 4;
6090          }
6091          else {
6092             if ((*pptr == '(') || (*pptr == ')') || (*pptr == '\\'))
6093 	       *qptr++ = '\\';
6094             *qptr++ = *pptr;
6095          }
6096       }
6097       if (qptr == bptr + 1) {	/* Empty string gets a NULL result, not "()" */
6098          qptr--;
6099       }
6100       else {
6101          *qptr++ = ')';
6102          *qptr++ = ' ';
6103       }
6104 
6105       if (lptr != NULL)
6106 	 *lptr = lsave;
6107 
6108       if (sptr == baseptr)
6109 	 break;
6110       else {
6111 	 lptr = sptr;
6112 	 lsave = *lptr;
6113 	 *lptr = '\0';
6114 	 (*extsegs)++;
6115       }
6116    }
6117 
6118    *qptr++ = '\0';
6119    return (char *)bptr;
6120 }
6121 
6122 /*--------------------------------------------------------------*/
6123 /* Write label segments to the output (in reverse order)	*/
6124 /*--------------------------------------------------------------*/
6125 
6126 short writelabel(FILE *ps, stringpart *chrtop, short *stcount)
6127 {
6128    short i, segs = 0;
6129    stringpart *chrptr;
6130    char **ostr = (char **)malloc(sizeof(char *));
6131    char *tmpstr;
6132    float lastscale = 1.0;
6133    int lastfont = -1;
6134    int margin = 0;
6135    int extsegs = 0;
6136 
6137    /* Write segments into string array, in forward order */
6138 
6139    for (chrptr = chrtop; chrptr != NULL; chrptr = chrptr->nextpart) {
6140       ostr = (char **)realloc(ostr, (segs + 1) * sizeof(char *));
6141       if (chrtop->type == PARAM_END) {	/* NULL parameter is empty string */
6142 	 ostr[segs] = (char *)malloc(4);
6143 	 strcpy(ostr[segs], "() ");
6144       }
6145       else {
6146 	 tmpstr = writesegment(chrptr, &lastscale, &lastfont, &margin, &extsegs);
6147 	 if (tmpstr[0] != '\0')
6148             ostr[segs] = tmpstr;
6149 	 else
6150 	    segs--;
6151       }
6152       segs++;
6153    }
6154 
6155    /* Write string array to output in reverse order */
6156    for (i = segs - 1; i >= 0; i--) {
6157       dostcount(ps, stcount, strlen(ostr[i]));
6158       fputs(ostr[i], ps);
6159       free(ostr[i]);
6160    }
6161    free(ostr);
6162 
6163    return segs + extsegs;
6164 }
6165 
6166 /*--------------------------------------------------------------*/
6167 /* Write a single label segment to the output			*/
6168 /* (Recursive, so we can write segments in the reverse order)	*/
6169 /*--------------------------------------------------------------*/
6170 
6171 char *writesegment(stringpart *chrptr, float *lastscale, int *lastfont, int *margin,
6172 	int *extsegs)
6173 {
6174    int type = chrptr->type;
6175    char *retstr, *validname;
6176 
6177    switch(type) {
6178       case PARAM_START:
6179 	 validname = create_valid_psname(chrptr->data.string, TRUE);
6180 	 sprintf(_STR, "%s ", validname);
6181 	 break;
6182       case PARAM_END:
6183 	 _STR[0] = '\0';
6184 	 chrptr->nextpart = NULL;
6185 	 break;
6186       case SUBSCRIPT:
6187 	 sprintf(_STR, "{ss} ");
6188 	 break;
6189       case SUPERSCRIPT:
6190 	 sprintf(_STR, "{Ss} ");
6191 	 break;
6192       case NORMALSCRIPT:
6193 	 *lastscale = 1.0;
6194          sprintf(_STR, "{ns} ");
6195          break;
6196       case UNDERLINE:
6197          sprintf(_STR, "{ul} ");
6198          break;
6199       case OVERLINE:
6200          sprintf(_STR, "{ol} ");
6201          break;
6202       case NOLINE:
6203          sprintf(_STR, "{} ");
6204          break;
6205       case HALFSPACE:
6206          sprintf(_STR, "{hS} ");
6207          break;
6208       case QTRSPACE:
6209 	 sprintf(_STR, "{qS} ");
6210 	 break;
6211       case RETURN:
6212 	 *lastscale = 1.0;
6213 	 if (chrptr->data.flags == 0)
6214 	    // Ignore automatically-generated line breaks
6215 	    sprintf(_STR, "{CR} ");
6216 	 else
6217 	    sprintf(_STR, "");
6218 	 break;
6219       case TABSTOP:
6220 	 sprintf(_STR, "{Ts} ");
6221 	 break;
6222       case TABFORWARD:
6223 	 sprintf(_STR, "{Tf} ");
6224 	 break;
6225       case TABBACKWARD:
6226 	 sprintf(_STR, "{Tb} ");
6227 	 break;
6228       case FONT_NAME:
6229 	 /* If font specifier is followed by a scale specifier, then	*/
6230 	 /* record the font change but defer the output.  Otherwise,	*/
6231 	 /* output the font record now.					*/
6232 
6233 	 if ((chrptr->nextpart == NULL) || (chrptr->nextpart->type != FONT_SCALE))
6234 	 {
6235 	    if (*lastscale == 1.0)
6236 	       sprintf(_STR, "{/%s cf} ", fonts[chrptr->data.font].psname);
6237 	    else
6238 	       sprintf(_STR, "{/%s %5.3f cf} ", fonts[chrptr->data.font].psname,
6239 			*lastscale);
6240 	 }
6241 	 else
6242 	    _STR[0] = '\0';
6243 	 *lastfont = chrptr->data.font;
6244 	 break;
6245       case FONT_SCALE:
6246 	 if (*lastfont == -1) {
6247 	    Fprintf(stderr, "Warning:  Font may not be the one that was intended.\n");
6248 	    *lastfont = 0;
6249 	 }
6250 	 *lastscale = chrptr->data.scale;
6251 	 sprintf(_STR, "{/%s %5.3f cf} ", fonts[*lastfont].psname, *lastscale);
6252 	 break;
6253       case FONT_COLOR:
6254 	 strcpy(_STR, "{");
6255 	 if (chrptr->data.color == DEFAULTCOLOR)
6256 	    strcat(_STR, "sce} ");
6257 	 else
6258 	    if (printRGBvalues(_STR + 1, chrptr->data.color, "scb} ") < 0)
6259 	       strcat(_STR, "sce} ");
6260 	 break;
6261       case MARGINSTOP:
6262 	 sprintf(_STR, "{%d MR} ", chrptr->data.width);
6263 	 *margin = chrptr->data.width;
6264 	 break;
6265       case KERN:
6266 	 sprintf(_STR, "{%d %d Kn} ", chrptr->data.kern[0], chrptr->data.kern[1]);
6267 	 break;
6268       case TEXT_STRING:
6269 	 /* Everything except TEXT_STRING will always fit in the _STR fixed-	*/
6270 	 /* length character array. 						*/
6271 	 return nosprint(chrptr->data.string, margin, extsegs);
6272    }
6273 
6274    retstr = (char *)malloc(1 + strlen(_STR));
6275    strcpy(retstr, _STR);
6276    return retstr;
6277 }
6278 
6279 /*--------------------------------------------------------------*/
6280 /* Routine to write all the label segments as stored in _STR	*/
6281 /*--------------------------------------------------------------*/
6282 
6283 int writelabelsegs(FILE *ps, short *stcount, stringpart *chrptr)
6284 {
6285    Boolean ismultipart;
6286    int segs;
6287 
6288    if (chrptr == NULL) return 0;
6289 
6290    ismultipart = ((chrptr->nextpart != NULL) &&
6291 	   (chrptr->nextpart->type != PARAM_END)) ? True : False;
6292 
6293    /* If there is only one part, but it is not a string or the	*/
6294    /* end of a parameter (empty parameter), then set multipart	*/
6295    /* anyway so we get the double brace {{ }}.	  		*/
6296 
6297    if ((!ismultipart) && (chrptr->type != TEXT_STRING) &&
6298 		(chrptr->type != PARAM_END))
6299       ismultipart = True;
6300 
6301    /* nextpart is not NULL if there are multiple parts to the string */
6302    if (ismultipart) {
6303       fprintf(ps, "{");
6304       (*stcount)++;
6305    }
6306    segs = writelabel(ps, chrptr, stcount);
6307 
6308    if (ismultipart) {
6309       fprintf(ps, "} ");
6310       (*stcount) += 2;
6311    }
6312    return segs;
6313 }
6314 
6315 /*--------------------------------------------------------------*/
6316 /* Write the dictionary of parameters belonging to an object	*/
6317 /*--------------------------------------------------------------*/
6318 
6319 void printobjectparams(FILE *ps, objectptr localdata)
6320 {
6321    int segs;
6322    short stcount;
6323    oparamptr ops;
6324    char *ps_expr, *validkey;
6325    float fp;
6326 
6327    /* Check for parameters and default values */
6328    if (localdata->params == NULL) return;
6329 
6330    fprintf(ps, "<<");
6331    stcount = 2;
6332 
6333    for (ops = localdata->params; ops != NULL; ops = ops->next) {
6334       validkey = create_valid_psname(ops->key, TRUE);
6335       fprintf(ps, "/%s ", validkey);
6336       dostcount (ps, &stcount, strlen(validkey) + 2);
6337 
6338       switch (ops->type) {
6339 	 case XC_EXPR:
6340 	    ps_expr = evaluate_expr(localdata, ops, NULL);
6341 	    if (ops->which == P_SUBSTRING || ops->which == P_EXPRESSION) {
6342 	       dostcount(ps, &stcount, 3 + strlen(ps_expr));
6343 	       fputs("(", ps);
6344 	       fputs(ps_expr, ps);
6345 	       fputs(") ", ps);
6346 	    }
6347 	    else if (ops->which == P_COLOR) {
6348 	       int ccol;
6349 	       /* Write R, G, B components for PostScript */
6350 	       if (sscanf(ps_expr, "%d", &ccol) == 1) {
6351 	          fputs("{", ps);
6352 	          printRGBvalues(_STR, ccol, "} ");
6353 	          dostcount(ps, &stcount, 1 + strlen(_STR));
6354 	          fputs(_STR, ps);
6355 	       }
6356 	       else {
6357 	          dostcount(ps, &stcount, 8);
6358 	          fputs("{0 0 0} ", ps);
6359 	       }
6360 	    }
6361 	    else if (sscanf(ps_expr, "%g", &fp) == 1) {
6362 	       dostcount(ps, &stcount, 1 + strlen(ps_expr));
6363 	       fputs(ps_expr, ps);
6364 	       fputs(" ", ps);
6365 	    }
6366 	    else {	/* Expression evaluates to error in object */
6367 	       dostcount(ps, &stcount, 2);
6368 	       fputs("0 ", ps);
6369             }
6370 	    dostcount(ps, &stcount, 7 + strlen(ops->parameter.expr));
6371 	    fputs("(", ps);
6372 	    fputs(ops->parameter.expr, ps);
6373 	    fputs(") pop ", ps);
6374 	    free(ps_expr);
6375 	    break;
6376 	 case XC_STRING:
6377 	    segs = writelabelsegs(ps, &stcount, ops->parameter.string);
6378 	    if (segs == 0) {
6379 	       /* When writing object parameters, we cannot allow a */
6380 	       /* NULL value.  Instead, print an empty string ().   */
6381 	       dostcount(ps, &stcount, 3);
6382 	       fputs("() ", ps);
6383 	    }
6384 	    break;
6385 	 case XC_INT:
6386 	    sprintf(_STR, "%d ", ops->parameter.ivalue);
6387 	    dostcount(ps, &stcount, strlen(_STR));
6388 	    fputs(_STR, ps);
6389 	    break;
6390 	 case XC_FLOAT:
6391 	    sprintf(_STR, "%g ", ops->parameter.fvalue);
6392 	    dostcount(ps, &stcount, strlen(_STR));
6393 	    fputs(_STR, ps);
6394 	    break;
6395       }
6396    }
6397 
6398    fprintf(ps, ">> ");
6399    dostcount (ps, &stcount, 3);
6400 }
6401 
6402 /*--------------------------------------------------------------*/
6403 /* Write the list of parameters belonging to an object instance */
6404 /*--------------------------------------------------------------*/
6405 
6406 short printparams(FILE *ps, objinstptr sinst, short stcount)
6407 {
6408    int segs;
6409    short loccount;
6410    oparamptr ops, objops;
6411    eparamptr epp;
6412    char *ps_expr, *validkey, *validref;
6413    short instances = 0;
6414 
6415    if (sinst->params == NULL) return stcount;
6416 
6417    for (ops = sinst->params; ops != NULL; ops = ops->next) {
6418       validref = strdup(create_valid_psname(ops->key, TRUE));
6419 
6420       /* Check for indirect parameter references */
6421       for (epp = sinst->passed; epp != NULL; epp = epp->next) {
6422 	 if ((epp->flags & P_INDIRECT) && (epp->pdata.refkey != NULL)) {
6423 	    if (!strcmp(epp->pdata.refkey, ops->key)) {
6424 	       if (instances++ == 0) {
6425 		  fprintf(ps, "<<");		/* begin PostScript dictionary */
6426 		  loccount = stcount + 2;
6427 	       }
6428 	       dostcount(ps, &loccount, strlen(validref + 3));
6429 	       fprintf(ps, "/%s ", validref);
6430 	       dostcount(ps, &loccount, strlen(epp->key + 1));
6431 	       validkey = create_valid_psname(epp->key, TRUE);
6432 	       fprintf(ps, "%s ", validkey);
6433 	       break;
6434 	    }
6435 	 }
6436       }
6437       if (epp == NULL) {	/* No indirection */
6438 	 Boolean nondefault = TRUE;
6439 	 char *deflt_expr = NULL;
6440 
6441 	 /* For instance values that are expression results, ignore if	*/
6442 	 /* the instance value is the same as the default value.	*/
6443 	 /* Correction 9/08:  We can't second-guess expression results,	*/
6444 	 /* in particular, this doesn't work for an expression like	*/
6445 	 /* "page", where the local and default values will evaluate to	*/
6446 	 /* the same result, when clearly each page is intended to have	*/
6447 	 /* its own instance value, and "page" for an object is not a	*/
6448 	 /* well-defined concept.					*/
6449 
6450 //	 ps_expr = NULL;
6451 //	 objops = match_param(sinst->thisobject, ops->key);
6452 //	 if (objops && (objops->type == XC_EXPR)) {
6453 //	    int i;
6454 //	    float f;
6455 //	    deflt_expr = evaluate_expr(sinst->thisobject, objops, NULL);
6456 //	    switch (ops->type) {
6457 //	       case XC_STRING:
6458 //		  if (!textcomp(ops->parameter.string, deflt_expr, sinst))
6459 //		     nondefault = FALSE;
6460 //		  break;
6461 //	       case XC_EXPR:
6462 //	          ps_expr = evaluate_expr(sinst->thisobject, ops, sinst);
6463 //		  if (!strcmp(ps_expr, deflt_expr)) nondefault = FALSE;
6464 //		  break;
6465 //	       case XC_INT:
6466 //		  sscanf(deflt_expr, "%d", &i);
6467 //		  if (i == ops->parameter.ivalue) nondefault = FALSE;
6468 //		  break;
6469 //	       case XC_FLOAT:
6470 //		  sscanf(deflt_expr, "%g", &f);
6471 //		  if (f == ops->parameter.fvalue) nondefault = FALSE;
6472 //		  break;
6473 //	    }
6474 //	    if (deflt_expr) free(deflt_expr);
6475 //	    if (nondefault == FALSE) {
6476 //	       if (ps_expr) free(ps_expr);
6477 //	       continue;
6478 //	    }
6479 //	 }
6480 
6481 	 if (instances++ == 0) {
6482 	    fprintf(ps, "<<");		/* begin PostScript dictionary */
6483 	    loccount = stcount + 2;
6484 	 }
6485          dostcount(ps, &loccount, strlen(validref) + 2);
6486          fprintf(ps, "/%s ", validref);
6487 
6488          switch (ops->type) {
6489 	    case XC_STRING:
6490 	       segs = writelabelsegs(ps, &loccount, ops->parameter.string);
6491 	       if (segs == 0) {
6492 		  /* When writing object parameters, we cannot allow a	*/
6493 		  /* NULL value.  Instead, print an empty string ().	*/
6494 		  dostcount(ps, &stcount, 3);
6495 		  fputs("() ", ps);
6496 	       }
6497 	       break;
6498 	    case XC_EXPR:
6499 	       ps_expr = evaluate_expr(sinst->thisobject, ops, sinst);
6500 	       dostcount(ps, &loccount, 3 + strlen(ps_expr));
6501 	       fputs("(", ps);
6502 	       fputs(ps_expr, ps);
6503 	       fputs(") ", ps);
6504 	       free(ps_expr);
6505 
6506 	       /* The instance parameter expression may have the	*/
6507 	       /* same expression as the object but a different		*/
6508 	       /* result if the expression uses another parameter.	*/
6509 	       /* Only print the expression itself if it's different	*/
6510 	       /* from the object's expression.				*/
6511 
6512 	       objops = match_param(sinst->thisobject, ops->key);
6513 	       if (objops && strcmp(ops->parameter.expr, objops->parameter.expr)) {
6514 	          dostcount(ps, &loccount, 3 + strlen(ops->parameter.expr));
6515 	          fputs("(", ps);
6516 	          fputs(ops->parameter.expr, ps);
6517 	          fputs(") pop ", ps);
6518 	       }
6519 	       break;
6520 	    case XC_INT:
6521 	       if (ops->which == P_COLOR) {
6522 		  /* Write R, G, B components */
6523 		  _STR[0] = '{';
6524 	          printRGBvalues(_STR + 1, ops->parameter.ivalue, "} ");
6525 	       }
6526 	       else
6527 	          sprintf(_STR, "%d ", ops->parameter.ivalue);
6528 	       dostcount(ps, &loccount, strlen(_STR));
6529 	       fputs(_STR, ps);
6530 	       break;
6531 	    case XC_FLOAT:
6532 	       sprintf(_STR, "%g ", ops->parameter.fvalue);
6533 	       dostcount(ps, &loccount, strlen(_STR));
6534 	       fputs(_STR, ps);
6535 	       break;
6536 	 }
6537       }
6538       free(validref);
6539    }
6540    if (instances > 0) {
6541       fprintf(ps, ">> ");		/* end PostScript dictionary */
6542       loccount += 3;
6543    }
6544    return loccount;
6545 }
6546 
6547 /*------------------------------------------------------------------*/
6548 /* Macro for point output (calls varpcheck() on x and y components) */
6549 /*------------------------------------------------------------------*/
6550 
6551 #define xyvarcheck(z, n, t) \
6552     varpcheck(ps, (z).x, localdata, n, &stcount, *(t), P_POSITION_X); \
6553     varpcheck(ps, (z).y, localdata, n, &stcount, *(t), P_POSITION_Y)
6554 
6555 #define xypathcheck(z, n, t, p) \
6556     varpathcheck(ps, (z).x, localdata, n, &stcount, t, TOPATH(p), P_POSITION_X); \
6557     varpathcheck(ps, (z).y, localdata, n, &stcount, t, TOPATH(p), P_POSITION_Y)
6558 
6559 /*--------------------------------------------------------------*/
6560 /* Main routine for writing the contents of a single object to	*/
6561 /* output file "ps".						*/
6562 /*--------------------------------------------------------------*/
6563 
6564 void printOneObject(FILE *ps, objectptr localdata, int ccolor)
6565 {
6566    int i, curcolor = ccolor;
6567    genericptr *savegen, *pgen;
6568    objinstptr sobj;
6569    graphicptr sg;
6570    Imagedata *img;
6571    pointlist savept;
6572    short stcount;
6573    short segs;
6574    Boolean has_parameter;
6575    char *fptr, *validname;
6576 
6577    /* first, get a total count of all objects and give warning if large */
6578 
6579    if ((is_page(localdata) == -1) && (localdata->parts > 255)) {
6580       Wprintf("Warning: \"%s\" may exceed printer's PS limit for definitions",
6581 	    localdata->name);
6582    }
6583 
6584    for (savegen = localdata->plist; savegen < localdata->plist +
6585 		localdata->parts; savegen++) {
6586 
6587       /* Check if this color is parameterized */
6588       eparamptr epp;
6589       oparamptr ops;
6590 
6591       /* FIXEDBBOX style is handled in the object header and	*/
6592       /* the part should be skipped.				*/
6593 
6594       if (ELEMENTTYPE(*savegen) == POLYGON)
6595 	 if (TOPOLY(savegen)->style & FIXEDBBOX)
6596 	    continue;
6597 
6598       for (epp = (*savegen)->passed; epp != NULL; epp = epp->next) {
6599 	 ops = match_param(localdata, epp->key);
6600 	 if (ops != NULL && (ops->which == P_COLOR)) {
6601 	    /* Ensure that the next element forces a color change */
6602 	    curcolor = ERRORCOLOR;
6603 	    sprintf(_STR, "%s scb\n", epp->key);
6604 	    fputs(_STR, ps);
6605 	    break;
6606 	 }
6607       }
6608 
6609       /* Enforce the rule that clipmasks must always be DEFAULTCOLOR */
6610 
6611       switch(ELEMENTTYPE(*savegen)) {
6612 	 case POLYGON: case SPLINE: case ARC: case PATH:
6613 	    if (TOPOLY(savegen)->style & CLIPMASK)
6614 	       (*savegen)->color = DEFAULTCOLOR;
6615 	    break;
6616       }
6617 
6618       /* change current color if different */
6619 
6620       if ((epp == NULL) && ((*savegen)->color != curcolor)) {
6621 	 if ((curcolor = (*savegen)->color) == DEFAULTCOLOR)
6622 	    fprintf(ps, "sce\n");
6623 	 else {
6624 	    if (printRGBvalues(_STR, (*savegen)->color, "scb\n") < 0) {
6625 	       fprintf(ps, "sce\n");
6626 	       curcolor = DEFAULTCOLOR;
6627 	    }
6628 	    else
6629 	       fputs(_STR, ps);
6630 	 }
6631       }
6632 
6633       stcount = 0;
6634       switch(ELEMENTTYPE(*savegen)) {
6635 
6636 	 case(POLYGON):
6637 	    varcheck(ps, TOPOLY(savegen)->style, localdata, &stcount,
6638 			*savegen, P_STYLE);
6639 	    varfcheck(ps, TOPOLY(savegen)->width, localdata, &stcount,
6640 			*savegen, P_LINEWIDTH);
6641             for (savept = TOPOLY(savegen)->points; savept < TOPOLY(savegen)->
6642 		      points + TOPOLY(savegen)->number; savept++) {
6643 	       varpcheck(ps, savept->x, localdata,
6644 			savept - TOPOLY(savegen)->points, &stcount, *savegen,
6645 			P_POSITION_X);
6646 	       varpcheck(ps, savept->y, localdata,
6647 			savept - TOPOLY(savegen)->points, &stcount, *savegen,
6648 			P_POSITION_Y);
6649 	    }
6650 	    sprintf(_STR, "%hd ", TOPOLY(savegen)->number);
6651 	    dostcount (ps, &stcount, strlen(_STR));
6652 	    fputs(_STR, ps);
6653 	    if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6654 			P_POSITION_X)) {
6655 	       sprintf(_STR, "addtox ");
6656 	       dostcount (ps, &stcount, strlen(_STR));
6657 	       fputs(_STR, ps);
6658 	    }
6659 	    if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6660 			P_POSITION_Y)) {
6661 	       sprintf(_STR, "addtoy ");
6662 	       dostcount (ps, &stcount, strlen(_STR));
6663 	       fputs(_STR, ps);
6664 	    }
6665 	    sprintf(_STR, "polygon\n");
6666 	    dostcount (ps, &stcount, strlen(_STR));
6667 	    fputs(_STR, ps);
6668 	    break;
6669 
6670 	 case(PATH):
6671 	    pgen = TOPATH(savegen)->plist;
6672 	    switch(ELEMENTTYPE(*pgen)) {
6673 	       case POLYGON:
6674 		  xypathcheck(TOPOLY(pgen)->points[0], 0, pgen, savegen);
6675 		  break;
6676 	       case SPLINE:
6677 		  xypathcheck(TOSPLINE(pgen)->ctrl[0], 0, pgen, savegen);
6678 		  break;
6679 	    }
6680 	    dostcount(ps, &stcount, 9);
6681 	    if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6682 			TOPATH(savegen), P_POSITION_X)) {
6683 	       sprintf(_STR, "addtox1 ");
6684 	       dostcount (ps, &stcount, strlen(_STR));
6685 	       fputs(_STR, ps);
6686 	    }
6687 	    if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6688 			TOPATH(savegen), P_POSITION_Y)) {
6689 	       sprintf(_STR, "addtoy1 ");
6690 	       dostcount (ps, &stcount, strlen(_STR));
6691 	       fputs(_STR, ps);
6692 	    }
6693 	    fprintf(ps, "beginpath\n");
6694 	    for (pgen = TOPATH(savegen)->plist; pgen < TOPATH(savegen)->plist
6695 			+ TOPATH(savegen)->parts; pgen++) {
6696 	       switch(ELEMENTTYPE(*pgen)) {
6697 		  case POLYGON:
6698                	     for (savept = TOPOLY(pgen)->points + TOPOLY(pgen)->number
6699 				- 1; savept > TOPOLY(pgen)->points; savept--) {
6700 			xypathcheck(*savept, savept - TOPOLY(pgen)->points, pgen,
6701 				savegen);
6702 	       	     }
6703 	       	     sprintf(_STR, "%hd ", TOPOLY(pgen)->number - 1);
6704 		     dostcount (ps, &stcount, strlen(_STR));
6705 		     fputs(_STR, ps);
6706 		     if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6707 				TOPATH(savegen), P_POSITION_X)) {
6708 	 	        sprintf(_STR, "addtox ");
6709 	 	        dostcount (ps, &stcount, strlen(_STR));
6710 		        fputs(_STR, ps);
6711 	 	     }
6712 	 	     if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6713 				TOPATH(savegen), P_POSITION_Y)) {
6714 	 	        sprintf(_STR, "addtoy ");
6715 		        dostcount (ps, &stcount, strlen(_STR));
6716 	 	        fputs(_STR, ps);
6717 	 	     }
6718 		     sprintf(_STR, "polyc\n");
6719 	       	     dostcount (ps, &stcount, strlen(_STR));
6720 	       	     fputs(_STR, ps);
6721 	       	     break;
6722 		  case SPLINE:
6723 		     xypathcheck(TOSPLINE(pgen)->ctrl[1], 1, pgen, savegen);
6724 		     xypathcheck(TOSPLINE(pgen)->ctrl[2], 2, pgen, savegen);
6725 		     xypathcheck(TOSPLINE(pgen)->ctrl[3], 3, pgen, savegen);
6726 	    	     if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6727 				TOPATH(savegen), P_POSITION_X)) {
6728 	     	        sprintf(_STR, "addtox3 ");
6729 	     	        dostcount (ps, &stcount, strlen(_STR));
6730 	     	        fputs(_STR, ps);
6731 	     	     }
6732 	     	     if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6733 				TOPATH(savegen), P_POSITION_Y)) {
6734 	     	        sprintf(_STR, "addtoy3 ");
6735 	     	        dostcount (ps, &stcount, strlen(_STR));
6736 	     	        fputs(_STR, ps);
6737 	     	     }
6738 		     fprintf(ps, "curveto\n");
6739 		     break;
6740 	       }
6741 	    }
6742 	    varcheck(ps, TOPATH(savegen)->style, localdata, &stcount,
6743 			*savegen, P_STYLE);
6744 	    varfcheck(ps, TOPATH(savegen)->width, localdata, &stcount,
6745 			*savegen, P_LINEWIDTH);
6746 	    fprintf(ps, "endpath\n");
6747 	    break;
6748 
6749 	 case(SPLINE):
6750 	    varcheck(ps, TOSPLINE(savegen)->style, localdata, &stcount,
6751 			*savegen, P_STYLE);
6752 	    varfcheck(ps, TOSPLINE(savegen)->width, localdata, &stcount,
6753 			*savegen, P_LINEWIDTH);
6754 	    xyvarcheck(TOSPLINE(savegen)->ctrl[1], 1, savegen);
6755 	    xyvarcheck(TOSPLINE(savegen)->ctrl[2], 2, savegen);
6756 	    xyvarcheck(TOSPLINE(savegen)->ctrl[3], 3, savegen);
6757 	    xyvarcheck(TOSPLINE(savegen)->ctrl[0], 0, savegen);
6758 	    if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6759 			P_POSITION_X)) {
6760 	       sprintf(_STR, "addtox4 ");
6761 	       dostcount (ps, &stcount, strlen(_STR));
6762 	       fputs(_STR, ps);
6763 	    }
6764 	    if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6765 			P_POSITION_Y)) {
6766 	       sprintf(_STR, "addtoy4 ");
6767 	       dostcount (ps, &stcount, strlen(_STR));
6768 	       fputs(_STR, ps);
6769 	    }
6770 	    fprintf(ps, "spline\n");
6771 	    break;
6772 
6773 	 case(ARC):
6774 	    varcheck(ps, TOARC(savegen)->style, localdata, &stcount,
6775 			*savegen, P_STYLE);
6776 	    varfcheck(ps, TOARC(savegen)->width, localdata, &stcount,
6777 			*savegen, P_LINEWIDTH);
6778 	    xyvarcheck(TOARC(savegen)->position, 0, savegen);
6779 	    varcheck(ps, abs(TOARC(savegen)->radius), localdata, &stcount,
6780 			*savegen, P_RADIUS);
6781 	    if (abs(TOARC(savegen)->radius) == TOARC(savegen)->yaxis) {
6782 	       varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount,
6783 			*savegen, P_ANGLE1);
6784 	       varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount,
6785 			*savegen, P_ANGLE2);
6786 	       fprintf(ps, "xcarc\n");
6787 	    }
6788 	    else {
6789 	       varcheck(ps, abs(TOARC(savegen)->yaxis), localdata, &stcount,
6790 			*savegen, P_MINOR_AXIS);
6791 	       varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount,
6792 			*savegen, P_ANGLE1);
6793 	       varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount,
6794 			*savegen, P_ANGLE2);
6795 	       fprintf(ps, "ellipse\n");
6796 	    }
6797 	    break;
6798 
6799 	 case(OBJINST):
6800 	    sobj = TOOBJINST(savegen);
6801 	    varfcheck(ps, sobj->scale, localdata, &stcount, *savegen, P_SCALE);
6802 	    if (!(sobj->style & LINE_INVARIANT)) fprintf(ps, "/sv ");
6803 	    varfcheck(ps, sobj->rotation, localdata, &stcount, *savegen, P_ROTATION);
6804 	    xyvarcheck(sobj->position, 0, savegen);
6805 	    if (sobj->style & INST_NONETLIST) fprintf(ps, "/nn ");
6806 
6807 	    opsubstitute(sobj->thisobject, sobj);
6808 	    stcount = printparams(ps, sobj, stcount);
6809 
6810 	    validname = create_valid_psname(sobj->thisobject->name, FALSE);
6811 
6812 	    /* Names without technologies get a leading string '::' 	*/
6813 	    /* (blank technology)					*/
6814 
6815 	    if (strstr(validname, "::") == NULL)
6816 	       fprintf(ps, "::%s\n", validname);
6817 	    else
6818 	       fprintf(ps, "%s\n", validname);
6819 	    break;
6820 
6821 	 case(GRAPHIC):
6822 	    sg = TOGRAPHIC(savegen);
6823 	    for (i = 0; i < xobjs.images; i++) {
6824 	       img = xobjs.imagelist + i;
6825 	       if (img->image == sg->source)
6826 		   break;
6827 	    }
6828 
6829 	    fptr = strrchr(img->filename, '/');
6830 	    if (fptr == NULL)
6831 	       fptr = img->filename;
6832 	    else
6833 	       fptr++;
6834 	    fprintf(ps, "/%s ", fptr);
6835 	    stcount += (2 + strlen(fptr));
6836 
6837 	    varfcheck(ps, sg->scale, localdata, &stcount, *savegen, P_SCALE);
6838 	    varfcheck(ps, sg->rotation, localdata, &stcount, *savegen, P_ROTATION);
6839 	    xyvarcheck(sg->position, 0, savegen);
6840 	    fprintf(ps, "graphic\n");
6841 	    break;
6842 
6843 	 case(LABEL):
6844 
6845 	    /* Don't save temporary labels from schematic capture system */
6846 	    if (TOLABEL(savegen)->string->type != FONT_NAME) break;
6847 
6848 	    /* Check for parameter --- must use "mark" to count # segments */
6849 	    has_parameter = hasparameter(TOLABEL(savegen));
6850 
6851 	    if (has_parameter) {
6852 	       fprintf(ps, "mark ");
6853 	       stcount += 5;
6854 	    }
6855 
6856 	    segs = writelabel(ps, TOLABEL(savegen)->string, &stcount);
6857 
6858 	    if (segs > 0) {
6859 	       if (has_parameter)
6860                   sprintf(_STR, "ctmk ");
6861 	       else
6862                   sprintf(_STR, "%hd ", segs);
6863 	       dostcount(ps, &stcount, strlen(_STR));
6864 	       fputs(_STR, ps);
6865 	       varcheck(ps, TOLABEL(savegen)->anchor, localdata, &stcount,
6866 			*savegen, P_ANCHOR);
6867 	       varfcheck(ps, TOLABEL(savegen)->rotation, localdata, &stcount,
6868 			*savegen, P_ROTATION);
6869 	       varfcheck(ps, TOLABEL(savegen)->scale, localdata, &stcount,
6870 			*savegen, P_SCALE);
6871 	       xyvarcheck(TOLABEL(savegen)->position, 0, savegen);
6872 
6873 	       switch(TOLABEL(savegen)->pin) {
6874 		  case LOCAL:
6875 		     strcpy(_STR, "pinlabel\n"); break;
6876 		  case GLOBAL:
6877 		     strcpy(_STR, "pinglobal\n"); break;
6878 		  case INFO:
6879 		     strcpy(_STR, "infolabel\n"); break;
6880 		  default:
6881 		     strcpy(_STR, "label\n");
6882 	       }
6883 	       dostcount(ps, &stcount, strlen(_STR));
6884 	       fputs(_STR, ps);
6885 	    }
6886 	    break;
6887       }
6888    }
6889 }
6890 
6891 /*----------------------------------------------------------------------*/
6892 /* Recursive routine to print out the library objects used in this	*/
6893 /* drawing, starting at the bottom of the object hierarchy so that each	*/
6894 /* object is defined before it is called.  A list of objects already	*/
6895 /* written is maintained so that no object is written twice. 		*/
6896 /*									*/
6897 /* When object "localdata" is not a top-level page, call this routine	*/
6898 /* with mpage=-1 (simpler than checking whether localdata is a page).	*/
6899 /*----------------------------------------------------------------------*/
6900 
6901 void printobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
6902 	short *written, int ccolor)
6903 {
6904    genericptr *gptr, *savegen;
6905    objectptr *optr;
6906    /* oparamptr ops; (jdk) */
6907    char *validname;
6908    int curcolor = ccolor;
6909    /* int libno; (jdk) */
6910 
6911    /* Search among the list of objects already written to the output	*/
6912    /* If this object has been written previously, then we ignore it.	*/
6913 
6914    for (optr = *wrotelist; optr < *wrotelist + *written; optr++)
6915       if (*optr == localdata)
6916 	  return;
6917 
6918    /* If this page is a schematic, write out the definiton of any symbol */
6919    /* attached to it, because that symbol may not be used anywhere else. */
6920 
6921    if (localdata->symschem && (localdata->schemtype == PRIMARY))
6922       printobjects(ps, localdata->symschem, wrotelist, written, curcolor);
6923 
6924    /* Search for all object definitions instantiated in this object,	*/
6925    /* and (recursively) print them to the output.			*/
6926 
6927    for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++)
6928       if (IS_OBJINST(*gptr))
6929          printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written, curcolor);
6930 
6931    /* Update the list of objects already written to the output */
6932 
6933    *wrotelist = (objectptr *)realloc(*wrotelist, (*written + 1) *
6934 		sizeof(objectptr));
6935    *(*wrotelist + *written) = localdata;
6936    (*written)++;
6937 
6938    validname = create_valid_psname(localdata->name, FALSE);
6939    if (strstr(validname, "::") == NULL)
6940       fprintf(ps, "/::%s {\n", validname);
6941    else
6942       fprintf(ps, "/%s {\n", validname);
6943 
6944    /* Write a "bbox" record if there is an element with style FIXEDBBOX	*/
6945    /* This is the only time a "bbox" record is written for an object.	*/
6946 
6947    for (savegen = localdata->plist; savegen < localdata->plist +
6948 		localdata->parts; savegen++) {
6949       if (ELEMENTTYPE(*savegen) == POLYGON) {
6950 	 if (TOPOLY(savegen)->style & FIXEDBBOX) {
6951 	    pointlist polypoints;
6952 	    int width, height;
6953 	    polypoints = TOPOLY(savegen)->points + 2;
6954 	    width = polypoints->x;
6955 	    height = polypoints->y;
6956 	    polypoints = TOPOLY(savegen)->points;
6957 	    width -= polypoints->x;
6958 	    height -= polypoints->y;
6959 	    fprintf(ps, "%% %d %d %d %d bbox\n",
6960 			polypoints->x, polypoints->y,
6961 			width, height);
6962 	    break;
6963 	 }
6964       }
6965    }
6966 
6967    if (localdata->hidden == True) fprintf(ps, "%% hidden\n");
6968 
6969    /* For symbols with schematics, and "trivial" schematics */
6970    if (localdata->symschem != NULL)
6971       fprintf(ps, "%% %s is_schematic\n", localdata->symschem->name);
6972    else if (localdata->schemtype == TRIVIAL)
6973       fprintf(ps, "%% trivial\n");
6974    else if (localdata->schemtype == NONETWORK)
6975       fprintf(ps, "%% nonetwork\n");
6976 
6977    printobjectparams(ps, localdata);
6978    fprintf(ps, "begingate\n");
6979 
6980    /* Write all the elements in order */
6981 
6982    opsubstitute(localdata, NULL);
6983    printOneObject(ps, localdata, curcolor);
6984 
6985    /* Write object (gate) trailer */
6986 
6987    fprintf(ps, "endgate\n} def\n\n");
6988 }
6989 
6990 /*--------------------------------------------------------------*/
6991 /* Print a page header followed by everything in the page.	*/
6992 /* this routine assumes that all objects used by the page have	*/
6993 /* already been handled and written to the output.		*/
6994 /*								*/
6995 /* "page" is the page number, counting consecutively from one.	*/
6996 /* "mpage" is the page number in xcircuit's pagelist structure.	*/
6997 /*--------------------------------------------------------------*/
6998 
6999 void printpageobject(FILE *ps, objectptr localdata, short page, short mpage)
7000 {
7001   /* genericptr *gptr; (jdk) */
7002    XPoint origin, corner;
7003    objinstptr writepage;
7004    int width, height;
7005    float psnorm, psscale;
7006    float xmargin, ymargin;
7007    char *rootptr = NULL;
7008    polyptr framebox;
7009 
7010    /* Output page header information */
7011 
7012    if (xobjs.pagelist[mpage]->filename)
7013       rootptr = strrchr(xobjs.pagelist[mpage]->filename, '/');
7014    if (rootptr == NULL)
7015       rootptr = xobjs.pagelist[mpage]->filename;
7016    else rootptr++;
7017 
7018    writepage = xobjs.pagelist[mpage]->pageinst;
7019 
7020    psnorm = xobjs.pagelist[mpage]->outscale;
7021    psscale = getpsscale(psnorm, mpage);
7022 
7023    /* Determine the margins (offset of drawing from page corner)	*/
7024    /* If a bounding box has been declared in the drawing, it is		*/
7025    /* centered on the page.  Otherwise, the drawing itself is		*/
7026    /* centered on the page.  If encapsulated, the bounding box		*/
7027    /* encompasses only the object itself.				*/
7028 
7029    width = toplevelwidth(writepage, &origin.x);
7030    height = toplevelheight(writepage, &origin.y);
7031 
7032    corner.x = origin.x + width;
7033    corner.y = origin.y + height;
7034 
7035    if (xobjs.pagelist[mpage]->pmode & 1) {	/* full page */
7036 
7037       if (xobjs.pagelist[mpage]->orient == 90) {
7038 	 xmargin = (xobjs.pagelist[mpage]->pagesize.x -
7039 		((float)height * psscale)) / 2;
7040 	 ymargin = (xobjs.pagelist[mpage]->pagesize.y -
7041 		((float)width * psscale)) / 2;
7042       }
7043       else {
7044          xmargin = (xobjs.pagelist[mpage]->pagesize.x -
7045 		((float)width * psscale)) / 2;
7046          ymargin = (xobjs.pagelist[mpage]->pagesize.y -
7047 		((float)height * psscale)) / 2;
7048       }
7049    }
7050    else {	/* encapsulated --- should have 1" border so that any */
7051 		/* drawing passed directly to a printer will not clip */
7052       xmargin = xobjs.pagelist[mpage]->margins.x;
7053       ymargin = xobjs.pagelist[mpage]->margins.y;
7054    }
7055 
7056    /* If a framebox is declared, then we adjust the page to be	*/
7057    /* centered on the framebox by translating through the	*/
7058    /* difference between the object center and the framebox	*/
7059    /* center.							*/
7060 
7061    if ((framebox = checkforbbox(localdata)) != NULL) {
7062       int i, fcentx = 0, fcenty = 0;
7063 
7064       for (i = 0; i < framebox->number; i++) {
7065 	 fcentx += framebox->points[i].x;
7066 	 fcenty += framebox->points[i].y;
7067       }
7068       fcentx /= framebox->number;
7069       fcenty /= framebox->number;
7070 
7071       xmargin += psscale * (float)(origin.x + (width >> 1) - fcentx);
7072       ymargin += psscale * (float)(origin.y + (height >> 1) - fcenty);
7073    }
7074 
7075    /* If the page label is just the root name of the file, or has been left	*/
7076    /* as "Page n" or "Page_n", just do the normal page numbering.  Otherwise,	*/
7077    /* write out the page label explicitly.					*/
7078 
7079    if ((rootptr == NULL) || (!strcmp(rootptr, localdata->name))
7080 		|| (strchr(localdata->name, ' ') != NULL)
7081 		|| (strstr(localdata->name, "Page_") != NULL))
7082       fprintf (ps, "%%%%Page: %d %d\n", page, page);
7083    else
7084       fprintf (ps, "%%%%Page: %s %d\n", localdata->name, page);
7085 
7086    if (xobjs.pagelist[mpage]->orient == 90)
7087       fprintf (ps, "%%%%PageOrientation: Landscape\n");
7088    else
7089       fprintf (ps, "%%%%PageOrientation: Portrait\n");
7090 
7091    if (xobjs.pagelist[mpage]->pmode & 1) { 	/* full page */
7092       fprintf(ps, "%%%%PageBoundingBox: 0 0 %d %d\n",
7093 		xobjs.pagelist[mpage]->pagesize.x,
7094 		xobjs.pagelist[mpage]->pagesize.y);
7095    }
7096 
7097    /* Encapsulated files do not get a PageBoundingBox line,	*/
7098    /* unless the bounding box was explicitly drawn.		*/
7099 
7100    else if (framebox != NULL) {
7101       fprintf(ps, "%%%%PageBoundingBox: %g %g %g %g\n",
7102 	xmargin, ymargin,
7103 	xmargin + psscale * (float)(width),
7104 	ymargin + psscale * (float)(height));
7105    }
7106 
7107    fprintf (ps, "/pgsave save def bop\n");
7108 
7109    /* Top-page definitions */
7110    if (localdata->params != NULL) {
7111       printobjectparams(ps, localdata);
7112       fprintf(ps, "begin\n");
7113    }
7114 
7115    if (localdata->symschem != NULL) {
7116       if (is_page(localdata->symschem) == -1)
7117          fprintf(ps, "%% %s is_symbol\n", localdata->symschem->name);
7118       else if (localdata->schemtype == SECONDARY)
7119          fprintf(ps, "%% %s is_primary\n", localdata->symschem->name);
7120       else
7121 	 Wprintf("Something is wrong. . . schematic \"%s\" is connected to"
7122 			" schematic \"%s\" but is not declared secondary.\n",
7123 			localdata->name, localdata->symschem->name);
7124    }
7125 
7126    /* Extend bounding box around schematic pins */
7127    extendschembbox(xobjs.pagelist[mpage]->pageinst, &origin, &corner);
7128 
7129    if (xobjs.pagelist[mpage]->drawingscale.x != 1
7130 		|| xobjs.pagelist[mpage]->drawingscale.y != 1)
7131       fprintf(ps, "%% %hd:%hd drawingscale\n", xobjs.pagelist[mpage]->drawingscale.x,
7132 	 	xobjs.pagelist[mpage]->drawingscale.y);
7133 
7134    if (xobjs.pagelist[mpage]->gridspace != 32
7135 		|| xobjs.pagelist[mpage]->snapspace != 16)
7136       fprintf(ps, "%% %4.2f %4.2f gridspace\n", xobjs.pagelist[mpage]->gridspace,
7137 		xobjs.pagelist[mpage]->snapspace);
7138 
7139    if (xobjs.pagelist[mpage]->background.name != (char *)NULL) {
7140      /* float iscale = (xobjs.pagelist[mpage]->coordstyle == CM) ? CMSCALE : INCHSCALE; (jdk) */
7141       if (xobjs.pagelist[mpage]->orient == 90)
7142 	 fprintf(ps, "%5.4f %d %d 90 psinsertion\n", psnorm,
7143 		  (int)(ymargin - xmargin),
7144 		  -((int)((float)(corner.y - origin.y) * psscale) +
7145 		  (int)(xmargin + ymargin)));
7146       else
7147 	 fprintf(ps, "%5.4f %d %d 0 psinsertion\n", psnorm,
7148 		(int)(xmargin / psscale) - origin.x,
7149 		(int)(ymargin / psscale) - origin.y);
7150       savebackground(ps, xobjs.pagelist[mpage]->background.name);
7151       fprintf(ps, "\nend_insert\n");
7152    }
7153 
7154    if (xobjs.pagelist[mpage]->orient == 90)
7155       fprintf(ps, "90 rotate %d %d translate\n", (int)(ymargin - xmargin),
7156 	     -((int)((float)(corner.y - origin.y) * psscale) +
7157 	     (int)(xmargin + ymargin)));
7158 
7159    fprintf(ps, "%5.4f ", psnorm);
7160    switch(xobjs.pagelist[mpage]->coordstyle) {
7161       case CM:
7162 	 fprintf(ps, "cmscale\n");
7163 	 break;
7164       default:
7165 	 fprintf(ps, "inchscale\n");
7166 	 break;
7167    }
7168 
7169    /* Final scale and translation */
7170    fprintf(ps, "%5.4f setlinewidth %d %d translate\n\n",
7171 		1.3 * xobjs.pagelist[mpage]->wirewidth,
7172 		(int)(xmargin / psscale) - origin.x,
7173 		(int)(ymargin / psscale) - origin.y);
7174 
7175    /* Output all the elements in the page */
7176    printOneObject(ps, localdata, DEFAULTCOLOR);
7177 
7178    /* Page trailer */
7179    if (localdata->params != NULL) fprintf(ps, "end ");
7180    fprintf(ps, "pgsave restore showpage\n");
7181 }
7182 
7183 /*--------------------------------------------------------------*/
7184 /* Print objects referenced from a particular page.  These get	*/
7185 /* bundled together at the beginning of the output file under	*/
7186 /* the DSC "Setup" section, so that the PostScript		*/
7187 /* interpreter knows that these definitions may be used by any	*/
7188 /* page.  This prevents ghostscript from producing an error	*/
7189 /* when backing up in a multi-page document.			*/
7190 /*--------------------------------------------------------------*/
7191 
7192 void printrefobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
7193 	short *written)
7194 {
7195    genericptr *gptr;
7196 
7197    /* If this page is a schematic, write out the definiton of any symbol */
7198    /* attached to it, because that symbol may not be used anywhere else. */
7199 
7200    if (localdata->symschem && (localdata->schemtype == PRIMARY))
7201       printobjects(ps, localdata->symschem, wrotelist, written, DEFAULTCOLOR);
7202 
7203    /* Search for all object definitions instantiated on the page and	*/
7204    /* write them to the output.						*/
7205 
7206    for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++)
7207       if (IS_OBJINST(*gptr))
7208          printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written,
7209 		DEFAULTCOLOR);
7210 }
7211 
7212 /*----------------------------------------------------------------------*/
7213