Initial commit at Tue Apr 25 08:36:02 EDT 2017 by tim on stravinsky
[xcircuit.git] / files.c
blob5678ec7c22b1cbd0448a177066a0fe1e4ada2747
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 #define VEPS 1e-3
60 /*------------------------------------------------------------------------*/
61 /* External Variable definitions */
62 /*------------------------------------------------------------------------*/
64 #ifdef TCL_WRAPPER
65 extern Tcl_Interp *xcinterp;
66 #endif
68 extern char _STR2[250], _STR[150];
69 extern Globaldata xobjs;
70 extern XCWindowData *areawin;
71 extern fontinfo *fonts;
72 extern short fontcount;
73 extern Cursor appcursors[NUM_CURSORS];
74 extern XtAppContext app;
75 extern Display *dpy;
76 extern Window win;
77 extern short beeper;
78 extern int number_colors;
79 extern colorindex *colorlist;
82 /*------------------------------------------------------*/
83 /* Global variable definitions */
84 /*------------------------------------------------------*/
86 Boolean load_in_progress = False;
87 float version;
89 /* Structure for remembering what names refer to the same object */
91 aliasptr aliastop;
93 /*------------------------------------------------------*/
94 /* Simple utility---get rid of newline character */
95 /*------------------------------------------------------*/
97 char *ridnewline(char *sptr)
99 char *tstrp;
101 for (tstrp = sptr; *tstrp != '\0' && *tstrp != '\n'; tstrp++);
102 if (*tstrp == '\n') *tstrp = '\0';
103 return tstrp;
106 /*----------------------------------------------------------------------*/
107 /* Check if two filenames are equivalent. This requires separately */
108 /* checking any absolute or relative pathnames in front of the filename */
109 /* as well as the filename itself. */
110 /*----------------------------------------------------------------------*/
112 #define PATHSEP '/'
114 int filecmp(char *filename1, char *filename2)
116 char *root1, *root2, *path1, *path2, *end1, *end2;
117 int rval;
118 struct stat statbuf;
119 ino_t inode1;
120 const char *cwdname = ".";
122 if (filename1 == NULL || filename2 == NULL) return 1;
124 if (!strcmp(filename1, filename2)) return 0; /* exact match */
126 root1 = strrchr(filename1, PATHSEP);
127 root2 = strrchr(filename2, PATHSEP);
129 if (root1 == NULL) {
130 path1 = (char *)cwdname;
131 end1 = NULL;
132 root1 = filename1;
134 else {
135 path1 = filename1;
136 end1 = root1;
137 root1++;
140 if (root2 == NULL) {
141 path2 = (char *)cwdname;
142 end2 = NULL;
143 root2 = filename2;
145 else {
146 path2 = filename2;
147 end2 = root2;
148 root2++;
151 if (strcmp(root1, root2)) return 1; /* root names don't match */
153 /* If we got here, one or both filenames specify a directory */
154 /* path, and the directory paths are different strings. */
155 /* However, one may be an absolute path and the other a */
156 /* relative path, so we check the inodes of the paths for */
157 /* equivalence. Note that the file itself is not assumed to */
158 /* exist. */
160 rval = 1;
161 if (end1 != NULL) *end1 = '\0';
162 if (stat(path1, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
163 inode1 = statbuf.st_ino;
164 if (end2 != NULL) *end2 = '\0';
165 if (stat(path2, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
166 if (inode1 == statbuf.st_ino)
167 rval = 0;
169 if (end2 != NULL) *end2 = PATHSEP;
171 if (end1 != NULL) *end1 = PATHSEP;
172 return rval;
175 /*--------------------------------------------------------------*/
176 /* Make sure that a string (object or parameter name) is a */
177 /* valid PostScript name. We do this by converting illegal */
178 /* characters to the PostScript \ooo (octal value) form. */
179 /* */
180 /* This routine does not consider whether the name might be a */
181 /* PostScript numeric value. This problem is taken care of by */
182 /* having the load/save routines prepend '@' to parameters and */
183 /* a technology namespace to object names. */
184 /* */
185 /* If "need_prefix" is TRUE, then prepend "@" to the result */
186 /* string, unless teststring is a numerical parameter name */
187 /* (p_...). */
188 /*--------------------------------------------------------------*/
190 char *create_valid_psname(char *teststring, Boolean need_prefix)
192 int i, isize, ssize;
193 static char *optr = NULL;
194 char *sptr, *pptr;
195 Boolean prepend = need_prefix;
196 char illegalchars[] = {'/', '}', '{', ']', '[', ')', '(', '<', '>', ' ', '%'};
198 /* Check for illegal characters which have syntactical meaning in */
199 /* PostScript, and the presence of nonprintable characters or */
200 /* whitespace. */
202 ssize = strlen(teststring);
203 isize = ssize;
205 if (need_prefix && !strncmp(teststring, "p_", 2))
206 prepend = FALSE;
207 else
208 isize++;
210 for (sptr = teststring; *sptr != '\0'; sptr++) {
211 if ((!isprint(*sptr)) || isspace(*sptr))
212 isize += 3;
213 else {
214 for (i = 0; i < sizeof(illegalchars); i++) {
215 if (*sptr == illegalchars[i]) {
216 isize += 3;
217 break;
222 if (isize == ssize) return teststring;
223 isize++;
225 if (optr == NULL)
226 optr = (char *)malloc(isize);
227 else
228 optr = (char *)realloc(optr, isize);
230 pptr = optr;
232 if (prepend) *pptr++ = '@';
234 for (sptr = teststring; *sptr != '\0'; sptr++) {
235 if ((!isprint(*sptr)) || isspace(*sptr)) {
236 sprintf(pptr, "\\%03o", *sptr);
237 pptr += 4;
239 else {
240 for (i = 0; i < sizeof(illegalchars); i++) {
241 if (*sptr == illegalchars[i]) {
242 sprintf(pptr, "\\%03o", *sptr);
243 pptr += 4;
244 break;
247 if (i == sizeof(illegalchars))
248 *pptr++ = *sptr;
251 *pptr++ = '\0';
252 return optr;
255 /*------------------------------------------------------*/
256 /* Turn a PostScript string with possible backslash */
257 /* escapes into a normal character string. */
258 /* */
259 /* if "spacelegal" is TRUE, we are parsing a PostScript */
260 /* string in parentheses () where whitespace is legal. */
261 /* If FALSE, we are parsing a PostScript name where */
262 /* whitespace is illegal, and any whitespace should be */
263 /* considered the end of the name. */
264 /* */
265 /* "dest" is ASSUMED to be large enough to hold the */
266 /* result. "dest" is always equal to or smaller than */
267 /* "src" in length. "size" should be the maximum size */
268 /* of the string, or 1 less that the allocated memory, */
269 /* allowing for a final NULL byte to be added. */
270 /* */
271 /* The fact that "dest" is always smaller than or equal */
272 /* to "src" means that parse_ps_string(a, a, ...) is */
273 /* legal. */
274 /* */
275 /* Return 0 if the result is empty, 1 otherwise. */
276 /*------------------------------------------------------*/
278 int parse_ps_string(char *src, char *dest, int size, Boolean spacelegal, Boolean strip)
280 char *sptr = src;
281 char *tptr = dest;
282 int tmpdig, rval = 0;
284 /* Strip leading "@", inserted by XCircuit to */
285 /* prevent conflicts with PostScript reserved */
286 /* keywords or numbers. */
288 if (strip && (*sptr == '@')) sptr++;
290 for (;; sptr++) {
291 if ((*sptr == '\0') || (isspace(*sptr) && !spacelegal)) {
292 *tptr = '\0';
293 break;
295 else {
296 if (*sptr == '\\') {
297 sptr++;
298 if (*sptr >= '0' && *sptr < '8') {
299 sscanf(sptr, "%3o", &tmpdig);
300 *tptr++ = (u_char)tmpdig;
301 sptr += 2;
303 else
304 *tptr++ = *sptr;
306 else
307 *tptr++ = *sptr;
308 rval = 1;
310 if ((int)(tptr - dest) > size) {
311 Wprintf("Warning: Name \"%s\" in input exceeded buffer length!\n", src);
312 *tptr = '\0';
313 return rval;
316 return rval;
319 /*------------------------------------------------------*/
320 /* Free memory allocated to a label string */
321 /*------------------------------------------------------*/
323 void freelabel(stringpart *string)
325 stringpart *strptr = string, *tmpptr;
327 while (strptr != NULL) {
328 if (strptr->type == TEXT_STRING || strptr->type == PARAM_START)
329 free(strptr->data.string);
330 tmpptr = strptr->nextpart;
331 free(strptr);
332 strptr = tmpptr;
336 /*------------------------------------------------------*/
337 /* Free memory for a single element */
338 /*------------------------------------------------------*/
340 void free_single(genericptr genobj)
342 objinstptr geninst;
343 oparamptr ops, fops;
345 if (IS_POLYGON(genobj)) free(((polyptr)(genobj))->points);
346 else if (IS_LABEL(genobj)) freelabel(((labelptr)(genobj))->string);
347 else if (IS_GRAPHIC(genobj)) freegraphic((graphicptr)(genobj));
348 else if (IS_PATH(genobj)) free(((pathptr)(genobj))->plist);
349 else if (IS_OBJINST(genobj)) {
350 geninst = (objinstptr)genobj;
351 ops = geninst->params;
352 while (ops != NULL) {
353 /* Don't try to free data from indirect parameters */
354 /* (That's not true---all data are copied by epsubstitute) */
355 /* if (find_indirect_param(geninst, ops->key) == NULL) { */
356 switch(ops->type) {
357 case XC_STRING:
358 freelabel(ops->parameter.string);
359 break;
360 case XC_EXPR:
361 free(ops->parameter.expr);
362 break;
364 /* } */
365 free(ops->key);
366 fops = ops;
367 ops = ops->next;
368 free(fops);
371 free_all_eparams(genobj);
374 /*---------------------------------------------------------*/
375 /* Reset an object structure by freeing all alloc'd memory */
376 /*---------------------------------------------------------*/
378 void reset(objectptr localdata, short mode)
380 /* short i; (jdk) */
382 if (localdata->polygons != NULL || localdata->labels != NULL)
383 destroynets(localdata);
385 localdata->valid = False;
387 if (localdata->parts > 0) {
388 genericptr *genobj;
390 if (mode != SAVE) {
392 for (genobj = localdata->plist; genobj < localdata->plist
393 + localdata->parts; genobj++)
395 /* (*genobj == NULL) only on library pages */
396 /* where the instances are kept in the library */
397 /* definition, and are only referenced on the page. */
399 if (*genobj != NULL) {
400 free_single(*genobj);
401 free(*genobj);
404 free(localdata->plist);
406 removeparams(localdata);
408 initmem(localdata);
409 if (mode == DESTROY)
410 free(localdata->plist);
414 /*---------------------------------------------------------*/
416 void pagereset(short rpage)
418 /* free alloc'd filename */
420 if (xobjs.pagelist[rpage]->filename != NULL)
421 free(xobjs.pagelist[rpage]->filename);
422 xobjs.pagelist[rpage]->filename = (char *)NULL;
424 if (xobjs.pagelist[rpage]->background.name != NULL)
425 free(xobjs.pagelist[rpage]->background.name);
426 xobjs.pagelist[rpage]->background.name = (char *)NULL;
428 clearselects();
430 /* New pages pick up their properties from page 0, which can be changed */
431 /* from the .xcircuitrc file on startup (or loaded from a script). */
432 /* Thanks to Norman Werner (norman.werner@student.uni-magdeburg.de) for */
433 /* pointing out this more obvious way of doing the reset, and providing */
434 /* a patch. */
436 xobjs.pagelist[rpage]->wirewidth = xobjs.pagelist[0]->wirewidth;
437 xobjs.pagelist[rpage]->orient = xobjs.pagelist[0]->orient;
438 xobjs.pagelist[rpage]->pmode = xobjs.pagelist[0]->pmode;
439 xobjs.pagelist[rpage]->outscale = xobjs.pagelist[0]->outscale;
440 xobjs.pagelist[rpage]->drawingscale.x = xobjs.pagelist[0]->drawingscale.x;
441 xobjs.pagelist[rpage]->drawingscale.y = xobjs.pagelist[0]->drawingscale.y;
442 xobjs.pagelist[rpage]->gridspace = xobjs.pagelist[0]->gridspace;
443 xobjs.pagelist[rpage]->snapspace = xobjs.pagelist[0]->snapspace;
444 xobjs.pagelist[rpage]->coordstyle = xobjs.pagelist[0]->coordstyle;
445 xobjs.pagelist[rpage]->margins = xobjs.pagelist[0]->margins;
447 if (xobjs.pagelist[rpage]->coordstyle == CM) {
448 xobjs.pagelist[rpage]->pagesize.x = 595;
449 xobjs.pagelist[rpage]->pagesize.y = 842; /* A4 */
451 else {
452 xobjs.pagelist[rpage]->pagesize.x = 612; /* letter */
453 xobjs.pagelist[rpage]->pagesize.y = 792;
457 /*---------------------------------------------------------*/
459 void initmem(objectptr localdata)
461 localdata->parts = 0;
462 localdata->plist = (genericptr *)malloc(sizeof(genericptr));
463 localdata->hidden = False;
464 localdata->changes = 0;
465 localdata->params = NULL;
467 localdata->viewscale = 0.5;
469 /* Object should not reference the window: this needs to be rethunk! */
470 if (areawin != NULL) {
471 localdata->pcorner.x = -areawin->width;
472 localdata->pcorner.y = -areawin->height;
474 localdata->bbox.width = 0;
475 localdata->bbox.height = 0;
476 localdata->bbox.lowerleft.x = 0;
477 localdata->bbox.lowerleft.y = 0;
479 localdata->highlight.netlist = NULL;
480 localdata->highlight.thisinst = NULL;
481 localdata->schemtype = PRIMARY;
482 localdata->symschem = NULL;
483 localdata->netnames = NULL;
484 localdata->polygons = NULL;
485 localdata->labels = NULL;
486 localdata->ports = NULL;
487 localdata->calls = NULL;
488 localdata->valid = False;
489 localdata->infolabels = False;
490 localdata->traversed = False;
493 /*--------------------------------------------------------------*/
494 /* Exhaustively compare the contents of two objects and return */
495 /* true if equivalent, false if not. */
496 /*--------------------------------------------------------------*/
498 Boolean elemcompare(genericptr *compgen, genericptr *gchk)
500 Boolean bres;
501 switch(ELEMENTTYPE(*compgen)) {
502 case(ARC):
503 bres = (TOARC(compgen)->position.x == TOARC(gchk)->position.x &&
504 TOARC(compgen)->position.y == TOARC(gchk)->position.y &&
505 TOARC(compgen)->style == TOARC(gchk)->style &&
506 TOARC(compgen)->width == TOARC(gchk)->width &&
507 abs(TOARC(compgen)->radius) == abs(TOARC(gchk)->radius) &&
508 TOARC(compgen)->yaxis == TOARC(gchk)->yaxis &&
509 TOARC(compgen)->angle1 == TOARC(gchk)->angle1 &&
510 TOARC(compgen)->angle2 == TOARC(gchk)->angle2);
511 break;
512 case(SPLINE):
513 bres = (TOSPLINE(compgen)->style == TOSPLINE(gchk)->style &&
514 TOSPLINE(compgen)->width == TOSPLINE(gchk)->width &&
515 TOSPLINE(compgen)->ctrl[0].x == TOSPLINE(gchk)->ctrl[0].x &&
516 TOSPLINE(compgen)->ctrl[0].y == TOSPLINE(gchk)->ctrl[0].y &&
517 TOSPLINE(compgen)->ctrl[1].x == TOSPLINE(gchk)->ctrl[1].x &&
518 TOSPLINE(compgen)->ctrl[1].y == TOSPLINE(gchk)->ctrl[1].y &&
519 TOSPLINE(compgen)->ctrl[2].x == TOSPLINE(gchk)->ctrl[2].x &&
520 TOSPLINE(compgen)->ctrl[2].y == TOSPLINE(gchk)->ctrl[2].y &&
521 TOSPLINE(compgen)->ctrl[3].x == TOSPLINE(gchk)->ctrl[3].x &&
522 TOSPLINE(compgen)->ctrl[3].y == TOSPLINE(gchk)->ctrl[3].y);
523 break;
524 case(POLYGON): {
525 int i;
526 if (TOPOLY(compgen)->style == TOPOLY(gchk)->style &&
527 TOPOLY(compgen)->width == TOPOLY(gchk)->width &&
528 TOPOLY(compgen)->number == TOPOLY(gchk)->number) {
529 for (i = 0; i < TOPOLY(compgen)->number; i++) {
530 if (TOPOLY(compgen)->points[i].x != TOPOLY(gchk)->points[i].x
531 || TOPOLY(compgen)->points[i].y != TOPOLY(gchk)->points[i].y)
532 break;
534 bres = (i == TOPOLY(compgen)->number);
536 else bres = False;
537 }break;
539 return bres;
542 /*--------------------------------------------------------------*/
543 /* Compare any element with any other element. */
544 /*--------------------------------------------------------------*/
546 Boolean compare_single(genericptr *compgen, genericptr *gchk)
548 Boolean bres = False;
550 if ((*gchk)->type == (*compgen)->type) {
551 switch(ELEMENTTYPE(*compgen)) {
552 case(OBJINST):{
553 objinst *newobj = TOOBJINST(compgen);
554 objinst *oldobj = TOOBJINST(gchk);
555 bres = (newobj->position.x == oldobj->position.x &&
556 newobj->position.y == oldobj->position.y &&
557 newobj->rotation == oldobj->rotation &&
558 newobj->scale == oldobj->scale &&
559 newobj->style == oldobj->style &&
560 newobj->thisobject == oldobj->thisobject);
561 } break;
562 case(LABEL):
563 bres = (TOLABEL(compgen)->position.x == TOLABEL(gchk)->position.x &&
564 TOLABEL(compgen)->position.y == TOLABEL(gchk)->position.y &&
565 TOLABEL(compgen)->rotation == TOLABEL(gchk)->rotation &&
566 TOLABEL(compgen)->scale == TOLABEL(gchk)->scale &&
567 TOLABEL(compgen)->anchor == TOLABEL(gchk)->anchor &&
568 TOLABEL(compgen)->pin == TOLABEL(gchk)->pin &&
569 !stringcomp(TOLABEL(compgen)->string, TOLABEL(gchk)->string));
570 break;
571 case(PATH): /* elements *must* be in same order for a path */
572 bres = (TOPATH(compgen)->parts == TOPATH(gchk)->parts &&
573 TOPATH(compgen)->style == TOPATH(gchk)->style &&
574 TOPATH(compgen)->width == TOPATH(gchk)->width);
575 if (bres) {
576 genericptr *pathchk, *gpath;
577 for (pathchk = TOPATH(compgen)->plist, gpath =
578 TOPATH(gchk)->plist; pathchk < TOPATH(compgen)->plist
579 + TOPATH(compgen)->parts; pathchk++, gpath++) {
580 if (!elemcompare(pathchk, gpath)) bres = False;
583 break;
584 case(ARC): case(SPLINE): case(POLYGON):
585 bres = elemcompare(compgen, gchk);
586 break;
589 return bres;
592 /*--------------------------------------------------------------------*/
594 short objcompare(objectptr obja, objectptr objb)
596 genericptr *compgen, *glist, *gchk, *remg;
597 short csize;
598 Boolean bres;
600 /* quick check on equivalence of number of objects */
602 if (obja->parts != objb->parts) return False;
604 /* check equivalence of parameters. Parameters need not be in any */
605 /* order; they must only match by key and value. */
607 if (obja->params == NULL && objb->params != NULL) return False;
608 else if (obja->params != NULL && objb->params == NULL) return False;
609 else if (obja->params != NULL || objb->params != NULL) {
610 oparamptr opsa, opsb;
611 for (opsa = obja->params; opsa != NULL; opsa = opsa->next) {
612 opsb = match_param(objb, opsa->key);
613 if (opsb == NULL) return False;
614 else if (opsa->type != opsb->type) return False;
615 switch (opsa->type) {
616 case XC_STRING:
617 if (stringcomp(opsa->parameter.string, opsb->parameter.string))
618 return False;
619 break;
620 case XC_EXPR:
621 if (strcmp(opsa->parameter.expr, opsb->parameter.expr))
622 return False;
623 break;
624 case XC_INT: case XC_FLOAT:
625 if (opsa->parameter.ivalue != opsb->parameter.ivalue)
626 return False;
627 break;
632 /* For the exhaustive check we must match component for component. */
633 /* Best not to assume that elements are in same order for both. */
635 csize = obja->parts;
637 glist = (genericptr *)malloc(csize * sizeof(genericptr));
638 for (compgen = objb->plist; compgen < objb->plist + csize; compgen++)
639 (*(glist + (int)(compgen - objb->plist))) = *compgen;
640 for (compgen = obja->plist; compgen < obja->plist + obja->parts;
641 compgen++) {
642 bres = False;
643 for (gchk = glist; gchk < glist + csize; gchk++) {
644 if ((*compgen)->color == (*gchk)->color)
645 bres = compare_single(compgen, gchk);
646 if (bres) {
647 csize--;
648 for (remg = gchk; remg < glist + csize; remg++)
649 *remg = *(remg + 1);
650 break;
654 free(glist);
655 if (csize != 0) return False;
657 /* Both objects cannot attempt to set an associated schematic/symbol to */
658 /* separate objects, although it is okay for one to make the association */
659 /* and the other not to. */
661 if (obja->symschem != NULL && objb->symschem != NULL)
662 if (obja->symschem != objb->symschem)
663 return False;
665 return(True);
668 /*------------------------*/
669 /* scale renormalization */
670 /*------------------------*/
672 float getpsscale(float value, short page)
674 if (xobjs.pagelist[page]->coordstyle != CM)
675 return (value * INCHSCALE);
676 else
677 return (value * CMSCALE);
680 /*---------------------------------------------------------------*/
681 /* Keep track of columns of output and split lines when too long */
682 /*---------------------------------------------------------------*/
684 void dostcount(FILE *ps, short *count, short addlength)
686 *count += addlength;
687 if (*count > OUTPUTWIDTH) {
688 *count = addlength;
689 fprintf(ps, "\n");
693 /*----------------------------------------------------------------------*/
694 /* Write a numerical value as a string to _STR, making a parameter */
695 /* substitution if appropriate. */
696 /* Return 1 if a parameter substitution was made, 0 if not. */
697 /*----------------------------------------------------------------------*/
699 Boolean varpcheck(FILE *ps, short value, objectptr localdata, int pointno,
700 short *stptr, genericptr thiselem, u_char which)
702 oparamptr ops;
703 eparamptr epp;
704 Boolean done = False;
706 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
707 if ((epp->pdata.pointno != -1) && (epp->pdata.pointno != pointno)) continue;
708 ops = match_param(localdata, epp->key);
709 if (ops != NULL && (ops->which == which)) {
710 sprintf(_STR, "%s ", epp->key);
711 done = True;
712 break;
716 if (!done) {
717 if (pointno == -1) return done;
718 sprintf(_STR, "%d ", (int)value);
720 else if ((epp->pdata.pointno == -1) && (pointno >= 0)) {
721 sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
724 dostcount (ps, stptr, strlen(_STR));
725 fputs(_STR, ps);
726 return done;
729 /*----------------------------------------------------------------------*/
730 /* like varpcheck(), but without pointnumber */
731 /*----------------------------------------------------------------------*/
733 void varcheck(FILE *ps, short value, objectptr localdata,
734 short *stptr, genericptr thiselem, u_char which)
736 varpcheck(ps, value, localdata, 0, stptr, thiselem, which);
739 /*----------------------------------------------------------------------*/
740 /* like varcheck(), but for floating-point values */
741 /*----------------------------------------------------------------------*/
743 void varfcheck(FILE *ps, float value, objectptr localdata, short *stptr,
744 genericptr thiselem, u_char which)
746 oparamptr ops;
747 eparamptr epp;
748 Boolean done = False;
750 for (epp = thiselem->passed; epp != NULL; epp = epp->next) {
751 ops = match_param(localdata, epp->key);
752 if (ops != NULL && (ops->which == which)) {
753 sprintf(_STR, "%s ", epp->key);
754 done = True;
755 break;
759 if (!done)
760 sprintf(_STR, "%3.3f ", value);
762 dostcount (ps, stptr, strlen(_STR));
763 fputs(_STR, ps);
766 /*----------------------------------------------------------------------*/
767 /* Like varpcheck(), for path types only. */
768 /*----------------------------------------------------------------------*/
770 Boolean varpathcheck(FILE *ps, short value, objectptr localdata, int pointno,
771 short *stptr, genericptr *thiselem, pathptr thispath, u_char which)
773 oparamptr ops;
774 eparamptr epp;
775 Boolean done = False;
777 for (epp = thispath->passed; epp != NULL; epp = epp->next) {
778 if ((epp->pdata.pathpt[0] != -1) && (epp->pdata.pathpt[1] != pointno)) continue;
779 if ((epp->pdata.pathpt[0] != -1) && (epp->pdata.pathpt[0] !=
780 (short)(thiselem - thispath->plist))) continue;
781 ops = match_param(localdata, epp->key);
782 if (ops != NULL && (ops->which == which)) {
783 sprintf(_STR, "%s ", epp->key);
784 done = True;
785 break;
789 if (!done) {
790 if (pointno == -1) return done;
791 sprintf(_STR, "%d ", (int)value);
793 else if ((epp->pdata.pathpt[0] == -1) && (pointno >= 0)) {
794 sprintf(_STR, "%d ", (int)value - ops->parameter.ivalue);
796 dostcount (ps, stptr, strlen(_STR));
797 fputs(_STR, ps);
798 return done;
801 /* Structure used to hold data specific to each load mode. See */
802 /* xcircuit.h for the list of load modes (enum loadmodes) */
804 typedef struct _loaddata {
805 void (*func)(); /* Routine to run to load the file */
806 char *prompt; /* Substring name of action, for prompting */
807 char *filext; /* Default extention of file to load */
808 } loaddata;
810 /*-------------------------------------------------------*/
811 /* Load a PostScript or Python (interpreter script) file */
812 /*-------------------------------------------------------*/
814 void getfile(xcWidget button, pointertype mode, caddr_t nulldata)
816 static loaddata loadmodes[LOAD_MODES] = {
817 {normalloadfile, "load", "ps"}, /* mode NORMAL */
818 {importfile, "import", "ps"}, /* mode IMPORT */
819 {loadbackground, "render", "ps"}, /* mode PSBKGROUND */
820 #ifdef HAVE_PYTHON
821 {execscript, "execute", "py"},
822 #else
823 {execscript, "execute", ""}, /* mode SCRIPT */
824 #endif
825 {crashrecover, "recover", "ps"}, /* mode RECOVER */
826 #ifdef ASG
827 {importspice, "import", "spice"}, /* mode IMPORTSPICE */
828 #endif
829 #ifdef HAVE_CAIRO
830 {importgraphic, "import", "ppm"}, /* mode IMPORTGRAPHIC */
831 #endif
834 buttonsave *savebutton = NULL;
835 char *promptstr = NULL;
836 /* char strext[10]; (jdk) */
837 int idx = (int)mode;
839 if (is_page(topobject) == -1) {
840 Wprintf("Can only read file into top-level page!");
841 return;
843 else if (idx >= LOAD_MODES) {
844 Wprintf("Unknown mode passed to routine getfile()\n");
845 return;
847 #ifndef TCL_WRAPPER
848 savebutton = getgeneric(button, getfile, (void *)mode);
849 #endif
850 if (idx == RECOVER) {
851 char *cfile = getcrashfilename();
852 promptstr = (char *)malloc(18 + ((cfile == NULL) ? 9 : strlen(cfile)));
853 sprintf(promptstr, "Recover file \'%s\'?", (cfile == NULL) ? "(unknown)" : cfile);
854 popupprompt(button, promptstr, NULL, loadmodes[idx].func, savebutton, NULL);
855 if (cfile) free(cfile);
857 else {
858 promptstr = (char *)malloc(18 + strlen(loadmodes[idx].prompt));
859 sprintf(promptstr, "Select file to %s:", loadmodes[idx].prompt);
860 popupprompt(button, promptstr, "\0", loadmodes[idx].func,
861 savebutton, loadmodes[idx].filext);
863 free(promptstr);
866 /*--------------------------------------------------------------*/
867 /* Tilde ('~') expansion in file name. Assumes that filename */
868 /* is a static character array of size "nchars". */
869 /*--------------------------------------------------------------*/
871 Boolean xc_tilde_expand(char *filename, int nchars)
873 #ifndef _MSC_VER
874 struct passwd *passwd;
875 char *username = NULL, *expanded, *sptr;
877 if (*filename == '~') {
878 sptr = filename + 1;
879 if (*sptr == '/' || *sptr == ' ' || *sptr == '\0')
880 username = getenv("HOME");
881 else {
882 for (; *sptr != '/' && *sptr != '\0'; sptr++);
883 if (*sptr == '\0') *(sptr + 1) = '\0';
884 *sptr = '\0';
886 passwd = getpwnam(filename + 1);
887 if (passwd != NULL)
888 username = passwd->pw_dir;
890 *sptr = '/';
892 if (username != NULL) {
893 expanded = (char *)malloc(strlen(username) +
894 strlen(filename));
895 strcpy(expanded, username);
896 strcat(expanded, sptr);
897 strncpy(filename, expanded, nchars);
898 free(expanded);
900 return True;
902 return False;
903 #else
904 return False;
905 #endif
908 /*--------------------------------------------------------------*/
909 /* Variable ('$') expansion in file name */
910 /*--------------------------------------------------------------*/
912 Boolean xc_variable_expand(char *filename, int nchars)
914 char *expanded, *sptr, tmpchar, *varpos, *varsub;
916 if ((varpos = strchr(filename, '$')) != NULL) {
917 for (sptr = varpos; *sptr != '/' && *sptr != '\0'; sptr++);
918 if (*sptr == '\0') *(sptr + 1) = '\0';
919 tmpchar = *sptr;
920 *sptr = '\0';
922 #ifdef TCL_WRAPPER
923 /* Interpret as a Tcl variable */
924 varsub = (char *)Tcl_GetVar(xcinterp, varpos + 1, TCL_NAMESPACE_ONLY);
925 #else
926 /* Interpret as an environment variable */
927 varsub = (char *)getenv((const char *)(varpos + 1));
928 #endif
930 if (varsub != NULL) {
932 *varpos = '\0';
933 expanded = (char *)malloc(strlen(varsub) + strlen(filename) +
934 strlen(sptr + 1) + 2);
935 strcpy(expanded, filename);
936 strcat(expanded, varsub);
937 *sptr = tmpchar;
938 strcat(expanded, sptr);
939 strncpy(filename, expanded, nchars);
940 free(expanded);
942 else
943 *sptr = tmpchar;
944 return True;
946 return False;
949 /*--------------------------------------------------------------*/
950 /* Attempt to find a file and open it. */
951 /*--------------------------------------------------------------*/
953 FILE *fileopen(char *filename, char *suffix, char *name_return, int nchars)
955 FILE *file = NULL;
956 char inname[250], expname[250], *sptr, *cptr, *iptr, *froot;
957 int slen;
959 sscanf(filename, "%249s", expname);
960 xc_tilde_expand(expname, 249);
961 while (xc_variable_expand(expname, 249));
963 sptr = xobjs.filesearchpath;
964 while (1) {
965 if ((xobjs.filesearchpath == NULL) || (expname[0] == '/')) {
966 strcpy(inname, expname);
967 iptr = inname;
969 else {
970 strcpy(inname, sptr);
971 cptr = strchr(sptr, ':');
972 slen = (cptr == NULL) ? strlen(sptr) : (int)(cptr - sptr);
973 sptr += (slen + ((cptr == NULL) ? 0 : 1));
974 iptr = inname + slen;
975 if (*(iptr - 1) != '/') strcpy(iptr++, "/");
976 strcpy(iptr, expname);
979 /* Attempt to open the filename with a suffix */
981 if ((froot = strrchr(iptr, '/')) == NULL) froot = iptr;
982 if (strrchr(froot, '.') == NULL) {
983 if (suffix) {
984 if (suffix[0] != '.')
985 strncat(inname, ".", 249);
986 strncat(inname, suffix, 249);
988 file = fopen(inname, "r");
991 /* Attempt to open the filename as given, without a suffix */
993 if (file == NULL) {
994 strcpy(iptr, expname);
995 file = fopen(inname, "r");
998 if (file != NULL) break;
999 else if (sptr == NULL) break;
1000 else if (*sptr == '\0') break;
1003 if (name_return) strncpy(name_return, inname, nchars);
1004 return file;
1007 /*---------------------------------------------------------*/
1009 Boolean nextfilename() /* extract next filename from _STR2 into _STR */
1011 char *cptr, *slptr;
1013 sprintf(_STR, "%.149s", _STR2);
1014 if ((cptr = strrchr(_STR2, ',')) != NULL) {
1015 slptr = strrchr(_STR, '/');
1016 if (slptr == NULL || ((slptr - _STR) > (cptr - _STR2))) slptr = _STR - 1;
1017 sprintf(slptr + 1, "%s", cptr + 1);
1018 *cptr = '\0';
1019 return True;
1021 else return False;
1024 /*---------------------------------------------------------*/
1026 void loadfontlib()
1028 loadlibrary(FONTLIB);
1031 /*------------------------------------------------------*/
1032 /* Handle library loading and refresh current page if */
1033 /* it is a library page that just changed. */
1034 /*------------------------------------------------------*/
1036 void loadglib(Boolean lflag, short ilib, short tlib)
1038 while (nextfilename()) {
1039 if (lflag)
1040 lflag = False;
1041 else
1042 ilib = createlibrary(False);
1043 loadlibrary(ilib);
1044 /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
1046 if (lflag)
1047 lflag = False;
1048 else
1049 ilib = createlibrary(False);
1050 loadlibrary(ilib);
1051 /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
1054 /*------------------------------------------------------*/
1055 /* Load new library: Create new library page and load */
1056 /* to it. */
1057 /*------------------------------------------------------*/
1059 void loadulib()
1061 loadglib(False, (short)0, (short)is_library(topobject) + LIBRARY);
1064 /*-----------------------------------------------------------*/
1065 /* Add to library: If on library page, add to that library. */
1066 /* Otherwise, create a new library page and load to it. */
1067 /*-----------------------------------------------------------*/
1069 void loadblib()
1071 short ilib, tlib;
1072 Boolean lflag = True;
1074 /* Flag whether current page is a library page or not */
1076 if ((tlib = is_library(topobject)) < 0) {
1077 ilib = LIBRARY;
1078 lflag = False;
1080 else
1081 ilib = tlib + LIBRARY;
1083 loadglib(lflag, ilib, tlib + LIBRARY);
1086 /*---------------------------------------------------------*/
1088 void getlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
1090 buttonsave *savebutton;
1091 #ifndef TCL_WRAPPER
1092 savebutton = getgeneric(button, getlib, NULL);
1093 #endif
1094 popupprompt(button, "Enter library to load:", "\0", loadblib, savebutton,
1095 "lps");
1098 /*---------------------------------------------------------*/
1100 void getuserlib(xcWidget button, caddr_t clientdata, caddr_t nulldata)
1102 buttonsave *savebutton;
1104 #ifndef TCL_WRAPPER
1105 savebutton = getgeneric(button, getuserlib, NULL);
1106 #endif
1107 popupprompt(button, "Enter library to load:", "\0", loadulib, savebutton,
1108 "lps");
1111 /*------------------------------------------------------*/
1112 /* Add a new name to the list of aliases for an object */
1113 /*------------------------------------------------------*/
1115 Boolean addalias(objectptr thisobj, char *newname)
1117 aliasptr aref;
1118 slistptr sref;
1119 /* Boolean retval = False; (jdk) */
1120 char *origname = thisobj->name;
1122 for (aref = aliastop; aref != NULL; aref = aref->next)
1123 if (aref->baseobj == thisobj)
1124 break;
1126 /* An equivalence, not an alias */
1127 if (!strcmp(origname, newname)) return True;
1129 if (aref == NULL) { /* entry does not exist; add new baseobj */
1130 aref = (aliasptr)malloc(sizeof(alias));
1131 aref->baseobj = thisobj;
1132 aref->aliases = NULL;
1133 aref->next = aliastop;
1134 aliastop = aref;
1137 for (sref = aref->aliases; sref != NULL; sref = sref->next)
1138 if (!strcmp(sref->alias, newname))
1139 break;
1141 if (sref == NULL) { /* needs new entry */
1142 sref = (slistptr)malloc(sizeof(stringlist));
1143 sref->alias = strdup(newname);
1144 sref->next = aref->aliases;
1145 aref->aliases = sref;
1146 return False;
1148 else return True; /* alias already exists! */
1151 /*------------------------------------------------------*/
1152 /* Remove all object name aliases */
1153 /*------------------------------------------------------*/
1155 void cleanupaliases(short mode)
1157 aliasptr aref;
1158 slistptr sref;
1159 objectptr baseobj;
1160 char *sptr; /* *basename, (jdk) */
1161 int i, j;
1163 if (aliastop == NULL) return;
1165 for (aref = aliastop; aref != NULL; aref = aref->next) {
1166 baseobj = aref->baseobj;
1167 for (sref = aref->aliases; sref != NULL; sref = sref->next)
1168 free(sref->alias);
1171 for (; (aref = aliastop->next); aliastop = aref)
1172 free(aliastop);
1173 free(aliastop);
1174 aliastop = NULL;
1176 /* Get rid of propagating underscores in names */
1178 for (i = 0; i < ((mode == FONTLIB) ? 1 : xobjs.numlibs); i++) {
1179 for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number :
1180 xobjs.userlibs[i].number); j++) {
1181 baseobj = (mode == FONTLIB) ? *(xobjs.fontlib.library + j) :
1182 *(xobjs.userlibs[i].library + j);
1184 sptr = baseobj->name;
1185 while (*sptr == '_') sptr++;
1186 /* need memmove to avoid overwriting? */
1187 memmove((void *)baseobj->name, (const void *)sptr, strlen(sptr) + 1);
1188 checkname(baseobj);
1193 /*------------------------------------------------------*/
1194 /* Open a library file by name and return a pointer to */
1195 /* the file structure, or NULL if an error occurred. */
1196 /*------------------------------------------------------*/
1198 FILE *libopen(char *libname, short mode, char *name_return, int nchars)
1200 FILE *file = NULL;
1201 char inname[150], expname[150], *sptr, *cptr, *iptr;
1202 int slen;
1203 char *suffix = (mode == FONTENCODING) ? ".xfe" : ".lps";
1205 sscanf(libname, "%149s", expname);
1206 xc_tilde_expand(expname, 149);
1207 while(xc_variable_expand(expname, 149));
1209 sptr = xobjs.libsearchpath;
1210 while (1) {
1212 if ((xobjs.libsearchpath == NULL) || (expname[0] == '/')) {
1213 strcpy(inname, expname);
1214 iptr = inname;
1216 else {
1217 strcpy(inname, sptr);
1218 cptr = strchr(sptr, ':');
1219 slen = (cptr == NULL) ? strlen(sptr) : (int)(cptr - sptr);
1220 sptr += (slen + ((cptr == NULL) ? 0 : 1));
1221 iptr = inname + slen;
1222 if (*(iptr - 1) != '/') strcpy(iptr++, "/");
1223 strcpy(iptr, expname);
1226 /* Try to open the filename with a suffix if it doesn't have one */
1228 if (strrchr(iptr, '.') == NULL) {
1229 strncat(inname, suffix, 149);
1230 file = fopen(inname, "r");
1233 /* Try to open the filename as given, without a suffix */
1235 if (file == NULL) {
1236 strcpy(iptr, expname);
1237 file = fopen(inname, "r");
1240 if (file != NULL) break;
1241 else if (sptr == NULL) break;
1242 else if (*sptr == '\0') break;
1245 if ((file == NULL) && (xobjs.libsearchpath == NULL)) {
1247 /* if not found in cwd and there is no library search */
1248 /* path, look for environment variable "XCIRCUIT_LIB_DIR" */
1249 /* defined (Thanks to Ali Moini, U. Adelaide, S. Australia) */
1251 char *tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");
1253 if (tmp_s != NULL) {
1254 sprintf(inname, "%s/%s", tmp_s, expname);
1255 file = fopen(inname, "r");
1256 if (file == NULL) {
1257 sprintf(inname, "%s/%s%s", tmp_s, expname, suffix);
1258 file = fopen(inname, "r");
1262 /* last resort: hard-coded directory BUILTINS_DIR */
1264 if (file == NULL) {
1265 sprintf(inname, "%s/%s", BUILTINS_DIR, expname);
1266 file = fopen(inname, "r");
1267 if (file == NULL) {
1268 sprintf(inname, "%s/%s%s", BUILTINS_DIR, expname, suffix);
1269 file = fopen(inname, "r");
1274 if (name_return) strncpy(name_return, inname, nchars);
1275 return file;
1278 /*--------------------------------------------------------------*/
1279 /* Add a record to the instlist list of the library indexed by */
1280 /* mode, create a new instance, and add it to the record. */
1281 /* */
1282 /* objname is the name of the library object to be instanced, */
1283 /* and buffer is the line containing the instance parameters */
1284 /* of the new instance, optionally preceded by scale and */
1285 /* rotation values. */
1286 /* */
1287 /*--------------------------------------------------------------*/
1289 objinstptr new_library_instance(short mode, char *objname, char *buffer,
1290 TechPtr defaulttech)
1292 char *lineptr;
1293 objectptr libobj, localdata;
1294 objinstptr newobjinst;
1295 int j;
1296 char *nsptr, *fullname = objname;
1298 localdata = xobjs.libtop[mode + LIBRARY]->thisobject;
1300 /* For (older) libraries that do not use technologies, give the */
1301 /* object a technology name in the form <library>::<object> */
1303 if ((nsptr = strstr(objname, "::")) == NULL) {
1304 int deftechlen = (defaulttech == NULL) ? 0 : strlen(defaulttech->technology);
1305 fullname = (char *)malloc(deftechlen + strlen(objname) + 3);
1306 if (defaulttech == NULL)
1307 sprintf(fullname, "::%s", objname);
1308 else
1309 sprintf(fullname, "%s::%s", defaulttech->technology, objname);
1312 for (j = 0; j < xobjs.userlibs[mode].number; j++) {
1313 libobj = *(xobjs.userlibs[mode].library + j);
1314 if (!strcmp(fullname, libobj->name)) {
1315 newobjinst = addtoinstlist(mode, libobj, TRUE);
1317 lineptr = buffer;
1318 while (isspace(*lineptr)) lineptr++;
1319 if (*lineptr != '<') {
1320 /* May declare instanced scale and rotation first */
1321 lineptr = varfscan(localdata, lineptr, &newobjinst->scale,
1322 (genericptr)newobjinst, P_SCALE);
1323 lineptr = varfscan(localdata, lineptr, &newobjinst->rotation,
1324 (genericptr)newobjinst, P_ROTATION);
1326 readparams(NULL, newobjinst, libobj, lineptr);
1327 if (fullname != objname) free(fullname);
1328 return newobjinst;
1331 if (fullname != objname) free(fullname);
1332 return NULL; /* error finding the library object */
1335 /*------------------------------------------------------*/
1336 /* Import a single object from a library file. "mode" */
1337 /* is the number of the library into which the object */
1338 /* will be placed. This function allows new libraries */
1339 /* to be generated by "cutting and pasting" from */
1340 /* existing libraries. It also allows better library */
1341 /* management when the number of objects becomes very */
1342 /* large, such as when using "diplib" (7400 series */
1343 /* chips, created from the PCB library). */
1344 /*------------------------------------------------------*/
1346 void importfromlibrary(short mode, char *libname, char *objname)
1348 FILE *ps;
1349 char temp[150], keyword[100];
1350 char inname[150], *tptr;
1351 objectptr *newobject;
1352 objlistptr redef;
1353 float saveversion;
1354 Boolean dependencies = False;
1355 TechPtr nsptr = NULL;
1357 ps = libopen(libname, mode, inname, 149);
1358 if (ps == NULL) {
1359 Fprintf(stderr, "Cannot open library %s for import.\n", libname);
1360 return;
1363 version = 2.0; /* Assume version is 2.0 unless found in header */
1365 for(;;) {
1366 if (fgets(temp, 149, ps) == NULL) {
1367 Wprintf("Error in library.");
1368 goto endload;
1370 else if (temp[0] == '/') {
1371 int s = 1;
1372 if (temp[1] == '@') s = 2;
1373 sscanf(&temp[s], "%s", keyword);
1374 if (!strcmp(keyword, objname))
1375 break;
1377 else if (*temp == '%') {
1378 char *tptr = temp + 1;
1379 while (isspace(*tptr)) tptr++;
1380 if (!strncmp(tptr, "Version:", 8)) {
1381 float tmpv;
1382 tptr += 9;
1383 if (sscanf(tptr, "%f", &tmpv) > 0) version = tmpv;
1385 else if (!strncmp(tptr, "Library", 7)) {
1386 char *techname = strstr(tptr, ":");
1387 if (techname != NULL) {
1388 techname++; /* skip over colon */
1389 while(isspace(*techname)) techname++;
1390 ridnewline(techname); /* Remove newline character */
1391 if ((tptr = strrchr(techname, '/')) != NULL)
1392 techname = tptr + 1;
1393 if ((tptr = strrchr(techname, '.')) != NULL)
1394 if (!strncmp(tptr, ".lps", 4))
1395 *tptr = '\0';
1396 nsptr = AddNewTechnology(techname, inname);
1397 if (nsptr) {
1398 // Set the IMPORTED flag, to help prevent overwriting
1399 // the techfile with a truncated version of it, unless
1400 // the filename of the techfile has been changed, in
1401 // which case the technology should be considered to
1402 // stand on its own, and is not considered a partially
1403 // complete imported version of the original techfile.
1405 if (!strcmp(inname, nsptr->filename))
1406 nsptr->flags |= TECH_IMPORTED;
1410 else if (!strncmp(tptr, "Depend", 6)) {
1411 dependencies = TRUE;
1412 tptr += 7;
1413 sscanf(tptr, "%s", keyword);
1414 if (!strcmp(keyword, objname)) {
1415 /* Load dependencies */
1416 while (1) {
1417 tptr += strlen(keyword) + 1;
1418 if (sscanf(tptr, "%s", keyword) != 1) break;
1419 if (keyword[0] == '\n' || keyword[0] == '\0') break;
1420 /* Recursive import */
1421 saveversion = version;
1422 importfromlibrary(mode, libname, keyword);
1423 version = saveversion;
1430 if ((version < 3.2) && (!dependencies)) {
1431 Fprintf(stderr, "Library does not have dependency list and cannot "
1432 "be trusted.\nLoad and rewrite library to update.\n");
1433 goto endload;
1436 newobject = new_library_object(mode, keyword, &redef, nsptr);
1438 load_in_progress = True;
1439 if (objectread(ps, *newobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr) == False) {
1441 if (library_object_unique(mode, *newobject, redef)) {
1442 add_object_to_library(mode, *newobject);
1443 cleanupaliases(mode);
1445 /* pull in any instances of this object that */
1446 /* are defined in the library */
1448 for(;;) {
1449 if (fgets(temp, 149, ps) == NULL) {
1450 Wprintf("Error in library.");
1451 goto endload;
1453 else if (!strncmp(temp, "% EndLib", 8))
1454 break;
1455 else if (strstr(temp, "libinst") != NULL) {
1456 if ((tptr = strstr(temp, objname)) != NULL) {
1457 if (*(tptr - 1) == '/') {
1458 char *eptr = tptr;
1459 while (!isspace(*++eptr));
1460 *eptr = '\0';
1461 new_library_instance(mode - LIBRARY, tptr, temp, nsptr);
1467 if (mode != FONTLIB) {
1468 composelib(mode);
1469 centerview(xobjs.libtop[mode]);
1474 endload:
1475 fclose(ps);
1476 version = PROG_VERSION;
1477 load_in_progress = False;
1480 /*------------------------------------------------------*/
1481 /* Copy all technologies' replace flags into temp flag. */
1482 /* Then clear the replace flags (replace none) */
1483 /*------------------------------------------------------*/
1485 void TechReplaceSave()
1487 TechPtr nsp;
1489 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1491 if (nsp->flags & TECH_REPLACE)
1492 nsp->flags |= TECH_REPLACE_TEMP;
1493 else
1494 nsp->flags &= ~TECH_REPLACE_TEMP;
1495 nsp->flags &= ~TECH_REPLACE;
1499 /*------------------------------------------------------*/
1500 /* Restore all technologies' replace flags */
1501 /*------------------------------------------------------*/
1503 void TechReplaceRestore()
1505 TechPtr nsp;
1507 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1509 if (nsp->flags & TECH_REPLACE_TEMP)
1510 nsp->flags |= TECH_REPLACE;
1511 else
1512 nsp->flags &= ~TECH_REPLACE;
1516 /*------------------------------------------------------*/
1517 /* Set all technologies' replace flags (replace all) */
1518 /*------------------------------------------------------*/
1520 void TechReplaceAll()
1522 TechPtr nsp;
1524 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1525 nsp->flags |= TECH_REPLACE;
1528 /*------------------------------------------------------*/
1529 /* Clear all technologies' replace flags (replace none) */
1530 /*------------------------------------------------------*/
1532 void TechReplaceNone()
1534 TechPtr nsp;
1536 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1537 nsp->flags &= ~TECH_REPLACE;
1541 /*------------------------------------------------------*/
1542 /* Compare an object's name with a specific technology */
1543 /* */
1544 /* A missing "::" prefix separator or an empty prefix */
1545 /* both match a NULL technology or a technology name */
1546 /* that is an empty string (""). All of these */
1547 /* conditions indicate the default "user" technology. */
1548 /*------------------------------------------------------*/
1550 Boolean CompareTechnology(objectptr cobj, char *technology)
1552 char *cptr;
1553 Boolean result = FALSE;
1555 if ((cptr = strstr(cobj->name, "::")) != NULL) {
1556 if (technology == NULL)
1557 result = (cobj->name == cptr) ? TRUE : FALSE;
1558 else {
1559 *cptr = '\0';
1560 if (!strcmp(cobj->name, technology)) result = TRUE;
1561 *cptr = ':';
1564 else if (technology == NULL)
1565 result = TRUE;
1567 return result;
1570 /*------------------------------------------------------*/
1571 /* Find a technology record */
1572 /*------------------------------------------------------*/
1574 TechPtr LookupTechnology(char *technology)
1576 TechPtr nsp;
1577 Boolean usertech = FALSE;
1579 // A NULL technology is allowed as equivalent to a
1580 // technology name of "" (null string)
1582 if (technology == NULL)
1583 usertech = TRUE;
1584 else if (*technology == '\0')
1585 usertech = TRUE;
1586 else if (!strcmp(technology, "(user)"))
1587 usertech = TRUE;
1589 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1590 if (usertech == TRUE) {
1591 if (*nsp->technology == '\0')
1592 return nsp;
1594 if ((technology != NULL) && !strcmp(technology, nsp->technology))
1595 return nsp;
1597 return NULL;
1600 /*------------------------------------------------------*/
1601 /* Find a technology according to the filename */
1602 /*------------------------------------------------------*/
1604 TechPtr GetFilenameTechnology(char *filename)
1606 TechPtr nsp;
1608 if (filename == NULL) return NULL;
1610 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1611 if (!filecmp(filename, nsp->filename))
1612 return nsp;
1614 return NULL;
1617 /*------------------------------------------------------*/
1618 /* Find a technology record corresponding to the */
1619 /* indicated object's technology. */
1620 /*------------------------------------------------------*/
1622 TechPtr GetObjectTechnology(objectptr thisobj)
1624 TechPtr nsp;
1625 char *cptr;
1626 /* int nlen; (jdk) */
1628 cptr = strstr(thisobj->name, "::");
1629 if (cptr == NULL) return NULL;
1630 else *cptr = '\0';
1632 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next)
1633 if (!strcmp(thisobj->name, nsp->technology))
1634 break;
1636 *cptr = ':';
1637 return nsp;
1640 /*------------------------------------------------------*/
1641 /* Add a new technology name to the list */
1642 /*------------------------------------------------------*/
1644 TechPtr AddNewTechnology(char *technology, char *filename)
1646 TechPtr nsp;
1647 char usertech[] = "";
1648 char *localtech = technology;
1650 // In the case where somebody has saved the contents of the user
1651 // technology to a file, create a technology->filename mapping
1652 // using a null string ("") for the technology name. If we are
1653 // only checking if a technology name exists (filename is NULL),
1654 // then ignore an reference to the user technology.
1656 if (technology == NULL) {
1657 if (filename == NULL) return NULL;
1658 else localtech = usertech;
1661 for (nsp = xobjs.technologies; nsp != NULL; nsp = nsp->next) {
1662 if (!strcmp(localtech, nsp->technology)) {
1664 /* A namespace may be created for an object that is a dependency */
1665 /* in a different technology. If so, it will have a NULL */
1666 /* filename, and the filename should be replaced if we ever load */
1667 /* the file that properly defines the technology. */
1669 if ((nsp->filename == NULL) && (filename != NULL))
1670 nsp->filename = strdup(filename);
1672 return nsp; /* Namespace already exists */
1676 nsp = (TechPtr)malloc(sizeof(Technology));
1677 nsp->next = xobjs.technologies;
1678 if (filename == NULL)
1679 nsp->filename = NULL;
1680 else
1681 nsp->filename = strdup(filename);
1682 nsp->technology = strdup(localtech);
1683 nsp->flags = (u_char)0;
1684 xobjs.technologies = nsp;
1686 return nsp;
1689 /*------------------------------------------------------*/
1690 /* Check an object's name for a technology, and add it */
1691 /* to the list of technologies if it has one. */
1692 /*------------------------------------------------------*/
1694 void AddObjectTechnology(objectptr thisobj)
1696 char *cptr;
1698 cptr = strstr(thisobj->name, "::");
1699 if (cptr != NULL) {
1700 *cptr = '\0';
1701 AddNewTechnology(thisobj->name, NULL);
1702 *cptr = ':';
1706 /*------------------------------------------------------*/
1707 /* Load a library page (given in parameter "mode") and */
1708 /* rename the library page to match the library name as */
1709 /* found in the file header. */
1710 /*------------------------------------------------------*/
1712 Boolean loadlibrary(short mode)
1714 FILE *ps;
1715 objinstptr saveinst;
1716 char temp[150], keyword[30], percentc, inname[150];
1717 TechPtr nsptr = NULL;
1719 ps = libopen(_STR, mode, inname, 149);
1721 if ((ps == NULL) && (mode == FONTLIB)) {
1722 /* We automatically try looking in all the usual places plus a */
1723 /* subdirectory named "fonts". */
1725 sprintf(temp, "fonts/%s", _STR);
1726 ps = libopen(temp, mode, inname, 149);
1728 if (ps == NULL) {
1729 Wprintf("Library not found.");
1730 return False;
1733 /* current version is PROG_VERSION; however, all libraries newer than */
1734 /* version 2.0 require "Version" in the header. So unnumbered */
1735 /* libraries may be assumed to be version 1.9 or earlier. */
1737 version = 1.9;
1738 for(;;) {
1739 if (fgets(temp, 149, ps) == NULL) {
1740 Wprintf("Error in library.");
1741 fclose(ps);
1742 return False;
1744 sscanf(temp, "%c %29s", &percentc, keyword);
1746 /* Commands in header are PostScript comments (%) */
1747 if (percentc == '%') {
1749 /* The library name in the header corresponds to the object */
1750 /* technology defined by the library. This no longer has */
1751 /* anything to do with the name of the library page where we */
1752 /* are loading this file. */
1754 /* Save the technology, filename, and flags in the technology list. */
1756 if ((mode != FONTLIB) && !strcmp(keyword, "Library")) {
1757 char *cptr, *nptr;
1758 cptr = strchr(temp, ':');
1759 if (cptr != NULL) {
1760 cptr += 2;
1762 /* Don't write terminating newline to the object's name string */
1763 ridnewline(cptr);
1765 /* The default user technology is written to the output */
1766 /* as "(user)". If this is found, replace it with a */
1767 /* null string. */
1768 if (!strcmp(cptr, "(user)")) cptr += 6;
1770 /* Removing any leading pathname from the library name */
1771 if ((nptr = strrchr(cptr, '/')) != NULL) cptr = nptr + 1;
1773 /* Remove any ".lps" extension from the library name */
1775 nptr = strrchr(cptr, '.');
1776 if ((nptr != NULL) && !strcmp(nptr, ".lps")) *nptr = '\0';
1778 nsptr = AddNewTechnology(cptr, inname);
1780 if (nsptr) {
1781 // If anything was previously imported from this file
1782 // using importfromlibrary(), then the IMPORTED flag
1783 // will be set and needs to be cleared.
1784 nsptr->flags &= ~TECH_IMPORTED;
1789 /* This comment gives the Xcircuit version number */
1790 else if (!strcmp(keyword, "Version:")) {
1791 float tmpv;
1792 if (sscanf(temp, "%*c %*s %f", &tmpv) > 0) version = tmpv;
1795 /* This PostScript comment marks the end of the file header */
1796 else if (!strcmp(keyword, "XCircuitLib")) break;
1800 /* Set the current top object to the library page so that any */
1801 /* expression parameters are computed with respect to the library, */
1802 /* not a page. Revert back to the page after loading the library. */
1804 saveinst = areawin->topinstance;
1805 areawin->topinstance = xobjs.libtop[mode];
1807 load_in_progress = True;
1808 objectread(ps, topobject, 0, 0, mode, temp, DEFAULTCOLOR, nsptr);
1809 load_in_progress = False;
1810 cleanupaliases(mode);
1812 areawin->topinstance = saveinst;
1814 if (mode != FONTLIB) {
1815 composelib(mode);
1816 centerview(xobjs.libtop[mode]);
1817 if (nsptr == NULL) nsptr = GetFilenameTechnology(inname);
1818 if (nsptr != NULL)
1819 Wprintf("Loaded library file %s", inname);
1820 else
1821 Wprintf("Loaded library file %s (technology %s)", inname,
1822 nsptr->technology);
1824 else
1825 Wprintf("Loaded font file %s", inname);
1827 version = PROG_VERSION;
1828 fclose(ps);
1830 /* Check if the library is read-only by opening for append */
1832 if ((mode != FONTLIB) && (nsptr != NULL)) {
1833 ps = fopen(inname, "a");
1834 if (ps == NULL)
1835 nsptr->flags |= TECH_READONLY;
1836 else
1837 fclose(ps);
1840 return True;
1843 /*---------------------------------------------------------*/
1845 void startloadfile(int libnum)
1847 int savemode;
1848 short firstpage = areawin->page;
1850 while (nextfilename()) {
1851 loadfile(0, libnum);
1853 /* find next undefined page */
1855 while(areawin->page < xobjs.pages &&
1856 xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
1857 changepage(areawin->page);
1859 loadfile(0, libnum);
1862 /* Prevent page change from being registered as an undoable action */
1863 savemode = eventmode;
1864 eventmode = UNDO_MODE;
1866 /* Display the first page loaded */
1867 newpage(firstpage);
1868 eventmode = savemode;
1870 setsymschem();
1873 /*------------------------------------------------------*/
1874 /* normalloadfile() is a call to startloadfile(-1) */
1875 /* meaning load symbols to the User Library */
1876 /*------------------------------------------------------*/
1878 void normalloadfile()
1880 startloadfile(-1);
1883 /*------------------------------------------------------*/
1884 /* Import an xcircuit file onto the current page */
1885 /*------------------------------------------------------*/
1887 void importfile()
1889 while (nextfilename()) loadfile(1, -1);
1890 loadfile(1, -1);
1893 /*------------------------------------------------------*/
1894 /* Import an PPM graphic file onto the current page */
1895 /*------------------------------------------------------*/
1897 #ifdef HAVE_CAIRO
1898 void importgraphic(void)
1900 char inname[250];
1901 FILE *spcfile;
1903 if (eventmode == CATALOG_MODE) {
1904 Wprintf("Cannot import a graphic while in the library window.");
1905 return;
1908 if (!nextfilename()) {
1909 xc_tilde_expand(_STR, 149);
1910 sscanf(_STR, "%149s", inname);
1911 if (!new_graphic(NULL, inname, 0, 0)) {
1912 Wprintf("Error: Graphic file not found.");
1913 return;
1916 else {
1917 Wprintf("Error: No graphic file to read.");
1918 return;
1921 #endif /* HAVE_CAIRO */
1923 /*--------------------------------------------------------------*/
1924 /* Skip forward in the input file to the next comment line */
1925 /*--------------------------------------------------------------*/
1927 void skiptocomment(char *temp, int length, FILE *ps)
1929 int pch;
1931 do {
1932 pch = getc(ps);
1933 } while (pch == '\n');
1935 ungetc(pch, ps);
1936 if (pch == '%') fgets(temp, length, ps);
1939 /*--------------------------------------------------------------*/
1940 /* ASG file import functions: */
1941 /* This function loads a SPICE deck (hspice format) */
1942 /*--------------------------------------------------------------*/
1944 #ifdef ASG
1946 void importspice()
1948 char inname[250];
1949 FILE *spcfile;
1951 if (eventmode == CATALOG_MODE) {
1952 Wprintf("Cannot import a netlist while in the library window.");
1953 return;
1956 if (!nextfilename()) {
1957 xc_tilde_expand(_STR, 149);
1958 sscanf(_STR, "%149s", inname);
1959 spcfile = fopen(inname, "r");
1960 if (spcfile != NULL) {
1961 ReadSpice(spcfile);
1962 Route(areawin, False);
1963 fclose(spcfile);
1965 else {
1966 Wprintf("Error: Spice file not found.");
1967 return;
1970 else {
1971 Wprintf("Error: No spice file to read.");
1972 return;
1976 #endif
1978 /*--------------------------------------------------------------*/
1979 /* Load an xcircuit file into xcircuit */
1980 /* */
1981 /* mode = 0 is a "load" function. Behavior: load on */
1982 /* current page if empty. Otherwise, load on first empty */
1983 /* page found. If no empty pages exist, create a new page */
1984 /* and load there. */
1985 /* mode = 1 is an "import" function. Behavior: add */
1986 /* contents of file to the current page. */
1987 /* mode = 2 is a "library load" function. Behavior: add */
1988 /* objects in file to the user library but ignore the */
1989 /* page contents. */
1990 /* */
1991 /* Return value: True if file was successfully loaded, False */
1992 /* if not. */
1993 /*--------------------------------------------------------------*/
1995 typedef struct _connects *connectptr;
1997 typedef struct _connects {
1998 short page;
1999 char *master;
2000 connectptr next;
2001 } connects;
2003 Boolean loadfile(short mode, int libnum)
2005 FILE *ps;
2006 char inname[150], temp[150], keyword[30], percentc, *pdchar;
2007 char teststr[50], teststr2[20], pagestr[100];
2008 short offx, offy, multipage, page, temppmode = 0;
2009 float tmpfl;
2010 XPoint pagesize;
2011 connects *connlist = NULL;
2012 struct stat statbuf;
2013 int loclibnum = (libnum == -1) ? USERLIB : libnum;
2015 /* First, if we're in catalog mode, return with error */
2017 if (eventmode == CATALOG_MODE) {
2018 Wprintf("Cannot load file from library window");
2019 return False;
2022 /* Do tilde/variable expansions on filename and open */
2023 ps = fileopen(_STR, "ps", inname, 149);
2025 /* Could possibly be a library file? */
2026 /* (Note---loadfile() has no problems loading libraries */
2027 /* except for setting technology names and setting the */
2028 /* library page view at the end. The loadlibrary() routine */
2029 /* should probably be merged into this one.) */
2031 if (ps == NULL) {
2032 ps = fileopen(_STR, "lps", NULL, 0);
2033 if (ps != NULL) {
2034 fclose(ps);
2035 loadlibrary(loclibnum);
2036 return True;
2039 else if (!strcmp(inname + strlen(inname) - 4, ".lps")) {
2040 fclose(ps);
2041 loadlibrary(loclibnum);
2042 return True;
2046 #ifdef LGF
2047 /* Could possibly be an LGF file? */
2048 if (ps == NULL) {
2049 ps = fileopen(_STR, "lgf", NULL, 0);
2050 if (ps != NULL) {
2051 fclose(ps);
2052 loadlgf(mode);
2053 return True;
2057 /* Could possibly be an LGF backup (.lfo) file? */
2058 if (ps == NULL) {
2059 ps = fileopen(_STR, "lfo", NULL, 0);
2060 if (ps != NULL) {
2061 fclose(ps);
2062 loadlgf(mode);
2063 return True;
2066 #endif /* LGF */
2068 /* Check for empty file---don't attempt to read empty files */
2069 if (ps != NULL) {
2070 if (fstat(fileno(ps), &statbuf) == 0 && (statbuf.st_size == (off_t)0)) {
2071 fclose(ps);
2072 ps = NULL;
2076 /* What to do if no file was found. . . */
2078 if (ps == NULL) {
2079 if (topobject->parts == 0 && (mode == 0)) {
2081 /* Check for file extension, and remove if "ps". */
2083 if ((pdchar = strchr(_STR, '.')) != NULL)
2084 if (!strcmp(pdchar + 1, "ps")) *pdchar = '\0';
2086 free(xobjs.pagelist[areawin->page]->filename);
2087 xobjs.pagelist[areawin->page]->filename = strdup(_STR);
2089 /* If the name has a path component, use only the root */
2090 /* for the object name, but the full path for the filename. */
2092 if ((pdchar = strrchr(_STR, '/')) != NULL)
2093 sprintf(topobject->name, "%s", pdchar + 1);
2094 else
2095 sprintf(topobject->name, "%s", _STR);
2097 renamepage(areawin->page);
2098 printname(topobject);
2099 Wprintf("Starting new drawing");
2101 else {
2102 Wprintf("Can't find file %s, won't overwrite current page", _STR);
2104 return False;
2107 version = 1.0;
2108 multipage = 1;
2109 pagesize.x = 612;
2110 pagesize.y = 792;
2112 for(;;) {
2113 if (fgets(temp, 149, ps) == NULL) {
2114 Wprintf("Error: EOF in or before prolog.");
2115 return False;
2117 sscanf(temp, "%c%29s", &percentc, keyword);
2118 for (pdchar = keyword; isspace(*pdchar); pdchar++);
2119 if (percentc == '%') {
2120 if (!strcmp(pdchar, "XCircuit")) break;
2121 if (!strcmp(pdchar, "XCircuitLib")) {
2122 /* version control in libraries is post version 1.9 */
2123 if (version == 1.0) version = 1.9;
2124 break;
2126 if (!strcmp(pdchar, "%Page:")) break;
2127 if (strstr(pdchar, "PS-Adobe") != NULL)
2128 temppmode = (strstr(temp, "EPSF") != NULL) ? 0 : 1;
2129 else if (!strcmp(pdchar, "Version:"))
2130 sscanf(temp, "%*c%*s %f", &version);
2131 else if (!strcmp(pdchar, "%Pages:")) {
2132 pdchar = advancetoken(temp);
2133 multipage = atoi(pdchar);
2135 /* Crash files get renamed back to their original filename */
2136 else if (!strcmp(pdchar, "%Title:")) {
2137 if (xobjs.tempfile != NULL)
2138 if (!strcmp(inname, xobjs.tempfile))
2139 sscanf(temp, "%*c%*s %s", inname);
2141 else if ((temppmode == 1) && !strcmp(pdchar, "%BoundingBox:")) {
2142 short botx, boty;
2143 sscanf(temp, "%*s %hd %hd %hd %hd", &botx, &boty,
2144 &(pagesize.x), &(pagesize.y));
2145 pagesize.x += botx;
2146 pagesize.y += boty;
2150 #ifdef LGF
2151 else if (percentc == '-' && !strcmp(keyword, "5")) {
2152 fclose(ps);
2153 loadlgf(mode);
2154 return True;
2156 #endif
2159 /* Look for old-style files (no %%Page; maximum one page in file) */
2161 if (!strcmp(pdchar, "XCircuit"))
2162 skiptocomment(temp, 149, ps);
2164 for (page = 0; page < multipage; page++) {
2165 sprintf(pagestr, "%d", page + 1);
2167 /* read out-of-page library definitions */
2169 if (strstr(temp, "%%Page:") == NULL && strstr(temp, "offsets") == NULL) {
2170 load_in_progress = True;
2171 objectread(ps, topobject, 0, 0, loclibnum, temp, DEFAULTCOLOR, NULL);
2172 load_in_progress = False;
2175 if (strstr(temp, "%%Page:") != NULL) {
2176 sscanf(temp + 8, "%99s", pagestr);
2178 /* Read the next line so any keywords in the Page name don't */
2179 /* confuse the parser. */
2180 if (fgets(temp, 149, ps) == NULL) {
2181 Wprintf("Error: bad page header.");
2182 return False;
2185 /* Library load mode: Ignore all pages, just load objects */
2186 if (mode == 2) {
2187 while (strstr(temp, "showpage") == NULL) {
2188 if (fgets(temp, 149, ps) == NULL) {
2189 Wprintf("Error: bad page definition.");
2190 return False;
2193 skiptocomment(temp, 149, ps);
2194 continue;
2198 /* go to new page if necessary */
2200 if (page > 0) {
2202 /* find next undefined page */
2204 while(areawin->page < xobjs.pages &&
2205 xobjs.pagelist[areawin->page]->pageinst != NULL) areawin->page++;
2206 changepage(areawin->page);
2209 /* If this file was a library file then there is no page to load */
2211 if (strstr(temp, "EndLib") != NULL) {
2212 composelib(loclibnum);
2213 centerview(xobjs.libtop[mode]);
2214 Wprintf("Loaded library.");
2215 return True;
2218 /* good so far; let's clear out the old data structure */
2220 if (mode == 0) {
2221 reset(topobject, NORMAL);
2222 pagereset(areawin->page);
2223 xobjs.pagelist[areawin->page]->pmode = temppmode;
2224 if (temppmode == 1) {
2225 xobjs.pagelist[areawin->page]->pagesize.x = pagesize.x;
2226 xobjs.pagelist[areawin->page]->pagesize.y = pagesize.y;
2229 else {
2230 invalidate_netlist(topobject);
2231 /* ensure that the netlist for topobject is destroyed */
2232 freenetlist(topobject);
2235 /* clear the undo record */
2236 flush_undo_stack();
2238 /* read to the "scale" line, picking up inch/cm type, drawing */
2239 /* scale, and grid/snapspace along the way */
2241 offx = offy = 0;
2242 for(;;) {
2243 if (strstr(temp, "offsets") != NULL) {
2244 /* Prior to version 3.1.28 only. . . */
2245 sscanf(temp, "%c %hd %hd %*s", &percentc, &offx, &offy);
2246 if(percentc != '%') {
2247 Wprintf("Something wrong in offsets line.");
2248 offx = offy = 0;
2252 if ((temppmode == 1) && strstr(temp, "%%PageBoundingBox:") != NULL) {
2253 /* Recast the individual page size if specified per page */
2254 sscanf(temp, "%*s %*d %*d %hd %hd",
2255 &xobjs.pagelist[areawin->page]->pagesize.x,
2256 &xobjs.pagelist[areawin->page]->pagesize.y);
2258 else if (strstr(temp, "drawingscale") != NULL)
2259 sscanf(temp, "%*c %hd:%hd %*s",
2260 &xobjs.pagelist[areawin->page]->drawingscale.x,
2261 &xobjs.pagelist[areawin->page]->drawingscale.y);
2263 else if (strstr(temp, "hidden") != NULL)
2264 topobject->hidden = True;
2266 else if (strstr(temp, "is_symbol") != NULL) {
2267 sscanf(temp, "%*c %49s", teststr);
2268 checkschem(topobject, teststr);
2270 else if (strstr(temp, "is_primary") != NULL) {
2271 /* objectptr master; (jdk) */
2272 connects *newconn;
2274 /* Save information about master schematic and link at end of load */
2275 sscanf(temp, "%*c %49s", teststr);
2276 newconn = (connects *)malloc(sizeof(connects));
2277 newconn->next = connlist;
2278 connlist = newconn;
2279 newconn->page = areawin->page;
2280 newconn->master = strdup(teststr);
2283 else if (strstr(temp, "gridspace"))
2284 sscanf(temp, "%*c %f %f %*s", &xobjs.pagelist[areawin->page]->gridspace,
2285 &xobjs.pagelist[areawin->page]->snapspace);
2286 else if (strstr(temp, "scale") != NULL || strstr(temp, "rotate") != NULL) {
2287 /* rotation (landscape mode) is optional; parse accordingly */
2289 sscanf(temp, "%f %49s", &tmpfl, teststr);
2290 if (strstr(teststr, "scale") != NULL) {
2291 #ifndef TCL_WRAPPER
2292 setgridtype(teststr);
2293 #else
2294 if (strstr(teststr, "inch"))
2295 Tcl_Eval(xcinterp, "xcircuit::coordstyle inches");
2296 else
2297 Tcl_Eval(xcinterp, "xcircuit::coordstyle centimeters");
2298 #endif
2299 xobjs.pagelist[areawin->page]->outscale = tmpfl;
2301 else if (!strcmp(teststr, "rotate")) {
2302 xobjs.pagelist[areawin->page]->orient = (short)tmpfl;
2303 fgets(temp, 149, ps);
2304 sscanf(temp, "%f %19s", &tmpfl, teststr2);
2305 if (strstr(teststr2, "scale") != NULL) {
2306 #ifndef TCL_WRAPPER
2307 setgridtype(teststr2);
2308 #else
2309 if (strstr(teststr2, "inch"))
2310 Tcl_Eval(xcinterp, "xcircuit::coordstyle inches");
2311 else
2312 Tcl_Eval(xcinterp, "xcircuit::coordstyle centimeters");
2313 #endif
2314 xobjs.pagelist[areawin->page]->outscale = tmpfl;
2316 else {
2317 sscanf(temp, "%*f %*f %19s", teststr2);
2318 if (!strcmp(teststr2, "scale"))
2319 xobjs.pagelist[areawin->page]->outscale = tmpfl /
2320 getpsscale(1.0, areawin->page);
2321 else {
2322 Wprintf("Error in scale/rotate constructs.");
2323 return False;
2327 else { /* old style scale? */
2328 sscanf(temp, "%*f %*s %19s", teststr2);
2329 if ((teststr2 != NULL) && (!strcmp(teststr2, "scale")))
2330 xobjs.pagelist[areawin->page]->outscale = tmpfl /
2331 getpsscale(1.0, areawin->page);
2332 else {
2333 Wprintf("Error in scale/rotate constructs.");
2334 return False;
2338 else if (strstr(temp, "setlinewidth") != NULL) {
2339 sscanf(temp, "%f %*s", &xobjs.pagelist[areawin->page]->wirewidth);
2340 xobjs.pagelist[areawin->page]->wirewidth /= 1.3;
2341 break;
2343 else if (strstr(temp, "insertion") != NULL) {
2344 /* read in an included background image */
2345 readbackground(ps);
2347 else if (strstr(temp, "<<") != NULL) {
2348 char *buffer = temp, *endptr;
2349 /* Make sure we have the whole dictionary before calling */
2350 while (strstr(buffer, ">>") == NULL) {
2351 if (buffer == temp) {
2352 buffer = (char *)malloc(strlen(buffer) + 150);
2353 strcpy(buffer, temp);
2355 else
2356 buffer = (char *)realloc(buffer, strlen(buffer) + 150);
2357 endptr = ridnewline(buffer);
2358 *endptr++ = ' ';
2359 fgets(endptr, 149, ps);
2361 /* read top-level parameter dictionary */
2362 readparams(NULL, NULL, topobject, buffer);
2363 if (buffer != temp) free(buffer);
2366 if (fgets(temp, 149, ps) == NULL) {
2367 Wprintf("Error: Problems encountered in page header.");
2368 return False;
2372 load_in_progress = True;
2373 objectread(ps, topobject, offx, offy, LIBRARY, temp, DEFAULTCOLOR, NULL);
2374 load_in_progress = False;
2376 /* skip to next page boundary or file trailer */
2378 if (strstr(temp, "showpage") != NULL && multipage != 1) {
2379 char *fstop;
2381 skiptocomment(temp, 149, ps);
2383 /* check for new filename if this is a crash recovery file */
2384 if ((fstop = strstr(temp, "is_filename")) != NULL) {
2385 strncpy(inname, temp + 2, (int)(fstop - temp - 3));
2386 *(inname + (int)(fstop - temp) - 3) = '\0';
2387 fgets(temp, 149, ps);
2388 skiptocomment(temp, 149, ps);
2392 /* Finally: set the filename and pagename for this page */
2394 if (mode == 0) {
2395 char tpstr[6], *rootptr;
2397 /* Set filename and page title. */
2399 if (xobjs.pagelist[areawin->page]->filename != NULL)
2400 free(xobjs.pagelist[areawin->page]->filename);
2401 xobjs.pagelist[areawin->page]->filename = strdup(inname);
2403 /* Get the root name (after all path components) */
2405 rootptr = strrchr(xobjs.pagelist[areawin->page]->filename, '/');
2406 if (rootptr == NULL) rootptr = xobjs.pagelist[areawin->page]->filename;
2407 else rootptr++;
2409 /* If we didn't read in a page name from the %%Page: header line, then */
2410 /* set the page name to the root name of the file. */
2412 sprintf(tpstr, "%d", page + 1);
2413 if (!strcmp(pagestr, tpstr)) {
2414 if (rootptr == NULL)
2415 sprintf(topobject->name, "Page %d", page + 1);
2416 else
2417 sprintf(topobject->name, "%.79s", rootptr);
2419 /* Delete filename extensions ".ps" or ".eps" from the page name */
2420 if ((pdchar = strrchr(topobject->name, '.')) != NULL) {
2421 if (!strcmp(pdchar + 1, "ps") || !strcmp(pdchar + 1, "eps"))
2422 *pdchar = '\0';
2425 else
2426 sprintf(topobject->name, "%.79s", pagestr);
2428 renamepage(areawin->page);
2431 /* set object position to fit to window separately for each page */
2432 calcbbox(areawin->topinstance);
2433 centerview(areawin->topinstance);
2436 /* Crash file recovery: read any out-of-page library definitions tacked */
2437 /* onto the end of the file into the user library. */
2438 if (strncmp(temp, "%%Trailer", 9)) {
2439 load_in_progress = True;
2440 objectread(ps, topobject, 0, 0, USERLIB, temp, DEFAULTCOLOR, NULL);
2441 load_in_progress = False;
2444 cleanupaliases(USERLIB);
2446 /* Connect master->slave schematics */
2447 while (connlist != NULL) {
2448 connects *thisconn = connlist;
2449 objectptr master = NameToPageObject(thisconn->master, NULL, NULL);
2450 if (master) {
2451 xobjs.pagelist[thisconn->page]->pageinst->thisobject->symschem = master;
2452 xobjs.pagelist[thisconn->page]->pageinst->thisobject->schemtype = SECONDARY;
2454 else {
2455 Fprintf(stderr, "Error: Cannot find primary schematic for %s\n",
2456 xobjs.pagelist[thisconn->page]->pageinst->thisobject->name);
2458 connlist = thisconn->next;
2459 free(thisconn->master);
2460 free(thisconn);
2463 Wprintf("Loaded file: %s (%d page%s)", inname, multipage,
2464 (multipage > 1 ? "s" : ""));
2466 composelib(loclibnum);
2467 centerview(xobjs.libtop[loclibnum]);
2468 composelib(PAGELIB);
2470 if (version > PROG_VERSION + VEPS) {
2471 Wprintf("WARNING: file %s is version %2.1f vs. executable version %2.1f",
2472 inname, version, PROG_VERSION);
2475 version = PROG_VERSION;
2476 fclose(ps);
2477 return True;
2480 /*------------------------------------------------------*/
2481 /* Object name comparison: True if names are equal, */
2482 /* not counting leading underscores. */
2483 /*------------------------------------------------------*/
2485 int objnamecmp(char *name1, char *name2)
2487 char *n1ptr = name1;
2488 char *n2ptr = name2;
2490 while (*n1ptr == '_') n1ptr++;
2491 while (*n2ptr == '_') n2ptr++;
2493 return (strcmp(n1ptr, n2ptr));
2496 /*------------------------------------------------------*/
2497 /* Standard delimiter matching character. */
2498 /*------------------------------------------------------*/
2500 char standard_delimiter_end(char source)
2502 char target;
2503 switch(source) {
2504 case '(': target = ')'; break;
2505 case '[': target = ']'; break;
2506 case '{': target = '}'; break;
2507 case '<': target = '>'; break;
2508 default: target = source;
2510 return target;
2513 /*------------------------------------------------------*/
2514 /* Find matching parenthesis, bracket, brace, or tag */
2515 /* Don't count when backslash character '\' is in front */
2516 /*------------------------------------------------------*/
2518 u_char *find_delimiter(u_char *fstring)
2520 int count = 1;
2522 u_char *search = fstring;
2523 u_char source = *fstring;
2524 u_char target;
2526 target = (u_char)standard_delimiter_end((char)source);
2527 while (*++search != '\0') {
2528 if (*search == source && *(search - 1) != '\\') count++;
2529 else if (*search == target && *(search - 1) != '\\') count--;
2530 if (count == 0) break;
2532 return search;
2535 /*----------------------------------------------------------------------*/
2536 /* Remove unnecessary font change information from a label */
2537 /*----------------------------------------------------------------------*/
2539 void cleanuplabel(stringpart **strhead)
2541 stringpart *curpart = *strhead;
2542 int oldfont, curfont;
2543 Boolean fline = False;
2545 oldfont = curfont = -1;
2547 while (curpart != NULL) {
2548 switch (curpart->type) {
2549 case FONT_NAME:
2550 if (curfont == curpart->data.font) {
2551 /* Font change is redundant: remove it */
2552 /* Careful! font changes remove overline/underline; if */
2553 /* either one is in effect, replace it with "noline" */
2554 if (fline)
2555 curpart->type = NOLINE;
2556 else
2557 curpart = deletestring(curpart, strhead, NULL);
2559 else {
2560 curfont = curpart->data.font;
2562 break;
2564 case FONT_SCALE:
2565 /* Old style font scale is always written absolute, not relative. */
2566 /* Changes in scale were not allowed, so just get rid of them. */
2567 if (version < 2.25)
2568 curpart = deletestring(curpart, strhead, areawin->topinstance);
2569 break;
2571 /* A font change may occur inside a parameter, so any font */
2572 /* declaration after a parameter must be considered to be */
2573 /* intentional. */
2575 case PARAM_END:
2576 curfont = oldfont = -1;
2577 break;
2579 case OVERLINE: case UNDERLINE:
2580 fline = True;
2581 break;
2583 case NOLINE:
2584 fline = False;
2585 break;
2587 case NORMALSCRIPT: case RETURN:
2588 if (oldfont != -1) {
2589 curfont = oldfont;
2590 oldfont = -1;
2592 break;
2594 case SUBSCRIPT: case SUPERSCRIPT:
2595 if (oldfont == -1)
2596 oldfont = curfont;
2597 break;
2599 if (curpart != NULL)
2600 curpart = curpart->nextpart;
2604 /*----------------------------------------------------------------------*/
2605 /* Read label segments */
2606 /*----------------------------------------------------------------------*/
2608 void readlabel(objectptr localdata, char *lineptr, stringpart **strhead)
2610 Boolean fline = False;
2611 /* char *sptr; (jdk) */
2612 short j;
2613 char *endptr, *segptr = lineptr;
2614 char key[100];
2615 stringpart *newpart;
2616 oparamptr ops;
2618 while (*segptr != '\0') { /* Look through all segments */
2620 while (isspace(*segptr) && (*segptr != '\0')) segptr++;
2622 if (*segptr == '(' || *segptr == '{') {
2623 endptr = find_delimiter(segptr);
2624 *endptr++ = '\0';
2625 /* null string (e.g., null parameter substitution) */
2626 if ((*segptr == '(') && (*(segptr + 1) == '\0')) {
2627 segptr = endptr;
2628 continue;
2631 else if (*segptr == '\0' || *segptr == '}') break;
2633 makesegment(strhead, *strhead);
2634 newpart = *strhead;
2636 /* Embedded command is in braces: {} */
2638 if (*segptr == '{') {
2640 /* Find the command for this PostScript procedure */
2641 char *cmdptr = endptr - 2;
2642 while (isspace(*cmdptr)) cmdptr--;
2643 while (!isspace(*cmdptr) && (cmdptr > segptr)) cmdptr--;
2644 cmdptr++;
2645 segptr++;
2647 if (!strncmp(cmdptr, "Ss", 2))
2648 newpart->type = SUPERSCRIPT;
2649 else if (!strncmp(cmdptr, "ss", 2))
2650 newpart->type = SUBSCRIPT;
2651 else if (!strncmp(cmdptr, "ns", 2))
2652 newpart->type = NORMALSCRIPT;
2653 else if (!strncmp(cmdptr, "hS", 2))
2654 newpart->type = HALFSPACE;
2655 else if (!strncmp(cmdptr, "qS", 2))
2656 newpart->type = QTRSPACE;
2657 else if (!strncmp(cmdptr, "CR", 2)) {
2658 newpart->type = RETURN;
2659 newpart->data.flags = 0;
2661 else if (!strcmp(cmdptr, "Ts")) /* "Tab set" command */
2662 newpart->type = TABSTOP;
2663 else if (!strcmp(cmdptr, "Tf")) /* "Tab forward" command */
2664 newpart->type = TABFORWARD;
2665 else if (!strcmp(cmdptr, "Tb")) /* "Tab backward" command */
2666 newpart->type = TABBACKWARD;
2667 else if (!strncmp(cmdptr, "ol", 2)) {
2668 newpart->type = OVERLINE;
2669 fline = True;
2671 else if (!strncmp(cmdptr, "ul", 2)) {
2672 newpart->type = UNDERLINE;
2673 fline = True;
2675 else if (!strncmp(cmdptr, "sce", 3)) { /* revert to default color */
2676 newpart->type = FONT_COLOR;
2677 newpart->data.color = DEFAULTCOLOR;
2679 else if (cmdptr == segptr) { /* cancel over- or underline */
2680 newpart->type = NOLINE;
2681 fline = False;
2683 /* To-do: replace old-style backspace with tab stop */
2684 else if (!strcmp(cmdptr, "bs")) {
2685 Wprintf("Warning: Obsolete backspace command ommitted in text");
2687 else if (!strcmp(cmdptr, "Kn")) { /* "Kern" command */
2688 int kx, ky;
2689 sscanf(segptr, "%d %d", &kx, &ky);
2690 newpart->type = KERN;
2691 newpart->data.kern[0] = kx;
2692 newpart->data.kern[1] = ky;
2694 else if (!strcmp(cmdptr, "MR")) { /* "Margin stop" command */
2695 int width;
2696 sscanf(segptr, "%d", &width);
2697 newpart->type = MARGINSTOP;
2698 newpart->data.width = width;
2700 else if (!strcmp(cmdptr, "scb")) { /* change color command */
2701 float cr, cg, cb;
2702 int cval, cindex;
2703 sscanf(segptr, "%f %f %f", &cr, &cg, &cb);
2704 newpart->type = FONT_COLOR;
2705 cindex = rgb_alloccolor((int)(cr * 65535), (int)(cg * 65535),
2706 (int)(cb * 65535));
2707 newpart->data.color = cindex;
2709 else if (!strcmp(cmdptr, "cf")) { /* change font or scale command */
2710 char *nextptr, *newptr = segptr;
2712 /* Set newptr to the fontname and nextptr to the next token. */
2713 while (*newptr != '/' && *newptr != '\0') newptr++;
2714 if (*newptr++ == '\0') {
2715 Wprintf("Error: Bad change-font command");
2716 newpart->type = NOLINE; /* placeholder */
2718 for (nextptr = newptr; !isspace(*nextptr); nextptr++);
2719 *(nextptr++) = '\0';
2720 while (isspace(*nextptr)) nextptr++;
2722 for (j = 0; j < fontcount; j++)
2723 if (!strcmp(newptr, fonts[j].psname))
2724 break;
2726 if (j == fontcount) /* this is a non-loaded font */
2727 if (loadfontfile(newptr) < 0) {
2728 if (fontcount > 0) {
2729 Wprintf("Error: Font \"%s\" not found---using default.", newptr);
2730 j = 0;
2732 else {
2733 Wprintf("Error: No fonts!");
2734 newpart->type = NOLINE; /* placeholder */
2738 if (isdigit(*nextptr)) { /* second form of "cf" command---includes scale */
2739 float locscale;
2740 sscanf(nextptr, "%f", &locscale);
2741 newpart->type = FONT_SCALE;
2742 newpart->data.scale = locscale;
2743 makesegment(strhead, *strhead);
2744 newpart = *strhead;
2746 newpart->type = FONT_NAME;
2747 newpart->data.font = j;
2749 else { /* This exec isn't a known label function */
2750 Wprintf("Error: unknown substring function");
2751 newpart->type = NOLINE; /* placeholder */
2755 /* Text substring is in parentheses: () */
2757 else if (*segptr == '(') {
2758 if (fline == True) {
2759 newpart->type = NOLINE;
2760 makesegment(strhead, *strhead);
2761 newpart = *strhead;
2762 fline = False;
2764 newpart->type = TEXT_STRING;
2765 newpart->data.string = (u_char *)malloc(1 + strlen(++segptr));
2767 /* Copy string, translating octal codes into 8-bit characters */
2768 parse_ps_string(segptr, newpart->data.string, strlen(segptr), TRUE, TRUE);
2771 /* Parameterized substrings are denoted by parameter key. */
2772 /* The parameter (default and/or substitution value) is */
2773 /* assumed to exist. */
2775 else {
2777 parse_ps_string(segptr, key, 99, FALSE, TRUE);
2778 if (strlen(key) > 0) {
2779 newpart->type = PARAM_START;
2780 newpart->data.string = (char *)malloc(1 + strlen(key));
2781 strcpy(newpart->data.string, key);
2783 /* check for compatibility between the parameter value and */
2784 /* the number of parameters and parameter type. */
2786 ops = match_param(localdata, key);
2787 if (ops == NULL) {
2788 Fprintf(stderr, "readlabel() error: No such parameter %s!\n", key);
2789 deletestring(newpart, strhead, areawin->topinstance);
2792 /* Fprintf(stdout, "Parameter %s called from object %s\n", */
2793 /* key, localdata->name); */
2795 endptr = segptr + 1;
2796 while (!isspace(*endptr) && (*endptr != '\0')) endptr++;
2798 segptr = endptr;
2802 /*--------------------------------------*/
2803 /* skip over whitespace in string input */
2804 /*--------------------------------------*/
2806 char *skipwhitespace(char *lineptr)
2808 char *locptr = lineptr;
2810 while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2811 return locptr;
2814 /*-------------------------------------------*/
2815 /* advance to the next token in string input */
2816 /*-------------------------------------------*/
2818 char *advancetoken(char *lineptr)
2820 char *locptr = lineptr;
2822 while (!isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2823 while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++;
2824 return locptr;
2827 /*------------------------------------------------------*/
2828 /* Read a parameter list for an object instance call. */
2829 /* This uses the key-value dictionary method but also */
2830 /* allows the "old style" in which each parameter */
2831 /* was automatically assigned the key v1, v2, etc. */
2832 /*------------------------------------------------------*/
2834 void readparams(objectptr localdata, objinstptr newinst, objectptr libobj,
2835 char *buffer)
2837 oparamptr newops, objops, fops;
2838 char *arrayptr, *endptr, *arraynext;
2839 int paramno = 0;
2840 char paramkey[100];
2842 if ((arrayptr = strstr(buffer, "<<")) == NULL)
2843 if ((arrayptr = strchr(buffer, '[')) == NULL)
2844 return;
2846 endptr = find_delimiter(arrayptr);
2847 if (*arrayptr == '<') {
2848 arrayptr++; /* move to second '<' in "<<" */
2849 endptr--; /* back up to first '>' in ">>" */
2852 /* move to next non-space token after opening bracket */
2853 arrayptr++;
2854 while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++;
2856 while ((*arrayptr != '\0') && (arrayptr < endptr)) {
2858 newops = (oparamptr)malloc(sizeof(oparam));
2860 /* Arrays contain values only. Dictionaries contain key:value pairs */
2861 if (*endptr == '>') { /* dictionary type */
2862 if (*arrayptr != '/') {
2863 Fprintf(stdout, "Error: Dictionary key is a literal, not a name\n");
2865 else arrayptr++; /* Skip PostScript name delimiter */
2866 parse_ps_string(arrayptr, paramkey, 99, FALSE, TRUE);
2867 newops->key = (char *)malloc(1 + strlen(paramkey));
2868 strcpy(newops->key, paramkey);
2869 arrayptr = advancetoken(arrayptr);
2871 else { /* array type; keys are "v1", "v2", etc. */
2872 paramno++;
2873 newops->key = (char *)malloc(6);
2874 sprintf(newops->key, "v%d", paramno);
2877 /* Find matching parameter in object definition */
2878 if (newinst) {
2879 objops = match_param(libobj, newops->key);
2880 if (objops == NULL) {
2881 Fprintf(stdout, "Error: parameter %s does not exist in object %s!\n",
2882 newops->key, libobj->name);
2883 free(newops->key);
2884 free(newops);
2885 return;
2889 /* Add to instance's parameter list */
2890 /* If newinst is null, then the parameters are added to libobj */
2891 newops->next = NULL;
2892 if (newinst) {
2894 /* Delete any parameters with duplicate names. This */
2895 /* This may indicate an expression parameter that was */
2896 /* precomputed while determining the bounding box. */
2898 for (fops = newinst->params; fops != NULL; fops = fops->next)
2899 if (!strcmp(fops->key, newops->key))
2900 if ((fops = free_instance_param(newinst, fops)) == NULL)
2901 break;
2903 if (newinst->params == NULL)
2904 newinst->params = newops;
2905 else {
2906 for (fops = newinst->params; fops->next != NULL; fops = fops->next);
2907 fops->next = newops;
2910 else {
2911 if (libobj->params == NULL)
2912 libobj->params = newops;
2913 else {
2914 for (fops = libobj->params; fops->next != NULL; fops = fops->next);
2915 fops->next = newops;
2919 /* Fill in "which" entry from the object default */
2920 newops->which = (newinst) ? objops->which : 0;
2922 /* Check next token. If not either end-of-dictionary or */
2923 /* the next parameter key, then value is an expression. */
2924 /* Expressions are written as two strings, the first the */
2925 /* result of evaluting the expression, and the second the */
2926 /* expression itself, followed by "pop" to prevent the */
2927 /* PostScript interpreter from trying to evaluate the */
2928 /* expression (which is not in PostScript). */
2930 if (*arrayptr == '(' || *arrayptr == '{')
2931 arraynext = find_delimiter(arrayptr);
2932 else
2933 arraynext = arrayptr;
2934 arraynext = advancetoken(arraynext);
2936 if ((*endptr == '>') && (arraynext < endptr) && (*arraynext != '/')) {
2937 char *substrend, *arraysave;
2939 if (*arraynext == '(' || *arraynext == '{') {
2941 substrend = find_delimiter(arraynext);
2942 arraysave = arraynext + 1;
2943 arraynext = advancetoken(substrend);
2945 newops->type = (u_char)XC_EXPR;
2946 newops->which = P_EXPRESSION; /* placeholder */
2949 if (strncmp(arraynext, "pop ", 4)) {
2950 Wprintf("Error: bad expression parameter!\n");
2951 #ifdef TCL_WRAPPER
2952 newops->parameter.expr = strdup("expr 0");
2953 #else
2954 newops->parameter.expr = strdup("0");
2955 #endif
2956 arrayptr = advancetoken(arrayptr);
2957 } else {
2958 *substrend = '\0';
2959 newops->parameter.expr = strdup(arraysave);
2960 arrayptr = advancetoken(arraynext);
2964 else if (*arrayptr == '(' || *arrayptr == '{') {
2965 float r, g, b;
2966 char *substrend, csave;
2967 stringpart *endpart;
2969 /* type XC_STRING */
2971 substrend = find_delimiter(arrayptr);
2972 csave = *(++substrend);
2973 *substrend = '\0';
2974 if (*arrayptr == '{') arrayptr++;
2976 /* A numerical value immediately following the opening */
2977 /* brace indicates a color parameter. */
2978 if (sscanf(arrayptr, "%f %f %f", &r, &g, &b) == 3) {
2979 newops->type = (u_char)XC_INT;
2980 newops->which = P_COLOR;
2981 newops->parameter.ivalue = rgb_alloccolor((int)(r * 65535),
2982 (int)(g * 65535), (int)(b * 65535));
2983 *substrend = csave;
2985 else {
2986 char *arraytmp = arrayptr;
2987 char linkdefault[5] = "(%n)";
2989 newops->type = (u_char)XC_STRING;
2990 newops->which = P_SUBSTRING;
2991 newops->parameter.string = NULL;
2993 /* Quick check for "link" parameter: make object name into "%n" */
2994 if (!strcmp(newops->key, "link"))
2995 if (!strncmp(arrayptr + 1, libobj->name, strlen(libobj->name)) &&
2996 !strcmp(arrayptr + strlen(libobj->name) + 1, ")"))
2997 arraytmp = linkdefault;
2999 readlabel(libobj, arraytmp, &(newops->parameter.string));
3000 *substrend = csave;
3002 /* Append a PARAM_END to the parameter string */
3004 endpart = makesegment(&(newops->parameter.string), NULL);
3005 endpart->type = PARAM_END;
3006 endpart->data.string = (u_char *)NULL;
3008 arrayptr = substrend;
3009 while (isspace(*arrayptr) && *arrayptr != '\0')
3010 arrayptr++;
3012 else {
3013 /* char *token; (jdk) */
3014 int scanned = 0;
3016 /* type XC_FLOAT or XC_INT, or an indirect reference */
3018 newops->type = (newinst) ? objops->type : (u_char)XC_FLOAT;
3020 if (newops->type == XC_FLOAT) {
3021 scanned = sscanf(arrayptr, "%f", &(newops->parameter.fvalue));
3022 /* Fprintf(stdout, "Object %s called with parameter "
3023 "%s value %g\n", libobj->name,
3024 newops->key, newops->parameter.fvalue); */
3026 else if (newops->type == XC_INT) {
3027 scanned = sscanf(arrayptr, "%d", &(newops->parameter.ivalue));
3028 /* Fprintf(stdout, "Object %s called with parameter "
3029 "%s value %d\n", libobj->name,
3030 newops->key, newops->parameter.ivalue); */
3032 else if (newops->type == XC_EXPR) {
3033 /* Instance values of parameters hold the last evaluated */
3034 /* result and will be regenerated, so we can ignore them */
3035 /* here. By ignoring it, we don't have to deal with issues */
3036 /* like type promotion. */
3037 free_instance_param(newinst, newops);
3038 scanned = 1; /* avoid treating as an indirect ref */
3040 else if (newops->type == XC_STRING) {
3041 /* Fill string record, so we have a valid record. This will */
3042 /* be blown away and replaced by opsubstitute(), but it must */
3043 /* have an initial valid entry. */
3044 stringpart *tmpptr;
3045 newops->parameter.string = NULL;
3046 tmpptr = makesegment(&newops->parameter.string, NULL);
3047 tmpptr->type = TEXT_STRING;
3048 tmpptr = makesegment(&newops->parameter.string, NULL);
3049 tmpptr->type = PARAM_END;
3051 else {
3052 Fprintf(stderr, "Error: unknown parameter type!\n");
3055 if (scanned == 0) {
3056 /* Indirect reference --- create an eparam in the instance */
3057 parse_ps_string(arrayptr, paramkey, 99, FALSE, TRUE);
3059 if (!newinst || !localdata) {
3060 /* Only object instances can use indirect references */
3061 Fprintf(stderr, "Error: parameter default %s cannot "
3062 "be parsed!\n", paramkey);
3064 else if (match_param(localdata, paramkey) == NULL) {
3065 /* Reference key must exist in the calling object */
3066 Fprintf(stderr, "Error: parameter value %s cannot be parsed!\n",
3067 paramkey);
3069 else {
3070 /* Create an eparam record in the instance */
3071 eparamptr newepp = make_new_eparam(paramkey);
3072 newepp->flags |= P_INDIRECT;
3073 newepp->pdata.refkey = strdup(newops->key);
3074 newepp->next = newinst->passed;
3075 newinst->passed = newepp;
3079 arrayptr = advancetoken(arrayptr);
3083 /* Calculate the unique bounding box for the instance */
3085 if (newinst && (newinst->params != NULL)) {
3086 opsubstitute(libobj, newinst);
3087 calcbboxinst(newinst);
3091 /*--------------------------------------------------------------*/
3092 /* Read a value which might be a short integer or a parameter. */
3093 /* If the value is a parameter, check the parameter list to see */
3094 /* if it needs to be re-typecast. Return the position to the */
3095 /* next token in "lineptr". */
3096 /*--------------------------------------------------------------*/
3098 char *varpscan(objectptr localdata, char *lineptr, short *hvalue,
3099 genericptr thiselem, int pointno, int offset, u_char which)
3101 oparamptr ops = NULL;
3102 char key[100];
3103 /* char *nexttok; (jdk) */
3104 eparamptr newepp;
3106 if (sscanf(lineptr, "%hd", hvalue) != 1) {
3107 parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3109 ops = match_param(localdata, key);
3110 newepp = make_new_eparam(key);
3112 /* Add parameter to the linked list */
3113 newepp->next = thiselem->passed;
3114 thiselem->passed = newepp;
3115 newepp->pdata.pointno = pointno;
3117 if (ops != NULL) {
3119 /* It cannot be known whether a parameter value is a float or int */
3120 /* until we see how the parameter is used. So we always read the */
3121 /* parameter default as a float, and re-typecast it if necessary. */
3123 if (ops->type == XC_FLOAT) {
3124 ops->type = XC_INT;
3125 /* (add 0.1 to avoid roundoff error in conversion to integer) */
3126 ops->parameter.ivalue = (int)(ops->parameter.fvalue +
3127 ((ops->parameter.fvalue < 0) ? -0.1 : 0.1));
3129 ops->which = which;
3130 *hvalue = (short)ops->parameter.ivalue;
3132 else {
3133 *hvalue = 0; /* okay; will get filled in later */
3134 Fprintf(stderr, "Error: parameter %s was used but not defined!\n", key);
3138 *hvalue -= (short)offset;
3140 return advancetoken(skipwhitespace(lineptr));
3143 /*--------------------------------------------------------------*/
3144 /* Read a value which might be a short integer or a parameter, */
3145 /* but which is not a point in a pointlist. */
3146 /*--------------------------------------------------------------*/
3148 char *varscan(objectptr localdata, char *lineptr, short *hvalue,
3149 genericptr thiselem, u_char which)
3151 return varpscan(localdata, lineptr, hvalue, thiselem, 0, 0, which);
3154 /*--------------------------------------------------------------*/
3155 /* Read a value which might be a float or a parameter. */
3156 /* Return the position to the next token in "lineptr". */
3157 /*--------------------------------------------------------------*/
3159 char *varfscan(objectptr localdata, char *lineptr, float *fvalue,
3160 genericptr thiselem, u_char which)
3162 oparamptr ops = NULL;
3163 eparamptr newepp;
3164 char key[100];
3166 if (sscanf(lineptr, "%f", fvalue) != 1) {
3167 parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3169 /* This bit of a hack takes care of scale-variant */
3170 /* linewidth specifiers for object instances. */
3172 if (!strncmp(key, "/sv", 3)) {
3173 ((objinstptr)thiselem)->style &= ~LINE_INVARIANT;
3174 return varfscan(localdata, advancetoken(skipwhitespace(lineptr)),
3175 fvalue, thiselem, which);
3178 ops = match_param(localdata, key);
3179 newepp = make_new_eparam(key);
3181 /* Add parameter to the linked list */
3182 newepp->next = thiselem->passed;
3183 thiselem->passed = newepp;
3185 if (ops != NULL) {
3186 ops->which = which;
3187 *fvalue = ops->parameter.fvalue;
3189 else
3190 Fprintf(stderr, "Error: no parameter \"%s\" defined!\n", key);
3193 /* advance to next token */
3194 return advancetoken(skipwhitespace(lineptr));
3197 /*--------------------------------------------------------------*/
3198 /* Same as varpscan(), but for path types only. */
3199 /*--------------------------------------------------------------*/
3201 char *varpathscan(objectptr localdata, char *lineptr, short *hvalue,
3202 genericptr *thiselem, pathptr thispath, int pointno, int offset,
3203 u_char which, eparamptr *nepptr)
3205 oparamptr ops = NULL;
3206 char key[100];
3207 eparamptr newepp;
3209 if (nepptr != NULL) *nepptr = NULL;
3211 if (sscanf(lineptr, "%hd", hvalue) != 1) {
3212 parse_ps_string(lineptr, key, 99, FALSE, TRUE);
3213 ops = match_param(localdata, key);
3214 newepp = make_new_eparam(key);
3215 newepp->pdata.pathpt[1] = pointno;
3217 if (thiselem == NULL)
3218 newepp->pdata.pathpt[0] = (short)0;
3219 else {
3220 short elemidx = (short)(thiselem - thispath->plist);
3221 if (elemidx >= 0 && elemidx < thispath->parts)
3222 newepp->pdata.pathpt[0] = (short)(thiselem - thispath->plist);
3223 else {
3224 Fprintf(stderr, "Error: Bad parameterized path point!\n");
3225 free(newepp);
3226 goto pathdone;
3229 if (nepptr != NULL) *nepptr = newepp;
3231 /* Add parameter to the linked list. */
3233 newepp->next = thispath->passed;
3234 thispath->passed = newepp;
3236 if (ops != NULL) {
3238 /* It cannot be known whether a parameter value is a float or int */
3239 /* until we see how the parameter is used. So we always read the */
3240 /* parameter default as a float, and re-typecast it if necessary. */
3242 if (ops->type == XC_FLOAT) {
3243 ops->type = XC_INT;
3244 /* (add 0.1 to avoid roundoff error in conversion to integer) */
3245 ops->parameter.ivalue = (int)(ops->parameter.fvalue +
3246 ((ops->parameter.fvalue < 0) ? -0.1 : 0.1));
3248 ops->which = which;
3249 *hvalue = (short)ops->parameter.ivalue;
3251 else {
3252 *hvalue = 0; /* okay; will get filled in later */
3253 Fprintf(stderr, "Error: parameter %s was used but not defined!\n", key);
3257 pathdone:
3258 *hvalue -= (short)offset;
3259 return advancetoken(skipwhitespace(lineptr));
3262 /*--------------------------------------------------------------*/
3263 /* Create a new instance of an object in the library's list of */
3264 /* instances. This instance will be used on the library page */
3265 /* when doing "composelib()". */
3266 /*--------------------------------------------------------------*/
3268 objinstptr addtoinstlist(int libnum, objectptr libobj, Boolean virtual)
3270 objinstptr newinst = (objinstptr) malloc(sizeof(objinst));
3271 liblistptr spec = (liblistptr) malloc(sizeof(liblist));
3272 liblistptr srch;
3274 newinst->type = OBJINST;
3275 instancedefaults(newinst, libobj, 0, 0);
3277 spec->virtual = (u_char)virtual;
3278 spec->thisinst = newinst;
3279 spec->next = NULL;
3281 /* Add to end, so that duplicate, parameterized instances */
3282 /* always come after the original instance with the default */
3283 /* parameters. */
3285 if ((srch = xobjs.userlibs[libnum].instlist) == NULL)
3286 xobjs.userlibs[libnum].instlist = spec;
3287 else {
3288 while (srch->next != NULL) srch = srch->next;
3289 srch->next = spec;
3292 /* Calculate the instance-specific bounding box */
3293 calcbboxinst(newinst);
3295 return newinst;
3298 /*--------------------------------------------------------------*/
3299 /* Deal with object reads: Create a new object and prepare for */
3300 /* reading. The library number is passed as "mode". */
3301 /*--------------------------------------------------------------*/
3303 objectptr *new_library_object(short mode, char *name, objlistptr *retlist,
3304 TechPtr defaulttech)
3306 objlistptr newdef, redef = NULL;
3307 objectptr *newobject, *libobj;
3308 objectptr *curlib = (mode == FONTLIB) ?
3309 xobjs.fontlib.library : xobjs.userlibs[mode - LIBRARY].library;
3310 short *libobjects = (mode == FONTLIB) ?
3311 &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;
3312 int i, j;
3313 char *nsptr, *fullname = name;
3315 curlib = (objectptr *) realloc(curlib, (*libobjects + 1)
3316 * sizeof(objectptr));
3317 if (mode == FONTLIB) xobjs.fontlib.library = curlib;
3318 else xobjs.userlibs[mode - LIBRARY].library = curlib;
3320 /* For (older) libraries that do not use technologies, give the */
3321 /* object a technology name in the form <library>::<object> */
3323 if ((nsptr = strstr(name, "::")) == NULL) {
3324 int deftechlen = (defaulttech == NULL) ? 0 : strlen(defaulttech->technology);
3325 fullname = (char *)malloc(deftechlen + strlen(name) + 3);
3326 if (defaulttech == NULL)
3327 sprintf(fullname, "::%s", name);
3328 else
3329 sprintf(fullname, "%s::%s", defaulttech->technology, name);
3332 /* initial 1-pointer allocations */
3334 newobject = curlib + (*libobjects);
3335 *newobject = (objectptr) malloc(sizeof(object));
3336 initmem(*newobject);
3338 /* check that this object is not already in list of objects */
3340 if (mode == FONTLIB) {
3341 for (libobj = xobjs.fontlib.library; libobj != xobjs.fontlib.library +
3342 xobjs.fontlib.number; libobj++) {
3343 /* This font character may be a redefinition of another */
3344 if (!objnamecmp(fullname, (*libobj)->name)) {
3345 newdef = (objlistptr) malloc(sizeof(objlist));
3346 newdef->libno = FONTLIB;
3347 newdef->thisobject = *libobj;
3348 newdef->next = redef;
3349 redef = newdef;
3353 else {
3354 for (i = 0; i < xobjs.numlibs; i++) {
3355 for (j = 0; j < xobjs.userlibs[i].number; j++) {
3356 libobj = xobjs.userlibs[i].library + j;
3357 /* This object may be a redefinition of another object */
3358 if (!objnamecmp(fullname, (*libobj)->name)) {
3359 newdef = (objlistptr) malloc(sizeof(objlist));
3360 newdef->libno = i + LIBRARY;
3361 newdef->thisobject = *libobj;
3362 newdef->next = redef;
3363 redef = newdef;
3369 (*libobjects)++;
3370 sprintf((*newobject)->name, "%s", fullname);
3371 if (fullname != name) free(fullname);
3373 /* initmem() initialized schemtype to PRIMARY; change it. */
3374 (*newobject)->schemtype = (mode == FONTLIB) ? GLYPH : SYMBOL;
3376 /* If the object declares a technology name that is different from the */
3377 /* default, then add the technology name to the list of technologies, */
3378 /* with a NULL filename. */
3380 if (mode != FONTLIB) AddObjectTechnology(*newobject);
3382 *retlist = redef;
3383 return newobject;
3386 /*--------------------------------------------------------------*/
3387 /* do an exhaustive comparison between a new object and any */
3388 /* object having the same name. If they are the same, destroy */
3389 /* the duplicate. If different, rename the original one. */
3390 /*--------------------------------------------------------------*/
3392 Boolean library_object_unique(short mode, objectptr newobject, objlistptr redef)
3394 Boolean is_unique = True;
3395 objlistptr newdef;
3396 short *libobjects = (mode == FONTLIB) ?
3397 &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;
3399 if (redef == NULL)
3400 return is_unique; /* No name conflicts; object is okay as-is */
3402 for (newdef = redef; newdef != NULL; newdef = newdef->next) {
3404 /* Must make sure that default parameter values are */
3405 /* plugged into both objects! */
3406 opsubstitute(newdef->thisobject, NULL);
3407 opsubstitute(newobject, NULL);
3409 if (objcompare(newobject, newdef->thisobject) == True) {
3410 addalias(newdef->thisobject, newobject->name);
3412 /* If the new object has declared an association to a */
3413 /* schematic, transfer it to the original, and make */
3414 /* sure that the page points to the object which will */
3415 /* be left, not the one which will be destroyed. */
3417 if (newobject->symschem != NULL) {
3418 newdef->thisobject->symschem = newobject->symschem;
3419 newdef->thisobject->symschem->symschem = newdef->thisobject;
3422 reset(newobject, DESTROY);
3423 (*libobjects)--;
3424 is_unique = False;
3425 break;
3428 /* Not the same object, but has the same name. This can't */
3429 /* happen within the same input file, so the name of the */
3430 /* original object can safely be altered. */
3432 else if (!strcmp(newobject->name, newdef->thisobject->name)) {
3434 /* Replacement---for project management, allow the technology */
3435 /* master version to take precedence over a local version. */
3437 TechPtr nsptr = GetObjectTechnology(newobject);
3439 if (nsptr && (nsptr->flags & TECH_REPLACE)) {
3440 reset(newobject, DESTROY);
3441 (*libobjects)--;
3442 is_unique = False;
3444 else
3445 checkname(newdef->thisobject);
3446 break;
3449 for (; (newdef = redef->next); redef = newdef)
3450 free(redef);
3451 free(redef);
3453 return is_unique;
3456 /*--------------------------------------------------------------*/
3457 /* Add an instance of the object to the library's instance list */
3458 /*--------------------------------------------------------------*/
3460 void add_object_to_library(short mode, objectptr newobject)
3462 objinstptr libinst;
3464 if (mode == FONTLIB) return;
3466 libinst = addtoinstlist(mode - LIBRARY, newobject, False);
3467 calcbboxvalues(libinst, (genericptr *)NULL);
3469 /* Center the view of the object in its instance */
3470 centerview(libinst);
3473 /*--------------------------------------------------------------*/
3474 /* Continuation Line --- add memory to "buffer" as necessary. */
3475 /* Add a space character to the current text in "buffer" and */
3476 /* return a pointer to the new end-of-text. */
3477 /*--------------------------------------------------------------*/
3479 char *continueline(char **buffer)
3481 char *lineptr;
3482 int bufsize;
3484 for (lineptr = *buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++);
3485 /* Repair Windoze-mangled files */
3486 if ((lineptr > *buffer) && (*lineptr == '\n') && (*(lineptr - 1) == '\r'))
3487 *(lineptr - 1) = ' ';
3488 if (*lineptr == '\n') *lineptr++ = ' ';
3490 bufsize = (int)(lineptr - (*buffer)) + 256;
3491 *buffer = (char *)realloc((*buffer), bufsize * sizeof(char));
3493 return ((*buffer) + (bufsize - 256));
3496 /*--------------------------------------------------------------*/
3497 /* Read image data out of the Setup block of the input */
3498 /* We assume that width and height have been parsed from the */
3499 /* "imagedata" line and the file pointer is at the next line. */
3500 /*--------------------------------------------------------------*/
3502 void readimagedata(FILE *ps, int width, int height)
3504 char temp[150], ascbuf[6];
3505 int x, y, p, q, r, g, b, ilen;
3506 char *pptr;
3507 Imagedata *iptr;
3508 Boolean do_flate = False, do_ascii = False;
3509 u_char *filtbuf, *flatebuf;
3510 union {
3511 u_char b[4];
3512 u_long i;
3513 } pixel;
3515 iptr = addnewimage(NULL, width, height);
3517 /* Read the image data */
3519 fgets(temp, 149, ps);
3520 if (strstr(temp, "ASCII85Decode") != NULL) do_ascii = TRUE;
3521 #ifdef HAVE_LIBZ
3522 if (strstr(temp, "FlateDecode") != NULL) do_flate = TRUE;
3523 #else
3524 if (strstr(temp, "FlateDecode") != NULL)
3525 Fprintf(stderr, "Error: Don't know how to Flate decode!"
3526 " Get zlib and recompile xcircuit!\n");
3527 #endif
3528 while (strstr(temp, "ReusableStreamDecode") == NULL)
3529 fgets(temp, 149, ps); /* Additional piped filter lines */
3531 fgets(temp, 149, ps); /* Initial data line */
3532 q = 0;
3533 pptr = temp;
3534 ilen = 3 * width * height;
3535 filtbuf = (u_char *)malloc(ilen + 4);
3537 if (!do_ascii) { /* ASCIIHexDecode algorithm */
3538 q = 0;
3539 for (y = 0; y < height; y++) {
3540 for (x = 0; x < width; x++) {
3541 sscanf(pptr, "%02x%02x%02x", &r, &g, &b);
3542 filtbuf[q++] = (u_char)r;
3543 filtbuf[q++] = (u_char)g;
3544 filtbuf[q++] = (u_char)b;
3545 pptr += 6;
3546 if (*pptr == '\n') {
3547 fgets(temp, 149, ps);
3548 pptr = temp;
3553 else { /* ASCII85Decode algorithm */
3554 p = 0;
3555 while (1) {
3556 ascbuf[0] = *pptr;
3557 pptr++;
3558 if (ascbuf[0] == '~')
3559 break;
3560 else if (ascbuf[0] == 'z') {
3561 for (y = 0; y < 5; y++) ascbuf[y] = '\0';
3563 else {
3564 for (y = 1; y < 5; y++) {
3565 if (*pptr == '\n') {
3566 fgets(temp, 149, ps);
3567 pptr = temp;
3569 ascbuf[y] = *pptr;
3570 if (ascbuf[y] == '~') {
3571 for (; y < 5; y++) {
3572 ascbuf[y] = '!';
3573 p++;
3575 break;
3577 else pptr++;
3579 for (y = 0; y < 5; y++) ascbuf[y] -= '!';
3582 if (*pptr == '\n') {
3583 fgets(temp, 149, ps);
3584 pptr = temp;
3587 /* Decode from ASCII85 to binary */
3589 pixel.i = ascbuf[4] + ascbuf[3] * 85 + ascbuf[2] * 7225 +
3590 ascbuf[1] * 614125 + ascbuf[0] * 52200625;
3592 /* Add in roundoff for final bytes */
3593 if (p > 0) {
3594 switch (p) {
3595 case 3:
3596 pixel.i += 0xff0000;
3597 case 2:
3598 pixel.i += 0xff00;
3599 case 1:
3600 pixel.i += 0xff;
3604 for (y = 0; y < (4 - p); y++) {
3605 filtbuf[q + y] = pixel.b[3 - y];
3607 q += (4 - p);
3608 if (q >= ilen) break;
3612 /* Extra decoding goes here */
3614 #ifdef HAVE_LIBZ
3615 if (do_flate) {
3616 flatebuf = (char *)malloc(ilen);
3617 large_inflate(filtbuf, q, &flatebuf, ilen);
3618 free(filtbuf);
3620 else
3621 #endif
3623 flatebuf = filtbuf;
3625 q = 0;
3626 for (y = 0; y < height; y++)
3627 for (x = 0; x < width; x++) {
3628 u_char r, g, b;
3629 r = flatebuf[q++];
3630 g = flatebuf[q++];
3631 b = flatebuf[q++];
3632 xcImagePutPixel(iptr->image, x, y, r, g, b);
3635 free(flatebuf);
3637 fgets(temp, 149, ps); /* definition line */
3638 fgets(temp, 149, ps); /* pick up name of image from here */
3639 for (pptr = temp; !isspace(*pptr); pptr++);
3640 *pptr = '\0';
3641 iptr->filename = strdup(temp + 1);
3642 for (x = 0; x < 5; x++) fgets(temp, 149, ps); /* skip image dictionary */
3645 /*--------------------------------------------------------------*/
3646 /* Read an object (page) from a file into xcircuit */
3647 /*--------------------------------------------------------------*/
3649 Boolean objectread(FILE *ps, objectptr localdata, short offx, short offy,
3650 short mode, char *retstr, int ccolor, TechPtr defaulttech)
3652 char *temp, *buffer, keyword[80];
3653 short tmpfont = -1;
3654 float tmpscale = 0.0;
3655 objectptr *libobj;
3656 int curcolor = ccolor;
3657 char *colorkey = NULL;
3658 char *widthkey = NULL;
3659 int i, j, k;
3660 short px, py;
3661 objinstptr *newinst;
3662 eparamptr epptrx, epptry; /* used for paths only */
3664 /* path-handling variables */
3665 pathptr *newpath;
3666 XPoint startpoint;
3668 keyword[0] = '\0';
3670 buffer = (char *)malloc(256 * sizeof(char));
3671 temp = buffer;
3673 for(;;) {
3674 char *lineptr, *keyptr, *saveptr;
3676 if (fgets(temp, 255, ps) == NULL) {
3677 if (strcmp(keyword, "restore")) {
3678 Wprintf("Error: end of file.");
3679 *retstr = '\0';
3681 break;
3683 temp = buffer;
3685 /* because PostScript is a stack language, we will scan from the end */
3686 for (lineptr = buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++);
3687 /* Avoid CR-LF at EOL added by stupid Windoze programs */
3688 if ((lineptr > buffer) && *(lineptr - 1) == '\r') lineptr--;
3689 if (lineptr != buffer) { /* ignore any blank lines */
3690 for (keyptr = lineptr - 1; isspace(*keyptr) && keyptr != buffer; keyptr--);
3691 for (; !isspace(*keyptr) && keyptr != buffer; keyptr--);
3692 sscanf(keyptr, "%79s", keyword);
3694 if (!strcmp(keyword, "showpage")) {
3695 strncpy(retstr, buffer, 150);
3696 retstr[149] = '\0';
3697 free(buffer);
3699 /* If we have just read a schematic that is attached */
3700 /* to a symbol, check all of the pin labels in the symbol */
3701 /* to see if they correspond to pin names in the schematic. */
3702 /* The other way around (pin in schematic with no */
3703 /* corresponding name in the symbol) is not an error. */
3705 if (localdata->symschem != NULL) {
3706 genericptr *pgen, *lgen;
3707 labelptr plab, lcmp;
3708 for (pgen = localdata->symschem->plist; pgen < localdata->
3709 symschem->plist + localdata->symschem->parts; pgen++) {
3710 if (IS_LABEL(*pgen)) {
3711 plab = TOLABEL(pgen);
3712 if (plab->pin == LOCAL) {
3713 for (lgen = localdata->plist; lgen < localdata->plist +
3714 localdata->parts; lgen++) {
3715 if (IS_LABEL(*lgen)) {
3716 lcmp = TOLABEL(lgen);
3717 if (lcmp->pin == LOCAL)
3718 if (!stringcomprelaxed(lcmp->string, plab->string,
3719 areawin->topinstance))
3720 break;
3723 if (lgen == localdata->plist + localdata->parts) {
3724 char *cptr, *d1ptr, *d2ptr;
3725 char *pch = textprint(plab->string, areawin->topinstance);
3727 /* Check for likely delimiters before applying warning */
3729 if ((cptr = strchr(pch, ':')) != NULL) {
3730 d1ptr = strchr(pch, '[');
3731 d2ptr = strchr(pch, ']');
3732 if (d1ptr != NULL && d2ptr != NULL &&
3733 d1ptr < cptr && d2ptr > cptr) {
3734 if (areawin->buschar != '[') {
3735 areawin->buschar = '[';
3736 Fprintf(stderr, "Warning: Bus character \'[\'"
3737 " apparently used but not declared.\n");
3740 d1ptr = strchr(pch, '{');
3741 d2ptr = strchr(pch, '}');
3742 if (d1ptr != NULL && d2ptr != NULL &&
3743 d1ptr < cptr && d2ptr > cptr) {
3744 if (areawin->buschar != '{') {
3745 areawin->buschar = '{';
3746 Fprintf(stderr, "Warning: Bus character \'{\'"
3747 " apparently used in pin \"%s\""
3748 " but not declared.\n", pch);
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 in pin \"%s\""
3759 " but not declared.\n", pch);
3762 d1ptr = strchr(pch, '(');
3763 d2ptr = strchr(pch, ')');
3764 if (d1ptr != NULL && d2ptr != NULL &&
3765 d1ptr < cptr && d2ptr > cptr) {
3766 if (areawin->buschar != '(') {
3767 areawin->buschar = '(';
3768 Fprintf(stderr, "Warning: Bus character \'(\'"
3769 " apparently used in pin \"%s\""
3770 " but not declared.\n", pch);
3774 else
3775 Fprintf(stderr, "Warning: Unattached pin \"%s\" in "
3776 "symbol %s\n", pch,
3777 localdata->symschem->name);
3778 free(pch);
3784 return False; /* end of page */
3787 /* make a color change, adding the color if necessary */
3789 else if (!strcmp(keyword, "scb")) {
3790 float red, green, blue;
3791 if (sscanf(buffer, "%f %f %f", &red, &green, &blue) == 3) {
3792 curcolor = rgb_alloccolor((int)(red * 65535), (int)(green * 65535),
3793 (int)(blue * 65535));
3794 colorkey = NULL;
3796 else {
3797 char tmpkey[30];
3798 oparamptr ops;
3800 parse_ps_string(buffer, tmpkey, 29, FALSE, TRUE);
3801 ops = match_param(localdata, tmpkey);
3802 if (ops != NULL) {
3803 /* Recast expression parameter, if necessary */
3804 if (ops->which == P_EXPRESSION) ops->which = P_COLOR;
3805 if (ops->which == P_COLOR) {
3806 colorkey = ops->key;
3807 switch (ops->type) {
3808 case XC_INT:
3809 curcolor = ops->parameter.ivalue;
3810 break;
3811 default:
3812 curcolor = DEFAULTCOLOR; /* placeholder */
3813 break;
3820 /* end the color change, returning to default */
3822 else if (!strcmp(keyword, "sce")) {
3823 curcolor = ccolor;
3824 colorkey = NULL;
3827 /* begin a path constructor */
3829 else if (!strcmp(keyword, "beginpath")) {
3830 px = py = 0;
3832 NEW_PATH(newpath, localdata);
3833 (*newpath)->plist = (genericptr *)malloc(sizeof(genericptr));
3834 (*newpath)->parts = 0;
3835 (*newpath)->color = curcolor;
3836 (*newpath)->passed = NULL;
3838 for (--keyptr; *keyptr == ' '; keyptr--);
3839 for (; *keyptr != ' '; keyptr--);
3841 /* check for "addtox" and "addtoy" parameter specification */
3842 while (!strncmp(keyptr + 1, "addto", 5)) {
3843 saveptr = keyptr + 1;
3845 for (--keyptr; *keyptr == ' '; keyptr--);
3846 for (; *keyptr != ' '; keyptr--);
3848 /* Get parameter and its value */
3849 if (*(saveptr + 5) == 'x')
3850 varpscan(localdata, keyptr + 1, &px, (genericptr)*newpath,
3851 -1, offx, P_POSITION_X);
3852 else
3853 varpscan(localdata, keyptr + 1, &py, (genericptr)*newpath,
3854 -1, offy, P_POSITION_Y);
3856 for (--keyptr; *keyptr == ' '; keyptr--);
3857 for (; *keyptr != ' '; keyptr--);
3860 lineptr = varpathscan(localdata, buffer, &startpoint.x,
3861 (genericptr *)NULL, *newpath, 0, offx + px, P_POSITION_X,
3862 &epptrx);
3863 lineptr = varpathscan(localdata, lineptr, &startpoint.y,
3864 (genericptr *)NULL, *newpath, 0, offy + py, P_POSITION_Y,
3865 &epptry);
3867 std_eparam((genericptr)(*newpath), colorkey);
3870 /* end the path constructor */
3872 else if (!strcmp(keyword, "endpath")) {
3874 lineptr = varscan(localdata, buffer, &(*newpath)->style,
3875 (genericptr)*newpath, P_STYLE);
3876 lineptr = varfscan(localdata, lineptr, &(*newpath)->width,
3877 (genericptr)*newpath, P_LINEWIDTH);
3879 if ((*newpath)->parts <= 0) { /* in case of an empty path */
3880 free((*newpath)->plist);
3881 free(*newpath);
3882 localdata->parts--;
3884 newpath = NULL;
3887 /* read path parts */
3889 else if (!strcmp(keyword, "polyc")) {
3890 polyptr *newpoly;
3891 pointlist newpoints;
3892 short tmpnum;
3894 px = py = 0;
3896 NEW_POLY(newpoly, (*newpath));
3898 for (--keyptr; *keyptr == ' '; keyptr--);
3899 for (; *keyptr != ' '; keyptr--);
3901 /* check for "addtox" and "addtoy" parameter specification */
3902 while (!strncmp(keyptr + 1, "addto", 5)) {
3903 saveptr = keyptr + 1;
3905 for (--keyptr; *keyptr == ' '; keyptr--);
3906 for (; *keyptr != ' '; keyptr--);
3908 /* Get parameter and its value */
3909 if (*(saveptr + 5) == 'x')
3910 varpscan(localdata, keyptr + 1, &px, (genericptr)*newpoly,
3911 -1, offx, P_POSITION_X);
3912 else
3913 varpscan(localdata, keyptr + 1, &py, (genericptr)*newpoly,
3914 -1, offy, P_POSITION_Y);
3916 for (--keyptr; *keyptr == ' '; keyptr--);
3917 for (; *keyptr != ' '; keyptr--);
3920 sscanf(keyptr, "%hd", &tmpnum);
3921 (*newpoly)->number = tmpnum + 1;
3922 (*newpoly)->width = 1.0;
3923 (*newpoly)->style = UNCLOSED;
3924 (*newpoly)->color = curcolor;
3925 (*newpoly)->passed = NULL;
3926 (*newpoly)->cycle = NULL;
3928 (*newpoly)->points = (pointlist) malloc((*newpoly)->number *
3929 sizeof(XPoint));
3931 /* If the last point on the last path part was parameterized, then */
3932 /* the first point of the spline must be, too. */
3934 if (epptrx != NULL) {
3935 eparamptr newepp = copyeparam(epptrx, (genericptr)(*newpath));
3936 newepp->next = (*newpath)->passed;
3937 (*newpath)->passed = newepp;
3938 newepp->pdata.pathpt[1] = 0;
3939 newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
3941 if (epptry != NULL) {
3942 eparamptr newepp = copyeparam(epptry, (genericptr)(*newpath));
3943 newepp->next = (*newpath)->passed;
3944 (*newpath)->passed = newepp;
3945 newepp->pdata.pathpt[1] = 0;
3946 newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
3949 lineptr = buffer;
3951 newpoints = (*newpoly)->points + (*newpoly)->number - 1;
3952 lineptr = varpathscan(localdata, lineptr, &newpoints->x,
3953 (genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3954 offx + px, P_POSITION_X, &epptrx);
3955 lineptr = varpathscan(localdata, lineptr, &newpoints->y,
3956 (genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3957 offy + py, P_POSITION_Y, &epptry);
3959 for (--newpoints; newpoints > (*newpoly)->points; newpoints--) {
3961 lineptr = varpathscan(localdata, lineptr, &newpoints->x,
3962 (genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3963 offx + px, P_POSITION_X, NULL);
3964 lineptr = varpathscan(localdata, lineptr, &newpoints->y,
3965 (genericptr *)newpoly, *newpath, newpoints - (*newpoly)->points,
3966 offy + py, P_POSITION_Y, NULL);
3968 newpoints->x = startpoint.x;
3969 newpoints->y = startpoint.y;
3970 startpoint.x = (newpoints + (*newpoly)->number - 1)->x;
3971 startpoint.y = (newpoints + (*newpoly)->number - 1)->y;
3974 else if (!strcmp(keyword, "arc") || !strcmp(keyword, "arcn")) {
3975 arcptr *newarc;
3976 NEW_ARC(newarc, (*newpath));
3977 (*newarc)->width = 1.0;
3978 (*newarc)->style = UNCLOSED;
3979 (*newarc)->color = curcolor;
3980 (*newarc)->passed = NULL;
3981 (*newarc)->cycle = NULL;
3983 lineptr = varpscan(localdata, buffer, &(*newarc)->position.x,
3984 (genericptr)*newarc, 0, offx, P_POSITION_X);
3985 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
3986 (genericptr)*newarc, 0, offy, P_POSITION_Y);
3987 lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
3988 (genericptr)*newarc, P_RADIUS);
3989 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
3990 (genericptr)*newarc, P_ANGLE1);
3991 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
3992 (genericptr)*newarc, P_ANGLE2);
3994 (*newarc)->yaxis = (*newarc)->radius;
3995 if (!strcmp(keyword, "arcn")) {
3996 float tmpang = (*newarc)->angle1;
3997 (*newarc)->radius = -((*newarc)->radius);
3998 (*newarc)->angle1 = (*newarc)->angle2;
3999 (*newarc)->angle2 = tmpang;
4002 calcarc(*newarc);
4003 startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x;
4004 startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y;
4005 decomposearc(*newpath);
4008 else if (!strcmp(keyword, "pellip") || !strcmp(keyword, "nellip")) {
4009 arcptr *newarc;
4010 NEW_ARC(newarc, (*newpath));
4011 (*newarc)->width = 1.0;
4012 (*newarc)->style = UNCLOSED;
4013 (*newarc)->color = curcolor;
4014 (*newarc)->passed = NULL;
4015 (*newarc)->cycle = NULL;
4017 lineptr = varpscan(localdata, buffer, &(*newarc)->position.x,
4018 (genericptr)*newarc, 0, offx, P_POSITION_X);
4019 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4020 (genericptr)*newarc, 0, offy, P_POSITION_Y);
4021 lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4022 (genericptr)*newarc, P_RADIUS);
4023 lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis,
4024 (genericptr)*newarc, P_MINOR_AXIS);
4025 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4026 (genericptr)*newarc, P_ANGLE1);
4027 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4028 (genericptr)*newarc, P_ANGLE2);
4030 if (!strcmp(keyword, "nellip")) {
4031 float tmpang = (*newarc)->angle1;
4032 (*newarc)->radius = -((*newarc)->radius);
4033 (*newarc)->angle1 = (*newarc)->angle2;
4034 (*newarc)->angle2 = tmpang;
4037 calcarc(*newarc);
4038 startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x;
4039 startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y;
4040 decomposearc(*newpath);
4043 else if (!strcmp(keyword, "curveto")) {
4044 splineptr *newspline;
4045 px = py = 0;
4047 NEW_SPLINE(newspline, (*newpath));
4048 (*newspline)->passed = NULL;
4049 (*newspline)->cycle = NULL;
4050 (*newspline)->width = 1.0;
4051 (*newspline)->style = UNCLOSED;
4052 (*newspline)->color = curcolor;
4054 /* If the last point on the last path part was parameterized, then */
4055 /* the first point of the spline must be, too. */
4057 if (epptrx != NULL) {
4058 eparamptr newepp = copyeparam(epptrx, (genericptr)(*newpath));
4059 newepp->next = (*newpath)->passed;
4060 (*newpath)->passed = newepp;
4061 newepp->pdata.pathpt[1] = 0;
4062 newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
4064 if (epptry != NULL) {
4065 eparamptr newepp = copyeparam(epptry, (genericptr)(*newpath));
4066 newepp->next = (*newpath)->passed;
4067 (*newpath)->passed = newepp;
4068 newepp->pdata.pathpt[1] = 0;
4069 newepp->pdata.pathpt[0] = (*newpath)->parts - 1;
4072 for (--keyptr; *keyptr == ' '; keyptr--);
4073 for (; *keyptr != ' '; keyptr--);
4075 /* check for "addtox" and "addtoy" parameter specification */
4076 while (!strncmp(keyptr + 1, "addto", 5)) {
4077 saveptr = keyptr + 1;
4079 for (--keyptr; *keyptr == ' '; keyptr--);
4080 for (; *keyptr != ' '; keyptr--);
4082 /* Get parameter and its value */
4083 if (*(saveptr + 5) == 'x')
4084 varpscan(localdata, keyptr + 1, &px, (genericptr)*newspline,
4085 -1, offx, P_POSITION_X);
4086 else
4087 varpscan(localdata, keyptr + 1, &py, (genericptr)*newspline,
4088 -1, offy, P_POSITION_Y);
4090 for (--keyptr; *keyptr == ' '; keyptr--);
4091 for (; *keyptr != ' '; keyptr--);
4095 lineptr = varpathscan(localdata, buffer, &(*newspline)->ctrl[1].x,
4096 (genericptr *)newspline, *newpath, 1, offx + px, P_POSITION_X,
4097 NULL);
4098 lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[1].y,
4099 (genericptr *)newspline, *newpath, 1, offy + py, P_POSITION_Y,
4100 NULL);
4101 lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[2].x,
4102 (genericptr *)newspline, *newpath, 2, offx + px, P_POSITION_X,
4103 NULL);
4104 lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[2].y,
4105 (genericptr *)newspline, *newpath, 2, offy + py, P_POSITION_Y,
4106 NULL);
4107 lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[3].x,
4108 (genericptr *)newspline, *newpath, 3, offx + px, P_POSITION_X,
4109 &epptrx);
4110 lineptr = varpathscan(localdata, lineptr, &(*newspline)->ctrl[3].y,
4111 (genericptr *)newspline, *newpath, 3, offy + py, P_POSITION_Y,
4112 &epptry);
4114 (*newspline)->ctrl[0].x = startpoint.x;
4115 (*newspline)->ctrl[0].y = startpoint.y;
4117 calcspline(*newspline);
4118 startpoint.x = (*newspline)->ctrl[3].x;
4119 startpoint.y = (*newspline)->ctrl[3].y;
4122 /* read arcs */
4124 else if (!strcmp(keyword, "xcarc")) {
4125 arcptr *newarc;
4127 NEW_ARC(newarc, localdata);
4128 (*newarc)->color = curcolor;
4129 (*newarc)->passed = NULL;
4130 (*newarc)->cycle = NULL;
4132 /* backward compatibility */
4133 if (version < 1.5) {
4134 sscanf(buffer, "%hd %hd %hd %f %f %f %hd", &(*newarc)->position.x,
4135 &(*newarc)->position.y, &(*newarc)->radius, &(*newarc)->angle1,
4136 &(*newarc)->angle2, &(*newarc)->width, &(*newarc)->style);
4137 (*newarc)->position.x -= offx;
4138 (*newarc)->position.y -= offy;
4140 else {
4141 lineptr = varscan(localdata, buffer, &(*newarc)->style,
4142 (genericptr)*newarc, P_STYLE);
4143 lineptr = varfscan(localdata, lineptr, &(*newarc)->width,
4144 (genericptr)*newarc, P_LINEWIDTH);
4145 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x,
4146 (genericptr)*newarc, 0, offx, P_POSITION_X);
4147 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4148 (genericptr)*newarc, 0, offy, P_POSITION_Y);
4149 lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4150 (genericptr)*newarc, P_RADIUS);
4151 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4152 (genericptr)*newarc, P_ANGLE1);
4153 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4154 (genericptr)*newarc, P_ANGLE2);
4157 (*newarc)->yaxis = (*newarc)->radius;
4158 calcarc(*newarc);
4159 std_eparam((genericptr)(*newarc), colorkey);
4162 /* read ellipses */
4164 else if (!strcmp(keyword, "ellipse")) {
4165 arcptr *newarc;
4167 NEW_ARC(newarc, localdata);
4169 (*newarc)->color = curcolor;
4170 (*newarc)->passed = NULL;
4171 (*newarc)->cycle = NULL;
4173 lineptr = varscan(localdata, buffer, &(*newarc)->style,
4174 (genericptr)*newarc, P_STYLE);
4175 lineptr = varfscan(localdata, lineptr, &(*newarc)->width,
4176 (genericptr)*newarc, P_LINEWIDTH);
4177 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x,
4178 (genericptr)*newarc, 0, offx, P_POSITION_X);
4179 lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y,
4180 (genericptr)*newarc, 0, offy, P_POSITION_Y);
4181 lineptr = varscan(localdata, lineptr, &(*newarc)->radius,
4182 (genericptr)*newarc, P_RADIUS);
4183 lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis,
4184 (genericptr)*newarc, P_MINOR_AXIS);
4185 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1,
4186 (genericptr)*newarc, P_ANGLE1);
4187 lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2,
4188 (genericptr)*newarc, P_ANGLE2);
4190 calcarc(*newarc);
4191 std_eparam((genericptr)(*newarc), colorkey);
4194 /* read polygons */
4195 /* (and wires---backward compatibility for v1.5 and earlier) */
4197 else if (!strcmp(keyword, "polygon") || !strcmp(keyword, "wire")) {
4198 polyptr *newpoly;
4199 pointlist newpoints;
4200 px = py = 0;
4202 NEW_POLY(newpoly, localdata);
4203 lineptr = buffer;
4205 (*newpoly)->passed = NULL;
4206 (*newpoly)->cycle = NULL;
4208 if (!strcmp(keyword, "wire")) {
4209 (*newpoly)->number = 2;
4210 (*newpoly)->width = 1.0;
4211 (*newpoly)->style = UNCLOSED;
4213 else {
4214 /* backward compatibility */
4215 if (version < 1.5) {
4216 for (--keyptr; *keyptr == ' '; keyptr--);
4217 for (; *keyptr != ' '; keyptr--);
4218 sscanf(keyptr, "%hd", &(*newpoly)->style);
4219 for (--keyptr; *keyptr == ' '; keyptr--);
4220 for (; *keyptr != ' '; keyptr--);
4221 sscanf(keyptr, "%f", &(*newpoly)->width);
4223 for (--keyptr; *keyptr == ' '; keyptr--);
4224 for (; *keyptr != ' '; keyptr--);
4225 /* check for "addtox" and "addtoy" parameter specification */
4226 while (!strncmp(keyptr + 1, "addto", 5)) {
4227 saveptr = keyptr + 1;
4229 for (--keyptr; *keyptr == ' '; keyptr--);
4230 for (; *keyptr != ' '; keyptr--);
4232 /* Get parameter and its value */
4233 if (*(saveptr + 5) == 'x')
4234 varpscan(localdata, keyptr + 1, &px, (genericptr)*newpoly,
4235 -1, offx, P_POSITION_X);
4236 else
4237 varpscan(localdata, keyptr + 1, &py, (genericptr)*newpoly,
4238 -1, offy, P_POSITION_Y);
4240 for (--keyptr; *keyptr == ' '; keyptr--);
4241 for (; *keyptr != ' '; keyptr--);
4243 sscanf(keyptr, "%hd", &(*newpoly)->number);
4245 if (version >= 1.5) {
4246 lineptr = varscan(localdata, lineptr, &(*newpoly)->style,
4247 (genericptr)*newpoly, P_STYLE);
4248 lineptr = varfscan(localdata, lineptr, &(*newpoly)->width,
4249 (genericptr)*newpoly, P_LINEWIDTH);
4253 if ((*newpoly)->style & BBOX)
4254 (*newpoly)->color = BBOXCOLOR;
4255 else
4256 (*newpoly)->color = curcolor;
4257 (*newpoly)->points = (pointlist) malloc((*newpoly)->number *
4258 sizeof(XPoint));
4260 for (newpoints = (*newpoly)->points; newpoints < (*newpoly)->points
4261 + (*newpoly)->number; newpoints++) {
4262 lineptr = varpscan(localdata, lineptr, &newpoints->x,
4263 (genericptr)*newpoly, newpoints - (*newpoly)->points,
4264 offx + px, P_POSITION_X);
4265 lineptr = varpscan(localdata, lineptr, &newpoints->y,
4266 (genericptr)*newpoly, newpoints - (*newpoly)->points,
4267 offy + py, P_POSITION_Y);
4269 std_eparam((genericptr)(*newpoly), colorkey);
4272 /* read spline curves */
4274 else if (!strcmp(keyword, "spline")) {
4275 splineptr *newspline;
4276 px = py = 0;
4278 NEW_SPLINE(newspline, localdata);
4279 (*newspline)->color = curcolor;
4280 (*newspline)->passed = NULL;
4281 (*newspline)->cycle = NULL;
4283 /* backward compatibility */
4284 if (version < 1.5) {
4285 sscanf(buffer, "%f %hd %hd %hd %hd %hd %hd %hd %hd %hd",
4286 &(*newspline)->width, &(*newspline)->ctrl[1].x,
4287 &(*newspline)->ctrl[1].y, &(*newspline)->ctrl[2].x,
4288 &(*newspline)->ctrl[2].y, &(*newspline)->ctrl[3].x,
4289 &(*newspline)->ctrl[3].y, &(*newspline)->ctrl[0].x,
4290 &(*newspline)->ctrl[0].y, &(*newspline)->style);
4291 (*newspline)->ctrl[1].x -= offx; (*newspline)->ctrl[2].x -= offx;
4292 (*newspline)->ctrl[0].x -= offx;
4293 (*newspline)->ctrl[3].x -= offx;
4294 (*newspline)->ctrl[1].y -= offy; (*newspline)->ctrl[2].y -= offy;
4295 (*newspline)->ctrl[3].y -= offy;
4296 (*newspline)->ctrl[0].y -= offy;
4298 else {
4300 for (--keyptr; *keyptr == ' '; keyptr--);
4301 for (; *keyptr != ' '; keyptr--);
4302 /* check for "addtox" and "addtoy" parameter specification */
4303 while (!strncmp(keyptr + 1, "addto", 5)) {
4304 saveptr = keyptr + 1;
4306 for (--keyptr; *keyptr == ' '; keyptr--);
4307 for (; *keyptr != ' '; keyptr--);
4309 /* Get parameter and its value */
4310 if (*(saveptr + 5) == 'x')
4311 varpscan(localdata, keyptr + 1, &px, (genericptr)*newspline,
4312 -1, offx, P_POSITION_X);
4313 else
4314 varpscan(localdata, keyptr + 1, &py, (genericptr)*newspline,
4315 -1, offy, P_POSITION_Y);
4317 for (--keyptr; *keyptr == ' '; keyptr--);
4318 for (; *keyptr != ' '; keyptr--);
4321 lineptr = varscan(localdata, buffer, &(*newspline)->style,
4322 (genericptr)*newspline, P_STYLE);
4323 lineptr = varfscan(localdata, lineptr, &(*newspline)->width,
4324 (genericptr)*newspline, P_LINEWIDTH);
4325 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].x,
4326 (genericptr)*newspline, 1, offx + px, P_POSITION_X);
4327 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].y,
4328 (genericptr)*newspline, 1, offy + py, P_POSITION_Y);
4329 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].x,
4330 (genericptr)*newspline, 2, offx + px, P_POSITION_X);
4331 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].y,
4332 (genericptr)*newspline, 2, offy + py, P_POSITION_Y);
4333 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].x,
4334 (genericptr)*newspline, 3, offx + px, P_POSITION_X);
4335 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].y,
4336 (genericptr)*newspline, 3, offy + py, P_POSITION_Y);
4337 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].x,
4338 (genericptr)*newspline, 0, offx + px, P_POSITION_X);
4339 lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].y,
4340 (genericptr)*newspline, 0, offy + py, P_POSITION_Y);
4342 /* check for "addtox" and "addtoy" parameter specification */
4345 calcspline(*newspline);
4346 std_eparam((genericptr)(*newspline), colorkey);
4349 /* read graphics image instances */
4351 else if (!strcmp(keyword, "graphic")) {
4352 graphicptr *newgp;
4353 Imagedata *img;
4355 lineptr = buffer + 1;
4356 for (i = 0; i < xobjs.images; i++) {
4357 img = xobjs.imagelist + i;
4358 if (!strncmp(img->filename, lineptr, strlen(img->filename))) {
4359 NEW_GRAPHIC(newgp, localdata);
4360 (*newgp)->color = curcolor;
4361 (*newgp)->passed = NULL;
4362 #ifndef HAVE_CAIRO
4363 (*newgp)->clipmask = (Pixmap)NULL;
4364 (*newgp)->target = NULL;
4365 (*newgp)->valid = False;
4366 #endif /* HAVE_CAIRO */
4367 (*newgp)->source = img->image;
4368 img->refcount++;
4369 lineptr += strlen(img->filename) + 1;
4370 break;
4373 if (i == xobjs.images) {
4374 /* Error: Line points to a non-existant image (no data) */
4375 /* See if we can load the image name as a filename, and */
4376 /* if that fails, then we must throw an error and ignore */
4377 /* the image element. */
4379 graphicptr locgp;
4380 char *sptr = strchr(lineptr, ' ');
4382 if (sptr != NULL)
4383 *sptr = '\0';
4384 else
4385 sptr = lineptr;
4386 locgp = new_graphic(NULL, lineptr, 0, 0);
4388 if (locgp == NULL) {
4389 Fprintf(stderr, "Error: No graphic data for \"%s\".\n",
4390 lineptr);
4391 newgp = NULL;
4393 else {
4394 lineptr = sptr;
4395 newgp = &locgp;
4399 if ((newgp != NULL) && (*newgp != NULL)) {
4400 lineptr = varfscan(localdata, lineptr, &(*newgp)->scale,
4401 (genericptr)*newgp, P_SCALE);
4402 lineptr = varfscan(localdata, lineptr, &(*newgp)->rotation,
4403 (genericptr)*newgp, P_ROTATION);
4404 lineptr = varpscan(localdata, lineptr, &(*newgp)->position.x,
4405 (genericptr)*newgp, 0, offx, P_POSITION_X);
4406 lineptr = varpscan(localdata, lineptr, &(*newgp)->position.y,
4407 (genericptr)*newgp, 0, offy, P_POSITION_Y);
4408 std_eparam((genericptr)(*newgp), colorkey);
4412 /* read labels */
4414 else if (!strcmp(keyword, "fontset")) { /* old style */
4415 char tmpstring[100];
4416 int i;
4417 sscanf(buffer, "%f %*c%99s", &tmpscale, tmpstring);
4418 for (i = 0; i < fontcount; i++)
4419 if (!strcmp(tmpstring, fonts[i].psname)) {
4420 tmpfont = i;
4421 break;
4423 if (i == fontcount) i = 0; /* Why bother with anything fancy? */
4426 else if (!strcmp(keyword, "label") || !strcmp(keyword, "pinlabel")
4427 || !strcmp(keyword, "pinglobal") || !strcmp(keyword, "infolabel")) {
4429 labelptr *newlabel;
4430 stringpart *firstscale, *firstfont;
4432 NEW_LABEL(newlabel, localdata);
4433 (*newlabel)->color = curcolor;
4434 (*newlabel)->string = NULL;
4435 (*newlabel)->passed = NULL;
4436 (*newlabel)->cycle = NULL;
4438 /* scan backwards to get the number of substrings */
4439 lineptr = keyptr - 1;
4440 for (i = 0; i < ((version < 2.25) ? 5 : 6); i++) {
4441 for (; *lineptr == ' '; lineptr--);
4442 for (; *lineptr != ' '; lineptr--);
4444 if ((strchr(lineptr, '.') != NULL) && (version < 2.25)) {
4445 Fprintf(stderr, "Error: File version claims to be %2.1f,"
4446 " but has version %2.1f labels\n", version, PROG_VERSION);
4447 Fprintf(stderr, "Attempting to resolve problem by updating version.\n");
4448 version = PROG_VERSION;
4449 for (; *lineptr == ' '; lineptr--);
4450 for (; *lineptr != ' '; lineptr--);
4452 /* no. segments is ignored---may be a derived quantity, anyway */
4453 if (version < 2.25) {
4454 sscanf(lineptr, "%*s %hd %hf %hd %hd", &(*newlabel)->anchor,
4455 &(*newlabel)->rotation, &(*newlabel)->position.x,
4456 &(*newlabel)->position.y);
4457 (*newlabel)->position.x -= offx; (*newlabel)->position.y -= offy;
4458 *lineptr = '\0';
4460 else {
4461 *lineptr++ = '\0';
4462 lineptr = advancetoken(lineptr); /* skip string token */
4463 lineptr = varscan(localdata, lineptr, &(*newlabel)->anchor,
4464 (genericptr)*newlabel, P_ANCHOR);
4465 lineptr = varfscan(localdata, lineptr, &(*newlabel)->rotation,
4466 (genericptr)*newlabel, P_ROTATION);
4467 lineptr = varfscan(localdata, lineptr, &(*newlabel)->scale,
4468 (genericptr)*newlabel, P_SCALE);
4469 lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.x,
4470 (genericptr)*newlabel, 0, offx, P_POSITION_X);
4471 lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.y,
4472 (genericptr)*newlabel, 0, offy, P_POSITION_Y);
4474 if (version < 2.4)
4475 (*newlabel)->rotation = -(*newlabel)->rotation;
4476 while ((*newlabel)->rotation < 0.0) (*newlabel)->rotation += 360.0;
4478 (*newlabel)->pin = False;
4479 if (strcmp(keyword, "label")) { /* all the schematic types */
4480 /* enable schematic capture if it is not already on. */
4481 if (!strcmp(keyword, "pinlabel"))
4482 (*newlabel)->pin = LOCAL;
4483 else if (!strcmp(keyword, "pinglobal"))
4484 (*newlabel)->pin = GLOBAL;
4485 else if (!strcmp(keyword, "infolabel")) {
4486 /* Do not turn top-level pages into symbols! */
4487 /* Info labels on schematics are treated differently. */
4488 if (localdata != topobject)
4489 localdata->schemtype = FUNDAMENTAL;
4490 (*newlabel)->pin = INFO;
4491 if (curcolor == DEFAULTCOLOR)
4492 (*newlabel)->color = INFOLABELCOLOR;
4496 lineptr = buffer; /* back to beginning of string */
4497 if (!strncmp(lineptr, "mark", 4)) lineptr += 4;
4499 readlabel(localdata, lineptr, &(*newlabel)->string);
4500 CheckMarginStop(*newlabel, areawin->topinstance, FALSE);
4502 if (version < 2.25) {
4503 /* Switch 1st scale designator to overall font scale */
4505 firstscale = (*newlabel)->string->nextpart;
4506 if (firstscale->type != FONT_SCALE) {
4507 if (tmpscale != 0.0)
4508 (*newlabel)->scale = 0.0;
4509 else
4510 (*newlabel)->scale = 1.0;
4512 else {
4513 (*newlabel)->scale = firstscale->data.scale;
4514 deletestring(firstscale, &((*newlabel)->string),
4515 areawin->topinstance);
4519 firstfont = (*newlabel)->string;
4520 if ((firstfont == NULL) || (firstfont->type != FONT_NAME)) {
4521 if (tmpfont == -1) {
4522 Fprintf(stderr, "Error: Label with no font designator?\n");
4523 tmpfont = 0;
4525 firstfont = makesegment(&((*newlabel)->string), (*newlabel)->string);
4526 firstfont->type = FONT_NAME;
4527 firstfont->data.font = tmpfont;
4529 cleanuplabel(&(*newlabel)->string);
4531 std_eparam((genericptr)(*newlabel), colorkey);
4534 /* read symbol-to-schematic connection */
4536 else if (!strcmp(keyword, "is_schematic")) {
4537 char tempstr[50];
4538 for (lineptr = buffer; *lineptr == ' '; lineptr++);
4539 parse_ps_string(++lineptr, tempstr, 49, FALSE, FALSE);
4540 checksym(localdata, tempstr);
4543 /* read bounding box (font files only) */
4545 else if (!strcmp(keyword, "bbox")) {
4546 for (lineptr = buffer; *lineptr == ' '; lineptr++);
4547 if (*lineptr != '%') {
4548 Wprintf("Illegal bbox.");
4549 free(buffer);
4550 *retstr = '\0';
4551 return True;
4553 sscanf(++lineptr, "%hd %hd %hd %hd",
4554 &localdata->bbox.lowerleft.x, &localdata->bbox.lowerleft.y,
4555 &localdata->bbox.width, &localdata->bbox.height);
4558 /* read "hidden" attribute */
4560 else if (!strcmp(keyword, "hidden")) {
4561 localdata->hidden = True;
4564 /* read "libinst" special instance of a library part */
4566 else if (!strcmp(keyword, "libinst")) {
4568 /* Read backwards from keyword to find name of object instanced. */
4569 for (lineptr = keyptr; *lineptr != '/' && lineptr > buffer;
4570 lineptr--);
4571 parse_ps_string(++lineptr, keyword, 79, FALSE, FALSE);
4572 new_library_instance(mode - LIBRARY, keyword, buffer, defaulttech);
4575 /* read objects */
4577 else if (!strcmp(keyword, "{")) { /* This is an object definition */
4578 objlistptr redef;
4579 objectptr *newobject;
4581 for (lineptr = buffer; *lineptr == ' '; lineptr++);
4582 if (*lineptr++ != '/') {
4583 /* This may be part of a label. . . treat as a continuation line */
4584 temp = continueline(&buffer);
4585 continue;
4587 parse_ps_string(lineptr, keyword, 79, FALSE, FALSE);
4589 newobject = new_library_object(mode, keyword, &redef, defaulttech);
4591 if (objectread(ps, *newobject, 0, 0, mode, retstr, curcolor,
4592 defaulttech) == True) {
4593 strncpy(retstr, buffer, 150);
4594 retstr[149] = '\0';
4595 free(buffer);
4596 return True;
4598 else {
4599 if (library_object_unique(mode, *newobject, redef))
4600 add_object_to_library(mode, *newobject);
4603 else if (!strcmp(keyword, "def")) {
4604 strncpy(retstr, buffer, 150);
4605 retstr[149] = '\0';
4606 free (buffer);
4607 return False; /* end of object def or end of object library */
4610 else if (!strcmp(keyword, "loadfontencoding")) {
4611 /* Deprecated, but retained for backward compatibility. */
4612 /* Load from script, .xcircuitrc, or command line instead. */
4613 for (lineptr = buffer; *lineptr != '%'; lineptr++);
4614 sscanf (lineptr + 1, "%149s", _STR);
4615 if (*(lineptr + 1) != '%') loadfontfile(_STR);
4617 else if (!strcmp(keyword, "loadlibrary")) {
4618 /* Deprecated, but retained for backward compatibility */
4619 /* Load from script, .xcircuitrc, or command line instead. */
4620 int ilib, tlib;
4622 for (lineptr = buffer; *lineptr != '%'; lineptr++);
4623 sscanf (++lineptr, "%149s", _STR);
4624 while (isspace(*lineptr)) lineptr++;
4625 while (!isspace(*++lineptr));
4626 while (isspace(*++lineptr));
4627 if (sscanf (lineptr, "%d", &ilib) > 0) {
4628 while ((ilib - 2 + LIBRARY) > xobjs.numlibs) {
4629 tlib = createlibrary(False);
4630 if (tlib != xobjs.numlibs - 2 + LIBRARY) {
4631 ilib = tlib;
4632 break;
4635 mode = ilib - 1 + LIBRARY;
4637 loadlibrary(mode);
4639 else if (!strcmp(keyword, "beginparm")) { /* parameterized object */
4640 short tmpnum, i;
4641 for (--keyptr; *keyptr == ' '; keyptr--);
4642 for (; isdigit(*keyptr) && (keyptr >= buffer); keyptr--);
4643 sscanf(keyptr, "%hd", &tmpnum);
4644 lineptr = buffer;
4645 while (isspace(*lineptr)) lineptr++;
4647 if (tmpnum < 256) { /* read parameter defaults in order */
4648 stringpart *newpart;
4649 /* oparamptr newops; (jdk) */
4651 for (i = 0; i < tmpnum; i++) {
4652 oparamptr newops;
4654 newops = (oparamptr)malloc(sizeof(oparam));
4655 newops->next = localdata->params;
4656 localdata->params = newops;
4657 newops->key = (char *)malloc(6);
4658 sprintf(newops->key, "v%d", i + 1);
4660 if (*lineptr == '(' || *lineptr == '{') { /* type is XC_STRING */
4661 char *linetmp, csave;
4663 newops->parameter.string = NULL;
4665 /* get simple substring or set of substrings and commands */
4666 linetmp = find_delimiter(lineptr);
4667 csave = *(++linetmp);
4668 *linetmp = '\0';
4669 if (*lineptr == '{') lineptr++;
4670 readlabel(localdata, lineptr, &(newops->parameter.string));
4672 /* Add the ending part to the parameter string */
4673 newpart = makesegment(&(newops->parameter.string), NULL);
4674 newpart->type = PARAM_END;
4675 newpart->data.string = (u_char *)NULL;
4677 newops->type = (u_char)XC_STRING;
4678 newops->which = P_SUBSTRING;
4679 /* Fprintf(stdout, "Parameter %d to object %s defaults "
4680 "to string \"%s\"\n", i + 1, localdata->name,
4681 ops->parameter.string); */
4682 *linetmp = csave;
4683 lineptr = linetmp;
4684 while (isspace(*lineptr)) lineptr++;
4686 else { /* type is assumed to be XC_FLOAT */
4687 newops->type = (u_char)XC_FLOAT;
4688 sscanf(lineptr, "%g", &newops->parameter.fvalue);
4689 /* Fprintf(stdout, "Parameter %s to object %s defaults to "
4690 "value %g\n", newops->key, localdata->name,
4691 newops->parameter.fvalue); */
4692 lineptr = advancetoken(lineptr);
4697 else if (!strcmp(keyword, "nonetwork")) {
4698 localdata->valid = True;
4699 localdata->schemtype = NONETWORK;
4701 else if (!strcmp(keyword, "trivial")) {
4702 localdata->schemtype = TRIVIAL;
4704 else if (!strcmp(keyword, "begingate")) {
4705 localdata->params = NULL;
4706 /* read dictionary of parameter key:value pairs */
4707 readparams(NULL, NULL, localdata, buffer);
4710 else if (!strcmp(keyword, "%%Trailer")) break;
4711 else if (!strcmp(keyword, "EndLib")) break;
4712 else if (!strcmp(keyword, "restore")); /* handled at top */
4713 else if (!strcmp(keyword, "grestore")); /* ignore */
4714 else if (!strcmp(keyword, "endgate")); /* also ignore */
4715 else if (!strcmp(keyword, "xyarray")); /* ignore for now */
4716 else {
4717 char *tmpptr, *libobjname, *objnamestart;
4718 Boolean matchtech, found = False;
4720 /* First, make sure this is not a general comment line */
4721 /* Return if we have a page boundary */
4722 /* Read an image if this is an imagedata line. */
4724 for (tmpptr = buffer; isspace(*tmpptr); tmpptr++);
4725 if (*tmpptr == '%') {
4726 if (strstr(buffer, "%%Page:") == tmpptr) {
4727 strncpy(retstr, buffer, 150);
4728 retstr[149] = '\0';
4729 free (buffer);
4730 return True;
4732 else if (strstr(buffer, "%imagedata") == tmpptr) {
4733 int width, height;
4734 sscanf(buffer, "%*s %d %d", &width, &height);
4735 readimagedata(ps, width, height);
4737 continue;
4740 parse_ps_string(keyword, keyword, 79, FALSE, FALSE);
4741 matchtech = (strstr(keyword, "::") == NULL) ? FALSE : TRUE;
4743 /* If the file contains a reference to a bare-word device */
4744 /* without a technology prefix, then it is probably an */
4745 /* older-version file. If this is the case, then the file */
4746 /* should define an unprefixed object, which will be given */
4747 /* a null prefix (just "::" by itself). Look for that */
4748 /* match. */
4750 /* (Assume that this line calls an object instance) */
4751 /* Double loop through user libraries */
4753 for (k = 0; k < ((mode == FONTLIB) ? 1 : xobjs.numlibs); k++) {
4754 for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number :
4755 xobjs.userlibs[k].number); j++) {
4757 libobj = (mode == FONTLIB) ? xobjs.fontlib.library + j :
4758 xobjs.userlibs[k].library + j;
4760 /* Objects which have a technology ("<lib>::<obj>") */
4761 /* must compare exactly. Objects which don't can */
4762 /* only match an object of the same name with a null */
4763 /* technology prefix. */
4765 libobjname = (*libobj)->name;
4766 if (!matchtech) {
4767 objnamestart = strstr(libobjname, "::");
4768 if (objnamestart != NULL) libobjname = objnamestart + 2;
4770 if (!objnamecmp(keyword, libobjname)) {
4772 /* If the name is not exactly the same (appended underscores) */
4773 /* check if the name is on the list of aliases. */
4775 if (strcmp(keyword, libobjname)) {
4776 Boolean is_alias = False;
4777 aliasptr ckalias = aliastop;
4778 slistptr sref;
4780 for (; ckalias != NULL; ckalias = ckalias->next) {
4781 if (ckalias->baseobj == (*libobj)) {
4782 sref = ckalias->aliases;
4783 for (; sref != NULL; sref = sref->next) {
4784 if (!strcmp(keyword, sref->alias)) {
4785 is_alias = True;
4786 break;
4789 if (is_alias) break;
4792 if (!is_alias) continue;
4795 if (!matchtech && ((*libobj)->name != objnamestart))
4796 if (version > 3.7)
4797 continue; // no prefix in file must match
4798 // null prefix in library object.
4800 found = True;
4801 NEW_OBJINST(newinst, localdata);
4802 (*newinst)->thisobject = *libobj;
4803 (*newinst)->color = curcolor;
4804 (*newinst)->params = NULL;
4805 (*newinst)->passed = NULL;
4806 (*newinst)->bbox.lowerleft.x = (*libobj)->bbox.lowerleft.x;
4807 (*newinst)->bbox.lowerleft.y = (*libobj)->bbox.lowerleft.y;
4808 (*newinst)->bbox.width = (*libobj)->bbox.width;
4809 (*newinst)->bbox.height = (*libobj)->bbox.height;
4810 (*newinst)->schembbox = NULL;
4812 lineptr = varfscan(localdata, buffer, &(*newinst)->scale,
4813 (genericptr)*newinst, P_SCALE);
4815 /* Format prior to xcircuit 3.7 did not have scale- */
4816 /* invariant linewidth. If scale is 1, though, keep it */
4817 /* invariant so as not to overuse the scale-variance */
4818 /* flag in the output file. */
4820 if ((version < 3.7) && ((*newinst)->scale != 1.0))
4821 (*newinst)->style = NORMAL;
4822 else
4823 (*newinst)->style = LINE_INVARIANT;
4825 lineptr = varfscan(localdata, lineptr, &(*newinst)->rotation,
4826 (genericptr)*newinst, P_ROTATION);
4827 lineptr = varpscan(localdata, lineptr, &(*newinst)->position.x,
4828 (genericptr)*newinst, 0, offx, P_POSITION_X);
4829 lineptr = varpscan(localdata, lineptr, &(*newinst)->position.y,
4830 (genericptr)*newinst, 0, offy, P_POSITION_Y);
4832 /* Negative rotations = flip in x in version 2.3.6 and */
4833 /* earlier. Later versions don't allow negative rotation */
4835 if (version < 2.4) {
4836 if ((*newinst)->rotation < 0.0) {
4837 (*newinst)->scale = -((*newinst)->scale);
4838 (*newinst)->rotation += 1.0;
4840 (*newinst)->rotation = -(*newinst)->rotation;
4843 while ((*newinst)->rotation > 360.0)
4844 (*newinst)->rotation -= 360.0;
4845 while ((*newinst)->rotation < 0.0)
4846 (*newinst)->rotation += 360.0;
4848 std_eparam((genericptr)(*newinst), colorkey);
4850 /* Does this instance contain parameters? */
4851 readparams(localdata, *newinst, *libobj, buffer);
4853 calcbboxinst(*newinst);
4855 break;
4857 } /* if !strcmp */
4858 } /* for j loop */
4859 if (found) break;
4860 } /* for k loop */
4861 if (!found) /* will assume that we have a continuation line */
4862 temp = continueline(&buffer);
4866 strncpy(retstr, buffer, 150);
4867 retstr[149] = '\0';
4868 free(buffer);
4869 return True;
4872 /*------------------------*/
4873 /* Save a PostScript file */
4874 /*------------------------*/
4876 #ifdef TCL_WRAPPER
4878 void setfile(char *filename, int mode)
4880 if ((filename == NULL) || (xobjs.pagelist[areawin->page]->filename == NULL)) {
4881 Wprintf("Error: No filename for schematic.");
4882 if (beeper) XBell(dpy, 100);
4883 return;
4886 /* see if name has been changed in the buffer */
4888 if (strcmp(xobjs.pagelist[areawin->page]->filename, filename)) {
4889 Wprintf("Changing name of edit file.");
4890 free(xobjs.pagelist[areawin->page]->filename);
4891 xobjs.pagelist[areawin->page]->filename = strdup(filename);
4894 if (strstr(xobjs.pagelist[areawin->page]->filename, "Page ") != NULL) {
4895 Wprintf("Warning: Enter a new name.");
4896 if (beeper) XBell(dpy, 100);
4898 else {
4899 savefile(mode);
4900 if (beeper) XBell(dpy, 100);
4904 #else /* !TCL_WRAPPER */
4906 void setfile(xcWidget button, xcWidget fnamewidget, caddr_t clientdata)
4908 /* see if name has been changed in the buffer */
4910 sprintf(_STR2, "%.249s", (char *)XwTextCopyBuffer(fnamewidget));
4911 if (xobjs.pagelist[areawin->page]->filename == NULL) {
4912 xobjs.pagelist[areawin->page]->filename = strdup(_STR2);
4914 else if (strcmp(xobjs.pagelist[areawin->page]->filename, _STR2)) {
4915 Wprintf("Changing name of edit file.");
4916 free(xobjs.pagelist[areawin->page]->filename);
4917 xobjs.pagelist[areawin->page]->filename = strdup(_STR2);
4919 if (strstr(xobjs.pagelist[areawin->page]->filename, "Page ") != NULL) {
4920 Wprintf("Warning: Enter a new name.");
4921 if (beeper) XBell(dpy, 100);
4923 else {
4925 Arg wargs[1];
4926 xcWidget db, di;
4928 savefile(CURRENT_PAGE);
4930 /* Change "close" button to read "done" */
4932 di = xcParent(button);
4933 db = XtNameToWidget(di, "Close");
4934 XtSetArg(wargs[0], XtNlabel, " Done ");
4935 XtSetValues(db, wargs, 1);
4936 if (beeper) XBell(dpy, 100);
4940 #endif /* TCL_WRAPPER */
4942 /*--------------------------------------------------------------*/
4943 /* Update number of changes for an object and initiate a temp */
4944 /* file save if total number of unsaved changes exceeds 20. */
4945 /*--------------------------------------------------------------*/
4947 void incr_changes(objectptr thisobj)
4949 /* It is assumed that empty pages are meant to be that way */
4950 /* and are not to be saved, so changes are marked as zero. */
4952 if (thisobj->parts == 0) {
4953 thisobj->changes = 0;
4954 return;
4957 /* Remove any pending timeout */
4959 if (xobjs.timeout_id != (xcIntervalId)NULL) {
4960 xcRemoveTimeOut(xobjs.timeout_id);
4961 xobjs.timeout_id = (xcIntervalId)NULL;
4964 thisobj->changes++;
4966 /* When in "suspend" mode, we assume that we are reading commands from
4967 * a script, and therefore we should not continuously increment changes
4968 * and keep saving the backup file.
4971 if (xobjs.suspend < 0)
4972 xobjs.new_changes++;
4974 if (xobjs.new_changes > MAXCHANGES) {
4975 #ifdef TCL_WRAPPER
4976 savetemp(NULL);
4977 #else
4978 savetemp(NULL, NULL);
4979 #endif
4982 /* Generate a new timeout */
4984 xobjs.timeout_id = xcAddTimeOut(app, 60000 * xobjs.save_interval,
4985 savetemp, NULL);
4988 /*--------------------------------------------------------------*/
4989 /* tempfile save */
4990 /*--------------------------------------------------------------*/
4992 #ifdef TCL_WRAPPER
4994 void savetemp(ClientData clientdata)
4997 #else
4999 void savetemp(XtPointer clientdata, xcIntervalId *id)
5002 #endif
5004 /* Remove the timeout ID. If this routine is called from */
5005 /* incr_changes() instead of from the timeout interrupt */
5006 /* service routine, then this value will already be NULL. */
5008 xobjs.timeout_id = (xcIntervalId)NULL;
5010 /* First see if there are any unsaved changes in the file. */
5011 /* If not, then just reset the counter and continue. */
5013 if (xobjs.new_changes > 0) {
5014 if (xobjs.tempfile == NULL)
5016 int fd;
5017 char *template = (char *)malloc(20 + strlen(xobjs.tempdir));
5018 int pid = (int)getpid();
5020 sprintf(template, "%s/XC%d.XXXXXX", xobjs.tempdir, pid);
5022 #ifdef _MSC_VER
5023 fd = mktemp(template);
5024 #else
5025 fd = mkstemp(template);
5026 #endif
5027 if (fd == -1) {
5028 Fprintf(stderr, "Error generating file for savetemp\n");
5029 free(template);
5031 close(fd);
5032 xobjs.tempfile = strdup(template);
5033 free(template);
5035 /* Show the "wristwatch" cursor, as graphic data may cause this */
5036 /* step to be inordinately long. */
5038 XDefineCursor(dpy, areawin->window, WAITFOR);
5039 savefile(ALL_PAGES);
5040 XDefineCursor(dpy, areawin->window, DEFAULTCURSOR);
5041 xobjs.new_changes = 0; /* reset the count */
5045 /*----------------------------------------------------------------------*/
5046 /* Set all objects in the list "wroteobjs" as having no unsaved changes */
5047 /*----------------------------------------------------------------------*/
5049 void setassaved(objectptr *wroteobjs, short written)
5051 int i;
5053 for (i = 0; i < written; i++)
5054 (*(wroteobjs + i))->changes = 0;
5057 /*---------------------------------------------------------------*/
5058 /* Save indicated library. If libno is 0, save current page if */
5059 /* the current page is a library. If not, save the user library */
5060 /*---------------------------------------------------------------*/
5062 void savelibpopup(xcWidget button, char *technology, caddr_t nulldata)
5064 TechPtr nsptr;
5066 #ifndef TCL_WRAPPER
5067 buttonsave *savebutton;
5068 #endif
5070 nsptr = LookupTechnology(technology);
5072 if (nsptr != NULL) {
5073 if ((nsptr->flags & TECH_READONLY) != 0) {
5074 Wprintf("Library technology \"%s\" is read-only.", technology);
5075 return;
5079 #ifndef TCL_WRAPPER
5080 savebutton = getgeneric(button, savelibpopup, technology);
5081 popupprompt(button, "Enter technology:", "\0", savelibrary,
5082 savebutton, NULL);
5083 #endif
5086 /*---------------------------------------------------------*/
5088 #ifndef TCL_WRAPPER
5090 void savelibrary(xcWidget w, char *technology)
5092 char outname[250];
5093 sscanf(_STR2, "%249s", outname);
5094 savetechnology(technology, outname);
5097 #endif
5099 /*---------------------------------------------------------*/
5101 void savetechnology(char *technology, char *outname)
5103 FILE *ps;
5104 char *outptr, *validname, outfile[150];
5105 objectptr *wroteobjs, libobjptr, *optr, depobj;
5106 genericptr *gptr;
5107 liblistptr spec;
5108 short written;
5109 short *glist;
5110 char *uname = NULL;
5111 char *hostname = NULL;
5112 struct passwd *mypwentry = NULL;
5113 int i, j, ilib;
5114 TechPtr nsptr;
5115 char *loctechnology;
5117 // Don't use the string "(user)" as a technology name; this is just
5118 // a GUI placeholder for a null string (""). This shouldn't happen,
5119 // though, as technology should be NULL if the user technology is
5120 // selected.
5122 if (technology && (!strcmp(technology, "(user)")))
5123 nsptr = LookupTechnology(NULL);
5124 else
5125 nsptr = LookupTechnology(technology);
5127 if ((outptr = strrchr(outname, '/')) == NULL)
5128 outptr = outname;
5129 else
5130 outptr++;
5131 strcpy(outfile, outname);
5132 if (strchr(outptr, '.') == NULL) strcat(outfile, ".lps");
5134 xc_tilde_expand(outfile, 149);
5135 while(xc_variable_expand(outfile, 149));
5137 if (nsptr != NULL && nsptr->filename != NULL) {
5138 // To be pedantic, we should probably check that the inodes of the
5139 // files are different, to be sure we are avoiding an unintentional
5140 // over-write.
5142 if (!strcmp(outfile, nsptr->filename)) {
5144 if ((nsptr->flags & TECH_READONLY) != 0) {
5145 Wprintf("Technology file \"%s\" is read-only.", technology);
5146 return;
5149 if ((nsptr->flags & TECH_IMPORTED) != 0) {
5150 Wprintf("Attempt to write a truncated technology file!");
5151 return;
5156 ps = fopen(outfile, "wb");
5157 if (ps == NULL) {
5158 Wprintf("Can't open PS file.");
5159 if (nsptr && nsptr->filename && (!strcmp(nsptr->filename, outfile))) {
5160 Wprintf("Marking technology \"%s\" as read-only.", technology);
5161 nsptr->flags |= TECH_READONLY;
5163 return;
5166 /* Did the technology name change? If so, register the new name. */
5167 /* Clear any "IMPORTED" or "READONLY" flags. */
5169 if (nsptr && nsptr->filename && strcmp(outfile, nsptr->filename)) {
5170 Wprintf("Technology filename changed from \"%s\" to \"%s\".",
5171 nsptr->filename, outfile);
5172 free(nsptr->filename);
5173 nsptr->filename = strdup(outfile);
5174 nsptr->flags &= ~(TECH_READONLY | TECH_IMPORTED);
5176 else if (nsptr && !nsptr->filename) {
5177 nsptr->filename = strdup(outfile);
5178 nsptr->flags &= ~(TECH_READONLY | TECH_IMPORTED);
5181 fprintf(ps, "%%! PostScript set of library objects for XCircuit\n");
5182 fprintf(ps, "%% Version: %2.1f\n", version);
5183 fprintf(ps, "%% Library name is: %s\n",
5184 (technology == NULL) ? "(user)" : technology);
5185 #ifdef _MSC_VER
5186 uname = getenv((const char *)"USERNAME");
5187 #else
5188 uname = getenv((const char *)"USER");
5189 if (uname != NULL) mypwentry = getpwnam(uname);
5190 #endif
5192 /* Check for both $HOST and $HOSTNAME environment variables. Thanks */
5193 /* to frankie liu <frankliu@Stanford.EDU> for this fix. */
5195 if ((hostname = getenv((const char *)"HOSTNAME")) == NULL)
5196 if ((hostname = getenv((const char *)"HOST")) == NULL) {
5197 if (gethostname(_STR, 149) != 0)
5198 hostname = uname;
5199 else
5200 hostname = _STR;
5203 #ifndef _MSC_VER
5204 if (mypwentry != NULL)
5205 fprintf(ps, "%% Author: %s <%s@%s>\n", mypwentry->pw_gecos, uname,
5206 hostname);
5207 #endif
5209 fprintf(ps, "%%\n\n");
5211 /* Print lists of object dependencies, where they exist. */
5212 /* Note that objects can depend on objects in other technologies; */
5213 /* this is allowed. */
5215 wroteobjs = (objectptr *) malloc(sizeof(objectptr));
5216 for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5217 for (j = 0; j < xobjs.userlibs[ilib].number; j++) {
5219 libobjptr = *(xobjs.userlibs[ilib].library + j);
5220 if (CompareTechnology(libobjptr, technology)) {
5221 written = 0;
5223 /* Search for all object definitions instantiated in this object, */
5224 /* and add them to the dependency list (non-recursive). */
5226 for (gptr = libobjptr->plist; gptr < libobjptr->plist
5227 + libobjptr->parts; gptr++) {
5228 if (IS_OBJINST(*gptr)) {
5229 depobj = TOOBJINST(gptr)->thisobject;
5231 /* Search among the list of objects already collected. */
5232 /* If this object has been previously, then ignore it. */
5233 /* Otherwise, update the list of object dependencies */
5235 for (optr = wroteobjs; optr < wroteobjs + written; optr++)
5236 if (*optr == depobj)
5237 break;
5239 if (optr == wroteobjs + written) {
5240 wroteobjs = (objectptr *)realloc(wroteobjs, (written + 1) *
5241 sizeof(objectptr));
5242 *(wroteobjs + written) = depobj;
5243 written++;
5247 if (written > 0) {
5248 fprintf(ps, "%% Depend %s", libobjptr->name);
5249 for (i = 0; i < written; i++) {
5250 depobj = *(wroteobjs + i);
5251 fprintf(ps, " %s", depobj->name);
5253 fprintf(ps, "\n");
5259 fprintf(ps, "\n%% XCircuitLib library objects\n");
5261 /* Start by looking for any graphic images in the library objects */
5262 /* and saving the graphic image data at the top of the file. */
5264 glist = (short *)malloc(xobjs.images * sizeof(short));
5265 for (i = 0; i < xobjs.images; i++) glist[i] = 0;
5267 for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5268 for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) {
5269 libobjptr = spec->thisinst->thisobject;
5270 if (CompareTechnology(libobjptr, technology))
5271 count_graphics(spec->thisinst->thisobject, glist);
5274 output_graphic_data(ps, glist);
5275 free(glist);
5277 /* list of library objects already written */
5279 wroteobjs = (objectptr *)realloc(wroteobjs, sizeof(objectptr));
5280 written = 0;
5282 /* write all of the object definitions used, bottom up, with virtual */
5283 /* instances in the correct placement. The need to find virtual */
5284 /* instances is why we do a look through the library pages and not */
5285 /* the libraries themselves when looking for objects matching the */
5286 /* given technology. */
5288 for (ilib = 0; ilib < xobjs.numlibs; ilib++) {
5289 for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) {
5290 libobjptr = spec->thisinst->thisobject;
5291 if (CompareTechnology(libobjptr, technology)) {
5292 if (!spec->virtual) {
5293 printobjects(ps, spec->thisinst->thisobject, &wroteobjs,
5294 &written, DEFAULTCOLOR);
5296 else {
5297 if ((spec->thisinst->scale != 1.0) ||
5298 (spec->thisinst->rotation != 0.0)) {
5299 fprintf(ps, "%3.3f %3.3f ", spec->thisinst->scale,
5300 spec->thisinst->rotation);
5302 printparams(ps, spec->thisinst, 0);
5303 validname = create_valid_psname(spec->thisinst->thisobject->name, FALSE);
5304 /* Names without technologies get '::' string (blank technology) */
5305 if (technology == NULL)
5306 fprintf(ps, "/::%s libinst\n", validname);
5307 else
5308 fprintf(ps, "/%s libinst\n", validname);
5309 if ((spec->next != NULL) && (!(spec->next->virtual)))
5310 fprintf(ps, "\n");
5316 setassaved(wroteobjs, written);
5317 if (nsptr) nsptr->flags &= (~TECH_CHANGED);
5318 xobjs.new_changes = countchanges(NULL);
5320 /* and the postlog */
5322 fprintf(ps, "\n%% EndLib\n");
5323 fclose(ps);
5324 if (technology != NULL)
5325 Wprintf("Library technology \"%s\" saved as file %s.",technology, outname);
5326 else
5327 Wprintf("Library technology saved as file %s.", outname);
5329 free(wroteobjs);
5332 /*----------------------------------------------------------------------*/
5333 /* Recursive routine to search the object hierarchy for fonts used */
5334 /*----------------------------------------------------------------------*/
5336 void findfonts(objectptr writepage, short *fontsused) {
5337 genericptr *dfp;
5338 stringpart *chp;
5339 int findex;
5341 for (dfp = writepage->plist; dfp < writepage->plist + writepage->parts; dfp++) {
5342 if (IS_LABEL(*dfp)) {
5343 for (chp = TOLABEL(dfp)->string; chp != NULL; chp = chp->nextpart) {
5344 if (chp->type == FONT_NAME) {
5345 findex = chp->data.font;
5346 if (fontsused[findex] == 0) {
5347 fontsused[findex] = 0x8000 | fonts[findex].flags;
5352 else if (IS_OBJINST(*dfp)) {
5353 findfonts(TOOBJINST(dfp)->thisobject, fontsused);
5358 /*------------------------------------------------------*/
5359 /* Write graphics image data to file "ps". "glist" is */
5360 /* a pointer to a vector of short integers, each one */
5361 /* being an index into xobjs.images for an image that */
5362 /* is to be output. */
5363 /*------------------------------------------------------*/
5365 void output_graphic_data(FILE *ps, short *glist)
5367 char *fptr, ascbuf[6];
5368 int i, j;
5369 for (i = 0; i < xobjs.images; i++) {
5370 Imagedata *img = xobjs.imagelist + i;
5371 int ilen, flen, k, m = 0, n, q = 0;
5372 u_char *filtbuf, *flatebuf;
5373 Boolean lastpix = False;
5374 union {
5375 u_long i;
5376 u_char b[4];
5377 } pixel;
5378 int width = xcImageGetWidth(img->image);
5379 int height = xcImageGetHeight(img->image);
5381 if (glist[i] == 0) continue;
5383 fprintf(ps, "%%imagedata %d %d\n", width, height);
5384 fprintf(ps, "currentfile /ASCII85Decode filter ");
5386 #ifdef HAVE_LIBZ
5387 fprintf(ps, "/FlateDecode filter\n");
5388 #endif
5390 fprintf(ps, "/ReusableStreamDecode filter\n");
5392 /* creating a stream buffer is wasteful if we're just using ASCII85 */
5393 /* decoding but is a must for compression filters. */
5395 ilen = 3 * width * height;
5396 filtbuf = (u_char *)malloc(ilen + 4);
5397 q = 0;
5399 for (j = 0; j < height; j++)
5400 for (k = 0; k < width; k++) {
5401 unsigned char r, g, b;
5402 xcImageGetPixel(img->image, k, j, &r, &g, &b);
5403 filtbuf[q++] = r;
5404 filtbuf[q++] = g;
5405 filtbuf[q++] = b;
5408 /* Extra encoding goes here */
5409 #ifdef HAVE_LIBZ
5410 flen = ilen * 2;
5411 flatebuf = (char *)malloc(flen);
5412 ilen = large_deflate(flatebuf, flen, filtbuf, ilen);
5413 free(filtbuf);
5414 #else
5415 flatebuf = filtbuf;
5416 #endif
5418 ascbuf[5] = '\0';
5419 pixel.i = 0;
5420 for (j = 0; j < ilen; j += 4) {
5421 if ((j + 4) > ilen) lastpix = TRUE;
5422 if (!lastpix && (flatebuf[j] + flatebuf[j + 1] + flatebuf[j + 2]
5423 + flatebuf[j + 3] == 0)) {
5424 fprintf(ps, "z");
5425 m++;
5427 else {
5428 for (n = 0; n < 4; n++)
5429 pixel.b[3 - n] = flatebuf[j + n];
5431 ascbuf[0] = '!' + (pixel.i / 52200625);
5432 pixel.i %= 52200625;
5433 ascbuf[1] = '!' + (pixel.i / 614125);
5434 pixel.i %= 614125;
5435 ascbuf[2] = '!' + (pixel.i / 7225);
5436 pixel.i %= 7225;
5437 ascbuf[3] = '!' + (pixel.i / 85);
5438 pixel.i %= 85;
5439 ascbuf[4] = '!' + pixel.i;
5440 if (lastpix)
5441 for (n = 0; n < ilen + 1 - j; n++)
5442 fprintf(ps, "%c", ascbuf[n]);
5443 else
5444 fprintf(ps, "%5s", ascbuf);
5445 m += 5;
5447 if (m > 75) {
5448 fprintf(ps, "\n");
5449 m = 0;
5452 fprintf(ps, "~>\n");
5453 free(flatebuf);
5455 /* Remove any filesystem path information from the image name. */
5456 /* Otherwise, the slashes will cause PostScript to err. */
5458 fptr = strrchr(img->filename, '/');
5459 if (fptr == NULL)
5460 fptr = img->filename;
5461 else
5462 fptr++;
5463 fprintf(ps, "/%sdata exch def\n", fptr);
5464 fprintf(ps, "/%s <<\n", fptr);
5465 fprintf(ps, " /ImageType 1 /Width %d /Height %d /BitsPerComponent 8\n",
5466 width, height);
5467 fprintf(ps, " /MultipleDataSources false\n");
5468 fprintf(ps, " /Decode [0 1 0 1 0 1]\n");
5469 fprintf(ps, " /ImageMatrix [1 0 0 -1 %d %d]\n",
5470 width >> 1, height >> 1);
5471 fprintf(ps, " /DataSource %sdata >> def\n\n", fptr);
5475 /*----------------------------------------------------------------------*/
5476 /* Main file saving routine */
5477 /*----------------------------------------------------------------------*/
5478 /* mode description */
5479 /*----------------------------------------------------------------------*/
5480 /* ALL_PAGES saves a crash recovery backup file */
5481 /* CURRENT_PAGE saves all pages associated with the same */
5482 /* filename as the current page, and all */
5483 /* dependent schematics (which have their */
5484 /* filenames changed to match). */
5485 /* NO_SUBCIRCUITS saves all pages associated with the same */
5486 /* filename as the current page, only. */
5487 /*----------------------------------------------------------------------*/
5489 void savefile(short mode)
5491 FILE *ps, *pro, *enc;
5492 char outname[150], temp[150], prologue[150], *fname, *fptr, ascbuf[6];
5493 /* u_char decodebuf[6]; (jdk */
5494 short written, fontsused[256], i, page, curpage, multipage;
5495 short savepage, stcount, *pagelist, *glist;
5496 objectptr *wroteobjs;
5497 objinstptr writepage;
5498 int findex, j;
5499 time_t tdate;
5500 char *tmp_s;
5502 if (mode != ALL_PAGES) {
5503 /* doubly-protected file write: protect against errors during file write */
5504 fname = xobjs.pagelist[areawin->page]->filename;
5505 sprintf(outname, "%s~", fname);
5506 rename(fname, outname);
5508 else {
5509 /* doubly-protected backup: protect against errors during file write */
5510 sprintf(outname, "%sB", xobjs.tempfile);
5511 rename(xobjs.tempfile, outname);
5512 fname = xobjs.tempfile;
5515 if ((fptr = strrchr(fname, '/')) != NULL)
5516 fptr++;
5517 else
5518 fptr = fname;
5520 if ((mode != ALL_PAGES) && (strchr(fptr, '.') == NULL))
5521 sprintf(outname, "%s.ps", fname);
5522 else sprintf(outname, "%s", fname);
5524 xc_tilde_expand(outname, 149);
5525 while(xc_variable_expand(outname, 149));
5527 ps = fopen(outname, "wb");
5528 if (ps == NULL) {
5529 Wprintf("Can't open file %s for writing.", outname);
5530 return;
5533 if ((mode != NO_SUBCIRCUITS) && (mode != ALL_PAGES))
5534 collectsubschems(areawin->page);
5536 /* Check for multiple-page output: get the number of pages; */
5537 /* ignore empty pages. */
5539 multipage = 0;
5541 if (mode == NO_SUBCIRCUITS)
5542 pagelist = pagetotals(areawin->page, INDEPENDENT);
5543 else if (mode == ALL_PAGES)
5544 pagelist = pagetotals(areawin->page, ALL_PAGES);
5545 else
5546 pagelist = pagetotals(areawin->page, TOTAL_PAGES);
5548 for (page = 0; page < xobjs.pages; page++)
5549 if (pagelist[page] > 0)
5550 multipage++;
5552 if (multipage == 0) {
5553 Wprintf("Panic: could not find this page in page list!");
5554 free (pagelist);
5555 fclose(ps);
5556 return;
5559 /* Print the PostScript DSC Document Header */
5561 fprintf(ps, "%%!PS-Adobe-3.0");
5562 if (multipage == 1 && !(xobjs.pagelist[areawin->page]->pmode & 1))
5563 fprintf(ps, " EPSF-3.0\n");
5564 else
5565 fprintf(ps, "\n");
5566 fprintf(ps, "%%%%Title: %s\n", fptr);
5567 fprintf(ps, "%%%%Creator: XCircuit v%2.1f rev%d\n", PROG_VERSION, PROG_REVISION);
5568 tdate = time(NULL);
5569 fprintf(ps, "%%%%CreationDate: %s", asctime(localtime(&tdate)));
5570 fprintf(ps, "%%%%Pages: %d\n", multipage);
5572 /* This is just a default value; each bounding box is declared per */
5573 /* page by the DSC "PageBoundingBox" keyword. */
5574 /* However, encapsulated files adjust the bounding box to center on */
5575 /* the object, instead of centering the object on the page. */
5577 if (multipage == 1 && !(xobjs.pagelist[areawin->page]->pmode & 1)) {
5578 objectptr thisobj = xobjs.pagelist[areawin->page]->pageinst->thisobject;
5579 float psscale = getpsscale(xobjs.pagelist[areawin->page]->outscale,
5580 areawin->page);
5582 /* The top-level bounding box determines the size of an encapsulated */
5583 /* drawing, regardless of the PageBoundingBox numbers. Therefore, */
5584 /* we size this to bound just the picture by closing up the 1" (72 */
5585 /* PostScript units) margins, except for a tiny sliver of a margin */
5586 /* (4 PostScript units) which covers a bit of sloppiness in the font */
5587 /* measurements. */
5589 fprintf(ps, "%%%%BoundingBox: 68 68 %d %d\n",
5590 (int)((float)thisobj->bbox.width * psscale)
5591 + xobjs.pagelist[areawin->page]->margins.x + 4,
5592 (int)((float)thisobj->bbox.height * psscale)
5593 + xobjs.pagelist[areawin->page]->margins.y + 4);
5595 else if (xobjs.pagelist[0]->coordstyle == CM)
5596 fprintf(ps, "%%%%BoundingBox: 0 0 595 842\n"); /* A4 default (fixed by jdk) */
5597 else
5598 fprintf(ps, "%%%%BoundingBox: 0 0 612 792\n"); /* letter default */
5600 for (i = 0; i < fontcount; i++) fontsused[i] = 0;
5601 fprintf(ps, "%%%%DocumentNeededResources: font ");
5602 stcount = 32;
5604 /* find all of the fonts used in this document */
5605 /* log all fonts which are native PostScript */
5607 for (curpage = 0; curpage < xobjs.pages; curpage++)
5608 if (pagelist[curpage] > 0) {
5609 writepage = xobjs.pagelist[curpage]->pageinst;
5610 findfonts(writepage->thisobject, fontsused);
5613 for (i = 0; i < fontcount; i++) {
5614 if (fontsused[i] & 0x8000)
5615 if ((fonts[i].flags & 0x8018) == 0x0) {
5616 stcount += strlen(fonts[i].psname) + 1;
5617 if (stcount > OUTPUTWIDTH) {
5618 stcount = strlen(fonts[i].psname) + 11;
5619 fprintf(ps, "\n%%%%+ font ");
5621 fprintf(ps, "%s ", fonts[i].psname);
5625 fprintf(ps, "\n%%%%EndComments\n");
5627 tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR");
5628 if (tmp_s != NULL) {
5629 sprintf(prologue, "%s/%s", tmp_s, PROLOGUE_FILE);
5630 pro = fopen(prologue, "r");
5632 else
5633 pro = NULL;
5635 if (pro == NULL) {
5636 sprintf(prologue, "%s/%s", PROLOGUE_DIR, PROLOGUE_FILE);
5637 pro = fopen(prologue, "r");
5638 if (pro == NULL) {
5639 sprintf(prologue, "%s", PROLOGUE_FILE);
5640 pro = fopen(prologue, "r");
5641 if (pro == NULL) {
5642 Wprintf("Can't open prolog.");
5643 free(pagelist);
5644 fclose(ps);
5645 return;
5650 /* write the prolog to the output */
5652 for(;;) {
5653 if (fgets(temp, 149, pro) == NULL) break;
5654 if (!strncmp(temp, "%%EndProlog", 11)) break;
5655 fputs(temp, ps);
5657 fclose(pro);
5659 /* Special font encodings not known to PostScript */
5660 /* (anything other than Standard and ISOLatin-1) */
5662 for (findex = 0; findex < fontcount; findex++) {
5663 if ((fontsused[findex] & 0xf80) == 0x400) {
5664 /* Cyrillic (ISO8859-5) */
5665 sprintf(prologue, "%s/%s", PROLOGUE_DIR, CYRILLIC_ENC_FILE);
5666 pro = fopen(prologue, "r");
5667 if (pro == NULL) {
5668 sprintf(prologue, "%s", CYRILLIC_ENC_FILE);
5669 pro = fopen(prologue, "r");
5670 if (pro == NULL) {
5671 Wprintf("Warning: Missing font encoding vectors.");
5672 Wprintf("Output may not print properly.");
5675 if (pro != NULL) {
5676 for(;;) {
5677 if (fgets(temp, 149, pro) == NULL) break;
5678 fputs(temp, ps);
5680 fclose(pro);
5683 else if ((fontsused[findex] & 0xf80) == 0x180) {
5684 /* Eastern European (ISOLatin2) */
5685 sprintf(prologue, "%s/%s", PROLOGUE_DIR, ISOLATIN2_ENC_FILE);
5686 pro = fopen(prologue, "r");
5687 if (pro == NULL) {
5688 sprintf(prologue, "%s", ISOLATIN2_ENC_FILE);
5689 pro = fopen(prologue, "r");
5690 if (pro == NULL) {
5691 Wprintf("Warning: Missing font encoding vectors.");
5692 Wprintf("Output may not print properly.");
5695 if (pro != NULL) {
5696 for(;;) {
5697 if (fgets(temp, 149, pro) == NULL) break;
5698 fputs(temp, ps);
5700 fclose(pro);
5703 else if ((fontsused[findex] & 0xf80) == 0x300) {
5704 /* Turkish (ISOLatin5) */
5705 sprintf(prologue, "%s/%s", PROLOGUE_DIR, ISOLATIN5_ENC_FILE);
5706 pro = fopen(prologue, "r");
5707 if (pro == NULL) {
5708 sprintf(prologue, "%s", ISOLATIN5_ENC_FILE);
5709 pro = fopen(prologue, "r");
5710 if (pro == NULL) {
5711 Wprintf("Warning: Missing font encoding vectors.");
5712 Wprintf("Output may not print properly.");
5715 if (pro != NULL) {
5716 for(;;) {
5717 if (fgets(temp, 149, pro) == NULL) break;
5718 fputs(temp, ps);
5720 fclose(pro);
5725 /* Finish off prolog */
5726 fputs("%%EndProlog\n", ps);
5728 /* Special font handling */
5730 for (findex = 0; findex < fontcount; findex++) {
5732 /* Derived font slant */
5734 if ((fontsused[findex] & 0x032) == 0x032)
5735 fprintf(ps, "/%s /%s .167 fontslant\n\n",
5736 fonts[findex].psname, fonts[findex].family);
5738 /* Derived ISO-Latin1 encoding */
5740 if ((fontsused[findex] & 0xf80) == 0x100) {
5741 char *fontorig = NULL;
5742 short i;
5743 /* find the original standard-encoded font (can be itself) */
5744 for (i = 0; i < fontcount; i++) {
5745 if (i == findex) continue;
5746 if (!strcmp(fonts[i].family, fonts[findex].family) &&
5747 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5748 fontorig = fonts[i].psname;
5749 break;
5752 if (fontorig == NULL) fontorig = fonts[findex].psname;
5753 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5754 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5755 fprintf(ps, "/Encoding ISOLatin1Encoding def currentdict end\n");
5756 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5759 /* Derived Cyrillic (ISO8859-5) encoding */
5761 if ((fontsused[findex] & 0xf80) == 0x400) {
5762 char *fontorig = NULL;
5763 short i;
5764 /* find the original standard-encoded font (can be itself) */
5765 for (i = 0; i < fontcount; i++) {
5766 if (i == findex) continue;
5767 if (!strcmp(fonts[i].family, fonts[findex].family) &&
5768 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5769 fontorig = fonts[i].psname;
5770 break;
5773 if (fontorig == NULL) fontorig = fonts[findex].psname;
5774 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5775 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5776 fprintf(ps, "/Encoding ISO8859_5Encoding def currentdict end\n");
5777 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5780 /* ISO-Latin2 encoding */
5782 if ((fontsused[findex] & 0xf80) == 0x180) {
5783 char *fontorig = NULL;
5784 short i;
5785 /* find the original standard-encoded font (can be itself) */
5786 for (i = 0; i < fontcount; i++) {
5787 if (i == findex) continue;
5788 if (!strcmp(fonts[i].family, fonts[findex].family) &&
5789 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5790 fontorig = fonts[i].psname;
5791 break;
5794 if (fontorig == NULL) fontorig = fonts[findex].psname;
5795 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5796 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5797 fprintf(ps, "/Encoding ISOLatin2Encoding def currentdict end\n");
5798 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5801 /* ISO-Latin5 encoding */
5803 if ((fontsused[findex] & 0xf80) == 0x300) {
5804 char *fontorig = NULL;
5805 short i;
5806 /* find the original standard-encoded font (can be itself) */
5807 for (i = 0; i < fontcount; i++) {
5808 if (i == findex) continue;
5809 if (!strcmp(fonts[i].family, fonts[findex].family) &&
5810 ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) {
5811 fontorig = fonts[i].psname;
5812 break;
5815 if (fontorig == NULL) fontorig = fonts[findex].psname;
5816 fprintf(ps, "/%s findfont dup length dict begin\n", fontorig);
5817 fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n");
5818 fprintf(ps, "/Encoding ISOLatin5Encoding def currentdict end\n");
5819 fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname);
5822 /* To do: Special encoding */
5824 if ((fontsused[findex] & 0xf80) == 0x80) {
5827 /* To do: Vectored (drawn) font */
5829 if (fontsused[findex] & 0x8) {
5833 /* List of objects already written */
5834 wroteobjs = (objectptr *) malloc (sizeof(objectptr));
5835 written = 0;
5837 fprintf(ps, "%% XCircuit output starts here.\n\n");
5838 fprintf(ps, "%%%%BeginSetup\n\n");
5840 /* Write out all of the images used */
5842 glist = collect_graphics(pagelist);
5843 output_graphic_data(ps, glist);
5844 free(glist);
5846 for (curpage = 0; curpage < xobjs.pages; curpage++) {
5847 if (pagelist[curpage] == 0) continue;
5849 /* Write all of the object definitions used, bottom up */
5850 printrefobjects(ps, xobjs.pagelist[curpage]->pageinst->thisobject,
5851 &wroteobjs, &written);
5854 fprintf(ps, "\n%%%%EndSetup\n\n");
5856 page = 0;
5857 for (curpage = 0; curpage < xobjs.pages; curpage++) {
5858 if (pagelist[curpage] == 0) continue;
5860 /* Print the page header, all elements in the page, and page trailer */
5861 savepage = areawin->page;
5862 /* Set the current page for the duration of printing so that any */
5863 /* page parameters will be printed correctly. */
5864 areawin->page = curpage;
5865 printpageobject(ps, xobjs.pagelist[curpage]->pageinst->thisobject,
5866 ++page, curpage);
5867 areawin->page = savepage;
5869 /* For crash recovery, log the filename for each page */
5870 if (mode == ALL_PAGES) {
5871 fprintf(ps, "%% %s is_filename\n",
5872 (xobjs.pagelist[curpage]->filename == NULL) ?
5873 xobjs.pagelist[curpage]->pageinst->thisobject->name :
5874 xobjs.pagelist[curpage]->filename);
5877 fprintf(ps, "\n");
5878 fflush(ps);
5881 /* For crash recovery, save all objects that have been edited but are */
5882 /* not in the list of objects already saved. */
5884 if (mode == ALL_PAGES)
5886 int i, j, k;
5887 objectptr thisobj;
5889 for (i = 0; i < xobjs.numlibs; i++) {
5890 for (j = 0; j < xobjs.userlibs[i].number; j++) {
5891 thisobj = *(xobjs.userlibs[i].library + j);
5892 if (thisobj->changes > 0 ) {
5893 for (k = 0; k < written; k++)
5894 if (thisobj == *(wroteobjs + k)) break;
5895 if (k == written)
5896 printobjects(ps, thisobj, &wroteobjs, &written, DEFAULTCOLOR);
5901 else { /* No unsaved changes in these objects */
5902 setassaved(wroteobjs, written);
5903 for (i = 0; i < xobjs.pages; i++)
5904 if (pagelist[i] > 0)
5905 xobjs.pagelist[i]->pageinst->thisobject->changes = 0;
5906 xobjs.new_changes = countchanges(NULL);
5909 /* Free allocated memory */
5910 free((char *)pagelist);
5911 free((char *)wroteobjs);
5913 /* Done! */
5915 fprintf(ps, "%%%%Trailer\n");
5916 fprintf(ps, "XCIRCsave restore\n");
5917 fprintf(ps, "%%%%EOF\n");
5918 fclose(ps);
5920 Wprintf("File %s saved (%d page%s).", fname, multipage,
5921 (multipage > 1 ? "s" : ""));
5923 if (mode == ALL_PAGES) {
5924 /* Remove the temporary redundant backup */
5925 sprintf(outname, "%sB", xobjs.tempfile);
5926 unlink(outname);
5928 else if (!xobjs.retain_backup) {
5929 /* Remove the backup file */
5930 sprintf(outname, "%s~", fname);
5931 unlink(outname);
5934 /* Write LATEX strings, if any are present */
5935 TopDoLatex();
5938 /*----------------------------------------------------------------------*/
5939 /* Given a color index, print the R, G, B values */
5940 /*----------------------------------------------------------------------*/
5942 int printRGBvalues(char *tstr, int index, const char *postfix)
5944 int i;
5945 if (index >= 0 && index < number_colors) {
5946 sprintf(tstr, "%4.3f %4.3f %4.3f %s",
5947 (float)colorlist[index].color.red / 65535,
5948 (float)colorlist[index].color.green / 65535,
5949 (float)colorlist[index].color.blue / 65535,
5950 postfix);
5951 return 0;
5954 /* The program can reach this point for any color which is */
5955 /* not listed in the table. This can happen when parameters */
5956 /* printed from printobjectparams object contain the string */
5957 /* "@p_color". Therefore print the default top-level */
5958 /* default color, which is black. */
5960 /* If the index is *not* DEFAULTCOLOR (-1), return an error */
5961 /* code. */
5963 sprintf(tstr, "0 0 0 %s", postfix);
5964 return (index == DEFAULTCOLOR) ? 0 : -1;
5967 /*----------------------------------------------------*/
5968 /* Write string to PostScript string, ignoring NO_OPs */
5969 /*----------------------------------------------------*/
5971 char *nosprint(char *baseptr, int *margin, int *extsegs)
5973 int qtmp, slen = 100;
5974 char *sptr, *lptr = NULL, lsave, *sptr2;
5975 u_char *pptr, *qptr, *bptr;
5977 bptr = (u_char *)malloc(slen); /* initial length 100 */
5978 qptr = bptr;
5980 while(1) { /* loop for breaking up margin-limited text into words */
5982 if (*margin > 0) {
5983 sptr = strrchr(baseptr, ' ');
5984 if (sptr == NULL)
5985 sptr = baseptr;
5986 else {
5987 if (*(sptr + 1) == '\0') {
5988 while (*sptr == ' ') sptr--;
5989 *(sptr + 1) = '\0';
5990 sptr2 = strrchr(baseptr, ' ');
5991 *(sptr + 1) = ' ';
5992 if (sptr2 == NULL)
5993 sptr = baseptr;
5994 else
5995 sptr = sptr2 + 1;
5997 else
5998 sptr++;
6001 else
6002 sptr = baseptr;
6004 *qptr++ = '(';
6006 /* Includes extended character set (non-ASCII) */
6008 for (pptr = sptr; pptr && *pptr != '\0'; pptr++) {
6009 /* Ensure enough space for the string, including everything */
6010 /* following the "for" loop */
6011 qtmp = qptr - bptr;
6012 if (qtmp + 7 >= slen) {
6013 slen += 7;
6014 bptr = (char *)realloc(bptr, slen);
6015 qptr = bptr + qtmp;
6018 /* Deal with non-printable characters and parentheses */
6019 if (*pptr > (char)126) {
6020 sprintf(qptr, "\\%3o", (int)(*pptr));
6021 qptr += 4;
6023 else {
6024 if ((*pptr == '(') || (*pptr == ')') || (*pptr == '\\'))
6025 *qptr++ = '\\';
6026 *qptr++ = *pptr;
6029 if (qptr == bptr + 1) { /* Empty string gets a NULL result, not "()" */
6030 qptr--;
6032 else {
6033 *qptr++ = ')';
6034 *qptr++ = ' ';
6037 if (lptr != NULL)
6038 *lptr = lsave;
6040 if (sptr == baseptr)
6041 break;
6042 else {
6043 lptr = sptr;
6044 lsave = *lptr;
6045 *lptr = '\0';
6046 (*extsegs)++;
6050 *qptr++ = '\0';
6051 return (char *)bptr;
6054 /*--------------------------------------------------------------*/
6055 /* Write label segments to the output (in reverse order) */
6056 /*--------------------------------------------------------------*/
6058 short writelabel(FILE *ps, stringpart *chrtop, short *stcount)
6060 short i, segs = 0;
6061 stringpart *chrptr;
6062 char **ostr = (char **)malloc(sizeof(char *));
6063 char *tmpstr;
6064 float lastscale = 1.0;
6065 int lastfont = -1;
6066 int margin = 0;
6067 int extsegs = 0;
6069 /* Write segments into string array, in forward order */
6071 for (chrptr = chrtop; chrptr != NULL; chrptr = chrptr->nextpart) {
6072 ostr = (char **)realloc(ostr, (segs + 1) * sizeof(char *));
6073 if (chrtop->type == PARAM_END) { /* NULL parameter is empty string */
6074 ostr[segs] = (char *)malloc(4);
6075 strcpy(ostr[segs], "() ");
6077 else {
6078 tmpstr = writesegment(chrptr, &lastscale, &lastfont, &margin, &extsegs);
6079 if (tmpstr[0] != '\0')
6080 ostr[segs] = tmpstr;
6081 else
6082 segs--;
6084 segs++;
6087 /* Write string array to output in reverse order */
6088 for (i = segs - 1; i >= 0; i--) {
6089 dostcount(ps, stcount, strlen(ostr[i]));
6090 fputs(ostr[i], ps);
6091 free(ostr[i]);
6093 free(ostr);
6095 return segs + extsegs;
6098 /*--------------------------------------------------------------*/
6099 /* Write a single label segment to the output */
6100 /* (Recursive, so we can write segments in the reverse order) */
6101 /*--------------------------------------------------------------*/
6103 char *writesegment(stringpart *chrptr, float *lastscale, int *lastfont, int *margin,
6104 int *extsegs)
6106 int type = chrptr->type;
6107 char *retstr, *validname;
6109 switch(type) {
6110 case PARAM_START:
6111 validname = create_valid_psname(chrptr->data.string, TRUE);
6112 sprintf(_STR, "%s ", validname);
6113 break;
6114 case PARAM_END:
6115 _STR[0] = '\0';
6116 chrptr->nextpart = NULL;
6117 break;
6118 case SUBSCRIPT:
6119 sprintf(_STR, "{ss} ");
6120 break;
6121 case SUPERSCRIPT:
6122 sprintf(_STR, "{Ss} ");
6123 break;
6124 case NORMALSCRIPT:
6125 *lastscale = 1.0;
6126 sprintf(_STR, "{ns} ");
6127 break;
6128 case UNDERLINE:
6129 sprintf(_STR, "{ul} ");
6130 break;
6131 case OVERLINE:
6132 sprintf(_STR, "{ol} ");
6133 break;
6134 case NOLINE:
6135 sprintf(_STR, "{} ");
6136 break;
6137 case HALFSPACE:
6138 sprintf(_STR, "{hS} ");
6139 break;
6140 case QTRSPACE:
6141 sprintf(_STR, "{qS} ");
6142 break;
6143 case RETURN:
6144 *lastscale = 1.0;
6145 if (chrptr->data.flags == 0)
6146 // Ignore automatically-generated line breaks
6147 sprintf(_STR, "{CR} ");
6148 else
6149 sprintf(_STR, "");
6150 break;
6151 case TABSTOP:
6152 sprintf(_STR, "{Ts} ");
6153 break;
6154 case TABFORWARD:
6155 sprintf(_STR, "{Tf} ");
6156 break;
6157 case TABBACKWARD:
6158 sprintf(_STR, "{Tb} ");
6159 break;
6160 case FONT_NAME:
6161 /* If font specifier is followed by a scale specifier, then */
6162 /* record the font change but defer the output. Otherwise, */
6163 /* output the font record now. */
6165 if ((chrptr->nextpart == NULL) || (chrptr->nextpart->type != FONT_SCALE))
6167 if (*lastscale == 1.0)
6168 sprintf(_STR, "{/%s cf} ", fonts[chrptr->data.font].psname);
6169 else
6170 sprintf(_STR, "{/%s %5.3f cf} ", fonts[chrptr->data.font].psname,
6171 *lastscale);
6173 else
6174 _STR[0] = '\0';
6175 *lastfont = chrptr->data.font;
6176 break;
6177 case FONT_SCALE:
6178 if (*lastfont == -1) {
6179 Fprintf(stderr, "Warning: Font may not be the one that was intended.\n");
6180 *lastfont = 0;
6182 *lastscale = chrptr->data.scale;
6183 sprintf(_STR, "{/%s %5.3f cf} ", fonts[*lastfont].psname, *lastscale);
6184 break;
6185 case FONT_COLOR:
6186 strcpy(_STR, "{");
6187 if (chrptr->data.color == DEFAULTCOLOR)
6188 strcat(_STR, "sce} ");
6189 else
6190 if (printRGBvalues(_STR + 1, chrptr->data.color, "scb} ") < 0)
6191 strcat(_STR, "sce} ");
6192 break;
6193 case MARGINSTOP:
6194 sprintf(_STR, "{%d MR} ", chrptr->data.width);
6195 *margin = chrptr->data.width;
6196 break;
6197 case KERN:
6198 sprintf(_STR, "{%d %d Kn} ", chrptr->data.kern[0], chrptr->data.kern[1]);
6199 break;
6200 case TEXT_STRING:
6201 /* Everything except TEXT_STRING will always fit in the _STR fixed- */
6202 /* length character array. */
6203 return nosprint(chrptr->data.string, margin, extsegs);
6206 retstr = (char *)malloc(1 + strlen(_STR));
6207 strcpy(retstr, _STR);
6208 return retstr;
6211 /*--------------------------------------------------------------*/
6212 /* Routine to write all the label segments as stored in _STR */
6213 /*--------------------------------------------------------------*/
6215 int writelabelsegs(FILE *ps, short *stcount, stringpart *chrptr)
6217 Boolean ismultipart;
6218 int segs;
6220 if (chrptr == NULL) return 0;
6222 ismultipart = ((chrptr->nextpart != NULL) &&
6223 (chrptr->nextpart->type != PARAM_END)) ? True : False;
6225 /* If there is only one part, but it is not a string or the */
6226 /* end of a parameter (empty parameter), then set multipart */
6227 /* anyway so we get the double brace {{ }}. */
6229 if ((!ismultipart) && (chrptr->type != TEXT_STRING) &&
6230 (chrptr->type != PARAM_END))
6231 ismultipart = True;
6233 /* nextpart is not NULL if there are multiple parts to the string */
6234 if (ismultipart) {
6235 fprintf(ps, "{");
6236 (*stcount)++;
6238 segs = writelabel(ps, chrptr, stcount);
6240 if (ismultipart) {
6241 fprintf(ps, "} ");
6242 (*stcount) += 2;
6244 return segs;
6247 /*--------------------------------------------------------------*/
6248 /* Write the dictionary of parameters belonging to an object */
6249 /*--------------------------------------------------------------*/
6251 void printobjectparams(FILE *ps, objectptr localdata)
6253 int segs;
6254 short stcount;
6255 oparamptr ops;
6256 char *ps_expr, *validkey;
6257 float fp;
6259 /* Check for parameters and default values */
6260 if (localdata->params == NULL) return;
6262 fprintf(ps, "<<");
6263 stcount = 2;
6265 for (ops = localdata->params; ops != NULL; ops = ops->next) {
6266 validkey = create_valid_psname(ops->key, TRUE);
6267 fprintf(ps, "/%s ", validkey);
6268 dostcount (ps, &stcount, strlen(validkey) + 2);
6270 switch (ops->type) {
6271 case XC_EXPR:
6272 ps_expr = evaluate_expr(localdata, ops, NULL);
6273 if (ops->which == P_SUBSTRING || ops->which == P_EXPRESSION) {
6274 dostcount(ps, &stcount, 3 + strlen(ps_expr));
6275 fputs("(", ps);
6276 fputs(ps_expr, ps);
6277 fputs(") ", ps);
6279 else if (ops->which == P_COLOR) {
6280 int ccol;
6281 /* Write R, G, B components for PostScript */
6282 if (sscanf(ps_expr, "%d", &ccol) == 1) {
6283 fputs("{", ps);
6284 printRGBvalues(_STR, ccol, "} ");
6285 dostcount(ps, &stcount, 1 + strlen(_STR));
6286 fputs(_STR, ps);
6288 else {
6289 dostcount(ps, &stcount, 8);
6290 fputs("{0 0 0} ", ps);
6293 else if (sscanf(ps_expr, "%g", &fp) == 1) {
6294 dostcount(ps, &stcount, 1 + strlen(ps_expr));
6295 fputs(ps_expr, ps);
6296 fputs(" ", ps);
6298 else { /* Expression evaluates to error in object */
6299 dostcount(ps, &stcount, 2);
6300 fputs("0 ", ps);
6302 dostcount(ps, &stcount, 7 + strlen(ops->parameter.expr));
6303 fputs("(", ps);
6304 fputs(ops->parameter.expr, ps);
6305 fputs(") pop ", ps);
6306 free(ps_expr);
6307 break;
6308 case XC_STRING:
6309 segs = writelabelsegs(ps, &stcount, ops->parameter.string);
6310 if (segs == 0) {
6311 /* When writing object parameters, we cannot allow a */
6312 /* NULL value. Instead, print an empty string (). */
6313 dostcount(ps, &stcount, 3);
6314 fputs("() ", ps);
6316 break;
6317 case XC_INT:
6318 sprintf(_STR, "%d ", ops->parameter.ivalue);
6319 dostcount(ps, &stcount, strlen(_STR));
6320 fputs(_STR, ps);
6321 break;
6322 case XC_FLOAT:
6323 sprintf(_STR, "%g ", ops->parameter.fvalue);
6324 dostcount(ps, &stcount, strlen(_STR));
6325 fputs(_STR, ps);
6326 break;
6330 fprintf(ps, ">> ");
6331 dostcount (ps, &stcount, 3);
6334 /*--------------------------------------------------------------*/
6335 /* Write the list of parameters belonging to an object instance */
6336 /*--------------------------------------------------------------*/
6338 short printparams(FILE *ps, objinstptr sinst, short stcount)
6340 int segs;
6341 short loccount;
6342 oparamptr ops, objops;
6343 eparamptr epp;
6344 char *ps_expr, *validkey, *validref;
6345 short instances = 0;
6347 if (sinst->params == NULL) return stcount;
6349 for (ops = sinst->params; ops != NULL; ops = ops->next) {
6350 validref = strdup(create_valid_psname(ops->key, TRUE));
6352 /* Check for indirect parameter references */
6353 for (epp = sinst->passed; epp != NULL; epp = epp->next) {
6354 if ((epp->flags & P_INDIRECT) && (epp->pdata.refkey != NULL)) {
6355 if (!strcmp(epp->pdata.refkey, ops->key)) {
6356 if (instances++ == 0) {
6357 fprintf(ps, "<<"); /* begin PostScript dictionary */
6358 loccount = stcount + 2;
6360 dostcount(ps, &loccount, strlen(validref + 3));
6361 fprintf(ps, "/%s ", validref);
6362 dostcount(ps, &loccount, strlen(epp->key + 1));
6363 validkey = create_valid_psname(epp->key, TRUE);
6364 fprintf(ps, "%s ", validkey);
6365 break;
6369 if (epp == NULL) { /* No indirection */
6370 Boolean nondefault = TRUE;
6371 char *deflt_expr = NULL;
6373 /* For instance values that are expression results, ignore if */
6374 /* the instance value is the same as the default value. */
6375 /* Correction 9/08: We can't second-guess expression results, */
6376 /* in particular, this doesn't work for an expression like */
6377 /* "page", where the local and default values will evaluate to */
6378 /* the same result, when clearly each page is intended to have */
6379 /* its own instance value, and "page" for an object is not a */
6380 /* well-defined concept. */
6382 // ps_expr = NULL;
6383 // objops = match_param(sinst->thisobject, ops->key);
6384 // if (objops && (objops->type == XC_EXPR)) {
6385 // int i;
6386 // float f;
6387 // deflt_expr = evaluate_expr(sinst->thisobject, objops, NULL);
6388 // switch (ops->type) {
6389 // case XC_STRING:
6390 // if (!textcomp(ops->parameter.string, deflt_expr, sinst))
6391 // nondefault = FALSE;
6392 // break;
6393 // case XC_EXPR:
6394 // ps_expr = evaluate_expr(sinst->thisobject, ops, sinst);
6395 // if (!strcmp(ps_expr, deflt_expr)) nondefault = FALSE;
6396 // break;
6397 // case XC_INT:
6398 // sscanf(deflt_expr, "%d", &i);
6399 // if (i == ops->parameter.ivalue) nondefault = FALSE;
6400 // break;
6401 // case XC_FLOAT:
6402 // sscanf(deflt_expr, "%g", &f);
6403 // if (f == ops->parameter.fvalue) nondefault = FALSE;
6404 // break;
6405 // }
6406 // if (deflt_expr) free(deflt_expr);
6407 // if (nondefault == FALSE) {
6408 // if (ps_expr) free(ps_expr);
6409 // continue;
6410 // }
6411 // }
6413 if (instances++ == 0) {
6414 fprintf(ps, "<<"); /* begin PostScript dictionary */
6415 loccount = stcount + 2;
6417 dostcount(ps, &loccount, strlen(validref) + 2);
6418 fprintf(ps, "/%s ", validref);
6420 switch (ops->type) {
6421 case XC_STRING:
6422 segs = writelabelsegs(ps, &loccount, ops->parameter.string);
6423 if (segs == 0) {
6424 /* When writing object parameters, we cannot allow a */
6425 /* NULL value. Instead, print an empty string (). */
6426 dostcount(ps, &stcount, 3);
6427 fputs("() ", ps);
6429 break;
6430 case XC_EXPR:
6431 ps_expr = evaluate_expr(sinst->thisobject, ops, sinst);
6432 dostcount(ps, &loccount, 3 + strlen(ps_expr));
6433 fputs("(", ps);
6434 fputs(ps_expr, ps);
6435 fputs(") ", ps);
6436 free(ps_expr);
6438 /* The instance parameter expression may have the */
6439 /* same expression as the object but a different */
6440 /* result if the expression uses another parameter. */
6441 /* Only print the expression itself if it's different */
6442 /* from the object's expression. */
6444 objops = match_param(sinst->thisobject, ops->key);
6445 if (objops && strcmp(ops->parameter.expr, objops->parameter.expr)) {
6446 dostcount(ps, &loccount, 3 + strlen(ops->parameter.expr));
6447 fputs("(", ps);
6448 fputs(ops->parameter.expr, ps);
6449 fputs(") pop ", ps);
6451 break;
6452 case XC_INT:
6453 if (ops->which == P_COLOR) {
6454 /* Write R, G, B components */
6455 _STR[0] = '{';
6456 printRGBvalues(_STR + 1, ops->parameter.ivalue, "} ");
6458 else
6459 sprintf(_STR, "%d ", ops->parameter.ivalue);
6460 dostcount(ps, &loccount, strlen(_STR));
6461 fputs(_STR, ps);
6462 break;
6463 case XC_FLOAT:
6464 sprintf(_STR, "%g ", ops->parameter.fvalue);
6465 dostcount(ps, &loccount, strlen(_STR));
6466 fputs(_STR, ps);
6467 break;
6470 free(validref);
6472 if (instances > 0) {
6473 fprintf(ps, ">> "); /* end PostScript dictionary */
6474 loccount += 3;
6476 return loccount;
6479 /*------------------------------------------------------------------*/
6480 /* Macro for point output (calls varpcheck() on x and y components) */
6481 /*------------------------------------------------------------------*/
6483 #define xyvarcheck(z, n, t) \
6484 varpcheck(ps, (z).x, localdata, n, &stcount, *(t), P_POSITION_X); \
6485 varpcheck(ps, (z).y, localdata, n, &stcount, *(t), P_POSITION_Y)
6487 #define xypathcheck(z, n, t, p) \
6488 varpathcheck(ps, (z).x, localdata, n, &stcount, t, TOPATH(p), P_POSITION_X); \
6489 varpathcheck(ps, (z).y, localdata, n, &stcount, t, TOPATH(p), P_POSITION_Y)
6491 /*--------------------------------------------------------------*/
6492 /* Main routine for writing the contents of a single object to */
6493 /* output file "ps". */
6494 /*--------------------------------------------------------------*/
6496 void printOneObject(FILE *ps, objectptr localdata, int ccolor)
6498 int i, curcolor = ccolor;
6499 genericptr *savegen, *pgen;
6500 objinstptr sobj;
6501 graphicptr sg;
6502 Imagedata *img;
6503 pointlist savept;
6504 short stcount;
6505 short segs;
6506 Boolean has_parameter;
6507 char *fptr, *validname;
6509 /* first, get a total count of all objects and give warning if large */
6512 if ((is_page(localdata) == -1) && (localdata->parts > 255)) {
6513 Wprintf("Warning: \"%s\" may exceed printer's PS limit for definitions",
6514 localdata->name);
6517 for (savegen = localdata->plist; savegen < localdata->plist +
6518 localdata->parts; savegen++) {
6520 /* Check if this color is parameterized */
6521 eparamptr epp;
6522 oparamptr ops;
6524 for (epp = (*savegen)->passed; epp != NULL; epp = epp->next) {
6525 ops = match_param(localdata, epp->key);
6526 if (ops != NULL && (ops->which == P_COLOR)) {
6527 /* Ensure that the next element forces a color change */
6528 curcolor = ERRORCOLOR;
6529 sprintf(_STR, "%s scb\n", epp->key);
6530 fputs(_STR, ps);
6531 break;
6535 /* Enforce the rule that clipmasks must always be DEFAULTCOLOR */
6537 switch(ELEMENTTYPE(*savegen)) {
6538 case POLYGON: case SPLINE: case ARC: case PATH:
6539 if (TOPOLY(savegen)->style & CLIPMASK)
6540 (*savegen)->color = DEFAULTCOLOR;
6541 break;
6544 /* change current color if different */
6546 if ((epp == NULL) && ((*savegen)->color != curcolor)) {
6547 if ((curcolor = (*savegen)->color) == DEFAULTCOLOR)
6548 fprintf(ps, "sce\n");
6549 else {
6550 if (printRGBvalues(_STR, (*savegen)->color, "scb\n") < 0) {
6551 fprintf(ps, "sce\n");
6552 curcolor = DEFAULTCOLOR;
6554 else
6555 fputs(_STR, ps);
6559 stcount = 0;
6560 switch(ELEMENTTYPE(*savegen)) {
6562 case(POLYGON):
6563 varcheck(ps, TOPOLY(savegen)->style, localdata, &stcount,
6564 *savegen, P_STYLE);
6565 varfcheck(ps, TOPOLY(savegen)->width, localdata, &stcount,
6566 *savegen, P_LINEWIDTH);
6567 for (savept = TOPOLY(savegen)->points; savept < TOPOLY(savegen)->
6568 points + TOPOLY(savegen)->number; savept++) {
6569 varpcheck(ps, savept->x, localdata,
6570 savept - TOPOLY(savegen)->points, &stcount, *savegen,
6571 P_POSITION_X);
6572 varpcheck(ps, savept->y, localdata,
6573 savept - TOPOLY(savegen)->points, &stcount, *savegen,
6574 P_POSITION_Y);
6576 sprintf(_STR, "%hd ", TOPOLY(savegen)->number);
6577 dostcount (ps, &stcount, strlen(_STR));
6578 fputs(_STR, ps);
6579 if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6580 P_POSITION_X)) {
6581 sprintf(_STR, "addtox ");
6582 dostcount (ps, &stcount, strlen(_STR));
6583 fputs(_STR, ps);
6585 if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6586 P_POSITION_Y)) {
6587 sprintf(_STR, "addtoy ");
6588 dostcount (ps, &stcount, strlen(_STR));
6589 fputs(_STR, ps);
6591 sprintf(_STR, "polygon\n");
6592 dostcount (ps, &stcount, strlen(_STR));
6593 fputs(_STR, ps);
6594 break;
6596 case(PATH):
6597 pgen = TOPATH(savegen)->plist;
6598 switch(ELEMENTTYPE(*pgen)) {
6599 case POLYGON:
6600 xypathcheck(TOPOLY(pgen)->points[0], 0, pgen, savegen);
6601 break;
6602 case SPLINE:
6603 xypathcheck(TOSPLINE(pgen)->ctrl[0], 0, pgen, savegen);
6604 break;
6606 dostcount(ps, &stcount, 9);
6607 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6608 TOPATH(savegen), P_POSITION_X)) {
6609 sprintf(_STR, "addtox1 ");
6610 dostcount (ps, &stcount, strlen(_STR));
6611 fputs(_STR, ps);
6613 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6614 TOPATH(savegen), P_POSITION_Y)) {
6615 sprintf(_STR, "addtoy1 ");
6616 dostcount (ps, &stcount, strlen(_STR));
6617 fputs(_STR, ps);
6619 fprintf(ps, "beginpath\n");
6620 for (pgen = TOPATH(savegen)->plist; pgen < TOPATH(savegen)->plist
6621 + TOPATH(savegen)->parts; pgen++) {
6622 switch(ELEMENTTYPE(*pgen)) {
6623 case POLYGON:
6624 for (savept = TOPOLY(pgen)->points + TOPOLY(pgen)->number
6625 - 1; savept > TOPOLY(pgen)->points; savept--) {
6626 xypathcheck(*savept, savept - TOPOLY(pgen)->points, pgen,
6627 savegen);
6629 sprintf(_STR, "%hd ", TOPOLY(pgen)->number - 1);
6630 dostcount (ps, &stcount, strlen(_STR));
6631 fputs(_STR, ps);
6632 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6633 TOPATH(savegen), P_POSITION_X)) {
6634 sprintf(_STR, "addtox ");
6635 dostcount (ps, &stcount, strlen(_STR));
6636 fputs(_STR, ps);
6638 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6639 TOPATH(savegen), P_POSITION_Y)) {
6640 sprintf(_STR, "addtoy ");
6641 dostcount (ps, &stcount, strlen(_STR));
6642 fputs(_STR, ps);
6644 sprintf(_STR, "polyc\n");
6645 dostcount (ps, &stcount, strlen(_STR));
6646 fputs(_STR, ps);
6647 break;
6648 case SPLINE:
6649 xypathcheck(TOSPLINE(pgen)->ctrl[1], 1, pgen, savegen);
6650 xypathcheck(TOSPLINE(pgen)->ctrl[2], 2, pgen, savegen);
6651 xypathcheck(TOSPLINE(pgen)->ctrl[3], 3, pgen, savegen);
6652 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6653 TOPATH(savegen), P_POSITION_X)) {
6654 sprintf(_STR, "addtox3 ");
6655 dostcount (ps, &stcount, strlen(_STR));
6656 fputs(_STR, ps);
6658 if (varpathcheck(ps, 0, localdata, -1, &stcount, pgen,
6659 TOPATH(savegen), P_POSITION_Y)) {
6660 sprintf(_STR, "addtoy3 ");
6661 dostcount (ps, &stcount, strlen(_STR));
6662 fputs(_STR, ps);
6664 fprintf(ps, "curveto\n");
6665 break;
6668 varcheck(ps, TOPATH(savegen)->style, localdata, &stcount,
6669 *savegen, P_STYLE);
6670 varfcheck(ps, TOPATH(savegen)->width, localdata, &stcount,
6671 *savegen, P_LINEWIDTH);
6672 fprintf(ps, "endpath\n");
6673 break;
6675 case(SPLINE):
6676 varcheck(ps, TOSPLINE(savegen)->style, localdata, &stcount,
6677 *savegen, P_STYLE);
6678 varfcheck(ps, TOSPLINE(savegen)->width, localdata, &stcount,
6679 *savegen, P_LINEWIDTH);
6680 xyvarcheck(TOSPLINE(savegen)->ctrl[1], 1, savegen);
6681 xyvarcheck(TOSPLINE(savegen)->ctrl[2], 2, savegen);
6682 xyvarcheck(TOSPLINE(savegen)->ctrl[3], 3, savegen);
6683 xyvarcheck(TOSPLINE(savegen)->ctrl[0], 0, savegen);
6684 if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6685 P_POSITION_X)) {
6686 sprintf(_STR, "addtox4 ");
6687 dostcount (ps, &stcount, strlen(_STR));
6688 fputs(_STR, ps);
6690 if (varpcheck(ps, 0, localdata, -1, &stcount, *savegen,
6691 P_POSITION_Y)) {
6692 sprintf(_STR, "addtoy4 ");
6693 dostcount (ps, &stcount, strlen(_STR));
6694 fputs(_STR, ps);
6696 fprintf(ps, "spline\n");
6697 break;
6699 case(ARC):
6700 varcheck(ps, TOARC(savegen)->style, localdata, &stcount,
6701 *savegen, P_STYLE);
6702 varfcheck(ps, TOARC(savegen)->width, localdata, &stcount,
6703 *savegen, P_LINEWIDTH);
6704 xyvarcheck(TOARC(savegen)->position, 0, savegen);
6705 varcheck(ps, abs(TOARC(savegen)->radius), localdata, &stcount,
6706 *savegen, P_RADIUS);
6707 if (abs(TOARC(savegen)->radius) == TOARC(savegen)->yaxis) {
6708 varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount,
6709 *savegen, P_ANGLE1);
6710 varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount,
6711 *savegen, P_ANGLE2);
6712 fprintf(ps, "xcarc\n");
6714 else {
6715 varcheck(ps, abs(TOARC(savegen)->yaxis), localdata, &stcount,
6716 *savegen, P_MINOR_AXIS);
6717 varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount,
6718 *savegen, P_ANGLE1);
6719 varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount,
6720 *savegen, P_ANGLE2);
6721 fprintf(ps, "ellipse\n");
6723 break;
6725 case(OBJINST):
6726 sobj = TOOBJINST(savegen);
6727 varfcheck(ps, sobj->scale, localdata, &stcount, *savegen, P_SCALE);
6728 if (!(sobj->style & LINE_INVARIANT)) fprintf(ps, "/sv ");
6729 varfcheck(ps, sobj->rotation, localdata, &stcount, *savegen, P_ROTATION);
6730 xyvarcheck(sobj->position, 0, savegen);
6732 opsubstitute(sobj->thisobject, sobj);
6733 stcount = printparams(ps, sobj, stcount);
6735 validname = create_valid_psname(sobj->thisobject->name, FALSE);
6737 /* Names without technologies get a leading string '::' */
6738 /* (blank technology) */
6740 if (strstr(validname, "::") == NULL)
6741 fprintf(ps, "::%s\n", validname);
6742 else
6743 fprintf(ps, "%s\n", validname);
6744 break;
6746 case(GRAPHIC):
6747 sg = TOGRAPHIC(savegen);
6748 for (i = 0; i < xobjs.images; i++) {
6749 img = xobjs.imagelist + i;
6750 if (img->image == sg->source)
6751 break;
6754 fptr = strrchr(img->filename, '/');
6755 if (fptr == NULL)
6756 fptr = img->filename;
6757 else
6758 fptr++;
6759 fprintf(ps, "/%s ", fptr);
6760 stcount += (2 + strlen(fptr));
6762 varfcheck(ps, sg->scale, localdata, &stcount, *savegen, P_SCALE);
6763 varfcheck(ps, sg->rotation, localdata, &stcount, *savegen, P_ROTATION);
6764 xyvarcheck(sg->position, 0, savegen);
6765 fprintf(ps, "graphic\n");
6766 break;
6768 case(LABEL):
6770 /* Don't save temporary labels from schematic capture system */
6771 if (TOLABEL(savegen)->string->type != FONT_NAME) break;
6773 /* Check for parameter --- must use "mark" to count # segments */
6774 has_parameter = hasparameter(TOLABEL(savegen));
6776 if (has_parameter) {
6777 fprintf(ps, "mark ");
6778 stcount += 5;
6781 segs = writelabel(ps, TOLABEL(savegen)->string, &stcount);
6783 if (segs > 0) {
6784 if (has_parameter)
6785 sprintf(_STR, "ctmk ");
6786 else
6787 sprintf(_STR, "%hd ", segs);
6788 dostcount(ps, &stcount, strlen(_STR));
6789 fputs(_STR, ps);
6790 varcheck(ps, TOLABEL(savegen)->anchor, localdata, &stcount,
6791 *savegen, P_ANCHOR);
6792 varfcheck(ps, TOLABEL(savegen)->rotation, localdata, &stcount,
6793 *savegen, P_ROTATION);
6794 varfcheck(ps, TOLABEL(savegen)->scale, localdata, &stcount,
6795 *savegen, P_SCALE);
6796 xyvarcheck(TOLABEL(savegen)->position, 0, savegen);
6798 switch(TOLABEL(savegen)->pin) {
6799 case LOCAL:
6800 strcpy(_STR, "pinlabel\n"); break;
6801 case GLOBAL:
6802 strcpy(_STR, "pinglobal\n"); break;
6803 case INFO:
6804 strcpy(_STR, "infolabel\n"); break;
6805 default:
6806 strcpy(_STR, "label\n");
6808 dostcount(ps, &stcount, strlen(_STR));
6809 fputs(_STR, ps);
6811 break;
6816 /*----------------------------------------------------------------------*/
6817 /* Recursive routine to print out the library objects used in this */
6818 /* drawing, starting at the bottom of the object hierarchy so that each */
6819 /* object is defined before it is called. A list of objects already */
6820 /* written is maintained so that no object is written twice. */
6821 /* */
6822 /* When object "localdata" is not a top-level page, call this routine */
6823 /* with mpage=-1 (simpler than checking whether localdata is a page). */
6824 /*----------------------------------------------------------------------*/
6826 void printobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
6827 short *written, int ccolor)
6829 genericptr *gptr;
6830 objectptr *optr;
6831 /* oparamptr ops; (jdk) */
6832 char *validname;
6833 int curcolor = ccolor;
6834 /* int libno; (jdk) */
6836 /* Search among the list of objects already written to the output */
6837 /* If this object has been written previously, then we ignore it. */
6839 for (optr = *wrotelist; optr < *wrotelist + *written; optr++)
6840 if (*optr == localdata)
6841 return;
6843 /* If this page is a schematic, write out the definiton of any symbol */
6844 /* attached to it, because that symbol may not be used anywhere else. */
6846 if (localdata->symschem && (localdata->schemtype == PRIMARY))
6847 printobjects(ps, localdata->symschem, wrotelist, written, curcolor);
6849 /* Search for all object definitions instantiated in this object, */
6850 /* and (recursively) print them to the output. */
6852 for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++)
6853 if (IS_OBJINST(*gptr))
6854 printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written, curcolor);
6856 /* Update the list of objects already written to the output */
6858 *wrotelist = (objectptr *)realloc(*wrotelist, (*written + 1) *
6859 sizeof(objectptr));
6860 *(*wrotelist + *written) = localdata;
6861 (*written)++;
6863 validname = create_valid_psname(localdata->name, FALSE);
6864 if (strstr(validname, "::") == NULL)
6865 fprintf(ps, "/::%s {\n", validname);
6866 else
6867 fprintf(ps, "/%s {\n", validname);
6869 /* No longer writes "bbox" record */
6870 if (localdata->hidden == True) fprintf(ps, "%% hidden\n");
6872 /* For symbols with schematics, and "trivial" schematics */
6873 if (localdata->symschem != NULL)
6874 fprintf(ps, "%% %s is_schematic\n", localdata->symschem->name);
6875 else if (localdata->schemtype == TRIVIAL)
6876 fprintf(ps, "%% trivial\n");
6877 else if (localdata->schemtype == NONETWORK)
6878 fprintf(ps, "%% nonetwork\n");
6880 printobjectparams(ps, localdata);
6881 fprintf(ps, "begingate\n");
6883 /* Write all the elements in order */
6885 opsubstitute(localdata, NULL);
6886 printOneObject(ps, localdata, curcolor);
6888 /* Write object (gate) trailer */
6890 fprintf(ps, "endgate\n} def\n\n");
6893 /*--------------------------------------------------------------*/
6894 /* Print a page header followed by everything in the page. */
6895 /* this routine assumes that all objects used by the page have */
6896 /* already been handled and written to the output. */
6897 /* */
6898 /* "page" is the page number, counting consecutively from one. */
6899 /* "mpage" is the page number in xcircuit's pagelist structure. */
6900 /*--------------------------------------------------------------*/
6902 void printpageobject(FILE *ps, objectptr localdata, short page, short mpage)
6904 /* genericptr *gptr; (jdk) */
6905 XPoint origin, corner;
6906 objinstptr writepage;
6907 int width, height;
6908 float psnorm, psscale;
6909 float xmargin, ymargin;
6910 char *rootptr = NULL;
6911 polyptr framebox;
6913 /* Output page header information */
6915 if (xobjs.pagelist[mpage]->filename)
6916 rootptr = strrchr(xobjs.pagelist[mpage]->filename, '/');
6917 if (rootptr == NULL)
6918 rootptr = xobjs.pagelist[mpage]->filename;
6919 else rootptr++;
6921 writepage = xobjs.pagelist[mpage]->pageinst;
6923 psnorm = xobjs.pagelist[mpage]->outscale;
6924 psscale = getpsscale(psnorm, mpage);
6926 /* Determine the margins (offset of drawing from page corner) */
6927 /* If a bounding box has been declared in the drawing, it is */
6928 /* centered on the page. Otherwise, the drawing itself is */
6929 /* centered on the page. If encapsulated, the bounding box */
6930 /* encompasses only the object itself. */
6932 width = toplevelwidth(writepage, &origin.x);
6933 height = toplevelheight(writepage, &origin.y);
6935 corner.x = origin.x + width;
6936 corner.y = origin.y + height;
6938 if (xobjs.pagelist[mpage]->pmode & 1) { /* full page */
6940 if (xobjs.pagelist[mpage]->orient == 90) {
6941 xmargin = (xobjs.pagelist[mpage]->pagesize.x -
6942 ((float)height * psscale)) / 2;
6943 ymargin = (xobjs.pagelist[mpage]->pagesize.y -
6944 ((float)width * psscale)) / 2;
6946 else {
6947 xmargin = (xobjs.pagelist[mpage]->pagesize.x -
6948 ((float)width * psscale)) / 2;
6949 ymargin = (xobjs.pagelist[mpage]->pagesize.y -
6950 ((float)height * psscale)) / 2;
6953 else { /* encapsulated --- should have 1" border so that any */
6954 /* drawing passed directly to a printer will not clip */
6955 xmargin = xobjs.pagelist[mpage]->margins.x;
6956 ymargin = xobjs.pagelist[mpage]->margins.y;
6959 /* If a framebox is declared, then we adjust the page to be */
6960 /* centered on the framebox by translating through the */
6961 /* difference between the object center and the framebox */
6962 /* center. */
6964 if ((framebox = checkforbbox(localdata)) != NULL) {
6965 int i, fcentx = 0, fcenty = 0;
6967 for (i = 0; i < framebox->number; i++) {
6968 fcentx += framebox->points[i].x;
6969 fcenty += framebox->points[i].y;
6971 fcentx /= framebox->number;
6972 fcenty /= framebox->number;
6974 xmargin += psscale * (float)(origin.x + (width >> 1) - fcentx);
6975 ymargin += psscale * (float)(origin.y + (height >> 1) - fcenty);
6978 /* If the page label is just the root name of the file, or has been left */
6979 /* as "Page n" or "Page_n", just do the normal page numbering. Otherwise, */
6980 /* write out the page label explicitly. */
6982 if ((rootptr == NULL) || (!strcmp(rootptr, localdata->name))
6983 || (strchr(localdata->name, ' ') != NULL)
6984 || (strstr(localdata->name, "Page_") != NULL))
6985 fprintf (ps, "%%%%Page: %d %d\n", page, page);
6986 else
6987 fprintf (ps, "%%%%Page: %s %d\n", localdata->name, page);
6989 if (xobjs.pagelist[mpage]->orient == 90)
6990 fprintf (ps, "%%%%PageOrientation: Landscape\n");
6991 else
6992 fprintf (ps, "%%%%PageOrientation: Portrait\n");
6994 if (xobjs.pagelist[mpage]->pmode & 1) { /* full page */
6995 fprintf(ps, "%%%%PageBoundingBox: 0 0 %d %d\n",
6996 xobjs.pagelist[mpage]->pagesize.x,
6997 xobjs.pagelist[mpage]->pagesize.y);
7000 /* Encapsulated files do not get a PageBoundingBox line, */
7001 /* unless the bounding box was explicitly drawn. */
7003 else if (framebox != NULL) {
7004 fprintf(ps, "%%%%PageBoundingBox: %g %g %g %g\n",
7005 xmargin, ymargin,
7006 xmargin + psscale * (float)(width),
7007 ymargin + psscale * (float)(height));
7010 fprintf (ps, "/pgsave save def bop\n");
7012 /* Top-page definitions */
7013 if (localdata->params != NULL) {
7014 printobjectparams(ps, localdata);
7015 fprintf(ps, "begin\n");
7018 if (localdata->symschem != NULL) {
7019 if (is_page(localdata->symschem) == -1)
7020 fprintf(ps, "%% %s is_symbol\n", localdata->symschem->name);
7021 else if (localdata->schemtype == SECONDARY)
7022 fprintf(ps, "%% %s is_primary\n", localdata->symschem->name);
7023 else
7024 Wprintf("Something is wrong. . . schematic \"%s\" is connected to"
7025 " schematic \"%s\" but is not declared secondary.\n",
7026 localdata->name, localdata->symschem->name);
7029 /* Extend bounding box around schematic pins */
7030 extendschembbox(xobjs.pagelist[mpage]->pageinst, &origin, &corner);
7032 if (xobjs.pagelist[mpage]->drawingscale.x != 1
7033 || xobjs.pagelist[mpage]->drawingscale.y != 1)
7034 fprintf(ps, "%% %hd:%hd drawingscale\n", xobjs.pagelist[mpage]->drawingscale.x,
7035 xobjs.pagelist[mpage]->drawingscale.y);
7037 if (xobjs.pagelist[mpage]->gridspace != 32
7038 || xobjs.pagelist[mpage]->snapspace != 16)
7039 fprintf(ps, "%% %4.2f %4.2f gridspace\n", xobjs.pagelist[mpage]->gridspace,
7040 xobjs.pagelist[mpage]->snapspace);
7042 if (xobjs.pagelist[mpage]->background.name != (char *)NULL) {
7043 /* float iscale = (xobjs.pagelist[mpage]->coordstyle == CM) ? CMSCALE : INCHSCALE; (jdk) */
7044 if (xobjs.pagelist[mpage]->orient == 90)
7045 fprintf(ps, "%5.4f %d %d 90 psinsertion\n", psnorm,
7046 (int)(ymargin - xmargin),
7047 -((int)((float)(corner.y - origin.y) * psscale) +
7048 (int)(xmargin + ymargin)));
7049 else
7050 fprintf(ps, "%5.4f %d %d 0 psinsertion\n", psnorm,
7051 (int)(xmargin / psscale) - origin.x,
7052 (int)(ymargin / psscale) - origin.y);
7053 savebackground(ps, xobjs.pagelist[mpage]->background.name);
7054 fprintf(ps, "\nend_insert\n");
7057 if (xobjs.pagelist[mpage]->orient == 90)
7058 fprintf(ps, "90 rotate %d %d translate\n", (int)(ymargin - xmargin),
7059 -((int)((float)(corner.y - origin.y) * psscale) +
7060 (int)(xmargin + ymargin)));
7062 fprintf(ps, "%5.4f ", psnorm);
7063 switch(xobjs.pagelist[mpage]->coordstyle) {
7064 case CM:
7065 fprintf(ps, "cmscale\n");
7066 break;
7067 default:
7068 fprintf(ps, "inchscale\n");
7069 break;
7072 /* Final scale and translation */
7073 fprintf(ps, "%5.4f setlinewidth %d %d translate\n\n",
7074 1.3 * xobjs.pagelist[mpage]->wirewidth,
7075 (int)(xmargin / psscale) - origin.x,
7076 (int)(ymargin / psscale) - origin.y);
7078 /* Output all the elements in the page */
7079 printOneObject(ps, localdata, DEFAULTCOLOR);
7081 /* Page trailer */
7082 if (localdata->params != NULL) fprintf(ps, "end ");
7083 fprintf(ps, "pgsave restore showpage\n");
7086 /*--------------------------------------------------------------*/
7087 /* Print objects referenced from a particular page. These get */
7088 /* bundled together at the beginning of the output file under */
7089 /* the DSC "Setup" section, so that the PostScript */
7090 /* interpreter knows that these definitions may be used by any */
7091 /* page. This prevents ghostscript from producing an error */
7092 /* when backing up in a multi-page document. */
7093 /*--------------------------------------------------------------*/
7095 void printrefobjects(FILE *ps, objectptr localdata, objectptr **wrotelist,
7096 short *written)
7098 genericptr *gptr;
7100 /* If this page is a schematic, write out the definiton of any symbol */
7101 /* attached to it, because that symbol may not be used anywhere else. */
7103 if (localdata->symschem && (localdata->schemtype == PRIMARY))
7104 printobjects(ps, localdata->symschem, wrotelist, written, DEFAULTCOLOR);
7106 /* Search for all object definitions instantiated on the page and */
7107 /* write them to the output. */
7109 for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++)
7110 if (IS_OBJINST(*gptr))
7111 printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written,
7112 DEFAULTCOLOR);
7115 /*----------------------------------------------------------------------*/