Continuing to track down the issue with a crash on startup on
[xcircuit.git] / files.c
blobb4f38df0c8fdd9f55154059933e7af4291bd37b1
1 /*-----------------------------------------------------------------------*/
2 /* files.c --- file handling routines for xcircuit */
3 /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */
4 /*-----------------------------------------------------------------------*/
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
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
33 /*------------------------------------------------------------------------*/
34 /* Local includes */
35 /*------------------------------------------------------------------------*/
37 #include "colordefs.h"
38 #include "xcircuit.h"
40 /*----------------------------------------------------------------------*/
41 /* Function prototype declarations */
42 /*----------------------------------------------------------------------*/
43 #include "prototypes.h"
45 #ifdef ASG
46 extern void Route(XCWindowData *, Boolean);
47 extern int ReadSpice(FILE *);
48 #endif
50 /*------------------------------------------------------------------------*/
51 /* Useful (local) defines */
52 /*------------------------------------------------------------------------*/
54 #define OUTPUTWIDTH 80 /* maximum text width of output */
56 #define S_OBLIQUE 13 /* position of Symbol-Oblique in font array */
58 /*------------------------------------------------------------------------*/
59 /* External Variable definitions */
60 /*------------------------------------------------------------------------*/
62 #ifdef TCL_WRAPPER
63 extern Tcl_Interp *xcinterp;
64 #endif
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;
80 /*------------------------------------------------------*/
81 /* Global variable definitions */
82 /*------------------------------------------------------*/
84 Boolean load_in_progress = False;
85 char version[20];
87 /* Structure for remembering what names refer to the same object */
89 aliasptr aliastop;
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 /*------------------------------------------------------*/
98 int compare_version(char *v1, char *v2)
100 int vers1, subvers1, vers2, subvers2;
102 sscanf(v1, "%d.%d", &vers1, &subvers1);
103 sscanf(v2, "%d.%d", &vers2, &subvers2);
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;
114 /*------------------------------------------------------*/
115 /* Simple utility---get rid of newline character */
116 /*------------------------------------------------------*/
118 char *ridnewline(char *sptr)
120 char *tstrp;
122 for (tstrp = sptr; *tstrp != '\0' && *tstrp != '\n'; tstrp++);
123 if (*tstrp == '\n') *tstrp = '\0';
124 return tstrp;
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 /*----------------------------------------------------------------------*/
133 #define PATHSEP '/'
135 int filecmp(char *filename1, char *filename2)
137 char *root1, *root2, *path1, *path2, *end1, *end2;
138 int rval;
139 struct stat statbuf;
140 ino_t inode1;
141 const char *cwdname = ".";
143 if (filename1 == NULL || filename2 == NULL) return 1;
145 if (!strcmp(filename1, filename2)) return 0; /* exact match */
147 root1 = strrchr(filename1, PATHSEP);
148 root2 = strrchr(filename2, PATHSEP);
150 if (root1 == NULL) {
151 path1 = (char *)cwdname;
152 end1 = NULL;
153 root1 = filename1;
155 else {
156 path1 = filename1;
157 end1 = root1;
158 root1++;
161 if (root2 == NULL) {
162 path2 = (char *)cwdname;
163 end2 = NULL;
164 root2 = filename2;
166 else {
167 path2 = filename2;
168 end2 = root2;
169 root2++;
172 if (strcmp(root1, root2)) return 1; /* root names don't match */
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. */
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;
190 if (end2 != NULL) *end2 = PATHSEP;
192 if (end1 != NULL) *end1 = PATHSEP;
193 return rval;
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 /*--------------------------------------------------------------*/
211 char *create_valid_psname(char *teststring, Boolean need_prefix)
213 int i, isize, ssize;
214 static char *optr = NULL;
215 char *sptr, *pptr;
216 Boolean prepend = need_prefix;
217 char illegalchars[] = {'/', '}', '{', ']', '[', ')', '(', '<', '>', ' ', '%'};
219 /* Check for illegal characters which have syntactical meaning in */
220 /* PostScript, and the presence of nonprintable characters or */
221 /* whitespace. */
223 ssize = strlen(teststring);
224 isize = ssize;
226 if (need_prefix && !strncmp(teststring, "p_", 2))
227 prepend = FALSE;
228 else
229 isize++;
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;
243 if (isize == ssize) return teststring;
244 isize++;
246 if (optr == NULL)
247 optr = (char *)malloc(isize);
248 else
249 optr = (char *)realloc(optr, isize);
251 pptr = optr;
253 if (prepend) *pptr++ = '@';
255 for (sptr = teststring; *sptr != '\0'; sptr++) {
256 if ((!isprint(*sptr)) || isspace(*sptr)) {
257 sprintf(pptr, "\\%03o", *sptr);
258 pptr += 4;
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;
268 if (i == sizeof(illegalchars))
269 *pptr++ = *sptr;
272 *pptr++ = '\0';
273 return optr;
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 /*------------------------------------------------------*/
299 int parse_ps_string(char *src, char *dest, int size, Boolean spacelegal, Boolean strip)
301 char *sptr = src;
302 char *tptr = dest;
303 int tmpdig, rval = 0;
305 /* Strip leading "@", inserted by XCircuit to */
306 /* prevent conflicts with PostScript reserved */
307 /* keywords or numbers. */
309 if (strip && (*sptr == '@')) sptr++;
311 for (;; sptr++) {
312 if ((*sptr == '\0') || (isspace(*sptr) && !spacelegal)) {
313 *tptr = '\0';
314 break;
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;
324 else
325 *tptr++ = *sptr;
327 else
328 *tptr++ = *sptr;
329 rval = 1;
331 if ((int)(tptr - dest) > size) {
332 Wprintf("Warning: Name \"%s\" in input exceeded buffer length!\n", src);
333 *tptr = '\0';
334 return rval;
337 return rval;
340 /*------------------------------------------------------*/
341 /* Free memory allocated to a label string */
342 /*------------------------------------------------------*/
344 void freelabel(stringpart *string)
346 stringpart *strptr = string, *tmpptr;
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;
357 /*------------------------------------------------------*/
358 /* Free memory for a single element */
359 /*------------------------------------------------------*/
361 void free_single(genericptr genobj)
363 objinstptr geninst;
364 oparamptr ops, fops;
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;
385 /* } */
386 free(ops->key);
387 fops = ops;
388 ops = ops->next;
389 free(fops);
392 free_all_eparams(genobj);
395 /*---------------------------------------------------------*/
396 /* Reset an object structure by freeing all alloc'd memory */
397 /*---------------------------------------------------------*/
399 void reset(objectptr localdata, short mode)
401 /* short i; (jdk) */
403 if (localdata->polygons != NULL || localdata->labels != NULL)
404 destroynets(localdata);
406 localdata->valid = False;
408 if (localdata->parts > 0) {
409 genericptr *genobj;
411 if (mode != SAVE) {
413 for (genobj = localdata->plist; genobj < localdata->plist
414 + localdata->parts; genobj++)
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. */
420 if (*genobj != NULL) {
421 free_single(*genobj);
422 free(*genobj);
425 free(localdata->plist);
427 removeparams(localdata);
429 initmem(localdata);
430 if (mode == DESTROY)
431 free(localdata->plist);
435 /*---------------------------------------------------------*/
437 void pagereset(short rpage)
439 /* free alloc'd filename */
441 if (xobjs.pagelist[rpage]->filename != NULL)
442 free(xobjs.pagelist[rpage]->filename);
443 xobjs.pagelist[rpage]->filename = (char *)NULL;
445 if (xobjs.pagelist[rpage]->background.name != NULL)
446 free(xobjs.pagelist[rpage]->background.name);
447 xobjs.pagelist[rpage]->background.name = (char *)NULL;
449 clearselects();
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. */
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;
468 if (xobjs.pagelist[rpage]->coordstyle == CM) {
469 xobjs.pagelist[rpage]->pagesize.x = 595;
470 xobjs.pagelist[rpage]->pagesize.y = 842; /* A4 */
472 else {
473 xobjs.pagelist[rpage]->pagesize.x = 612; /* letter */
474 xobjs.pagelist[rpage]->pagesize.y = 792;
478 /*---------------------------------------------------------*/
480 void initmem(objectptr localdata)
482 localdata->parts = 0;
483 localdata->plist = (genericptr *)malloc(sizeof(genericptr));
484 localdata->hidden = False;
485 localdata->changes = 0;
486 localdata->params = NULL;
488 localdata->viewscale = 0.5;
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;
495 localdata->bbox.width = 0;
496 localdata->bbox.height = 0;
497 localdata->bbox.lowerleft.x = 0;
498 localdata->bbox.lowerleft.y = 0;
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;
514 /*--------------------------------------------------------------*/
515 /* Exhaustively compare the contents of two objects and return */
516 /* true if equivalent, false if not. */
517 /*--------------------------------------------------------------*/
519 Boolean elemcompare(genericptr *compgen, genericptr *gchk)
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;
555 bres = (i == TOPOLY(compgen)->number);
557 else bres = False;
558 }break;
560 return bres;
563 /*--------------------------------------------------------------*/
564 /* Compare any element with any other element. */
565 /*--------------------------------------------------------------*/
567 Boolean compare_single(genericptr *compgen, genericptr *gchk)
569 Boolean bres = False;
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;
604 break;
605 case(ARC): case(SPLINE): case(POLYGON):
606 bres = elemcompare(compgen, gchk);
607 break;
610 return bres;
613 /*--------------------------------------------------------------------*/
615 short objcompare(objectptr obja, objectptr objb)
617 genericptr *compgen, *glist, *gchk, *remg;
618 short csize;
619 Boolean bres;
621 /* quick check on equivalence of number of objects */
623 if (obja->parts != objb->parts) return False;
625 /* check equivalence of parameters. Parameters need not be in any */
626 /* order; they must only match by key and value. */
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;
653 /* For the exhaustive check we must match component for component. */
654 /* Best not to assume that elements are in same order for both. */
656 csize = obja->parts;
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;
675 free(glist);
676 if (csize != 0) return False;
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. */
682 if (obja->symschem != NULL && objb->symschem != NULL)
683 if (obja->symschem != objb->symschem)
684 return False;
686 return(True);
689 /*------------------------*/
690 /* scale renormalization */
691 /*------------------------*/
693 float getpsscale(float value, short page)
695 if (xobjs.pagelist[page]->coordstyle != CM)
696 return (value * INCHSCALE);
697 else
698 return (value * CMSCALE);
701 /*---------------------------------------------------------------*/
702 /* Keep track of columns of output and split lines when too long */
703 /*---------------------------------------------------------------*/
705 void dostcount(FILE *ps, short *count, short addlength)
707 *count += addlength;
708 if (*count > OUTPUTWIDTH) {
709 *count = addlength;
710 fprintf(ps, "\n");
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 /*----------------------------------------------------------------------*/
720 Boolean varpcheck(FILE *ps, short value, objectptr localdata, int pointno,
721 short *stptr, genericptr thiselem, u_char which)
723 oparamptr ops;
724 eparamptr epp;
725 Boolean done = False;
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;
737 if (!done) {
738 if (pointno == -1) return done;
739 sprintf(_STR, "%d ", (int)value);
741 else if ((epp->pdata.pointno == -1) && (pointno >= 0)) {
742 sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
745 dostcount (ps, stptr, strlen(_STR));
746 fputs(_STR, ps);
747 return done;
750 /*----------------------------------------------------------------------*/
751 /* like varpcheck(), but without pointnumber */
752 /*----------------------------------------------------------------------*/
754 void varcheck(FILE *ps, short value, objectptr localdata,
755 short *stptr, genericptr thiselem, u_char which)
757 varpcheck(ps, value, localdata, 0, stptr, thiselem, which);
760 /*----------------------------------------------------------------------*/
761 /* like varcheck(), but for floating-point values */
762 /*----------------------------------------------------------------------*/
764 void varfcheck(FILE *ps, float value, objectptr localdata, short *stptr,
765 genericptr thiselem, u_char which)
767 oparamptr ops;
768 eparamptr epp;
769 Boolean done = False;
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;
780 if (!done)
781 sprintf(_STR, "%3.3f ", value);
783 dostcount (ps, stptr, strlen(_STR));
784 fputs(_STR, ps);
787 /*----------------------------------------------------------------------*/
788 /* Like varpcheck(), for path types only. */
789 /*----------------------------------------------------------------------*/
791 Boolean varpathcheck(FILE *ps, short value, objectptr localdata, int pointno,
792 short *stptr, genericptr *thiselem, pathptr thispath, u_char which)
794 oparamptr ops;
795 eparamptr epp;
796 Boolean done = False;
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;
810 if (!done) {
811 if (pointno == -1) return done;
812 sprintf(_STR, "%d ", (int)value);
814 else if ((epp->pdata.pathpt[0] == -1) && (pointno >= 0)) {
815 sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
817 dostcount (ps, stptr, strlen(_STR));
818 fputs(_STR, ps);
819 return done;
822 /* Structure used to hold data specific to each load mode. See */
823 /* xcircuit.h for the list of load modes (enum loadmodes) */
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;
831 /*-------------------------------------------------------*/
832 /* Load a PostScript or Python (interpreter script) file */
833 /*-------------------------------------------------------*/
835 void getfile(xcWidget button, pointertype mode, caddr_t nulldata)
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
855 buttonsave *savebutton = NULL;
856 char *promptstr = NULL;
857 /* char strext[10]; (jdk) */
858 int idx = (int)mode;
860 if (is_page(topobject) == -1) {
861 Wprintf("Can only read file into top-level page!");
862 return;
864 else if (idx >= LOAD_MODES) {
865 Wprintf("Unknown mode passed to routine getfile()\n");
866 return;
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);
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);
884 free(promptstr);
887 /*--------------------------------------------------------------*/
888 /* Tilde ('~') expansion in file name. Assumes that filename */
889 /* is a static character array of size "nchars". */
890 /*--------------------------------------------------------------*/
892 Boolean xc_tilde_expand(char *filename, int nchars)
894 #ifndef _MSC_VER
895 struct passwd *passwd;
896 char *username = NULL, *expanded, *sptr;
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';
907 passwd = getpwnam(filename + 1);
908 if (passwd != NULL)
909 username = passwd->pw_dir;
911 *sptr = '/';
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);
921 return True;
923 return False;
924 #else
925 return False;
926 #endif
929 /*--------------------------------------------------------------*/
930 /* Variable ('$') expansion in file name */
931 /*--------------------------------------------------------------*/
933 Boolean xc_variable_expand(char *filename, int nchars)
935 char *expanded, *sptr, tmpchar, *varpos, *varsub;
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';
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
951 if (varsub != NULL) {
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);
963 else
964 *sptr = tmpchar;
965 return True;
967 return False;
970 /*--------------------------------------------------------------*/
971 /* Attempt to find a file and open it. */
972 /*--------------------------------------------------------------*/
974 FILE *fileopen(char *filename, char *suffix, char *name_return, int nchars)
976 FILE *file = NULL;
977 char inname[250], expname[250], *sptr, *cptr, *iptr, *froot;
978 int slen;
980 sscanf(filename, "%249s", expname);
981 xc_tilde_expand(expname, 249);
982 while (xc_variable_expand(expname, 249));
984 sptr = xobjs.filesearchpath;
985 while (1) {
986 if ((xobjs.filesearchpath == NULL) || (expname[0] == '/')) {
987 strcpy(inname, expname);
988 iptr = inname;
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);
1000 /* Attempt to open the filename with a suffix */
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);
1009 file = fopen(inname, "r");
1012 /* Attempt to open the filename as given, without a suffix */
1014 if (file == NULL) {
1015 strcpy(iptr, expname);
1016 file = fopen(inname, "r");
1019 if (file != NULL) break;
1020 else if (sptr == NULL) break;
1021 else if (*sptr == '\0') break;
1024 if (name_return) strncpy(name_return, inname, nchars);
1025 return file;
1028 /*---------------------------------------------------------*/
1030 Boolean nextfilename() /* extract next filename from _STR2 into _STR */
1032 char *cptr, *slptr;
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;
1042 else return False;
1045 /*---------------------------------------------------------*/
1047 void loadfontlib()
1049 loadlibrary(FONTLIB);
1052 /*------------------------------------------------------*/
1053 /* Handle library loading and refresh current page if */
1054 /* it is a library page that just changed. */
1055 /*------------------------------------------------------*/
1057 void loadglib(Boolean lflag, short ilib, short tlib)
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); */
1067 if (lflag)
1068 lflag = False;
1069 else
1070 ilib = createlibrary(False);
1071 loadlibrary(ilib);
1072 /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
1075 /*------------------------------------------------------*/
1076 /* Load new library: Create new library page and load */
1077 /* to it. */
1078 /*------------------------------------------------------*/
1080 void loadulib()
1082 loadglib(False, (short)0, (short)is_library(topobject) + LIBRARY);
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 /*-----------------------------------------------------------*/
1090 void loadblib()
1092 short ilib, tlib;
1093 Boolean lflag = True;
1095 /* Flag whether current page is a library page or not */
1097 if ((tlib = is_library(topobject)) < 0) {
1098 ilib = LIBRARY;
1099 lflag = False;
1101 else
1102 ilib = tlib + LIBRARY;
1104 loadglib(lflag, ilib, tlib + LIBRARY);
1107 /*---------------------------------------------------------*/
1109 void getlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
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");
1119 /*---------------------------------------------------------*/
1121 void getuserlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
1123 buttonsave *savebutton;
1125 #ifndef TCL_WRAPPER
1126 savebutton = getgeneric(button, getuserlib, NULL);
1127 #endif
1128 popupprompt(button, "Enter library to load:", "\0", loadulib, savebutton,
1129 "lps");
1132 /*------------------------------------------------------*/
1133 /* Add a new name to the list of aliases for an object */
1134 /*------------------------------------------------------*/
1136 Boolean addalias(objectptr thisobj, char *newname)
1138 aliasptr aref;
1139 slistptr sref;
1140 /* Boolean retval = False; (jdk) */
1141 char *origname = thisobj->name;
1143 for (aref = aliastop; aref != NULL; aref = aref->next)
1144 if (aref->baseobj == thisobj)
1145 break;
1147 /* An equivalence, not an alias */
1148 if (!strcmp(origname, newname)) return True;
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;
1158 for (sref = aref->aliases; sref != NULL; sref = sref->next)
1159 if (!strcmp(sref->alias, newname))
1160 break;
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;
1169 else return True; /* alias already exists! */
1172 /*------------------------------------------------------*/
1173 /* Remove all object name aliases */
1174 /*------------------------------------------------------*/
1176 void cleanupaliases(short mode)
1178 aliasptr aref;
1179 slistptr sref;
1180 objectptr baseobj;
1181 char *sptr; /* *basename, (jdk) */
1182 int i, j;
1184 if (aliastop == NULL) return;
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);
1192 for (; (aref = aliastop->next); aliastop = aref)
1193 free(aliastop);
1194 free(aliastop);
1195 aliastop = NULL;
1197 /* Get rid of propagating underscores in names */
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);
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);
1214 /*------------------------------------------------------*/
1215 /* Open a library file by name and return a pointer to */
1216 /* the file structure, or NULL if an error occurred. */
1217 /*------------------------------------------------------*/
1219 FILE *libopen(char *libname, short mode, char *name_return, int nchars)
1221 FILE *file = NULL;
1222 char inname[150], expname[150], *sptr, *cptr, *iptr;
1223 int slen;
1224 char *suffix = (mode == FONTENCODING) ? ".xfe" : ".lps";
1226 sscanf(libname, "%149s", expname);
1227 xc_tilde_expand(expname, 149);
1228 while(xc_variable_expand(expname, 149));
1230 sptr = xobjs.libsearchpath;
1231 while (1) {
1233 if ((xobjs.libsearchpath == NULL) || (expname[0] == '/')) {
1234 strcpy(inname, expname);
1235 iptr = inname;
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);
1247 /* Try to open the filename with a suffix if it doesn't have one */
1249 if (strrchr(iptr, '.') == NULL) {
1250 strncat(inname, suffix, 149);
1251 file = fopen(inname, "r");
1254 /* Try to open the filename as given, without a suffix */
1256 if (file == NULL) {
1257 strcpy(iptr, expname);
1258 file = fopen(inname, "r");
1261 if (file != NULL) break;
1262 else if (sptr == NULL) break;
1263 else if (*sptr == '\0') break;
1266 if ((file == NULL) && (xobjs.libsearchpath == NULL)) {
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) */
1272 char *tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");
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");
1283 /* last resort: hard-coded directory BUILTINS_DIR */
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");
1295 if (name_return) strncpy(name_return, inname, nchars);
1296 return file;
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 /*--------------------------------------------------------------*/
1310 objinstptr new_library_instance(short mode, char *objname, char *buffer,
1311 TechPtr defaulttech)
1313 char *lineptr;
1314 objectptr libobj, localdata;
1315 objinstptr newobjinst;
1316 int j;
1317 char *nsptr, *fullname = objname;
1319 localdata = xobjs.libtop[mode + LIBRARY]->thisobject;
1321 /* For (older) libraries that do not use technologies, give the */
1322 /* object a technology name in the form <library>::<object> */
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);
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);
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);
1347 readparams(NULL, newobjinst, libobj, lineptr);
1348 if (fullname != objname) free(fullname);
1349 return newobjinst;
1352 if (fullname != objname) free(fullname);
1353 return NULL; /* error finding the library object */
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 /*------------------------------------------------------*/
1367 void importfromlibrary(short mode, char *libname, char *objname)
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;
1378 ps = libopen(libname, mode, inname, 149);
1379 if (ps == NULL) {
1380 Fprintf(stderr, "Cannot open library %s for import.\n", libname);
1381 return;
1384 strcpy(version, "2.0"); /* Assume version is 2.0 unless found in header */
1386 for(;;) {
1387 if (fgets(temp, 149, ps) == NULL) {
1388 Wprintf("Error in library.");
1389 goto endload;
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;
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);
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.
1426 if (!strcmp(inname, nsptr->filename))
1427 nsptr->flags |= TECH_IMPORTED;
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);
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;
1457 newobject = new_library_object(mode, keyword, &redef, nsptr);
1459 load_in_progress = True;
1460 if (objectread(ps, *newobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr) == False) {
1462 if (library_object_unique(mode, *newobject, redef)) {
1463 add_object_to_library(mode, *newobject);
1464 cleanupaliases(mode);
1466 /* pull in any instances of this object that */
1467 /* are defined in the library */
1469 for(;;) {
1470 if (fgets(temp, 149, ps) == NULL) {
1471 Wprintf("Error in library.");
1472 goto endload;
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);
1488 if (mode != FONTLIB) {
1489 composelib(mode);
1490 centerview(xobjs.libtop[mode]);
1495 endload:
1496 fclose(ps);
1497 strcpy(version, PROG_VERSION);
1498 load_in_progress = False;
1501 /*------------------------------------------------------*/
1502 /* Copy all technologies' replace flags into temp flag. */
1503 /* Then clear the replace flags (replace none) */
1504 /*------------------------------------------------------*/
1506 void TechReplaceSave()
1508 TechPtr nsp;
1510 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
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;
1520 /*------------------------------------------------------*/
1521 /* Restore all technologies' replace flags */
1522 /*------------------------------------------------------*/
1524 void TechReplaceRestore()
1526 TechPtr nsp;
1528 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1530 if (nsp->flags & TECH_REPLACE_TEMP)
1531 nsp->flags |= TECH_REPLACE;
1532 else
1533 nsp->flags &= ~TECH_REPLACE;
1537 /*------------------------------------------------------*/
1538 /* Set all technologies' replace flags (replace all) */
1539 /*------------------------------------------------------*/
1541 void TechReplaceAll()
1543 TechPtr nsp;
1545 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1546 nsp->flags |= TECH_REPLACE;
1549 /*------------------------------------------------------*/
1550 /* Clear all technologies' replace flags (replace none) */
1551 /*------------------------------------------------------*/
1553 void TechReplaceNone()
1555 TechPtr nsp;
1557 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1558 nsp->flags &= ~TECH_REPLACE;
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 /*------------------------------------------------------*/
1571 Boolean CompareTechnology(objectptr cobj, char *technology)
1573 char *cptr;
1574 Boolean result = FALSE;
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 = ':';
1585 else if (technology == NULL)
1586 result = TRUE;
1588 return result;
1591 /*------------------------------------------------------*/
1592 /* Find a technology record */
1593 /*------------------------------------------------------*/
1595 TechPtr LookupTechnology(char *technology)
1597 TechPtr nsp;
1598 Boolean usertech = FALSE;
1600 // A NULL technology is allowed as equivalent to a
1601 // technology name of "" (null string)
1603 if (technology == NULL)
1604 usertech = TRUE;
1605 else if (*technology == '\0')
1606 usertech = TRUE;
1607 else if (!strcmp(technology, "(user)"))
1608 usertech = TRUE;
1610 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1611 if (usertech == TRUE) {
1612 if (*nsp->technology == '\0')
1613 return nsp;
1615 if ((technology != NULL) && !strcmp(technology, nsp->technology))
1616 return nsp;
1618 return NULL;
1621 /*------------------------------------------------------*/
1622 /* Find a technology according to the filename */
1623 /*------------------------------------------------------*/
1625 TechPtr GetFilenameTechnology(char *filename)
1627 TechPtr nsp;
1629 if (filename == NULL) return NULL;
1631 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1632 if (!filecmp(filename, nsp->filename))
1633 return nsp;
1635 return NULL;
1638 /*------------------------------------------------------*/
1639 /* Find a technology record corresponding to the */
1640 /* indicated object's technology. */
1641 /*------------------------------------------------------*/
1643 TechPtr GetObjectTechnology(objectptr thisobj)
1645 TechPtr nsp;
1646 char *cptr;
1647 /* int nlen; (jdk) */
1649 cptr = strstr(thisobj->name, "::");
1650 if (cptr == NULL) return NULL;
1651 else *cptr = '\0';
1653 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1654 if (!strcmp(thisobj->name, nsp->technology))
1655 break;
1657 *cptr = ':';
1658 return nsp;
1661 /*------------------------------------------------------*/
1662 /* Add a new technology name to the list */
1663 /*------------------------------------------------------*/
1665 TechPtr AddNewTechnology(char *technology, char *filename)
1667 TechPtr nsp;
1668 char usertech[] = "";
1669 char *localtech = technology;
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.
1677 if (technology == NULL) {
1678 if (filename == NULL) return NULL;
1679 else localtech = usertech;
1682 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1683 if (!strcmp(localtech, nsp->technology)) {
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. */
1690 if ((nsp->filename == NULL) && (filename != NULL))
1691 nsp->filename = strdup(filename);
1693 return nsp; /* Namespace already exists */
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;
1707 return nsp;
1710 /*------------------------------------------------------*/
1711 /* Check an object's name for a technology, and add it */
1712 /* to the list of technologies if it has one. */
1713 /*------------------------------------------------------*/
1715 void AddObjectTechnology(objectptr thisobj)
1717 char *cptr;
1719 cptr = strstr(thisobj->name, "::");
1720 if (cptr != NULL) {
1721 *cptr = '\0';
1722 AddNewTechnology(thisobj->name, NULL);
1723 *cptr = ':';
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 /*------------------------------------------------------*/
1733 Boolean loadlibrary(short mode)
1735 FILE *ps;
1736 objinstptr saveinst;
1737 char temp[150], keyword[30], percentc, inname[150];
1738 TechPtr nsptr = NULL;
1740 ps = libopen(_STR, mode, inname, 149);
1742 if ((ps == NULL) && (mode == FONTLIB)) {
1743 /* We automatically try looking in all the usual places plus a */
1744 /* subdirectory named "fonts". */
1746 sprintf(temp, "fonts/%s", _STR);
1747 ps = libopen(temp, mode, inname, 149);
1749 if (ps == NULL) {
1750 Wprintf("Library not found.");
1751 return False;
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. */
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;
1765 sscanf(temp, "%c %29s", &percentc, keyword);
1767 /* Commands in header are PostScript comments (%) */
1768 if (percentc == '%') {
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. */
1775 /* Save the technology, filename, and flags in the technology list. */
1777 if ((mode != FONTLIB) && !strcmp(keyword, "Library")) {
1778 char *cptr, *nptr;
1779 cptr = strchr(temp, ':');
1780 if (cptr != NULL) {
1781 cptr += 2;
1783 /* Don't write terminating newline to the object's name string */
1784 ridnewline(cptr);
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;
1791 /* Removing any leading pathname from the library name */
1792 if ((nptr = strrchr(cptr, '/')) != NULL) cptr = nptr + 1;
1794 /* Remove any ".lps" extension from the library name */
1796 nptr = strrchr(cptr, '.');
1797 if ((nptr != NULL) && !strcmp(nptr, ".lps")) *nptr = '\0';
1799 nsptr = AddNewTechnology(cptr, inname);
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;
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);
1816 /* This PostScript comment marks the end of the file header */
1817 else if (!strcmp(keyword, "XCircuitLib")) break;
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. */
1825 saveinst = areawin->topinstance;
1826 areawin->topinstance = xobjs.libtop[mode];
1828 load_in_progress = True;
1829 objectread(ps, topobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr);
1830 load_in_progress = False;
1831 cleanupaliases(mode);
1833 areawin->topinstance = saveinst;
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);
1845 else
1846 Wprintf("Loaded font file %s", inname);
1848 strcpy(version, PROG_VERSION);
1849 fclose(ps);
1851 /* Check if the library is read-only by opening for append */
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);
1861 return True;
1864 /*---------------------------------------------------------*/
1866 void startloadfile(int libnum)
1868 int savemode;
1869 short firstpage = areawin->page;
1871 while (nextfilename()) {
1872 loadfile(0, libnum);
1874 /* find next undefined page */
1876 while(areawin->page < xobjs.pages &&
1877 xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
1878 changepage(areawin->page);
1880 loadfile(0, libnum);
1883 /* Prevent page change from being registered as an undoable action */
1884 savemode = eventmode;
1885 eventmode = UNDO_MODE;
1887 /* Display the first page loaded */
1888 newpage(firstpage);
1889 eventmode = savemode;
1891 setsymschem();
1894 /*------------------------------------------------------*/
1895 /* normalloadfile() is a call to startloadfile(-1) */
1896 /* meaning load symbols to the User Library */
1897 /*------------------------------------------------------*/
1899 void normalloadfile()
1901 startloadfile(-1);
1904 /*------------------------------------------------------*/
1905 /* Import an xcircuit file onto the current page */
1906 /*------------------------------------------------------*/
1908 void importfile()
1910 while (nextfilename()) loadfile(1, -1);
1911 loadfile(1, -1);
1914 /*------------------------------------------------------*/
1915 /* Import an PPM graphic file onto the current page */
1916 /*------------------------------------------------------*/
1918 #ifdef HAVE_CAIRO
1919 void importgraphic(void)
1921 char inname[250];
1922 FILE *spcfile;
1924 if (eventmode == CATALOG_MODE) {
1925 Wprintf("Cannot import a graphic while in the library window.");
1926 return;
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;
1937 else {
1938 Wprintf("Error: No graphic file to read.");
1939 return;
1942 #endif /* HAVE_CAIRO */
1944 /*--------------------------------------------------------------*/
1945 /* Skip forward in the input file to the next comment line */
1946 /*--------------------------------------------------------------*/
1948 void skiptocomment(char *temp, int length, FILE *ps)
1950 int pch;
1952 do {
1953 pch = getc(ps);
1954 } while (pch == '\n');
1956 ungetc(pch, ps);
1957 if (pch == '%') fgets(temp, length, ps);
1960 /*--------------------------------------------------------------*/
1961 /* ASG file import functions: */
1962 /* This function loads a SPICE deck (hspice format) */
1963 /*--------------------------------------------------------------*/
1965 #ifdef ASG
1967 void importspice()
1969 char inname[250];
1970 FILE *spcfile;
1972 if (eventmode == CATALOG_MODE) {
1973 Wprintf("Cannot import a netlist while in the library window.");
1974 return;
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);
1986 else {
1987 Wprintf("Error: Spice file not found.");
1988 return;
1991 else {
1992 Wprintf("Error: No spice file to read.");
1993 return;
1997 #endif
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 /*--------------------------------------------------------------*/
2016 typedef struct _connects *connectptr;
2018 typedef struct _connects {
2019 short page;
2020 char *master;
2021 connectptr next;
2022 } connects;
2024 Boolean loadfile(short mode, int libnum)
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;
2036 /* First, if we're in catalog mode, return with error */
2038 if (eventmode == CATALOG_MODE) {
2039 Wprintf("Cannot load file from library window");
2040 return False;
2043 /* Do tilde/variable expansions on filename and open */
2044 ps = fileopen(_STR, "ps", inname, 149);
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.) */
2052 if (ps == NULL) {
2053 ps = fileopen(_STR, "lps", NULL, 0);
2054 if (ps != NULL) {
2055 fclose(ps);
2056 loadlibrary(loclibnum);
2057 return True;
2060 else if (!strcmp(inname + strlen(inname) - 4, ".lps")) {
2061 fclose(ps);
2062 loadlibrary(loclibnum);
2063 return True;
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;
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;
2087 #endif /* LGF */
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;
2097 /* What to do if no file was found. . . */
2099 if (ps == NULL) {
2100 if (topobject->parts == 0 && (mode == 0)) {
2102 /* Check for file extension, and remove if "ps". */
2104 if ((pdchar = strchr(_STR, '.')) != NULL)
2105 if (!strcmp(pdchar + 1, "ps")) *pdchar = '\0';
2107 free(xobjs.pagelist[areawin->page]->filename);
2108 xobjs.pagelist[areawin->page]->filename = strdup(_STR);
2110 /* If the name has a path component, use only the root */
2111 /* for the object name, but the full path for the filename. */
2113 if ((pdchar = strrchr(_STR, '/')) != NULL)
2114 sprintf(topobject->name, "%s", pdchar + 1);
2115 else
2116 sprintf(topobject->name, "%s", _STR);
2118 renamepage(areawin->page);
2119 printname(topobject);
2120 Wprintf("Starting new drawing");
2122 else {
2123 Wprintf("Can't find file %s, won't overwrite current page", _STR);
2125 return False;
2128 strcpy(version, "1.0");
2129 multipage = 1;
2130 pagesize.x = 612;
2131 pagesize.y = 792;
2133 for(;;) {
2134 if (fgets(temp, 149, ps) == NULL) {
2135 Wprintf("Error: EOF in or before prolog.");
2136 return False;
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;
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);
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);
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;
2171 #ifdef LGF
2172 else if (percentc == '-' && !strcmp(keyword, "5")) {
2173 fclose(ps);
2174 loadlgf(mode);
2175 return True;
2177 #endif
2180 /* Look for old-style files (no %%Page; maximum one page in file) */
2182 if (!strcmp(pdchar, "XCircuit"))
2183 skiptocomment(temp, 149, ps);
2185 for (page = 0; page < multipage; page++) {
2186 sprintf(pagestr, "%d", page + 1);
2188 /* read out-of-page library definitions */
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;
2196 if (strstr(temp, "%%Page:") != NULL) {
2197 sscanf(temp + 8, "%99s", pagestr);
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;
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;
2214 skiptocomment(temp, 149, ps);
2215 continue;
2219 /* go to new page if necessary */
2221 if (page > 0) {
2223 /* find next undefined page */
2225 while(areawin->page < xobjs.pages &&
2226 xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
2227 changepage(areawin->page);
2230 /* If this file was a library file then there is no page to load */
2232 if (strstr(temp, "EndLib") != NULL) {
2233 composelib(loclibnum);
2234 centerview(xobjs.libtop[mode]);
2235 Wprintf("Loaded library.");
2236 return True;
2239 /* good so far; let's clear out the old data structure */
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;
2250 else {
2251 invalidate_netlist(topobject);
2252 /* ensure that the netlist for topobject is destroyed */
2253 freenetlist(topobject);
2256 /* clear the undo record */
2257 flush_undo_stack();
2259 /* read to the "scale" line, picking up inch/cm type, drawing */
2260 /* scale, and grid/snapspace along the way */
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;
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);
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);
2284 else if (strstr(temp, "hidden") != NULL)
2285 topobject->hidden = True;
2287 else if (strstr(temp, "is_symbol") != NULL) {
2288 sscanf(temp, "%*c %49s", teststr);
2289 checkschem(topobject, teststr);
2291 else if (strstr(temp, "is_primary") != NULL) {
2292 /* objectptr master; (jdk) */
2293 connects *newconn;
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);
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 */
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;
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;
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;
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;
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;
2364 else if (strstr(temp, "insertion") != NULL) {
2365 /* read in an included background image */
2366 readbackground(ps);
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);
2376 else
2377 buffer = (char *)realloc(buffer, strlen(buffer) + 150);
2378 endptr = ridnewline(buffer);
2379 *endptr++ = ' ';
2380 fgets(endptr, 149, ps);
2382 /* read top-level parameter dictionary */
2383 readparams(NULL, NULL, topobject, buffer);
2384 if (buffer != temp) free(buffer);
2387 if (fgets(temp, 149, ps) == NULL) {
2388 Wprintf("Error: Problems encountered in page header.");
2389 return False;
2393 load_in_progress = True;
2394 objectread(ps, topobject, offx, offy, LIBRARY, temp, DEFAULTCOLOR, NULL);
2395 load_in_progress = False;
2397 /* skip to next page boundary or file trailer */
2399 if (strstr(temp, "showpage") != NULL && multipage != 1) {
2400 char *fstop;
2402 skiptocomment(temp, 149, ps);
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);
2413 /* Finally: set the filename and pagename for this page */
2415 if (mode == 0) {
2416 char tpstr[6], *rootptr;
2418 /* Set filename and page title. */
2420 if (xobjs.pagelist[areawin->page]->filename != NULL)
2421 free(xobjs.pagelist[areawin->page]->filename);
2422 xobjs.pagelist[areawin->page]->filename = strdup(inname);
2424 /* Get the root name (after all path components) */
2426 rootptr = strrchr(xobjs.pagelist[areawin->page]->filename, '/');
2427 if (rootptr == NULL) rootptr = xobjs.pagelist[areawin->page]->filename;
2428 else rootptr++;
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. */
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);
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';
2446 else
2447 sprintf(topobject->name, "%.79s", pagestr);
2449 renamepage(areawin->page);
2452 /* set object position to fit to window separately for each page */
2453 calcbbox(areawin->topinstance);
2454 centerview(areawin->topinstance);
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;
2465 cleanupaliases(USERLIB);
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;
2475 else {
2476 Fprintf(stderr, "Error: Cannot find primary schematic for %s\n",
2477 xobjs.pagelist[thisconn->page]->pageinst->thisobject->name);
2479 connlist = thisconn->next;
2480 free(thisconn->master);
2481 free(thisconn);
2484 Wprintf("Loaded file: %s (%d page%s)", inname, multipage,
2485 (multipage > 1 ? "s" : ""));
2487 composelib(loclibnum);
2488 centerview(xobjs.libtop[loclibnum]);
2489 composelib(PAGELIB);
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);
2496 strcpy(version, PROG_VERSION);
2497 fclose(ps);
2498 return True;
2501 /*------------------------------------------------------*/
2502 /* Object name comparison: True if names are equal, */
2503 /* not counting leading underscores. */
2504 /*------------------------------------------------------*/
2506 int objnamecmp(char *name1, char *name2)
2508 char *n1ptr = name1;
2509 char *n2ptr = name2;
2511 while (*n1ptr == '_') n1ptr++;
2512 while (*n2ptr == '_') n2ptr++;
2514 return (strcmp(n1ptr, n2ptr));
2517 /*------------------------------------------------------*/
2518 /* Standard delimiter matching character. */
2519 /*------------------------------------------------------*/
2521 char standard_delimiter_end(char source)
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;
2531 return target;
2534 /*------------------------------------------------------*/
2535 /* Find matching parenthesis, bracket, brace, or tag */
2536 /* Don't count when backslash character '\' is in front */
2537 /*------------------------------------------------------*/
2539 u_char *find_delimiter(u_char *fstring)
2541 int count = 1;
2543 u_char *search = fstring;
2544 u_char source = *fstring;
2545 u_char target;
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;
2553 return search;
2556 /*----------------------------------------------------------------------*/
2557 /* Remove unnecessary font change information from a label */
2558 /*----------------------------------------------------------------------*/
2560 void cleanuplabel(stringpart **strhead)
2562 stringpart *curpart = *strhead;
2563 int oldfont, curfont;
2564 Boolean fline = False;
2566 oldfont = curfont = -1;
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);
2580 else {
2581 curfont = curpart->data.font;
2583 break;
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;
2592 /* A font change may occur inside a parameter, so any font */
2593 /* declaration after a parameter must be considered to be */
2594 /* intentional. */
2596 case PARAM_END:
2597 curfont = oldfont = -1;
2598 break;
2600 case OVERLINE: case UNDERLINE:
2601 fline = True;
2602 break;
2604 case NOLINE:
2605 fline = False;
2606 break;
2608 case NORMALSCRIPT: case RETURN:
2609 if (oldfont != -1) {
2610 curfont = oldfont;
2611 oldfont = -1;
2613 break;
2615 case SUBSCRIPT: case SUPERSCRIPT:
2616 if (oldfont == -1)
2617 oldfont = curfont;
2618 break;
2620 if (curpart != NULL)
2621 curpart = curpart->nextpart;
2625 /*----------------------------------------------------------------------*/
2626 /* Read label segments */
2627 /*----------------------------------------------------------------------*/
2629 void readlabel(objectptr localdata, char *lineptr, stringpart **strhead)
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;
2639 while (*segptr != '\0') { /* Look through all segments */
2641 while (isspace(*segptr) && (*segptr != '\0')) segptr++;
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;
2652 else if (*segptr == '\0' || *segptr == '}') break;
2654 makesegment(strhead, *strhead);
2655 newpart = *strhead;
2657 /* Embedded command is in braces: {} */
2659 if (*segptr == '{') {
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++;
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;
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;
2692 else if (!strncmp(cmdptr, "ul", 2)) {
2693 newpart->type = UNDERLINE;
2694 fline = True;
2696 else if (!strncmp(cmdptr, "sce", 3)) { /* revert to default color */
2697 newpart->type = FONT_COLOR;
2698 newpart->data.color = DEFAULTCOLOR;
2700 else if (cmdptr == segptr) { /* cancel over- or underline */
2701 newpart->type = NOLINE;
2702 fline = False;
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");
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;
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;
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;
2730 else if (!strcmp(cmdptr, "cf")) { /* change font or scale command */
2731 char *nextptr, *newptr = segptr;
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 */
2739 for (nextptr = newptr; !isspace(*nextptr); nextptr++);
2740 *(nextptr++) = '\0';
2741 while (isspace(*nextptr)) nextptr++;
2743 for (j = 0; j < fontcount; j++)
2744 if (!strcmp(newptr, fonts[j].psname))
2745 break;
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;
2753 else {
2754 Wprintf("Error: No fonts!");
2755 newpart->type = NOLINE; /* placeholder */
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;
2767 newpart->type = FONT_NAME;
2768 newpart->data.font = j;
2770 else { /* This exec isn't a known label function */
2771 Wprintf("Error: unknown substring function");
2772 newpart->type = NOLINE; /* placeholder */
2776 /* Text substring is in parentheses: () */
2778 else if (*segptr == '(') {
2779 if (fline == True) {
2780 newpart->type = NOLINE;
2781 makesegment(strhead, *strhead);
2782 newpart = *strhead;
2783 fline = False;
2785 newpart->type = TEXT_STRING;
2786 newpart->data.string = (u_char *)malloc(1 + strlen(++segptr));
2788 /* Copy string, translating octal codes into 8-bit characters */
2789 parse_ps_string(segptr, newpart->data.string, strlen(segptr), TRUE, TRUE);
2792 /* Parameterized substrings are denoted by parameter key. */
2793 /* The parameter (default and/or substitution value) is */
2794 /* assumed to exist. */
2796 else {
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);
2804 /* check for compatibility between the parameter value and */
2805 /* the number of parameters and parameter type. */
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);
2813 /* Fprintf(stdout, "Parameter %s called from object %s\n", */
2814 /* key, localdata->name); */
2816 endptr = segptr + 1;
2817 while (!isspace(*endptr) && (*endptr != '\0')) endptr++;
2819 segptr = endptr;
2823 /*--------------------------------------*/
2824 /* skip over whitespace in string input */
2825 /*--------------------------------------*/
2827 char *skipwhitespace(char *lineptr)
2829 char *locptr = lineptr;
2831 while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2832 return locptr;
2835 /*-------------------------------------------*/
2836 /* advance to the next token in string input */
2837 /*-------------------------------------------*/
2839 char *advancetoken(char *lineptr)
2841 char *locptr = lineptr;
2843 while (!isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2844 while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2845 return locptr;
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 /*------------------------------------------------------*/
2855 void readparams(objectptr localdata, objinstptr newinst, objectptr libobj,
2856 char *buffer)
2858 oparamptr newops, objops, fops;
2859 char *arrayptr, *endptr, *arraynext;
2860 int paramno = 0;
2861 char paramkey[100];
2863 if ((arrayptr = strstr(buffer, "<<")) == NULL)
2864 if ((arrayptr = strchr(buffer, '[')) == NULL)
2865 return;
2867 endptr = find_delimiter(arrayptr);
2868 if (*arrayptr == '<') {
2869 arrayptr++; /* move to second '<' in "<<" */
2870 endptr--; /* back up to first '>' in ">>" */
2873 /* move to next non-space token after opening bracket */
2874 arrayptr++;
2875 while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++;
2877 while ((*arrayptr != '\0') && (arrayptr < endptr)) {
2879 newops = (oparamptr)malloc(sizeof(oparam));
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");
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);
2892 else { /* array type; keys are "v1", "v2", etc. */
2893 paramno++;
2894 newops->key = (char *)malloc(6);
2895 sprintf(newops->key, "v%d", paramno);
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;
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) {
2915 /* Delete any parameters with duplicate names. This */
2916 /* This may indicate an expression parameter that was */
2917 /* precomputed while determining the bounding box. */
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;
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;
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;
2940 /* Fill in "which" entry from the object default */
2941 newops->which = (newinst) ? objops->which : 0;
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). */
2951 if (*arrayptr == '(' || *arrayptr == '{')
2952 arraynext = find_delimiter(arrayptr);
2953 else
2954 arraynext = arrayptr;
2955 arraynext = advancetoken(arraynext);
2957 if ((*endptr == '>') && (arraynext < endptr) && (*arraynext != '/')) {
2958 char *substrend, *arraysave;
2960 if (*arraynext == '(' || *arraynext == '{') {
2962 substrend = find_delimiter(arraynext);
2963 arraysave = arraynext + 1;
2964 arraynext = advancetoken(substrend);
2966 newops->type = (u_char)XC_EXPR;
2967 newops->which = P_EXPRESSION; /* placeholder */
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);
2985 else if (*arrayptr == '(' || *arrayptr == '{') {
2986 float r, g, b;
2987 char *substrend, csave;
2988 stringpart *endpart;
2990 /* type XC_STRING */
2992 substrend = find_delimiter(arrayptr);
2993 csave = *(++substrend);
2994 *substrend = '\0';
2995 if (*arrayptr == '{') arrayptr++;
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;
3006 else {
3007 char *arraytmp = arrayptr;
3008 char linkdefault[5] = "(%n)";
3010 newops->type = (u_char)XC_STRING;
3011 newops->which = P_SUBSTRING;
3012 newops->parameter.string = NULL;
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;
3020 readlabel(libobj, arraytmp, &(newops->parameter.string));
3021 *substrend = csave;
3023 /* Append a PARAM_END to the parameter string */
3025 endpart = makesegment(&(newops->parameter.string), NULL);
3026 endpart->type = PARAM_END;
3027 endpart->data.string = (u_char *)NULL;
3029 arrayptr = substrend;
3030 while (isspace(*arrayptr) && *arrayptr != '\0')
3031 arrayptr++;
3033 else {
3034 /* char *token; (jdk) */
3035 int scanned = 0;
3037 /* type XC_FLOAT or XC_INT, or an indirect reference */
3039 newops->type = (newinst) ? objops->type : (u_char)XC_FLOAT;
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); */
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); */
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 */
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;
3072 else {
3073 Fprintf(stderr, "Error: unknown parameter type!\n");
3076 if (scanned == 0) {
3077 /* Indirect reference --- create an eparam in the instance */
3078 parse_ps_string(arrayptr, paramkey, 99, FALSE, TRUE);
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);
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);
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;
3100 arrayptr = advancetoken(arrayptr);
3104 /* Calculate the unique bounding box for the instance */
3106 if (newinst && (newinst->params != NULL)) {
3107 opsubstitute(libobj, newinst);
3108 calcbboxinst(newinst);
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 /*--------------------------------------------------------------*/
3119 char *varpscan(objectptr localdata, char *lineptr, short *hvalue,
3120 genericptr thiselem, int pointno, int offset, u_char which)
3122 oparamptr ops = NULL;
3123 char key[100];
3124 /* char *nexttok; (jdk) */
3125 eparamptr newepp;
3127 if (sscanf(lineptr, "%hd", hvalue) != 1) {
3128 parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3130 ops = match_param(localdata, key);
3131 newepp = make_new_eparam(key);
3133 /* Add parameter to the linked list */
3134 newepp->next = thiselem->passed;
3135 thiselem->passed = newepp;
3136 newepp->pdata.pointno = pointno;
3138 if (ops != NULL) {
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. */
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));
3150 ops->which = which;
3151 *hvalue = (short)ops->parameter.ivalue;
3153 else {
3154 *hvalue = 0; /* okay; will get filled in later */
3155 Fprintf(stderr, "Error: parameter %s was used but not defined!\n", key);
3159 *hvalue -= (short)offset;
3161 return advancetoken(skipwhitespace(lineptr));
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 /*--------------------------------------------------------------*/
3169 char *varscan(objectptr localdata, char *lineptr, short *hvalue,
3170 genericptr thiselem, u_char which)
3172 return varpscan(localdata, lineptr, hvalue, thiselem, 0, 0, which);
3175 /*--------------------------------------------------------------*/
3176 /* Read a value which might be a float or a parameter. */
3177 /* Return the position to the next token in "lineptr". */
3178 /*--------------------------------------------------------------*/
3180 char *varfscan(objectptr localdata, char *lineptr, float *fvalue,
3181 genericptr thiselem, u_char which)
3183 oparamptr ops = NULL;
3184 eparamptr newepp;
3185 char key[100];
3187 if (sscanf(lineptr, "%f", fvalue) != 1) {
3188 parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3190 /* This bit of a hack takes care of scale-variant */
3191 /* linewidth specifiers for object instances. */
3193 if (!strncmp(key, "/sv", 3)) {
3194 ((objinstptr)thiselem)->style &= ~LINE_INVARIANT;
3195 return varfscan(localdata, advancetoken(skipwhitespace(lineptr)),
3196 fvalue, thiselem, which);
3199 ops = match_param(localdata, key);
3200 newepp = make_new_eparam(key);
3202 /* Add parameter to the linked list */
3203 newepp->next = thiselem->passed;
3204 thiselem->passed = newepp;
3206 if (ops != NULL) {
3207 ops->which = which;
3208 *fvalue = ops->parameter.fvalue;
3210 else
3211 Fprintf(stderr, "Error: no parameter \"%s\" defined!\n", key);
3214 /* advance to next token */
3215 return advancetoken(skipwhitespace(lineptr));
3218 /*--------------------------------------------------------------*/
3219 /* Same as varpscan(), but for path types only. */
3220 /*--------------------------------------------------------------*/
3222 char *varpathscan(objectptr localdata, char *lineptr, short *hvalue,
3223 genericptr *thiselem, pathptr thispath, int pointno, int offset,
3224 u_char which, eparamptr *nepptr)
3226 oparamptr ops = NULL;
3227 char key[100];
3228 eparamptr newepp;
3230 if (nepptr != NULL) *nepptr = NULL;
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;
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;
3250 if (nepptr != NULL) *nepptr = newepp;
3252 /* Add parameter to the linked list. */
3254 newepp->next = thispath->passed;
3255 thispath->passed = newepp;
3257 if (ops != NULL) {
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. */
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));
3269 ops->which = which;
3270 *hvalue = (short)ops->parameter.ivalue;
3272 else {
3273 *hvalue = 0; /* okay; will get filled in later */
3274 Fprintf(stderr, "Error: parameter %s was used but not defined!\n", key);
3278 pathdone:
3279 *hvalue -= (short)offset;
3280 return advancetoken(skipwhitespace(lineptr));
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 /*--------------------------------------------------------------*/
3289 objinstptr addtoinstlist(int libnum, objectptr libobj, Boolean virtual)
3291 objinstptr newinst = (objinstptr) malloc(sizeof(objinst));
3292 liblistptr spec = (liblistptr) malloc(sizeof(liblist));
3293 liblistptr srch;
3295 newinst->type = OBJINST;
3296 instancedefaults(newinst, libobj, 0, 0);
3298 spec->virtual = (u_char)virtual;
3299 spec->thisinst = newinst;
3300 spec->next = NULL;
3302 /* Add to end, so that duplicate, parameterized instances */
3303 /* always come after the original instance with the default */
3304 /* parameters. */
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;
3313 /* Calculate the instance-specific bounding box */
3314 calcbboxinst(newinst);
3316 return newinst;
3319 /*--------------------------------------------------------------*/
3320 /* Deal with object reads: Create a new object and prepare for */
3321 /* reading. The library number is passed as "mode". */
3322 /*--------------------------------------------------------------*/
3324 objectptr *new_library_object(short mode, char *name, objlistptr *retlist,
3325 TechPtr defaulttech)
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;
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;
3341 /* For (older) libraries that do not use technologies, give the */
3342 /* object a technology name in the form <library>::<object> */
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);
3353 /* initial 1-pointer allocations */
3355 newobject = curlib + (*libobjects);
3356 *newobject = (objectptr) malloc(sizeof(object));
3357 initmem(*newobject);
3359 /* check that this object is not already in list of objects */
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;
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;
3390 (*libobjects)++;
3391 sprintf((*newobject)->name, "%s", fullname);
3392 if (fullname != name) free(fullname);
3394 /* initmem() initialized schemtype to PRIMARY; change it. */
3395 (*newobject)->schemtype = (mode == FONTLIB) ? GLYPH : SYMBOL;
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. */
3401 if (mode != FONTLIB) AddObjectTechnology(*newobject);
3403 *retlist = redef;
3404 return newobject;
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 /*--------------------------------------------------------------*/
3413 Boolean library_object_unique(short mode, objectptr newobject, objlistptr redef)
3415 Boolean is_unique = True;
3416 objlistptr newdef;
3417 short *libobjects = (mode == FONTLIB) ?
3418 &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;
3420 if (redef == NULL)
3421 return is_unique; /* No name conflicts; object is okay as-is */
3423 for (newdef = redef; newdef != NULL; newdef = newdef->next) {
3425 /* Must make sure that default parameter values are */
3426 /* plugged into both objects! */
3427 opsubstitute(newdef->thisobject, NULL);
3428 opsubstitute(newobject, NULL);
3430 if (objcompare(newobject, newdef->thisobject) == True) {
3431 addalias(newdef->thisobject, newobject->name);
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. */
3438 if (newobject->symschem != NULL) {
3439 newdef->thisobject->symschem = newobject->symschem;
3440 newdef->thisobject->symschem->symschem = newdef->thisobject;
3443 reset(newobject, DESTROY);
3444 (*libobjects)--;
3445 is_unique = False;
3446 break;
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. */
3453 else if (!strcmp(newobject->name, newdef->thisobject->name)) {
3455 /* Replacement---for project management, allow the technology */
3456 /* master version to take precedence over a local version. */
3458 TechPtr nsptr = GetObjectTechnology(newobject);
3460 if (nsptr && (nsptr->flags & TECH_REPLACE)) {
3461 reset(newobject, DESTROY);
3462 (*libobjects)--;
3463 is_unique = False;
3465 else
3466 checkname(newdef->thisobject);
3467 break;
3470 for (; (newdef = redef->next); redef = newdef)
3471 free(redef);
3472 free(redef);
3474 return is_unique;
3477 /*--------------------------------------------------------------*/
3478 /* Add an instance of the object to the library's instance list */
3479 /*--------------------------------------------------------------*/
3481 void add_object_to_library(short mode, objectptr newobject)
3483 objinstptr libinst;
3485 if (mode == FONTLIB) return;
3487 libinst = addtoinstlist(mode - LIBRARY, newobject, False);
3488 calcbboxvalues(libinst, (genericptr *)NULL);
3490 /* Center the view of the object in its instance */
3491 centerview(libinst);
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 /*--------------------------------------------------------------*/
3500 char *continueline(char **buffer)
3502 char *lineptr;
3503 int bufsize;
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++ = ' ';
3511 bufsize = (int)(lineptr - (*buffer)) + 256;
3512 *buffer = (char *)realloc((*buffer), bufsize * sizeof(char));
3514 return ((*buffer) + (bufsize - 256));
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 /*--------------------------------------------------------------*/
3523 void readimagedata(FILE *ps, int width, int height)
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;
3536 iptr = addnewimage(NULL, width, height);
3538 /* Read the image data */
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 */
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);
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;
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';
3584 else {
3585 for (y = 1; y < 5; y++) {
3586 if (*pptr == '\n') {
3587 fgets(temp, 149, ps);
3588 pptr = temp;
3590 ascbuf[y] = *pptr;
3591 if (ascbuf[y] == '~') {
3592 for (; y < 5; y++) {
3593 ascbuf[y] = '!';
3594 p++;
3596 break;
3598 else pptr++;
3600 for (y = 0; y < 5; y++) ascbuf[y] -= '!';
3603 if (*pptr == '\n') {
3604 fgets(temp, 149, ps);
3605 pptr = temp;
3608 /* Decode from ASCII85 to binary */
3610 pixel.i = ascbuf[4] + ascbuf[3] * 85 + ascbuf[2] * 7225 +
3611 ascbuf[1] * 614125 + ascbuf[0] * 52200625;
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;
3625 for (y = 0; y < (4 - p); y++) {
3626 filtbuf[q + y] = pixel.b[3 - y];
3628 q += (4 - p);
3629 if (q >= ilen) break;
3633 /* Extra decoding goes here */
3635 #ifdef HAVE_LIBZ
3636 if (do_flate) {
3637 flatebuf = (char *)malloc(ilen);
3638 large_inflate(filtbuf, q, &flatebuf, ilen);
3639 free(filtbuf);
3641 else
3642 #endif
3644 flatebuf = filtbuf;
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);
3656 free(flatebuf);
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 */
3666 /*--------------------------------------------------------------*/
3667 /* Read an object (page) from a file into xcircuit */
3668 /*--------------------------------------------------------------*/
3670 Boolean objectread(FILE *ps, objectptr localdata, short offx, short offy,
3671 short mode, char *retstr, int ccolor, TechPtr defaulttech)
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 */
3685 /* path-handling variables */
3686 pathptr *newpath;
3687 XPoint startpoint;
3689 keyword[0] = '\0';
3691 buffer = (char *)malloc(256 * sizeof(char));
3692 temp = buffer;
3694 for(;;) {
3695 char *lineptr, *keyptr, *saveptr;
3697 if (fgets(temp, 255, ps) == NULL) {
3698 if (strcmp(keyword, "restore")) {
3699 Wprintf("Error: end of file.");
3700 *retstr = '\0';
3702 break;
3704 temp = buffer;
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);
3715 if (!strcmp(keyword, "showpage")) {
3716 strncpy(retstr, buffer, 150);
3717 retstr[149] = '\0';
3718 free(buffer);
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. */
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;
3744 if (lgen == localdata->plist + localdata->parts) {
3745 char *cptr, *d1ptr, *d2ptr;
3746 char *pch = textprint(plab->string, areawin->topinstance);
3748 /* Check for likely delimiters before applying warning */
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");
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);
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);
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);
3795 else
3796 Fprintf(stderr, "Warning: Unattached pin \"%s\" in "
3797 "symbol %s\n", pch,
3798 localdata->symschem->name);
3799 free(pch);
3805 return False; /* end of page */
3808 /* make a color change, adding the color if necessary */
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;
3817 else {
3818 char tmpkey[30];
3819 oparamptr ops;
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;
3841 /* end the color change, returning to default */
3843 else if (!strcmp(keyword, "sce")) {
3844 curcolor = ccolor;
3845 colorkey = NULL;
3848 /* begin a path constructor */
3850 else if (!strcmp(keyword, "beginpath")) {
3851 px = py = 0;
3853 NEW_PATH(newpath, localdata);
3854 (*newpath)->plist = (genericptr *)malloc(sizeof(genericptr));
3855 (*newpath)->parts = 0;
3856 (*newpath)->color = curcolor;
3857 (*newpath)->passed = NULL;
3859 for (--keyptr; *keyptr == ' '; keyptr--);
3860 for (; *keyptr != ' '; keyptr--);
3862 /* check for "addtox" and "addtoy" parameter specification */
3863 while (!strncmp(keyptr + 1, "addto", 5)) {
3864 saveptr = keyptr + 1;
3866 for (--keyptr; *keyptr == ' '; keyptr--);
3867 for (; *keyptr != ' '; keyptr--);
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);
3877 for (--keyptr; *keyptr == ' '; keyptr--);
3878 for (; *keyptr != ' '; keyptr--);
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);
3888 std_eparam((genericptr)(*newpath), colorkey);
3891 /* end the path constructor */
3893 else if (!strcmp(keyword, "endpath")) {
3895 lineptr = varscan(localdata, buffer, &(*newpath)->style,
3896 (genericptr)*newpath, P_STYLE);
3897 lineptr = varfscan(localdata, lineptr, &(*newpath)->width,
3898 (genericptr)*newpath, P_LINEWIDTH);
3900 if ((*newpath)->parts <= 0) { /* in case of an empty path */
3901 free((*newpath)->plist);
3902 free(*newpath);
3903 localdata->parts--;
3905 newpath = NULL;
3908 /* read path parts */
3910 else if (!strcmp(keyword, "polyc")) {
3911 polyptr *newpoly;
3912 pointlist newpoints;
3913 short tmpnum;
3915 px = py = 0;
3917 NEW_POLY(newpoly, (*newpath));
3919 for (--keyptr; *keyptr == ' '; keyptr--);
3920 for (; *keyptr != ' '; keyptr--);
3922 /* check for "addtox" and "addtoy" parameter specification */
3923 while (!strncmp(keyptr + 1, "addto", 5)) {
3924 saveptr = keyptr + 1;
3926 for (--keyptr; *keyptr == ' '; keyptr--);
3927 for (; *keyptr != ' '; keyptr--);
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);
3937 for (--keyptr; *keyptr == ' '; keyptr--);
3938 for (; *keyptr != ' '; keyptr--);
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;
3949 (*newpoly)->points = (pointlist) malloc((*newpoly)->number *
3950 sizeof(XPoint));
3952 /* If the last point on the last path part was parameterized, then */
3953 /* the first point of the spline must be, too. */
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;
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;
3970 lineptr = buffer;
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);
3980 for (--newpoints; newpoints > (*newpoly)->points; newpoints--) {
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);
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;
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;
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);
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;
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);
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;
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);
4055 if (!strcmp(keyword, "nellip")) {
4056 float tmpang = (*newarc)->angle1;
4057 (*newarc)->radius = -((*newarc)->radius);
4058 (*newarc)->angle1 = (*newarc)->angle2;
4059 (*newarc)->angle2 = tmpang;
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);
4070 else if (!strcmp(keyword, "curveto")) {
4071 splineptr *newspline;
4072 px = py = 0;
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;
4081 /* If the last point on the last path part was parameterized, then */
4082 /* the first point of the spline must be, too. */
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;
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;
4099 for (--keyptr; *keyptr == ' '; keyptr--);
4100 for (; *keyptr != ' '; keyptr--);
4102 /* check for "addtox" and "addtoy" parameter specification */
4103 while (!strncmp(keyptr + 1, "addto", 5)) {
4104 saveptr = keyptr + 1;
4106 for (--keyptr; *keyptr == ' '; keyptr--);
4107 for (; *keyptr != ' '; keyptr--);
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);
4117 for (--keyptr; *keyptr == ' '; keyptr--);
4118 for (; *keyptr != ' '; keyptr--);
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);
4141 (*newspline)->ctrl[0].x = startpoint.x;
4142 (*newspline)->ctrl[0].y = startpoint.y;
4144 calcspline(*newspline);
4145 startpoint.x = (*newspline)->ctrl[3].x;
4146 startpoint.y = (*newspline)->ctrl[3].y;
4149 /* read arcs */
4151 else if (!strcmp(keyword, "xcarc")) {
4152 arcptr *newarc;
4154 NEW_ARC(newarc, localdata);
4155 (*newarc)->color = curcolor;
4156 (*newarc)->passed = NULL;
4157 (*newarc)->cycle = NULL;
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;
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);
4184 (*newarc)->yaxis = (*newarc)->radius;
4185 calcarc(*newarc);
4186 std_eparam((genericptr)(*newarc), colorkey);
4189 /* read ellipses */
4191 else if (!strcmp(keyword, "ellipse")) {
4192 arcptr *newarc;
4194 NEW_ARC(newarc, localdata);
4196 (*newarc)->color = curcolor;
4197 (*newarc)->passed = NULL;
4198 (*newarc)->cycle = NULL;
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);
4217 calcarc(*newarc);
4218 std_eparam((genericptr)(*newarc), colorkey);
4221 /* read polygons */
4222 /* (and wires---backward compatibility for v1.5 and earlier) */
4224 else if (!strcmp(keyword, "polygon") || !strcmp(keyword, "wire")) {
4225 polyptr *newpoly;
4226 pointlist newpoints;
4227 px = py = 0;
4229 NEW_POLY(newpoly, localdata);
4230 lineptr = buffer;
4232 (*newpoly)->passed = NULL;
4233 (*newpoly)->cycle = NULL;
4235 if (!strcmp(keyword, "wire")) {
4236 (*newpoly)->number = 2;
4237 (*newpoly)->width = 1.0;
4238 (*newpoly)->style = UNCLOSED;
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);
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;
4256 for (--keyptr; *keyptr == ' '; keyptr--);
4257 for (; *keyptr != ' '; keyptr--);
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);
4267 for (--keyptr; *keyptr == ' '; keyptr--);
4268 for (; *keyptr != ' '; keyptr--);
4270 sscanf(keyptr, "%hd", &(*newpoly)->number);
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);
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));
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);
4296 std_eparam((genericptr)(*newpoly), colorkey);
4299 /* read spline curves */
4301 else if (!strcmp(keyword, "spline")) {
4302 splineptr *newspline;
4303 px = py = 0;
4305 NEW_SPLINE(newspline, localdata);
4306 (*newspline)->color = curcolor;
4307 (*newspline)->passed = NULL;
4308 (*newspline)->cycle = NULL;
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;
4325 else {
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;
4333 for (--keyptr; *keyptr == ' '; keyptr--);
4334 for (; *keyptr != ' '; keyptr--);
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);
4344 for (--keyptr; *keyptr == ' '; keyptr--);
4345 for (; *keyptr != ' '; keyptr--);
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);
4369 /* check for "addtox" and "addtoy" parameter specification */
4372 calcspline(*newspline);
4373 std_eparam((genericptr)(*newspline), colorkey);
4376 /* read graphics image instances */
4378 else if (!strcmp(keyword, "graphic")) {
4379 graphicptr *newgp;
4380 Imagedata *img;
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;
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. */
4406 graphicptr locgp;
4407 char *sptr = strchr(lineptr, ' ');
4409 if (sptr != NULL)
4410 *sptr = '\0';
4411 else
4412 sptr = lineptr;
4413 locgp = new_graphic(NULL, lineptr, 0, 0);
4415 if (locgp == NULL) {
4416 Fprintf(stderr, "Error: No graphic data for \"%s\".\n",
4417 lineptr);
4418 newgp = NULL;
4420 else {
4421 lineptr = sptr;
4422 newgp = &locgp;
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);
4439 /* read labels */
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;
4450 if (i == fontcount) i = 0; /* Why bother with anything fancy? */
4453 else if (!strcmp(keyword, "label") || !strcmp(keyword, "pinlabel")
4454 || !strcmp(keyword, "pinglobal") || !strcmp(keyword, "infolabel")) {
4456 labelptr *newlabel;
4457 stringpart *firstscale, *firstfont;
4459 NEW_LABEL(newlabel, localdata);
4460 (*newlabel)->color = curcolor;
4461 (*newlabel)->string = NULL;
4462 (*newlabel)->passed = NULL;
4463 (*newlabel)->cycle = NULL;
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--);
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--);
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';
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);
4501 if (compare_version(version, "2.4") < 0)
4502 (*newlabel)->rotation = -(*newlabel)->rotation;
4503 while ((*newlabel)->rotation < 0.0) (*newlabel)->rotation += 360.0;
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;
4523 lineptr = buffer; /* back to beginning of string */
4524 if (!strncmp(lineptr, "mark", 4)) lineptr += 4;
4526 readlabel(localdata, lineptr, &(*newlabel)->string);
4527 CheckMarginStop(*newlabel, areawin->topinstance, FALSE);
4529 if (compare_version(version, "2.3") < 0) {
4530 /* Switch 1st scale designator to overall font scale */
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;
4539 else {
4540 (*newlabel)->scale = firstscale->data.scale;
4541 deletestring(firstscale, &((*newlabel)->string),
4542 areawin->topinstance);
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;
4552 firstfont = makesegment(&((*newlabel)->string), (*newlabel)->string);
4553 firstfont->type = FONT_NAME;
4554 firstfont->data.font = tmpfont;
4556 cleanuplabel(&(*newlabel)->string);
4558 std_eparam((genericptr)(*newlabel), colorkey);
4561 /* read symbol-to-schematic connection */
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);
4570 /* read bounding box (font files only) */
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;
4580 sscanf(++lineptr, "%hd %hd %hd %hd",
4581 &localdata->bbox.lowerleft.x, &localdata->bbox.lowerleft.y,
4582 &localdata->bbox.width, &localdata->bbox.height);
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.
4588 if (mode != FONTLIB) {
4589 polyptr *newpoly;
4590 pointlist newpoints;
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);
4616 /* read "hidden" attribute */
4618 else if (!strcmp(keyword, "hidden")) {
4619 localdata->hidden = True;
4622 /* read "libinst" special instance of a library part */
4624 else if (!strcmp(keyword, "libinst")) {
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);
4633 /* read objects */
4635 else if (!strcmp(keyword, "{")) { /* This is an object definition */
4636 objlistptr redef;
4637 objectptr *newobject;
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;
4645 parse_ps_string(lineptr, keyword, 79, FALSE, FALSE);
4647 newobject = new_library_object(mode, keyword, &redef, defaulttech);
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;
4656 else {
4657 if (library_object_unique(mode, *newobject, redef))
4658 add_object_to_library(mode, *newobject);
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 */
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);
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;
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;
4693 mode = ilib - 1 + LIBRARY;
4695 loadlibrary(mode);
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++;
4705 if (tmpnum < 256) { /* read parameter defaults in order */
4706 stringpart *newpart;
4707 /* oparamptr newops; (jdk) */
4709 for (i = 0; i < tmpnum; i++) {
4710 oparamptr newops;
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);
4718 if (*lineptr == '(' || *lineptr == '{') { /* type is XC_STRING */
4719 char *linetmp, csave;
4721 newops->parameter.string = NULL;
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));
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;
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++;
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);
4755 else if (!strcmp(keyword, "nonetwork")) {
4756 localdata->valid = True;
4757 localdata->schemtype = NONETWORK;
4759 else if (!strcmp(keyword, "trivial")) {
4760 localdata->schemtype = TRIVIAL;
4762 else if (!strcmp(keyword, "begingate")) {
4763 localdata->params = NULL;
4764 /* read dictionary of parameter key:value pairs */
4765 readparams(NULL, NULL, localdata, buffer);
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;
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. */
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;
4790 else if (strstr(buffer, "%imagedata") == tmpptr) {
4791 int width, height;
4792 sscanf(buffer, "%*s %d %d", &width, &height);
4793 readimagedata(ps, width, height);
4795 continue;
4798 parse_ps_string(keyword, keyword, 79, FALSE, FALSE);
4799 matchtech = (strstr(keyword, "::") == NULL) ? FALSE : TRUE;
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. */
4808 /* (Assume that this line calls an object instance) */
4809 /* Double loop through user libraries */
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++) {
4815 libobj = (mode == FONTLIB) ? xobjs.fontlib.library + j :
4816 xobjs.userlibs[k].library + j;
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. */
4823 libobjname = (*libobj)->name;
4824 if (!matchtech) {
4825 objnamestart = strstr(libobjname, "::");
4826 if (objnamestart != NULL) libobjname = objnamestart + 2;
4828 if (!objnamecmp(keyword, libobjname)) {
4830 /* If the name is not exactly the same (appended underscores) */
4831 /* check if the name is on the list of aliases. */
4833 if (strcmp(keyword, libobjname)) {
4834 Boolean is_alias = False;
4835 aliasptr ckalias = aliastop;
4836 slistptr sref;
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;
4847 if (is_alias) break;
4850 if (!is_alias) continue;
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.
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;
4870 lineptr = varfscan(localdata, buffer, &(*newinst)->scale,
4871 (genericptr)*newinst, P_SCALE);
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. */
4878 if ((compare_version(version, "3.7") < 0) && ((*newinst)->scale != 1.0))
4879 (*newinst)->style = NORMAL;
4880 else
4881 (*newinst)->style = LINE_INVARIANT;
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);
4890 /* Look for the "not netlistable" flag */
4891 if (*lineptr == '/') {
4892 if (!strncmp(lineptr, "/nn", 3))
4893 (*newinst)->style |= INST_NONETLIST;
4896 /* Negative rotations = flip in x in version 2.3.6 and */
4897 /* earlier. Later versions don't allow negative rotation */
4899 if (compare_version(version, "2.4") < 0) {
4900 if ((*newinst)->rotation < 0.0) {
4901 (*newinst)->scale = -((*newinst)->scale);
4902 (*newinst)->rotation += 1.0;
4904 (*newinst)->rotation = -(*newinst)->rotation;
4907 while ((*newinst)->rotation > 360.0)
4908 (*newinst)->rotation -= 360.0;
4909 while ((*newinst)->rotation < 0.0)
4910 (*newinst)->rotation += 360.0;
4912 std_eparam((genericptr)(*newinst), colorkey);
4914 /* Does this instance contain parameters? */
4915 readparams(localdata, *newinst, *libobj, buffer);
4917 calcbboxinst(*newinst);
4919 break;
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);
4930 strncpy(retstr, buffer, 150);
4931 retstr[149] = '\0';
4932 free(buffer);
4933 return True;
4936 /*------------------------*/
4937 /* Save a PostScript file */
4938 /*------------------------*/
4940 #ifdef TCL_WRAPPER
4942 void setfile(char *filename, int mode)
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;
4950 /* see if name has been changed in the buffer */
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);
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);
4962 else {
4963 savefile(mode);
4964 if (areawin->area && beeper) XBell(dpy, 100);
4968 #else /* !TCL_WRAPPER */
4970 void setfile(xcWidget button, xcWidget fnamewidget, caddr_t clientdata)
4972 /* see if name has been changed in the buffer */
4974 sprintf(_STR2, "%.249s", (char *)XwTextCopyBuffer(fnamewidget));
4975 if (xobjs.pagelist[areawin->page]->filename == NULL) {
4976 xobjs.pagelist[areawin->page]->filename = strdup(_STR2);
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);
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);
4987 else {
4989 Arg wargs[1];
4990 xcWidget db, di;
4992 savefile(CURRENT_PAGE);
4994 /* Change "close" button to read "done" */
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);
5004 #endif /* TCL_WRAPPER */
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 /*--------------------------------------------------------------*/
5011 void incr_changes(objectptr thisobj)
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. */
5016 if (thisobj->parts == 0) {
5017 thisobj->changes = 0;
5018 return;
5021 /* Remove any pending timeout */
5023 if (xobjs.timeout_id != (xcIntervalId)NULL) {
5024 xcRemoveTimeOut(xobjs.timeout_id);
5025 xobjs.timeout_id = (xcIntervalId)NULL;
5028 thisobj->changes++;
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.
5035 if (xobjs.suspend < 0)
5036 xobjs.new_changes++;
5038 if (xobjs.new_changes > MAXCHANGES) {
5039 #ifdef TCL_WRAPPER
5040 savetemp(NULL);
5041 #else
5042 savetemp(NULL, NULL);
5043 #endif
5046 /* Generate a new timeout */
5048 if (areawin->area)
5049 xobjs.timeout_id = xcAddTimeOut(app, 60000 * xobjs.save_interval,
5050 savetemp, NULL);
5053 /*--------------------------------------------------------------*/
5054 /* tempfile save */
5055 /*--------------------------------------------------------------*/
5057 #ifdef TCL_WRAPPER
5059 void savetemp(ClientData clientdata)
5062 #else
5064 void savetemp(XtPointer clientdata, xcIntervalId *id)
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;
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. */
5076 xobjs.timeout_id = (xcIntervalId)NULL;
5078 /* First see if there are any unsaved changes in the file. */
5079 /* If not, then just reset the counter and continue. */
5081 if (xobjs.new_changes > 0) {
5082 if (xobjs.tempfile == NULL)
5084 int fd;
5085 char *template = (char *)malloc(20 + strlen(xobjs.tempdir));
5086 int pid = (int)getpid();
5088 sprintf(template, "%s/XC%d.XXXXXX", xobjs.tempdir, pid);
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);
5099 close(fd);
5100 xobjs.tempfile = strdup(template);
5101 free(template);
5103 /* Show the "wristwatch" cursor, as graphic data may cause this */
5104 /* step to be inordinately long. */
5106 XDefineCursor(dpy, areawin->window, WAITFOR);
5107 savefile(ALL_PAGES);
5108 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5109 xobjs.new_changes = 0; /* reset the count */
5113 /*----------------------------------------------------------------------*/
5114 /* Set all objects in the list "wroteobjs" as having no unsaved changes */
5115 /*----------------------------------------------------------------------*/
5117 void setassaved(objectptr *wroteobjs, short written)
5119 int i;
5121 for (i = 0; i < written; i++)
5122 (*(wroteobjs + i))->changes = 0;
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 /*---------------------------------------------------------------*/
5130 void savelibpopup(xcWidget button, char *technology, caddr_t nulldata)
5132 TechPtr nsptr;
5134 #ifndef TCL_WRAPPER
5135 buttonsave *savebutton;
5136 #endif
5138 nsptr = LookupTechnology(technology);
5140 if (nsptr != NULL) {
5141 if ((nsptr->flags & TECH_READONLY) != 0) {
5142 Wprintf("Library technology \"%s\" is read-only.", technology);
5143 return;
5147 #ifndef TCL_WRAPPER
5148 savebutton = getgeneric(button, savelibpopup, technology);
5149 popupprompt(button, "Enter technology:", "\0", savelibrary,
5150 savebutton, NULL);
5151 #endif
5154 /*---------------------------------------------------------*/
5156 #ifndef TCL_WRAPPER
5158 void savelibrary(xcWidget w, char *technology)
5160 char outname[250];
5161 sscanf(_STR2, "%249s", outname);
5162 savetechnology(technology, outname);
5165 #endif
5167 /*---------------------------------------------------------*/
5169 void savetechnology(char *technology, char *outname)
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;
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.
5190 if (technology && (!strcmp(technology, "(user)")))
5191 nsptr = LookupTechnology(NULL);
5192 else
5193 nsptr = LookupTechnology(technology);
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");
5202 xc_tilde_expand(outfile, 149);
5203 while(xc_variable_expand(outfile, 149));
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.
5210 if (!strcmp(outfile, nsptr->filename)) {
5212 if ((nsptr->flags & TECH_READONLY) != 0) {
5213 Wprintf("Technology file \"%s\" is read-only.", technology);
5214 return;
5217 if ((nsptr->flags & TECH_IMPORTED) != 0) {
5218 Wprintf("Attempt to write a truncated technology file!");
5219 return;
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;
5231 return;
5234 /* Did the technology name change? If so, register the new name. */
5235 /* Clear any "IMPORTED" or "READONLY" flags. */
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);
5244 else if (nsptr && !nsptr->filename) {
5245 nsptr->filename = strdup(outfile);
5246 nsptr->flags &= ~(TECH_READONLY | TECH_IMPORTED);
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
5260 /* Check for both $HOST and $HOSTNAME environment variables. Thanks */
5261 /* to frankie liu <frankliu@Stanford.EDU> for this fix. */
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;
5271 #ifndef _MSC_VER
5272 if (mypwentry != NULL)
5273 fprintf(ps, "%% Author: %s <%s@%s>\n", mypwentry->pw_gecos, uname,
5274 hostname);
5275 #endif
5277 fprintf(ps, "%%\n\n");
5279 /* Print lists of object dependencies, where they exist. */
5280 /* Note that objects can depend on objects in other technologies; */
5281 /* this is allowed. */
5283 wroteobjs = (objectptr *) malloc(sizeof(objectptr));
5284 for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5285 for (j = 0; j < xobjs.userlibs[ilib].number; j++) {
5287 libobjptr = *(xobjs.userlibs[ilib].library + j);
5288 if (CompareTechnology(libobjptr, technology)) {
5289 written = 0;
5291 /* Search for all object definitions instantiated in this object, */
5292 /* and add them to the dependency list (non-recursive). */
5294 for (gptr = libobjptr->plist; gptr < libobjptr->plist
5295 + libobjptr->parts; gptr++) {
5296 if (IS_OBJINST(*gptr)) {
5297 depobj = TOOBJINST(gptr)->thisobject;
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 */
5303 for (optr = wroteobjs; optr < wroteobjs + written; optr++)
5304 if (*optr == depobj)
5305 break;
5307 if (optr == wroteobjs + written) {
5308 wroteobjs = (objectptr *)realloc(wroteobjs, (written + 1) *
5309 sizeof(objectptr));
5310 *(wroteobjs + written) = depobj;
5311 written++;
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);
5321 fprintf(ps, "\n");
5327 fprintf(ps, "\n%% XCircuitLib library objects\n");
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. */
5332 glist = (short *)malloc(xobjs.images * sizeof(short));
5333 for (i = 0; i < xobjs.images; i++) glist[i] = 0;
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);
5342 output_graphic_data(ps, glist);
5343 free(glist);
5345 /* list of library objects already written */
5347 wroteobjs = (objectptr *)realloc(wroteobjs, sizeof(objectptr));
5348 written = 0;
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. */
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);
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);
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");
5384 setassaved(wroteobjs, written);
5385 if (nsptr) nsptr->flags &= (~TECH_CHANGED);
5386 xobjs.new_changes = countchanges(NULL);
5388 /* and the postlog */
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);
5397 free(wroteobjs);
5400 /*----------------------------------------------------------------------*/
5401 /* Recursive routine to search the object hierarchy for fonts used */
5402 /*----------------------------------------------------------------------*/
5404 void findfonts(objectptr writepage, short *fontsused) {
5405 genericptr *dfp;
5406 stringpart *chp;
5407 int findex;
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;
5420 else if (IS_OBJINST(*dfp)) {
5421 findfonts(TOOBJINST(dfp)->thisobject, fontsused);
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 /*------------------------------------------------------*/
5433 void output_graphic_data(FILE *ps, short *glist)
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);
5449 if (glist[i] == 0) continue;
5451 fprintf(ps, "%%imagedata %d %d\n", width, height);
5452 fprintf(ps, "currentfile /ASCII85Decode filter ");
5454 #ifdef HAVE_LIBZ
5455 fprintf(ps, "/FlateDecode filter\n");
5456 #endif
5458 fprintf(ps, "/ReusableStreamDecode filter\n");
5460 /* creating a stream buffer is wasteful if we're just using ASCII85 */
5461 /* decoding but is a must for compression filters. */
5463 ilen = 3 * width * height;
5464 filtbuf = (u_char *)malloc(ilen + 4);
5465 q = 0;
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;
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
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++;
5495 else {
5496 for (n = 0; n < 4; n++)
5497 pixel.b[3 - n] = flatebuf[j + n];
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;
5515 if (m > 75) {
5516 fprintf(ps, "\n");
5517 m = 0;
5520 fprintf(ps, "~>\n");
5521 free(flatebuf);
5523 /* Remove any filesystem path information from the image name. */
5524 /* Otherwise, the slashes will cause PostScript to err. */
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);
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 /*----------------------------------------------------------------------*/
5557 void savefile(short mode)
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;
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);
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;
5583 if ((fptr = strrchr(fname, '/')) != NULL)
5584 fptr++;
5585 else
5586 fptr = fname;
5588 if ((mode != ALL_PAGES) && (strchr(fptr, '.') == NULL))
5589 sprintf(outname, "%s.ps", fname);
5590 else sprintf(outname, "%s", fname);
5592 xc_tilde_expand(outname, 149);
5593 while(xc_variable_expand(outname, 149));
5595 ps = fopen(outname, "wb");
5596 if (ps == NULL) {
5597 Wprintf("Can't open file %s for writing.", outname);
5598 return;
5601 if ((mode != NO_SUBCIRCUITS) && (mode != ALL_PAGES))
5602 collectsubschems(areawin->page);
5604 /* Check for multiple-page output: get the number of pages; */
5605 /* ignore empty pages. */
5607 multipage = 0;
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);
5616 for (page = 0; page < xobjs.pages; page++)
5617 if (pagelist[page] > 0)
5618 multipage++;
5620 if (multipage == 0) {
5621 Wprintf("Panic: could not find this page in page list!");
5622 free (pagelist);
5623 fclose(ps);
5624 return;
5627 /* Print the PostScript DSC Document Header */
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);
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. */
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);
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. */
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);
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 */
5668 for (i = 0; i < fontcount; i++) fontsused[i] = 0;
5669 fprintf(ps, "%%%%DocumentNeededResources: font ");
5670 stcount = 32;
5672 /* find all of the fonts used in this document */
5673 /* log all fonts which are native PostScript */
5675 for (curpage = 0; curpage < xobjs.pages; curpage++)
5676 if (pagelist[curpage] > 0) {
5677 writepage = xobjs.pagelist[curpage]->pageinst;
5678 findfonts(writepage->thisobject, fontsused);
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 ");
5689 fprintf(ps, "%s ", fonts[i].psname);
5693 fprintf(ps, "\n%%%%EndComments\n");
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");
5700 else
5701 pro = NULL;
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;
5718 /* write the prolog to the output */
5720 for(;;) {
5721 if (fgets(temp, 149, pro) == NULL) break;
5722 if (!strncmp(temp, "%%EndProlog", 11)) break;
5723 fputs(temp, ps);
5725 fclose(pro);
5727 /* Special font encodings not known to PostScript */
5728 /* (anything other than Standard and ISOLatin-1) */
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.");
5743 if (pro != NULL) {
5744 for(;;) {
5745 if (fgets(temp, 149, pro) == NULL) break;
5746 fputs(temp, ps);
5748 fclose(pro);
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.");
5763 if (pro != NULL) {
5764 for(;;) {
5765 if (fgets(temp, 149, pro) == NULL) break;
5766 fputs(temp, ps);
5768 fclose(pro);
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.");
5783 if (pro != NULL) {
5784 for(;;) {
5785 if (fgets(temp, 149, pro) == NULL) break;
5786 fputs(temp, ps);
5788 fclose(pro);
5793 /* Finish off prolog */
5794 fputs("%%EndProlog\n", ps);
5796 /* Special font handling */
5798 for (findex = 0; findex < fontcount; findex++) {
5800 /* Derived font slant */
5802 if ((fontsused[findex] & 0x032) == 0x032)
5803 fprintf(ps, "/%s /%s .167 fontslant\n\n",
5804 fonts[findex].psname, fonts[findex].family);
5806 /* Derived ISO-Latin1 encoding */
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;
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);
5827 /* Derived Cyrillic (ISO8859-5) encoding */
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;
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);
5848 /* ISO-Latin2 encoding */
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;
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);
5869 /* ISO-Latin5 encoding */
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;
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);
5890 /* To do: Special encoding */
5892 if ((fontsused[findex] & 0xf80) == 0x80) {
5895 /* To do: Vectored (drawn) font */
5897 if (fontsused[findex] & 0x8) {
5901 /* List of objects already written */
5902 wroteobjs = (objectptr *) malloc (sizeof(objectptr));
5903 written = 0;
5905 fprintf(ps, "%% XCircuit output starts here.\n\n");
5906 fprintf(ps, "%%%%BeginSetup\n\n");
5908 /* Write out all of the images used */
5910 glist = collect_graphics(pagelist);
5911 output_graphic_data(ps, glist);
5912 free(glist);
5914 for (curpage = 0; curpage < xobjs.pages; curpage++) {
5915 if (pagelist[curpage] == 0) continue;
5917 /* Write all of the object definitions used, bottom up */
5918 printrefobjects(ps, xobjs.pagelist[curpage]->pageinst->thisobject,
5919 &wroteobjs, &written);
5922 fprintf(ps, "\n%%%%EndSetup\n\n");
5924 page = 0;
5925 for (curpage = 0; curpage < xobjs.pages; curpage++) {
5926 if (pagelist[curpage] == 0) continue;
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;
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);
5945 fprintf(ps, "\n");
5946 fflush(ps);
5949 /* For crash recovery, save all objects that have been edited but are */
5950 /* not in the list of objects already saved. */
5952 if (mode == ALL_PAGES)
5954 int i, j, k;
5955 objectptr thisobj;
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);
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);
5977 /* Free allocated memory */
5978 free((char *)pagelist);
5979 free((char *)wroteobjs);
5981 /* Done! */
5983 fprintf(ps, "%%%%Trailer\n");
5984 fprintf(ps, "XCIRCsave restore\n");
5985 fprintf(ps, "%%%%EOF\n");
5986 fclose(ps);
5988 Wprintf("File %s saved (%d page%s).", fname, multipage,
5989 (multipage > 1 ? "s" : ""));
5991 if (mode == ALL_PAGES) {
5992 /* Remove the temporary redundant backup */
5993 sprintf(outname, "%sB", xobjs.tempfile);
5994 unlink(outname);
5996 else if (!xobjs.retain_backup) {
5997 /* Remove the backup file */
5998 sprintf(outname, "%s~", fname);
5999 unlink(outname);
6002 /* Write LATEX strings, if any are present */
6003 TopDoLatex();
6006 /*----------------------------------------------------------------------*/
6007 /* Given a color index, print the R, G, B values */
6008 /*----------------------------------------------------------------------*/
6010 int printRGBvalues(char *tstr, int index, const char *postfix)
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;
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. */
6028 /* If the index is *not* DEFAULTCOLOR (-1), return an error */
6029 /* code. */
6031 sprintf(tstr, "0 0 0 %s", postfix);
6032 return (index == DEFAULTCOLOR) ? 0 : -1;
6035 /*----------------------------------------------------*/
6036 /* Write string to PostScript string, ignoring NO_OPs */
6037 /*----------------------------------------------------*/
6039 char *nosprint(char *baseptr, int *margin, int *extsegs)
6041 int qtmp, slen = 100;
6042 char *sptr, *lptr = NULL, lsave, *sptr2;
6043 u_char *pptr, *qptr, *bptr;
6045 bptr = (u_char *)malloc(slen); /* initial length 100 */
6046 qptr = bptr;
6048 while(1) { /* loop for breaking up margin-limited text into words */
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;
6065 else
6066 sptr++;
6069 else
6070 sptr = baseptr;
6072 *qptr++ = '(';
6074 /* Includes extended character set (non-ASCII) */
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;
6086 /* Deal with non-printable characters and parentheses */
6087 if (*pptr > (char)126) {
6088 sprintf(qptr, "\\%3o", (int)(*pptr));
6089 qptr += 4;
6091 else {
6092 if ((*pptr == '(') || (*pptr == ')') || (*pptr == '\\'))
6093 *qptr++ = '\\';
6094 *qptr++ = *pptr;
6097 if (qptr == bptr + 1) { /* Empty string gets a NULL result, not "()" */
6098 qptr--;
6100 else {
6101 *qptr++ = ')';
6102 *qptr++ = ' ';
6105 if (lptr != NULL)
6106 *lptr = lsave;
6108 if (sptr == baseptr)
6109 break;
6110 else {
6111 lptr = sptr;
6112 lsave = *lptr;
6113 *lptr = '\0';
6114 (*extsegs)++;
6118 *qptr++ = '\0';
6119 return (char *)bptr;
6122 /*--------------------------------------------------------------*/
6123 /* Write label segments to the output (in reverse order) */
6124 /*--------------------------------------------------------------*/
6126 short writelabel(FILE *ps, stringpart *chrtop, short *stcount)
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;
6137 /* Write segments into string array, in forward order */
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], "() ");
6145 else {
6146 tmpstr = writesegment(chrptr, &lastscale, &lastfont, &margin, &extsegs);
6147 if (tmpstr[0] != '\0')
6148 ostr[segs] = tmpstr;
6149 else
6150 segs--;
6152 segs++;
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]);
6161 free(ostr);
6163 return segs + extsegs;
6166 /*--------------------------------------------------------------*/
6167 /* Write a single label segment to the output */
6168 /* (Recursive, so we can write segments in the reverse order) */
6169 /*--------------------------------------------------------------*/
6171 char *writesegment(stringpart *chrptr, float *lastscale, int *lastfont, int *margin,
6172 int *extsegs)
6174 int type = chrptr->type;
6175 char *retstr, *validname;
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. */
6233 if ((chrptr->nextpart == NULL) || (chrptr->nextpart->type != FONT_SCALE))
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);
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;
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);
6274 retstr = (char *)malloc(1 + strlen(_STR));
6275 strcpy(retstr, _STR);
6276 return retstr;
6279 /*--------------------------------------------------------------*/
6280 /* Routine to write all the label segments as stored in _STR */
6281 /*--------------------------------------------------------------*/
6283 int writelabelsegs(FILE *ps, short *stcount, stringpart *chrptr)
6285 Boolean ismultipart;
6286 int segs;
6288 if (chrptr == NULL) return 0;
6290 ismultipart = ((chrptr->nextpart != NULL) &&
6291 (chrptr->nextpart->type != PARAM_END)) ? True : False;
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 {{ }}. */
6297 if ((!ismultipart) && (chrptr->type != TEXT_STRING) &&
6298 (chrptr->type != PARAM_END))
6299 ismultipart = True;
6301 /* nextpart is not NULL if there are multiple parts to the string */
6302 if (ismultipart) {
6303 fprintf(ps, "{");
6304 (*stcount)++;
6306 segs = writelabel(ps, chrptr, stcount);
6308 if (ismultipart) {
6309 fprintf(ps, "} ");
6310 (*stcount) += 2;
6312 return segs;
6315 /*--------------------------------------------------------------*/
6316 /* Write the dictionary of parameters belonging to an object */
6317 /*--------------------------------------------------------------*/
6319 void printobjectparams(FILE *ps, objectptr localdata)
6321 int segs;
6322 short stcount;
6323 oparamptr ops;
6324 char *ps_expr, *validkey;
6325 float fp;
6327 /* Check for parameters and default values */
6328 if (localdata->params == NULL) return;
6330 fprintf(ps, "<<");
6331 stcount = 2;
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);
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);
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);
6356 else {
6357 dostcount(ps, &stcount, 8);
6358 fputs("{0 0 0} ", ps);
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);
6366 else { /* Expression evaluates to error in object */
6367 dostcount(ps, &stcount, 2);
6368 fputs("0 ", ps);
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);
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;
6398 fprintf(ps, ">> ");
6399 dostcount (ps, &stcount, 3);
6402 /*--------------------------------------------------------------*/
6403 /* Write the list of parameters belonging to an object instance */
6404 /*--------------------------------------------------------------*/
6406 short printparams(FILE *ps, objinstptr sinst, short stcount)
6408 int segs;
6409 short loccount;
6410 oparamptr ops, objops;
6411 eparamptr epp;
6412 char *ps_expr, *validkey, *validref;
6413 short instances = 0;
6415 if (sinst->params == NULL) return stcount;
6417 for (ops = sinst->params; ops != NULL; ops = ops->next) {
6418 validref = strdup(create_valid_psname(ops->key, TRUE));
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;
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;
6437 if (epp == NULL) { /* No indirection */
6438 Boolean nondefault = TRUE;
6439 char *deflt_expr = NULL;
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. */
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 // }
6481 if (instances++ == 0) {
6482 fprintf(ps, "<<"); /* begin PostScript dictionary */
6483 loccount = stcount + 2;
6485 dostcount(ps, &loccount, strlen(validref) + 2);
6486 fprintf(ps, "/%s ", validref);
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);
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);
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. */
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);
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, "} ");
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;
6538 free(validref);
6540 if (instances > 0) {
6541 fprintf(ps, ">> "); /* end PostScript dictionary */
6542 loccount += 3;
6544 return loccount;
6547 /*------------------------------------------------------------------*/
6548 /* Macro for point output (calls varpcheck() on x and y components) */
6549 /*------------------------------------------------------------------*/
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)
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)
6559 /*--------------------------------------------------------------*/
6560 /* Main routine for writing the contents of a single object to */
6561 /* output file "ps". */
6562 /*--------------------------------------------------------------*/
6564 void printOneObject(FILE *ps, objectptr localdata, int ccolor)
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;
6577 /* first, get a total count of all objects and give warning if large */
6579 if ((is_page(localdata) == -1) && (localdata->parts > 255)) {
6580 Wprintf("Warning: \"%s\" may exceed printer's PS limit for definitions",
6581 localdata->name);
6584 for (savegen = localdata->plist; savegen < localdata->plist +
6585 localdata->parts; savegen++) {
6587 /* Check if this color is parameterized */
6588 eparamptr epp;
6589 oparamptr ops;
6591 /* FIXEDBBOX style is handled in the object header and */
6592 /* the part should be skipped. */
6594 if (ELEMENTTYPE(*savegen) == POLYGON)
6595 if (TOPOLY(savegen)->style & FIXEDBBOX)
6596 continue;
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;
6609 /* Enforce the rule that clipmasks must always be DEFAULTCOLOR */
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;
6618 /* change current color if different */
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;
6628 else
6629 fputs(_STR, ps);
6633 stcount = 0;
6634 switch(ELEMENTTYPE(*savegen)) {
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);
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);
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);
6665 sprintf(_STR, "polygon\n");
6666 dostcount (ps, &stcount, strlen(_STR));
6667 fputs(_STR, ps);
6668 break;
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;
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);
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);
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);
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);
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);
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);
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);
6738 fprintf(ps, "curveto\n");
6739 break;
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;
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);
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);
6770 fprintf(ps, "spline\n");
6771 break;
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");
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");
6797 break;
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 ");
6807 opsubstitute(sobj->thisobject, sobj);
6808 stcount = printparams(ps, sobj, stcount);
6810 validname = create_valid_psname(sobj->thisobject->name, FALSE);
6812 /* Names without technologies get a leading string '::' */
6813 /* (blank technology) */
6815 if (strstr(validname, "::") == NULL)
6816 fprintf(ps, "::%s\n", validname);
6817 else
6818 fprintf(ps, "%s\n", validname);
6819 break;
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;
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));
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;
6843 case(LABEL):
6845 /* Don't save temporary labels from schematic capture system */
6846 if (TOLABEL(savegen)->string->type != FONT_NAME) break;
6848 /* Check for parameter --- must use "mark" to count # segments */
6849 has_parameter = hasparameter(TOLABEL(savegen));
6851 if (has_parameter) {
6852 fprintf(ps, "mark ");
6853 stcount += 5;
6856 segs = writelabel(ps, TOLABEL(savegen)->string, &stcount);
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);
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");
6883 dostcount(ps, &stcount, strlen(_STR));
6884 fputs(_STR, ps);
6886 break;
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 /*----------------------------------------------------------------------*/
6901 void printobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
6902 short *written, int ccolor)
6904 genericptr *gptr, *savegen;
6905 objectptr *optr;
6906 /* oparamptr ops; (jdk) */
6907 char *validname;
6908 int curcolor = ccolor;
6909 /* int libno; (jdk) */
6911 /* Search among the list of objects already written to the output */
6912 /* If this object has been written previously, then we ignore it. */
6914 for (optr = *wrotelist; optr < *wrotelist + *written; optr++)
6915 if (*optr == localdata)
6916 return;
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. */
6921 if (localdata->symschem && (localdata->schemtype == PRIMARY))
6922 printobjects(ps, localdata->symschem, wrotelist, written, curcolor);
6924 /* Search for all object definitions instantiated in this object, */
6925 /* and (recursively) print them to the output. */
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);
6931 /* Update the list of objects already written to the output */
6933 *wrotelist = (objectptr *)realloc(*wrotelist, (*written + 1) *
6934 sizeof(objectptr));
6935 *(*wrotelist + *written) = localdata;
6936 (*written)++;
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);
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. */
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;
6967 if (localdata->hidden == True) fprintf(ps, "%% hidden\n");
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");
6977 printobjectparams(ps, localdata);
6978 fprintf(ps, "begingate\n");
6980 /* Write all the elements in order */
6982 opsubstitute(localdata, NULL);
6983 printOneObject(ps, localdata, curcolor);
6985 /* Write object (gate) trailer */
6987 fprintf(ps, "endgate\n} def\n\n");
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 /*--------------------------------------------------------------*/
6999 void printpageobject(FILE *ps, objectptr localdata, short page, short mpage)
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;
7010 /* Output page header information */
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++;
7018 writepage = xobjs.pagelist[mpage]->pageinst;
7020 psnorm = xobjs.pagelist[mpage]->outscale;
7021 psscale = getpsscale(psnorm, mpage);
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. */
7029 width = toplevelwidth(writepage, &origin.x);
7030 height = toplevelheight(writepage, &origin.y);
7032 corner.x = origin.x + width;
7033 corner.y = origin.y + height;
7035 if (xobjs.pagelist[mpage]->pmode & 1) { /* full page */
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;
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;
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;
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. */
7061 if ((framebox = checkforbbox(localdata)) != NULL) {
7062 int i, fcentx = 0, fcenty = 0;
7064 for (i = 0; i < framebox->number; i++) {
7065 fcentx += framebox->points[i].x;
7066 fcenty += framebox->points[i].y;
7068 fcentx /= framebox->number;
7069 fcenty /= framebox->number;
7071 xmargin += psscale * (float)(origin.x + (width >> 1) - fcentx);
7072 ymargin += psscale * (float)(origin.y + (height >> 1) - fcenty);
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. */
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);
7086 if (xobjs.pagelist[mpage]->orient == 90)
7087 fprintf (ps, "%%%%PageOrientation: Landscape\n");
7088 else
7089 fprintf (ps, "%%%%PageOrientation: Portrait\n");
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);
7097 /* Encapsulated files do not get a PageBoundingBox line, */
7098 /* unless the bounding box was explicitly drawn. */
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));
7107 fprintf (ps, "/pgsave save def bop\n");
7109 /* Top-page definitions */
7110 if (localdata->params != NULL) {
7111 printobjectparams(ps, localdata);
7112 fprintf(ps, "begin\n");
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);
7126 /* Extend bounding box around schematic pins */
7127 extendschembbox(xobjs.pagelist[mpage]->pageinst, &origin, &corner);
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);
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);
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");
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)));
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;
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);
7175 /* Output all the elements in the page */
7176 printOneObject(ps, localdata, DEFAULTCOLOR);
7178 /* Page trailer */
7179 if (localdata->params != NULL) fprintf(ps, "end ");
7180 fprintf(ps, "pgsave restore showpage\n");
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 /*--------------------------------------------------------------*/
7192 void printrefobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
7193 short *written)
7195 genericptr *gptr;
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. */
7200 if (localdata->symschem && (localdata->schemtype == PRIMARY))
7201 printobjects(ps, localdata->symschem, wrotelist, written, DEFAULTCOLOR);
7203 /* Search for all object definitions instantiated on the page and */
7204 /* write them to the output. */
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);
7212 /*----------------------------------------------------------------------*/