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