/*-----------------------------------------------------------------------*/ /* files.c --- file handling routines for xcircuit */ /* Copyright (c) 2002 Tim Edwards, Johns Hopkins University */ /*-----------------------------------------------------------------------*/ #include #include #include #include #ifndef XC_WIN32 #include #endif #include #include #include #ifndef XC_WIN32 #include #include #include #else #include #endif #ifdef TCL_WRAPPER #include #else #ifndef XC_WIN32 #include "Xw/TextEdit.h" /* for XwTextCopyBuffer() */ #endif #endif /*------------------------------------------------------------------------*/ /* Local includes */ /*------------------------------------------------------------------------*/ #include "colordefs.h" #include "xcircuit.h" /*----------------------------------------------------------------------*/ /* Function prototype declarations */ /*----------------------------------------------------------------------*/ #include "prototypes.h" #ifdef ASG extern void Route(Clientdata *, Boolean); extern int ReadSpice(FILE *); #endif /*------------------------------------------------------------------------*/ /* Useful (local) defines */ /*------------------------------------------------------------------------*/ #define OUTPUTWIDTH 80 /* maximum text width of output */ #define S_OBLIQUE 13 /* position of Symbol-Oblique in font array */ #define VEPS 1e-3 /*------------------------------------------------------------------------*/ /* External Variable definitions */ /*------------------------------------------------------------------------*/ #ifdef TCL_WRAPPER extern Tcl_Interp *xcinterp; #endif extern char _STR2[250], _STR[150]; extern Globaldata xobjs; extern Clientdata areastruct; extern fontinfo *fonts; extern short fontcount; extern XtAppContext app; extern Display *dpy; extern Window win; extern short eventmode; extern short beeper; extern int *appcolors; extern int number_colors; extern colorindex *colorlist; /*------------------------------------------------------*/ /* Global variable definitions */ /*------------------------------------------------------*/ float version; /* Structure for remembering what names refer to the same object */ aliasptr aliastop; /*------------------------------------------------------*/ /* Simple utility---get rid of newline character */ /*------------------------------------------------------*/ char *ridnewline(char *sptr) { char *tstrp; for (tstrp = sptr; *tstrp != '\0' && *tstrp != '\n'; tstrp++); if (*tstrp == '\n') *tstrp = '\0'; return tstrp; } /*------------------------------------------------------*/ /* Free memory allocated to a label string */ /*------------------------------------------------------*/ void freelabel(stringpart *string) { stringpart *strptr = string, *tmpptr; while (strptr != NULL) { if (strptr->type == TEXT_STRING || strptr->type == PARAM_START) free(strptr->data.string); tmpptr = strptr->nextpart; free(strptr); strptr = tmpptr; } } /*------------------------------------------------------*/ /* Free memory for a single element */ /*------------------------------------------------------*/ void free_single(genericptr genobj) { objinstptr geninst; oparamptr ops, fops; if (IS_POLYGON(genobj)) free(((polyptr)(genobj))->points); else if (IS_LABEL(genobj)) freelabel(((labelptr)(genobj))->string); else if (IS_GRAPHIC(genobj)) freegraphic((graphicptr)(genobj)); else if (IS_PATH(genobj)) free(((pathptr)(genobj))->plist); else if (IS_OBJINST(genobj)) { geninst = (objinstptr)genobj; ops = geninst->params; while (ops != NULL) { /* Don't try to free data from indirect parameters */ if (find_indirect_param(geninst, ops->key) == NULL) { switch(ops->type) { case XC_STRING: freelabel(ops->parameter.string); break; case XC_EXPR: free(ops->parameter.expr); break; } } free(ops->key); fops = ops; ops = ops->next; free(fops); } } free_all_eparams(genobj); } /*---------------------------------------------------------*/ /* Reset an object structure by freeing all alloc'd memory */ /*---------------------------------------------------------*/ void reset(objectptr localdata, short mode) { short i; if (localdata->polygons != NULL || localdata->labels != NULL) destroynets(localdata); if (localdata->parts > 0) { genericptr *genobj; if (mode != SAVE) { for (genobj = localdata->plist; genobj < localdata->plist + localdata->parts; genobj++) /* (*genobj == NULL) only on library pages */ /* where the instances are kept in the library */ /* definition, and are only referenced on the page. */ if (*genobj != NULL) { free_single(*genobj); free(*genobj); } } free(localdata->plist); removeparams(localdata); initmem(localdata); if (mode == DESTROY) free(localdata->plist); } } /*---------------------------------------------------------*/ void pagereset(short rpage) { /* free alloc'd filename */ if (xobjs.pagelist[rpage]->filename != NULL) free(xobjs.pagelist[rpage]->filename); if (xobjs.pagelist[rpage]->background.name != NULL) free(xobjs.pagelist[rpage]->background.name); clearselects(); /* New pages pick up their properties from page 0, which can be changed */ /* from the .xcircuitrc file on startup (or loaded from a script). */ /* Thanks to Norman Werner (norman.werner@student.uni-magdeburg.de) for */ /* pointing out this more obvious way of doing the reset, and providing */ /* a patch. */ xobjs.pagelist[rpage]->wirewidth = xobjs.pagelist[0]->wirewidth; xobjs.pagelist[rpage]->orient = xobjs.pagelist[0]->orient; xobjs.pagelist[rpage]->pmode = xobjs.pagelist[0]->pmode; xobjs.pagelist[rpage]->outscale = xobjs.pagelist[0]->outscale; xobjs.pagelist[rpage]->filename = (char *)malloc ((1 + strlen(xobjs.pagelist[rpage]->pageinst->thisobject->name)) * sizeof(char)); xobjs.pagelist[rpage]->background.name = (char *)NULL; strcpy(xobjs.pagelist[rpage]->filename, xobjs.pagelist[rpage]->pageinst->thisobject->name); xobjs.pagelist[rpage]->drawingscale.x = xobjs.pagelist[0]->drawingscale.x; xobjs.pagelist[rpage]->drawingscale.y = xobjs.pagelist[0]->drawingscale.y; xobjs.pagelist[rpage]->gridspace = xobjs.pagelist[0]->gridspace; xobjs.pagelist[rpage]->snapspace = xobjs.pagelist[0]->snapspace; if (xobjs.pagelist[0]->coordstyle == CM) { xobjs.pagelist[rpage]->coordstyle = CM; xobjs.pagelist[rpage]->pagesize.x = 595; xobjs.pagelist[rpage]->pagesize.y = 842; /* A4 */ } else { xobjs.pagelist[rpage]->coordstyle = FRAC_INCH; xobjs.pagelist[rpage]->pagesize.x = 612; /* letter */ xobjs.pagelist[rpage]->pagesize.y = 792; } } /*---------------------------------------------------------*/ void initmem(objectptr localdata) { localdata->parts = 0; localdata->plist = (genericptr *)malloc(sizeof(genericptr)); localdata->hidden = False; localdata->changes = 0; localdata->params = NULL; localdata->viewscale = 0.5; localdata->pcorner.x = -areastruct.width; localdata->pcorner.y = -areastruct.height; localdata->bbox.width = 0; localdata->bbox.height = 0; localdata->bbox.lowerleft.x = 0; localdata->bbox.lowerleft.y = 0; localdata->highlight.netlist = NULL; localdata->highlight.thisinst = NULL; localdata->schemtype = PRIMARY; localdata->symschem = NULL; localdata->netnames = NULL; localdata->polygons = NULL; localdata->labels = NULL; localdata->ports = NULL; localdata->calls = NULL; localdata->valid = False; localdata->traversed = False; } /*--------------------------------------------------------------*/ /* Exhaustively compare the contents of two objects and return */ /* true if equivalent, false if not. */ /*--------------------------------------------------------------*/ Boolean elemcompare(genericptr *compgen, genericptr *gchk) { Boolean bres; switch(ELEMENTTYPE(*compgen)) { case(ARC): bres = (TOARC(compgen)->position.x == TOARC(gchk)->position.x && TOARC(compgen)->position.y == TOARC(gchk)->position.y && TOARC(compgen)->style == TOARC(gchk)->style && TOARC(compgen)->width == TOARC(gchk)->width && abs(TOARC(compgen)->radius) == abs(TOARC(gchk)->radius) && TOARC(compgen)->yaxis == TOARC(gchk)->yaxis && TOARC(compgen)->angle1 == TOARC(gchk)->angle1 && TOARC(compgen)->angle2 == TOARC(gchk)->angle2); break; case(SPLINE): bres = (TOSPLINE(compgen)->style == TOSPLINE(gchk)->style && TOSPLINE(compgen)->width == TOSPLINE(gchk)->width && TOSPLINE(compgen)->ctrl[0].x == TOSPLINE(gchk)->ctrl[0].x && TOSPLINE(compgen)->ctrl[0].y == TOSPLINE(gchk)->ctrl[0].y && TOSPLINE(compgen)->ctrl[1].x == TOSPLINE(gchk)->ctrl[1].x && TOSPLINE(compgen)->ctrl[1].y == TOSPLINE(gchk)->ctrl[1].y && TOSPLINE(compgen)->ctrl[2].x == TOSPLINE(gchk)->ctrl[2].x && TOSPLINE(compgen)->ctrl[2].y == TOSPLINE(gchk)->ctrl[2].y && TOSPLINE(compgen)->ctrl[3].x == TOSPLINE(gchk)->ctrl[3].x && TOSPLINE(compgen)->ctrl[3].y == TOSPLINE(gchk)->ctrl[3].y); break; case(POLYGON): { int i; if (TOPOLY(compgen)->style == TOPOLY(gchk)->style && TOPOLY(compgen)->width == TOPOLY(gchk)->width && TOPOLY(compgen)->number == TOPOLY(gchk)->number) { for (i = 0; i < TOPOLY(compgen)->number; i++) { if (TOPOLY(compgen)->points[i].x != TOPOLY(gchk)->points[i].x || TOPOLY(compgen)->points[i].y != TOPOLY(gchk)->points[i].y) break; } bres = (i == TOPOLY(compgen)->number); } else bres = False; }break; } return bres; } /*--------------------------------------------------------------*/ /* Compare any element with any other element. */ /*--------------------------------------------------------------*/ Boolean compare_single(genericptr *compgen, genericptr *gchk) { Boolean bres = False; if ((*gchk)->type == (*compgen)->type) { switch(ELEMENTTYPE(*compgen)) { case(OBJINST):{ objinst *newobj = TOOBJINST(compgen); objinst *oldobj = TOOBJINST(gchk); bres = (newobj->position.x == oldobj->position.x && newobj->position.y == oldobj->position.y && newobj->rotation == oldobj->rotation && newobj->scale == oldobj->scale && newobj->thisobject == oldobj->thisobject); } break; case(LABEL): bres = (TOLABEL(compgen)->position.x == TOLABEL(gchk)->position.x && TOLABEL(compgen)->position.y == TOLABEL(gchk)->position.y && TOLABEL(compgen)->rotation == TOLABEL(gchk)->rotation && TOLABEL(compgen)->scale == TOLABEL(gchk)->scale && TOLABEL(compgen)->justify == TOLABEL(gchk)->justify && ((TOLABEL(compgen)->pin == TOLABEL(gchk)->pin) || !areastruct.schemon) && !stringcomp(TOLABEL(compgen)->string, TOLABEL(gchk)->string)); break; case(PATH): /* elements *must* be in same order for a path */ bres = (TOPATH(compgen)->parts == TOPATH(gchk)->parts && TOPATH(compgen)->style == TOPATH(gchk)->style && TOPATH(compgen)->width == TOPATH(gchk)->width); if (bres) { genericptr *pathchk, *gpath; for (pathchk = TOPATH(compgen)->plist, gpath = TOPATH(gchk)->plist; pathchk < TOPATH(compgen)->plist + TOPATH(compgen)->parts; pathchk++, gpath++) { if (!elemcompare(pathchk, gpath)) bres = False; } } break; case(ARC): case(SPLINE): case(POLYGON): bres = elemcompare(compgen, gchk); break; } } return bres; } /*--------------------------------------------------------------------*/ short objcompare(objectptr obja, objectptr objb) { genericptr *compgen, *glist, *gchk, *remg; short csize; Boolean bres; /* quick check on equivalence of number of objects */ if (obja->parts != objb->parts) return False; /* check equivalence of parameters. Parameters need not be in any */ /* order; they must only match by key and value. */ if (obja->params == NULL && objb->params != NULL) return False; else if (obja->params != NULL && objb->params == NULL) return False; else if (obja->params != NULL || objb->params != NULL) { oparamptr opsa, opsb; for (opsa = obja->params; opsa != NULL; opsa = opsa->next) { opsb = match_param(objb, opsa->key); if (opsb == NULL) return False; else if (opsa->type != opsb->type) return False; switch (opsa->type) { case XC_STRING: if (stringcomp(opsa->parameter.string, opsb->parameter.string)) return False; break; case XC_EXPR: if (strcmp(opsa->parameter.expr, opsb->parameter.expr)) return False; break; case XC_INT: case XC_FLOAT: if (opsa->parameter.ivalue != opsb->parameter.ivalue) return False; break; } } } /* For the exhaustive check we must match component for component. */ /* Best not to assume that elements are in same order for both. */ csize = obja->parts; glist = (genericptr *)malloc(csize * sizeof(genericptr)); for (compgen = objb->plist; compgen < objb->plist + csize; compgen++) (*(glist + (int)(compgen - objb->plist))) = *compgen; for (compgen = obja->plist; compgen < obja->plist + obja->parts; compgen++) { bres = False; for (gchk = glist; gchk < glist + csize; gchk++) { if ((*compgen)->color == (*gchk)->color) bres = compare_single(compgen, gchk); if (bres) { csize--; for (remg = gchk; remg < glist + csize; remg++) *remg = *(remg + 1); break; } } } free(glist); if (csize != 0) return False; /* Both objects cannot attempt to set an associated schematic/symbol to */ /* separate objects, although it is okay for one to make the association */ /* and the other not to. */ if (obja->symschem != NULL && objb->symschem != NULL) if (obja->symschem != objb->symschem) return False; return(True); } /*------------------------*/ /* scale renormalization */ /*------------------------*/ float getpsscale(float value, short page) { if (xobjs.pagelist[page]->coordstyle != CM) return (value * INCHSCALE); else return (value * CMSCALE); } /*---------------------------------------------------------------*/ /* Keep track of columns of output and split lines when too long */ /*---------------------------------------------------------------*/ void dostcount(FILE *ps, short *count, short addlength) { *count += addlength; if (*count > OUTPUTWIDTH) { *count = addlength; fprintf(ps, "\n"); } } /*----------------------------------------------------------------------*/ /* Write a numerical value as a string to _STR, making a parameter */ /* substitution if appropriate. */ /*----------------------------------------------------------------------*/ void varpcheck(FILE *ps, short value, objectptr localdata, int pointno, short *stptr, genericptr thiselem, u_char which) { oparamptr ops; eparamptr epp; Boolean done = False; for (epp = thiselem->passed; epp != NULL; epp = epp->next) { if (epp->pdata.pointno != pointno) continue; ops = match_param(localdata, epp->key); if (ops != NULL && (ops->which == which)) { sprintf(_STR, "%s ", epp->key); done = True; break; } } if (!done) sprintf(_STR, "%d ", (int)value); dostcount (ps, stptr, strlen(_STR)); fputs(_STR, ps); } /*----------------------------------------------------------------------*/ /* like varpcheck(), but without pointnumber */ /*----------------------------------------------------------------------*/ void varcheck(FILE *ps, short value, objectptr localdata, short *stptr, genericptr thiselem, u_char which) { varpcheck(ps, value, localdata, 0, stptr, thiselem, which); } /*----------------------------------------------------------------------*/ /* like varcheck(), but for floating-point values */ /*----------------------------------------------------------------------*/ void varfcheck(FILE *ps, float value, objectptr localdata, short *stptr, genericptr thiselem, u_char which) { oparamptr ops; eparamptr epp; Boolean done = False; for (epp = thiselem->passed; epp != NULL; epp = epp->next) { ops = match_param(localdata, epp->key); if (ops != NULL && (ops->which == which)) { sprintf(_STR, "%s ", epp->key); done = True; break; } } if (!done) sprintf(_STR, "%3.2f ", value); dostcount (ps, stptr, strlen(_STR)); fputs(_STR, ps); } /* Structure used to hold data specific to each load mode. See */ /* xcircuit.h for the list of load modes (enum loadmodes) */ typedef struct _loaddata { void (*func)(); /* Routine to run to load the file */ char *prompt; /* Substring name of action, for prompting */ char *filext; /* Default extention of file to load */ } loaddata; /*-------------------------------------------------------*/ /* Load a PostScript or Python (interpreter script) file */ /*-------------------------------------------------------*/ void getfile(xcWidget button, pointertype mode, caddr_t nulldata) { static loaddata loadmodes[LOAD_MODES] = { {startloadfile, "load", "ps"}, /* mode NORMAL */ {importfile, "import", "ps"}, /* mode IMPORT */ {loadbackground, "render", "ps"}, /* mode PSBKGROUND */ #ifdef HAVE_PYTHON {execscript, "execute", "py"}, #else {execscript, "execute", ""}, /* mode SCRIPT */ #endif {crashrecover, "recover", "ps"}, /* mode RECOVER */ #ifdef ASG {importspice, "import", "spice"}, /* mode IMPORTSPICE */ #endif #ifdef USE_WIN32_COM {win32_comscript, "execute", ".js;*.vbs"}, /* mode COMSCRIPT */ #ifdef USE_WIN32_DOTNET {win32_comdotnet, "execute", ".dll"}, /* mode COMDOTNET */ #endif #endif }; buttonsave *savebutton; char *promptstr = NULL; char strext[10]; int idx = (int)mode; if (is_page(topobject) == -1) { Wprintf("Can only read file into top-level page!"); return; } else if (idx >= LOAD_MODES) { Wprintf("Unknown mode passed to routine getfile()\n"); return; } savebutton = (buttonsave *)malloc(sizeof(buttonsave)); getgeneric(savebutton, button, getfile, (void *)mode); if (idx == RECOVER) { char *cfile = getcrashfilename(); promptstr = (char *)malloc(18 + ((cfile == NULL) ? 9 : strlen(cfile))); sprintf(promptstr, "Recover file \'%s\'?", (cfile == NULL) ? "(unknown)" : cfile); popupprompt(button, promptstr, NULL, loadmodes[idx].func, savebutton, NULL); if (cfile) free(cfile); } else { promptstr = (char *)malloc(18 + strlen(loadmodes[idx].prompt)); sprintf(promptstr, "Select file to %s:", loadmodes[idx].prompt); popupprompt(button, promptstr, "\0", loadmodes[idx].func, savebutton, loadmodes[idx].filext); } free(promptstr); } /*--------------------------------------------------------------*/ /* Tilde ('~') expansion in file name (stored in _STR) */ /*--------------------------------------------------------------*/ Boolean xc_tilde_expand(char *filename) { #ifndef _MSC_VER struct passwd *passwd; char *username = NULL, *expanded, *sptr; if (*filename == '~') { sptr = filename + 1; if (*sptr == '/' || *sptr == ' ' || *sptr == '\0') username = getenv("HOME"); else { for (; *sptr != '/' && *sptr != '\0'; sptr++); if (*sptr == '\0') *(sptr + 1) = '\0'; *sptr = '\0'; passwd = getpwnam(filename + 1); if (passwd != NULL) username = passwd->pw_dir; *sptr = '/'; } if (username != NULL) { expanded = (char *)malloc(strlen(username) + strlen(filename)); strcpy(expanded, username); strcat(expanded, sptr); strcpy(filename, expanded); free(expanded); } return True; } return False; #else return False; #endif } /*---------------------------------------------------------*/ Boolean nextfilename() /* extract next filename from _STR2 into _STR */ { char *cptr, *slptr; sprintf(_STR, "%.149s", _STR2); if ((cptr = strrchr(_STR2, ',')) != NULL) { slptr = strrchr(_STR, '/'); if (slptr == NULL || ((slptr - _STR) > (cptr - _STR2))) slptr = _STR - 1; sprintf(slptr + 1, "%s", cptr + 1); *cptr = '\0'; return True; } else return False; } /*---------------------------------------------------------*/ void loadfontlib() { loadlibrary(FONTLIB); } /*------------------------------------------------------*/ /* Handle library loading and refresh current page if */ /* it is a library page that just changed. */ /*------------------------------------------------------*/ void loadglib(Boolean lflag, short ilib, short tlib) { while (nextfilename()) { if (lflag) lflag = False; else ilib = createlibrary(FALSE); loadlibrary(ilib); /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */ } if (lflag) lflag = False; else ilib = createlibrary(FALSE); loadlibrary(ilib); /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */ } /*------------------------------------------------------*/ /* Load new library: Create new library page and load */ /* to it. */ /*------------------------------------------------------*/ void loadulib() { loadglib(False, (short)0, (short)is_library(topobject) + LIBRARY); } /*-----------------------------------------------------------*/ /* Add to library: If on library page, add to that library. */ /* Otherwise, create a new library page and load to it. */ /*-----------------------------------------------------------*/ void loadblib() { short ilib, tlib; Boolean lflag = True; /* Flag whether current page is a library page or not */ if ((tlib = is_library(topobject)) < 0) { ilib = LIBRARY; lflag = False; } else ilib = tlib + LIBRARY; loadglib(lflag, ilib, tlib + LIBRARY); } /*---------------------------------------------------------*/ void getlib(xcWidget button, caddr_t clientdata, caddr_t nulldata) { buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave)); getgeneric(savebutton, button, getlib, NULL); popupprompt(button, "Enter library to load:", "\0", loadblib, savebutton, "lps"); } /*---------------------------------------------------------*/ void getuserlib(xcWidget button, caddr_t clientdata, caddr_t nulldata) { buttonsave *savebutton = (buttonsave *)malloc(sizeof(buttonsave)); getgeneric(savebutton, button, getuserlib, NULL); popupprompt(button, "Enter library to load:", "\0", loadulib, savebutton, "lps"); } /*------------------------------------------------------*/ /* Add a new name to the list of aliases for an object */ /*------------------------------------------------------*/ Boolean addalias(objectptr thisobj, char *newname) { aliasptr aref; slistptr sref; Boolean retval = False; char *origname = thisobj->name; for (aref = aliastop; aref != NULL; aref = aref->next) if (aref->baseobj == thisobj) break; /* An equivalence, not an alias */ if (!strcmp(origname, newname)) return True; if (aref == NULL) { /* entry does not exist; add new baseobj */ aref = (aliasptr)malloc(sizeof(alias)); aref->baseobj = thisobj; aref->aliases = NULL; aref->next = aliastop; aliastop = aref; } for (sref = aref->aliases; sref != NULL; sref = sref->next) if (!strcmp(sref->alias, newname)) break; if (sref == NULL) { /* needs new entry */ sref = (slistptr)malloc(sizeof(stringlist)); sref->alias = strdup(newname); sref->next = aref->aliases; aref->aliases = sref; return False; } else return True; /* alias already exists! */ } /*------------------------------------------------------*/ /* Remove all object name aliases */ /*------------------------------------------------------*/ void cleanupaliases(short mode) { aliasptr aref; slistptr sref; objectptr baseobj; char *basename, *sptr; int i, j; if (aliastop == NULL) return; for (aref = aliastop; aref != NULL; aref = aref->next) { baseobj = aref->baseobj; for (sref = aref->aliases; sref != NULL; sref = sref->next) free(sref->alias); } for (; (aref = aliastop->next); aliastop = aref) free(aliastop); free(aliastop); aliastop = NULL; /* Get rid of propagating underscores in names */ for (i = 0; i < ((mode == FONTLIB) ? 1 : xobjs.numlibs); i++) { for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number : xobjs.userlibs[i].number); j++) { baseobj = (mode == FONTLIB) ? *(xobjs.fontlib.library + j) : *(xobjs.userlibs[i].library + j); sptr = baseobj->name; while (*sptr == '_') sptr++; /* need memmove to avoid overwriting? */ memmove((void *)baseobj->name, (const void *)sptr, strlen(sptr) + 1); checkname(baseobj); } } } /*------------------------------------------------------*/ /* Open a library file by name and return a pointer to */ /* the file structure, or NULL if an error occurred. */ /*------------------------------------------------------*/ FILE *libopen(char *libname, short mode, char name_return[]) { FILE *ps; char inname[150], expname[150]; sscanf(libname, "%149s", expname); xc_tilde_expand(expname); strcpy(inname, expname); ps = fopen(inname, "r"); if (ps == NULL) { char *inptr; /* try adding a .lps */ if ((inptr = strrchr(inname, '/')) == NULL) inptr = inname; if (strchr(inptr, '.') == NULL) { sprintf(inname, "%s.lps", expname); ps = fopen(inname, "r"); } if (ps == NULL) { /* if not found in cwd, look for environment variable */ /* "XCIRCUIT_LIB_DIR" defined */ /* (Thanks to Ali Moini, U. Adelaide, S. Australia) */ char *tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR"); if (tmp_s != NULL) { sprintf(inname, "%s/%s", tmp_s, expname); ps = fopen(inname, "r"); if (ps == NULL) { sprintf(inname, "%s/%s.lps", tmp_s, expname); ps = fopen(inname, "r"); } if (ps == NULL && mode == FONTLIB) { /* Fprintf(stdout, "looking in %s/fonts\n", tmp_s); */ sprintf(inname, "%s/fonts/%s", tmp_s, expname); ps = fopen(inname, "r"); if (ps == NULL) { sprintf(inname, "%s/fonts/%s.lps", tmp_s, expname); ps = fopen(inname, "r"); } if (ps == NULL) Fprintf(stdout, "%s not found, still trying. . .\n", inname); } } /* last resort: hard-coded directory BUILTINS_DIR */ if (ps == NULL) { sprintf(inname, "%s/%s", BUILTINS_DIR, expname); ps = fopen(inname, "r"); if (ps == NULL) { sprintf(inname, "%s/%s.lps", BUILTINS_DIR, expname); ps = fopen(inname, "r"); } if (ps == NULL && mode == FONTLIB) { /* Fprintf(stdout, "looking in %s/fonts\n", BUILTINS_DIR); */ sprintf(inname, "%s/fonts/%s", BUILTINS_DIR, expname); ps = fopen(inname, "r"); if (ps == NULL) { sprintf(inname, "%s/fonts/%s.lps", BUILTINS_DIR, expname); ps = fopen(inname, "r"); } if (ps == NULL) Fprintf(stdout, "%s not found.\n", inname); } if (ps == NULL) { Wprintf("No library file found."); } } } } if (name_return) strcpy(name_return, inname); return ps; } /*--------------------------------------------------------------*/ /* Add a record to the instlist list of the library indexed by */ /* mode, create a new instance, and add it to the record. */ /* */ /* objname is the name of the library object to be instanced, */ /* and buffer is the line containing the instance parameters */ /* of the new instance, optionally preceded by scale and */ /* rotation values. */ /* */ /*--------------------------------------------------------------*/ objinstptr new_library_instance(short mode, char *objname, char *buffer) { char *lineptr; objectptr libobj, localdata; objinstptr newobjinst; int j, libno; localdata = xobjs.libtop[mode + LIBRARY]->thisobject; for (j = 0; j < xobjs.userlibs[mode].number; j++) { libobj = *(xobjs.userlibs[mode].library + j); if (!strcmp(objname, libobj->name)) { newobjinst = addtoinstlist(mode, libobj, TRUE); lineptr = buffer; while (isspace(*lineptr)) lineptr++; if (*lineptr != '<') { /* May declare instanced scale and rotation first */ lineptr = varfscan(localdata, lineptr, &newobjinst->scale, (genericptr)newobjinst, P_SCALE); lineptr = varscan(localdata, lineptr, &newobjinst->rotation, (genericptr)newobjinst, P_ROTATION); } readparams(NULL, newobjinst, libobj, lineptr); return newobjinst; } } return NULL; /* error finding the library object */ } /*------------------------------------------------------*/ /* Import a single object from a library file. "mode" */ /* is the number of the library into which the object */ /* will be placed. This function allows new libraries */ /* to be generated by "cutting and pasting" from */ /* existing libraries. It also allows better library */ /* management when the number of objects becomes very */ /* large, such as when using "diplib" (7400 series */ /* chips, created from the PCB library). */ /*------------------------------------------------------*/ void importfromlibrary(short mode, char *libname, char *objname) { FILE *ps; char temp[150], keyword[100], *tptr; objectptr *newobject; objlistptr redef; float saveversion; Boolean dependencies = FALSE; ps = libopen(libname, mode, NULL); if (ps == NULL) return; version = 2.0; /* Assume version is 2.0 unless found in header */ for(;;) { if (fgets(temp, 149, ps) == NULL) { Wprintf("Error in library."); goto endload; } else if (temp[0] == '/') { sscanf(&temp[1], "%s", keyword); if (!strcmp(keyword, objname)) break; } else if (*temp == '%') { char *tptr = temp + 1; while (isspace(*tptr)) tptr++; if (!strncmp(tptr, "Version:", 8)) { float tmpv; tptr += 9; if (sscanf(tptr, "%f", &tmpv) > 0) version = tmpv; } else if (!strncmp(tptr, "Depend", 6)) { dependencies = TRUE; tptr += 7; sscanf(tptr, "%s", keyword); if (!strcmp(keyword, objname)) { /* Load dependencies */ while (1) { tptr += strlen(keyword) + 1; if (sscanf(tptr, "%s", keyword) != 1) break; if (keyword[0] == '\n' || keyword[0] == '\0') break; /* Recursive import */ saveversion = version; importfromlibrary(mode, libname, keyword); version = saveversion; } } } } } if ((version < 3.2) && (!dependencies)) { Fprintf(stderr, "Library does not have dependency list and cannot " "be trusted.\nLoad and rewrite library to update.\n"); goto endload; } newobject = new_library_object(mode, keyword, &redef); if (objectread(ps, *newobject, 0, 0, mode, temp, DEFAULTCOLOR) == False) { if (library_object_unique(mode, *newobject, redef)) { add_object_to_library(mode, *newobject); cleanupaliases(mode); /* pull in any instances of this object that */ /* are defined in the library */ for(;;) { if (fgets(temp, 149, ps) == NULL) { Wprintf("Error in library."); goto endload; } else if (!strncmp(temp, "% EndLib", 8)) break; else if (strstr(temp, "libinst") != NULL) { if ((tptr = strstr(temp, objname)) != NULL) { if (*(tptr - 1) == '/') { char *eptr = tptr; while (!isspace(*++eptr)); *eptr = '\0'; new_library_instance(mode - LIBRARY, tptr, temp); } } } } if (mode != FONTLIB) composelib(mode); } } endload: fclose(ps); version = PROG_VERSION; } /*------------------------------------------------------*/ /* Load a library page (given in parameter "mode") and */ /* rename the library page to match the library name as */ /* found in the file header. */ /*------------------------------------------------------*/ void loadlibrary(short mode) { FILE *ps; char temp[150], keyword[30], percentc, inname[150]; ps = libopen(_STR, mode, inname); if (ps == NULL) return; /* current version is PROG_VERSION; however, all libraries newer than */ /* version 2.0 require "Version" in the header. So unnumbered */ /* libraries may be assumed to be version 1.9 or earlier. */ version = 1.9; for(;;) { if (fgets(temp, 149, ps) == NULL) { Wprintf("Error in library."); fclose(ps); return; } sscanf(temp, "%c %29s", &percentc, keyword); /* Commands in header are PostScript comments (%) */ if (percentc == '%') { /* Rename the library page if the library name is found in the header */ /* and this library is not a font description. */ if ((mode != FONTLIB) && !strcmp(keyword, "Library")) { char *cptr, *nptr; cptr = strchr(temp, ':'); if (cptr != NULL) { /* Don't write terminating newline to the object's name string */ if ((nptr = strchr(cptr + 2, '\n')) != NULL) *nptr = '\0'; if (xobjs.userlibs[mode - LIBRARY].number == 0) { sprintf(xobjs.libtop[mode]->thisobject->name, "Library: %.79s", cptr + 2); renamelib(mode); } } } /* This comment gives the Xcircuit version number */ else if (!strcmp(keyword, "Version:")) { float tmpv; if (sscanf(temp, "%*c %*s %f", &tmpv) > 0) version = tmpv; } /* This PostScript comment marks the end of the file header */ else if (!strcmp(keyword, "XCircuitLib")) break; } } objectread(ps, topobject, 0, 0, mode, temp, DEFAULTCOLOR); cleanupaliases(mode); if (mode != FONTLIB) composelib(mode); /* Save the filename if it is the first one. */ if (mode >= LIBRARY && xobjs.userlibs[mode - LIBRARY].filename == NULL) xobjs.userlibs[mode - LIBRARY].filename = strdup(inname); /* Fprintf(stdout, "Loaded library %s\n", inname); */ sprintf(_STR, "Loaded library %s", inname); Wprintf(_STR); version = PROG_VERSION; fclose(ps); } /*---------------------------------------------------------*/ void startloadfile() { short firstpage = areastruct.page; while (nextfilename()) { loadfile(0); /* find next undefined page */ while(areastruct.page < xobjs.pages && xobjs.pagelist[areastruct.page]->pageinst != NULL) areastruct.page++; changepage(areastruct.page); } loadfile(0); /* Display the first page loaded */ newpage(firstpage); setsymschem(); } /*------------------------------------------------------*/ /* Import an xcircuit file onto the current page */ /*------------------------------------------------------*/ void importfile() { while (nextfilename()) loadfile(1); loadfile(1); } /*--------------------------------------------------------------*/ /* Skip forward in the input file to the next comment line */ /*--------------------------------------------------------------*/ void skiptocomment(char *temp, int length, FILE *ps) { int pch; do { pch = getc(ps); } while (pch == '\n'); ungetc(pch, ps); if (pch == '%') fgets(temp, length, ps); } /*--------------------------------------------------------------*/ /* ASG file import functions: */ /* This function loads a SPICE deck (hspice format) */ /*--------------------------------------------------------------*/ #ifdef ASG void importspice() { char inname[250]; FILE *spcfile; if (eventmode == CATALOG_MODE) { Wprintf("Cannot import a netlist while in the library window."); return; } if (!nextfilename()) { xc_tilde_expand(_STR); sscanf(_STR, "%149s", inname); spcfile = fopen(inname, "r"); ReadSpice(spcfile); Route(&areastruct, FALSE); fclose(spcfile); } else { Wprintf("Error: No spice file to read."); return; } } #endif /*--------------------------------------------------------------*/ /* Load an xcircuit file into xcircuit */ /* */ /* mode = 0 is a "load" function. Behavior: load on */ /* current page if empty. Otherwise, load on first empty */ /* page found. If no empty pages exist, create a new page */ /* and load there. */ /* mode = 1 is an "import" function. Behavior: add */ /* contents of file to the current page. */ /*--------------------------------------------------------------*/ typedef struct _connects *connectptr; typedef struct _connects { short page; char *master; connectptr next; } connects; void loadfile(short mode) { FILE *ps; char inname[250], temp[150], keyword[30], percentc, *pdchar; char teststr[50], teststr2[20], pagestr[100]; short offx, offy, multipage, page, temppmode = 0; float tmpfl; XPoint pagesize; connects *connlist = NULL; /* First, if we're in catalog mode, return with error */ if (eventmode == CATALOG_MODE) { Wprintf("Cannot load file from library window"); return; } /* Do tilde expansion on filename */ xc_tilde_expand(_STR); #ifdef LGF /* quick check for .lgf or .lfo file */ if ((pdchar = strchr(_STR, '.')) != NULL) if (!strcmp(pdchar + 1, "lgf") || !strcmp(pdchar + 1, "lfo")) { loadlgf(mode); return; } #endif #ifdef XC_WIN32 strncpy(inname, _STR, 149); #else sscanf(_STR, "%149s", inname); #endif ps = fopen(inname, "r"); if (ps == NULL) { sprintf(inname, "%s.ps", _STR); ps = fopen(inname, "r"); if (ps == NULL) { /* Could possibly be a library file? */ if ((pdchar = strchr(_STR, '.')) == NULL) { sprintf(inname, "%s.lps", _STR); if ((ps = fopen(inname, "r")) != NULL) { fclose(ps); loadlibrary(USERLIB); return; } #ifdef LGF sprintf(inname, "%s.lgf", _STR); if ((ps = fopen(inname, "r")) != NULL) { fclose(ps); loadlgf(mode); return; } sprintf(inname, "%s.lfo", _STR); if ((ps = fopen(inname, "r")) != NULL) { fclose(ps); loadlgf(mode); return; } #endif } /* What to do if no file was found: */ /* If extension was .lgf or .lfo, just flag an error. */ /* Otherwise, if we're on an empty page, go ahead and rename it. */ #ifdef LGF else { if (!strcmp(pdchar + 1, "lgf") || !strcmp(pdchar + 1, "lfo")) { sprintf(temp, "Error: Can't find lgf-format file %s", _STR); Wprintf(temp); return; } } #endif if (topobject->parts == 0 && (mode == 0)) { /* Check for file extension, and remove if "ps". */ if ((pdchar = strchr(_STR, '.')) != NULL) if (!strcmp(pdchar + 1, "ps")) *pdchar = '\0'; free(xobjs.pagelist[areastruct.page]->filename); xobjs.pagelist[areastruct.page]->filename = strdup(_STR); /* If the name has a path component, use only the root */ /* for the object name, but the full path for the filename. */ if ((pdchar = strrchr(_STR, '/')) != NULL) sprintf(topobject->name, "%s", pdchar + 1); else sprintf(topobject->name, "%s", _STR); renamepage(areastruct.page); printname(topobject); Wprintf("Starting new drawing"); } else { sprintf(temp, "Can't find file %s, won't overwrite current page", _STR); Wprintf(temp); } return; } } version = 1.0; multipage = 1; pagesize.x = 612; pagesize.y = 792; for(;;) { if (fgets(temp, 149, ps) == NULL) { Wprintf("Error: EOF in or before prolog."); return; } sscanf(temp, "%c%29s", &percentc, keyword); for (pdchar = keyword; isspace(*pdchar); pdchar++); if (percentc == '%') { if (!strcmp(pdchar, "XCircuit")) break; if (!strcmp(pdchar, "XCircuitLib")) { /* version control in libraries is post version 1.9 */ if (version == 1.0) version = 1.9; break; } if (!strcmp(pdchar, "%Page:")) break; if (strstr(pdchar, "PS-Adobe") != NULL) temppmode = (strstr(temp, "EPSF") != NULL) ? 0 : 1; else if (!strcmp(pdchar, "Version:")) sscanf(temp, "%*c%*s %f", &version); else if (!strcmp(pdchar, "%Pages:")) { pdchar = advancetoken(temp); multipage = atoi(pdchar); } /* Crash files get renamed back to their original filename */ else if (!strcmp(pdchar, "%Title:")) { if (xobjs.tempfile != NULL) if (!strcmp(inname, xobjs.tempfile)) sscanf(temp, "%*c%*s %s", inname); } else if ((temppmode == 1) && !strcmp(pdchar, "%BoundingBox:")) { short botx, boty; sscanf(temp, "%*s %hd %hd %hd %hd", &botx, &boty, &(pagesize.x), &(pagesize.y)); pagesize.x += botx; pagesize.y += boty; } } #ifdef LGF else if (percentc == '-' && !strcmp(keyword, "5")) { fclose(ps); loadlgf(mode); return; } #endif } /* Look for old-style files (no %%Page; maximum one page in file) */ if (!strcmp(pdchar, "XCircuit")) skiptocomment(temp, 149, ps); for (page = 0; page < multipage; page++) { sprintf(pagestr, "%d", page + 1); /* read out-of-page library definitions */ if (strstr(temp, "%%Page:") == NULL && strstr(temp, "offsets") == NULL) objectread(ps, topobject, 0, 0, USERLIB, temp, DEFAULTCOLOR); if (strstr(temp, "%%Page:") != NULL) { sscanf(temp + 8, "%99s", pagestr); /* Read the next line so any keywords in the Page name don't */ /* confuse the parser. */ if (fgets(temp, 149, ps) == NULL) { Wprintf("Error: bad page header."); return; } } /* go to new page if necessary */ if (page > 0) { /* find next undefined page */ while(areastruct.page < xobjs.pages && xobjs.pagelist[areastruct.page]->pageinst != NULL) areastruct.page++; changepage(areastruct.page); } /* If this file was a library file then there is no page to load */ if (strstr(temp, "EndLib") != NULL) { composelib(USERLIB); Wprintf("Loaded library."); return; } /* good so far; let's clear out the old data structure */ if (mode == 0) { reset(topobject, NORMAL); pagereset(areastruct.page); xobjs.pagelist[areastruct.page]->pmode = temppmode; if (temppmode == 1) { xobjs.pagelist[areastruct.page]->pagesize.x = pagesize.x; xobjs.pagelist[areastruct.page]->pagesize.y = pagesize.y; } } else { invalidate_netlist(topobject); /* ensure that the netlist for topobject is destroyed */ freenetlist(topobject); } /* clear the undo record */ flush_undo_stack(); /* read to the "scale" line, picking up inch/cm type, drawing */ /* scale, and grid/snapspace along the way */ offx = offy = 0; for(;;) { if (strstr(temp, "offsets") != NULL) { /* Prior to version 3.1.28 only. . . */ sscanf(temp, "%c %hd %hd %*s", &percentc, &offx, &offy); if(percentc != '%') { Wprintf("Something wrong in offsets line."); offx = offy = 0; } } if ((temppmode == 1) && strstr(temp, "%%PageBoundingBox:") != NULL) { /* Recast the individual page size if specified per page */ sscanf(temp, "%*s %*d %*d %hd %hd", &xobjs.pagelist[areastruct.page]->pagesize.x, &xobjs.pagelist[areastruct.page]->pagesize.y); } else if (strstr(temp, "drawingscale") != NULL) sscanf(temp, "%*c %hd:%hd %*s", &xobjs.pagelist[areastruct.page]->drawingscale.x, &xobjs.pagelist[areastruct.page]->drawingscale.y); else if (strstr(temp, "hidden") != NULL) topobject->hidden = True; else if (strstr(temp, "is_symbol") != NULL) { sscanf(temp, "%*c %49s", teststr); checkschem(topobject, teststr); } else if (strstr(temp, "is_primary") != NULL) { objectptr master; connects *newconn; /* Save information about master schematic and link at end of load */ sscanf(temp, "%*c %49s", teststr); newconn = (connects *)malloc(sizeof(connects)); newconn->next = connlist; connlist = newconn; newconn->page = areastruct.page; newconn->master = strdup(teststr); } else if (strstr(temp, "gridspace")) sscanf(temp, "%*c %f %f %*s", &xobjs.pagelist[areastruct.page]->gridspace, &xobjs.pagelist[areastruct.page]->snapspace); else if (strstr(temp, "scale") != NULL || strstr(temp, "rotate") != NULL) { /* rotation (landscape mode) is optional; parse accordingly */ sscanf(temp, "%f %49s", &tmpfl, teststr); if (strstr(teststr, "scale") != NULL) { setgridtype(teststr); xobjs.pagelist[areastruct.page]->outscale = tmpfl; } else if (!strcmp(teststr, "rotate")) { xobjs.pagelist[areastruct.page]->orient = (short)tmpfl; fgets(temp, 149, ps); sscanf(temp, "%f %19s", &tmpfl, teststr2); if (strstr(teststr2, "scale") != NULL) { setgridtype(teststr2); xobjs.pagelist[areastruct.page]->outscale = tmpfl; } else { sscanf(temp, "%*f %*f %19s", teststr2); if (!strcmp(teststr2, "scale")) xobjs.pagelist[areastruct.page]->outscale = tmpfl / getpsscale(1.0, areastruct.page); else { Wprintf("Error in scale/rotate constructs."); return; } } } else { /* old style scale? */ sscanf(temp, "%*f %*s %19s", teststr2); if ((teststr2 != NULL) && (!strcmp(teststr2, "scale"))) xobjs.pagelist[areastruct.page]->outscale = tmpfl / getpsscale(1.0, areastruct.page); else { Wprintf("Error in scale/rotate constructs."); return; } } } else if (strstr(temp, "setlinewidth") != NULL) { sscanf(temp, "%f %*s", &xobjs.pagelist[areastruct.page]->wirewidth); xobjs.pagelist[areastruct.page]->wirewidth /= 1.3; break; } else if (strstr(temp, "insertion") != NULL) { /* read in an included background image */ readbackground(ps); } else if (strstr(temp, "<<") != NULL) { char *buffer = temp, *endptr; /* Make sure we have the whole dictionary before calling */ while (strstr(buffer, ">>") == NULL) { if (buffer == temp) { buffer = (char *)malloc(strlen(buffer) + 150); strcpy(buffer, temp); } else buffer = (char *)realloc(buffer, strlen(buffer) + 150); endptr = ridnewline(buffer); *endptr++ = ' '; fgets(endptr, 149, ps); } /* read top-level parameter dictionary */ readparams(NULL, NULL, topobject, buffer); if (buffer != temp) free(buffer); } if (fgets(temp, 149, ps) == NULL) { Wprintf("Error: Problems encountered in page header."); return; } } objectread(ps, topobject, offx, offy, LIBRARY, temp, DEFAULTCOLOR); /* skip to next page boundary or file trailer */ if (strstr(temp, "showpage") != NULL && multipage != 1) { skiptocomment(temp, 149, ps); /* check for new filename if this is a crash recovery file */ if (strstr(temp, "is_filename") != NULL) { sscanf(temp + 2, "%s", inname); fgets(temp, 149, ps); skiptocomment(temp, 149, ps); } } /* Finally: set the filename and pagename for this page */ if (mode == 0) { char tpstr[6], *rootptr; /* Set filename and page title. */ if (xobjs.pagelist[areastruct.page]->filename != NULL) free(xobjs.pagelist[areastruct.page]->filename); xobjs.pagelist[areastruct.page]->filename = strdup(inname); /* Delete the filename suffix if it is ".ps" */ if ((pdchar = strchr(xobjs.pagelist[areastruct.page]->filename, '.')) != NULL) if (!strcmp(pdchar + 1, "ps")) *pdchar = '\0'; /* Get the root name (after all path components) */ rootptr = strrchr(xobjs.pagelist[areastruct.page]->filename, '/'); if (rootptr == NULL) rootptr = xobjs.pagelist[areastruct.page]->filename; else rootptr++; /* If we didn't read in a page name from the %%Page: header line, then */ /* set the page name to the root name of the file. */ sprintf(tpstr, "%d", page + 1); if (!strcmp(pagestr, tpstr)) sprintf(topobject->name, "%.79s", rootptr); else sprintf(topobject->name, "%.79s", pagestr); renamepage(areastruct.page); } /* set object position to fit to window separately for each page */ calcbbox(areastruct.topinstance); centerview(areastruct.topinstance); } /* Crash file recovery: read any out-of-page library definitions tacked */ /* onto the end of the file into the user library. */ if (strncmp(temp, "%%Trailer", 9)) objectread(ps, topobject, 0, 0, USERLIB, temp, DEFAULTCOLOR); cleanupaliases(USERLIB); /* Connect master->slave schematics */ while (connlist != NULL) { connects *thisconn = connlist; objectptr master = NameToPageObject(thisconn->master, NULL, NULL); if (master) { xobjs.pagelist[thisconn->page]->pageinst->thisobject->symschem = master; xobjs.pagelist[thisconn->page]->pageinst->thisobject->schemtype = SECONDARY; } else { Fprintf(stderr, "Error: Cannot find primary schematic for %s\n", xobjs.pagelist[thisconn->page]->pageinst->thisobject->name); } connlist = thisconn->next; free(thisconn->master); free(thisconn); } sprintf(_STR, "Loaded file: %s (%d page%s)", inname, multipage, (multipage > 1 ? "s" : "")); Wprintf(_STR); composelib(USERLIB); composelib(PAGELIB); if (version > PROG_VERSION + VEPS) { sprintf(_STR, "WARNING: file %s is version %2.1f vs. executable version %2.1f", inname, version, PROG_VERSION); Wprintf(_STR); } version = PROG_VERSION; fclose(ps); } /*------------------------------------------------------*/ /* Object name comparison: True if names are equal, */ /* not counting leading underscores. */ /*------------------------------------------------------*/ int objnamecmp(char *name1, char *name2) { char *n1ptr = name1; char *n2ptr = name2; while (*n1ptr == '_') n1ptr++; while (*n2ptr == '_') n2ptr++; return (strcmp(n1ptr, n2ptr)); } /*------------------------------------------------------*/ /* Standard delimiter matching character. */ /*------------------------------------------------------*/ char standard_delimiter_end(char source) { char target; switch(source) { case '(': target = ')'; break; case '[': target = ']'; break; case '{': target = '}'; break; case '<': target = '>'; break; default: target = source; } return target; } /*------------------------------------------------------*/ /* Find matching parenthesis, bracket, brace, or tag */ /* Don't count when backslash character '\' is in front */ /*------------------------------------------------------*/ u_char *find_delimiter(u_char *fstring) { int count = 1; u_char *search = fstring; u_char source = *fstring; u_char target; target = (u_char)standard_delimiter_end((char)source); while (*++search != '\0') { if (*search == source && *(search - 1) != '\\') count++; else if (*search == target && *(search - 1) != '\\') count--; if (count == 0) break; } return search; } /*----------------------------------------------------------------------*/ /* Remove unnecessary font change information from a label */ /*----------------------------------------------------------------------*/ void cleanuplabel(stringpart **strhead) { stringpart *curpart = *strhead; int oldfont, curfont; oldfont = curfont = -1; while (curpart != NULL) { switch (curpart->type) { case FONT_NAME: if (curfont == curpart->data.font) { /* Font change is redundant: remove it */ curpart = deletestring(curpart, strhead, NULL); } else { curfont = curpart->data.font; } break; case FONT_SCALE: /* Old style font scale is always written absolute, not relative. */ /* Changes in scale were not allowed, so just get rid of them. */ if (version < 2.25) curpart = deletestring(curpart, strhead, areastruct.topinstance); break; /* A font change may occur inside a parameter, so any font */ /* declaration after a parameter must be considered to be */ /* intentional. */ case PARAM_END: curfont = oldfont = -1; break; case NORMALSCRIPT: case RETURN: if (oldfont != -1) { curfont = oldfont; oldfont = -1; } break; case SUBSCRIPT: case SUPERSCRIPT: if (oldfont == -1) oldfont = curfont; break; } if (curpart != NULL) curpart = curpart->nextpart; } } /*----------------------------------------------------------------------*/ /* Read label segments */ /*----------------------------------------------------------------------*/ void readlabel(objectptr localdata, char *lineptr, stringpart **strhead) { Boolean fline = False; char *sptr; short j; char *endptr, *segptr = lineptr; char key[100]; stringpart *newpart; oparamptr ops; while (*segptr != '\0') { /* Look through all segments */ while (isspace(*segptr) && (*segptr != '\0')) segptr++; if (*segptr == '(' || *segptr == '{') { endptr = find_delimiter(segptr); *endptr++ = '\0'; /* null string (e.g., null parameter substitution) */ if ((*segptr == '(') && (*(segptr + 1) == '\0')) { segptr = endptr; continue; } } else if (*segptr == '\0' || *segptr == '}') break; makesegment(strhead, *strhead); newpart = *strhead; /* Embedded command is in braces: {} */ if (*segptr == '{') { /* Find the command for this PostScript procedure */ char *cmdptr = endptr - 2; while (isspace(*cmdptr)) cmdptr--; while (!isspace(*cmdptr) && (cmdptr > segptr)) cmdptr--; cmdptr++; segptr++; if (!strncmp(cmdptr, "Ss", 2)) newpart->type = SUPERSCRIPT; else if (!strncmp(cmdptr, "ss", 2)) newpart->type = SUBSCRIPT; else if (!strncmp(cmdptr, "ns", 2)) newpart->type = NORMALSCRIPT; else if (!strncmp(cmdptr, "hS", 2)) newpart->type = HALFSPACE; else if (!strncmp(cmdptr, "qS", 2)) newpart->type = QTRSPACE; else if (!strncmp(cmdptr, "CR", 2)) newpart->type = RETURN; else if (!strcmp(cmdptr, "Ts")) /* "Tab set" command */ newpart->type = TABSTOP; else if (!strcmp(cmdptr, "Tf")) /* "Tab forward" command */ newpart->type = TABFORWARD; else if (!strcmp(cmdptr, "Tb")) /* "Tab backward" command */ newpart->type = TABBACKWARD; else if (!strncmp(cmdptr, "ol", 2)) { newpart->type = OVERLINE; fline = True; } else if (!strncmp(cmdptr, "ul", 2)) { newpart->type = UNDERLINE; fline = True; } else if (!strncmp(cmdptr, "sce", 3)) { /* revert to default color */ newpart->type = FONT_COLOR; newpart->data.color = DEFAULTCOLOR; } else if (cmdptr == segptr) { /* cancel over- or underline */ newpart->type = NOLINE; fline = False; } /* To-do: replace old-style backspace with tab stop */ else if (!strcmp(cmdptr, "bs")) { Wprintf("Warning: Obsolete backspace command ommitted in text"); } else if (!strcmp(cmdptr, "Kn")) { /* "Kern" command */ int kx, ky; sscanf(segptr, "%d %d", &kx, &ky); newpart->type = KERN; newpart->data.kern[0] = kx; newpart->data.kern[1] = ky; } else if (!strcmp(cmdptr, "scb")) { /* change color command */ float cr, cg, cb; int cval, cindex; sscanf(segptr, "%f %f %f", &cr, &cg, &cb); newpart->type = FONT_COLOR; cval = rgb_alloccolor((int)(cr * 65535), (int)(cg * 65535), (int)(cb * 65535)); for (cindex = 0; cindex < number_colors; cindex++) if (colorlist[cindex].color.pixel == cval) break; if (cindex == number_colors) { Wprintf("Error: Cannot allocate another color"); cindex = DEFAULTCOLOR; } newpart->data.color = cindex; } else if (!strcmp(cmdptr, "cf")) { /* change font or scale command */ char *nextptr, *newptr = segptr; /* Set newptr to the fontname and nextptr to the next token. */ while (*newptr != '/' && *newptr != '\0') newptr++; if (*newptr++ == '\0') { Wprintf("Error: Bad change-font command"); return; } for (nextptr = newptr; !isspace(*nextptr); nextptr++); *(nextptr++) = '\0'; while (isspace(*nextptr)) nextptr++; for (j = 0; j < fontcount; j++) if (!strcmp(newptr, fonts[j].psname)) break; if (j == fontcount) /* this is a non-loaded font */ loadfontfile(newptr); if (isdigit(*nextptr)) { /* second form of "cf" command---includes scale */ float locscale; sscanf(nextptr, "%f", &locscale); if (locscale != 1.0) { newpart->type = FONT_SCALE; newpart->data.scale = locscale; makesegment(strhead, *strhead); newpart = *strhead; } } newpart->type = FONT_NAME; newpart->data.font = j; } else { /* This exec isn't a known label function */ Wprintf("Error: unknown substring function"); newpart->type = NOLINE; /* This is fairly benign */ } } /* Text substring is in parentheses: () */ else if (*segptr == '(') { u_char *tmpptr; if (fline == True) { newpart->type = NOLINE; makesegment(strhead, *strhead); newpart = *strhead; fline = False; } newpart->type = TEXT_STRING; newpart->data.string = (u_char *)malloc(1 + strlen(++segptr)); /* Copy string, translating octal codes into 8-bit characters */ tmpptr = newpart->data.string; for (sptr = segptr; *sptr != '\0'; sptr++) { if (*sptr == '\\') { sptr++; if (*sptr >= '0' && *sptr < '8') { int tmpdig; sscanf(sptr, "%3o", &tmpdig); *tmpptr++ = (u_char)tmpdig; sptr += 2; } else *tmpptr++ = (u_char) *sptr; } else *tmpptr++ = (u_char) *sptr; } *tmpptr = '\0'; } /* Parameterized substrings are denoted by parameter key. */ /* The parameter (default and/or substitution value) is */ /* assumed to exist. */ else { if (sscanf(segptr, "%99s", key) == 1) { newpart->type = PARAM_START; newpart->data.string = (char *)malloc(1 + strlen(key)); strcpy(newpart->data.string, key); /* check for compatibility between the parameter value and */ /* the number of parameters and parameter type. */ ops = match_param(localdata, key); if (ops == NULL) { Fprintf(stderr, "readlabel() error: No such parameter %s!\n", key); deletestring(newpart, strhead, areastruct.topinstance); } /* Fprintf(stdout, "Parameter %s called from object %s\n", */ /* key, localdata->name); */ } endptr = segptr + 1; while (!isspace(*endptr) && (*endptr != '\0')) endptr++; } segptr = endptr; } } /*--------------------------------------*/ /* skip over whitespace in string input */ /*--------------------------------------*/ char *skipwhitespace(char *lineptr) { char *locptr = lineptr; while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++; return locptr; } /*-------------------------------------------*/ /* advance to the next token in string input */ /*-------------------------------------------*/ char *advancetoken(char *lineptr) { char *locptr = lineptr; while (!isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++; while (isspace(*locptr) && (*locptr != '\n') && (*locptr != '\0')) locptr++; return locptr; } /*------------------------------------------------------*/ /* Read a parameter list for an object instance call. */ /* This uses the key-value dictionary method but also */ /* allows the "old style" in which each parameter */ /* was automatically assigned the key v1, v2, etc. */ /*------------------------------------------------------*/ void readparams(objectptr localdata, objinstptr newinst, objectptr libobj, char *buffer) { oparamptr newops, objops, fops; char *arrayptr, *endptr, *arraynext; int paramno = 0; char paramkey[100]; if ((arrayptr = strstr(buffer, "<<")) == NULL) if ((arrayptr = strchr(buffer, '[')) == NULL) return; endptr = find_delimiter(arrayptr); if (*arrayptr == '<') { arrayptr++; /* move to second '<' in "<<" */ endptr--; /* back up to first '>' in ">>" */ } /* move to next non-space token after opening bracket */ arrayptr++; while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++; while ((*arrayptr != '\0') && (arrayptr < endptr)) { newops = (oparamptr)malloc(sizeof(oparam)); /* Arrays contain values only. Dictionaries contain key:value pairs */ if (*endptr == '>') { /* dictionary type */ if (*arrayptr != '/') { Fprintf(stdout, "Error: Dictionary key is a literal, not a name\n"); } else arrayptr++; /* Skip PostScript name delimiter */ sscanf(arrayptr, "%99s", paramkey); newops->key = (char *)malloc(1 + strlen(paramkey)); strcpy(newops->key, paramkey); arrayptr = advancetoken(arrayptr); } else { /* array type; keys are "v1", "v2", etc. */ paramno++; newops->key = (char *)malloc(6); sprintf(newops->key, "v%d", paramno); } /* Add to instance's parameter list */ /* If newinst is null, then the parameters are added to libobj */ newops->next = NULL; if (newinst) { if (newinst->params == NULL) newinst->params = newops; else { for (fops = newinst->params; fops->next != NULL; fops = fops->next); fops->next = newops; } } else { if (libobj->params == NULL) libobj->params = newops; else { for (fops = libobj->params; fops->next != NULL; fops = fops->next); fops->next = newops; } } /* Find matching parameter in object definition */ if (newinst) { objops = match_param(libobj, newops->key); if (objops == NULL) { Fprintf(stdout, "Error: parameter %s does not exist in object %s!\n", newops->key, libobj->name); return; } } /* Fill in "which" entry from the object default */ newops->which = (newinst) ? objops->which : 0; /* Check next token. If not either end-of-dictionary or */ /* the next parameter key, then value is an expression. */ /* Expressions are written as two strings, the first the */ /* result of evaluting the expression, and the second the */ /* expression itself, followed by "pop" to prevent the */ /* PostScript interpreter from trying to evaluate the */ /* expression (which is not in PostScript). */ if (*arrayptr == '(' || *arrayptr == '{') arraynext = find_delimiter(arrayptr); else arraynext = arrayptr; arraynext = advancetoken(arraynext); if ((*endptr == '>') && (arraynext < endptr) && (*arraynext != '/')) { char *substrend, *arraysave; if (*arraynext == '(' || *arraynext == '{') { substrend = find_delimiter(arraynext); arraysave = arraynext + 1; arraynext = advancetoken(substrend); newops->type = (u_char)XC_EXPR; newops->which = P_EXPRESSION; } if (strncmp(arraynext, "pop ", 4)) { Wprintf("Error: bad expression parameter!\n"); #ifdef TCL_WRAPPER newops->parameter.expr = strdup("expr 0"); #else newops->parameter.expr = strdup("0"); #endif arrayptr = advancetoken(arrayptr); } else { *substrend = '\0'; newops->parameter.expr = strdup(arraysave); arrayptr = advancetoken(arraynext); } } else if (*arrayptr == '(' || *arrayptr == '{') { float r, g, b; char *substrend, csave; stringpart *endpart; /* type XC_STRING */ substrend = find_delimiter(arrayptr); csave = *(++substrend); *substrend = '\0'; if (*arrayptr == '{') arrayptr++; /* A numerical value immediately following the opening */ /* brace indicates a color parameter. */ if (sscanf(arrayptr, "%f %f %f", &r, &g, &b) == 3) { newops->type = (u_char)XC_INT; newops->which = P_COLOR; newops->parameter.ivalue = rgb_alloccolor((int)(r * 65535), (int)(g * 65535), (int)(b * 65535)); addnewcolorentry(newops->parameter.ivalue); } else { newops->type = (u_char)XC_STRING; newops->which = P_SUBSTRING; newops->parameter.string = NULL; readlabel(libobj, arrayptr, &(newops->parameter.string)); *substrend = csave; /* Append a PARAM_END to the parameter string */ endpart = makesegment(&(newops->parameter.string), NULL); endpart->type = PARAM_END; endpart->data.string = (u_char *)NULL; } arrayptr = substrend; while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++; } else { char *token; int scanned = 0; /* type XC_FLOAT or XC_INT, or an indirect reference */ newops->type = (newinst) ? objops->type : (u_char)XC_FLOAT; if (newops->type == XC_FLOAT) { scanned = sscanf(arrayptr, "%f", &(newops->parameter.fvalue)); /* Fprintf(stdout, "Object %s called with parameter " "%s value %g\n", libobj->name, newops->key, newops->parameter.fvalue); */ } else if (newops->type == XC_INT) { scanned = sscanf(arrayptr, "%d", &(newops->parameter.ivalue)); /* Fprintf(stdout, "Object %s called with parameter " "%s value %d\n", libobj->name, newops->key, newops->parameter.ivalue); */ } else if (newops->type == XC_EXPR) { /* Instance values of parameters hold the last evaluated */ /* result and will be regenerated, so we can ignore them */ /* here. By ignoring it, we don't have to deal with issues */ /* like type promotion. */ free_instance_param(newinst, newops); scanned = 1; /* avoid treating as an indirect ref */ } else if (newops->type == XC_STRING) { /* Fill string record, so we have a valid record. This will */ /* be blown away and replaced by opsubstitute(), but it must */ /* have an initial valid entry. */ stringpart *tmpptr; newops->parameter.string = NULL; tmpptr = makesegment(&newops->parameter.string, NULL); tmpptr->type = TEXT_STRING; tmpptr = makesegment(&newops->parameter.string, NULL); tmpptr->type = PARAM_END; } else { Fprintf(stderr, "Error: unknown parameter type!\n"); } if (scanned == 0) { /* Indirect reference --- create an eparam in the instance */ sscanf(arrayptr, "%99s", paramkey); if (!newinst || !localdata) { /* Only object instances can use indirect references */ Fprintf(stderr, "Error: parameter default %s cannot " "be parsed!\n", paramkey); } else if (match_param(localdata, paramkey) == NULL) { /* Reference key must exist in the calling object */ Fprintf(stderr, "Error: parameter value %s cannot be parsed!\n", paramkey); } else { /* Create an eparam record in the instance */ eparamptr newepp = make_new_eparam(paramkey); newepp->pdata.refkey = strdup(newops->key); newepp->next = newinst->passed; newinst->passed = newepp; } } arrayptr = advancetoken(arrayptr); } } /* Calculate the unique bounding box for the instance */ if (newinst && (newinst->params != NULL)) { opsubstitute(libobj, newinst); calcbboxinst(newinst); } } /*--------------------------------------------------------------*/ /* Read a value which might be a short integer or a parameter. */ /* If the value is a parameter, check the parameter list to see */ /* if it needs to be re-typecast. Return the position to the */ /* next token in "lineptr". */ /*--------------------------------------------------------------*/ char *varpscan(objectptr localdata, char *lineptr, short *hvalue, genericptr thiselem, int pointno, int offset, u_char which) { oparamptr ops = NULL; char key[100]; char *nexttok; eparamptr newepp; if (sscanf(lineptr, "%hd", hvalue) != 1) { sscanf(lineptr, "%99s", key); ops = match_param(localdata, key); newepp = make_new_eparam(key); /* Add parameter to the linked list */ newepp->next = thiselem->passed; thiselem->passed = newepp; newepp->pdata.pointno = pointno; if (ops != NULL) { /* It cannot be known whether a parameter value is a float or int */ /* until we see how the parameter is used. So we always read the */ /* parameter default as a float, and re-typecast it if necessary. */ if (ops->type == XC_FLOAT) { ops->type = XC_INT; /* (add 0.1 to avoid roundoff error in conversion to integer) */ ops->parameter.ivalue = (int)(ops->parameter.fvalue + ((ops->parameter.fvalue < 0) ? -0.1 : 0.1)); } ops->which = which; *hvalue = (short)ops->parameter.ivalue; } else { *hvalue = 0; /* okay; will get filled in later */ Fprintf(stderr, "Error: parameter %s was used but not defined!\n", key); } } *hvalue -= (short)offset; return advancetoken(skipwhitespace(lineptr)); } /*--------------------------------------------------------------*/ /* Read a value which might be a short integer or a parameter, */ /* but which is not a point in a pointlist. */ /*--------------------------------------------------------------*/ char *varscan(objectptr localdata, char *lineptr, short *hvalue, genericptr thiselem, u_char which) { return varpscan(localdata, lineptr, hvalue, thiselem, 0, 0, which); } /*--------------------------------------------------------------*/ /* Read a value which might be a float or a parameter. */ /* Return the position to the next token in "lineptr". */ /*--------------------------------------------------------------*/ char *varfscan(objectptr localdata, char *lineptr, float *fvalue, genericptr thiselem, u_char which) { oparamptr ops = NULL; eparamptr newepp; char *nexttok; char key[100]; if (sscanf(lineptr, "%f", fvalue) != 1) { sscanf(lineptr, "%99s", key); ops = match_param(localdata, key); newepp = make_new_eparam(key); /* Add parameter to the linked list */ newepp->next = thiselem->passed; thiselem->passed = newepp; if (ops != NULL) { ops->which = which; *fvalue = ops->parameter.fvalue; } else Fprintf(stderr, "Error: no parameter defined!\n"); } /* advance to next token */ return advancetoken(skipwhitespace(lineptr)); } /*--------------------------------------------------------------*/ /* Create a new instance of an object in the library's list of */ /* instances. This instance will be used on the library page */ /* when doing "composelib()". */ /*--------------------------------------------------------------*/ objinstptr addtoinstlist(int libnum, objectptr libobj, Boolean virtual) { objinstptr newinst = (objinstptr) malloc(sizeof(objinst)); liblistptr spec = (liblistptr) malloc(sizeof(liblist)); liblistptr srch; newinst->type = OBJINST; instancedefaults(newinst, libobj, 0, 0); spec->virtual = (u_char)virtual; spec->thisinst = newinst; spec->next = NULL; /* Add to end, so that duplicate, parameterized instances */ /* always come after the original instance with the default */ /* parameters. */ if ((srch = xobjs.userlibs[libnum].instlist) == NULL) xobjs.userlibs[libnum].instlist = spec; else { while (srch->next != NULL) srch = srch->next; srch->next = spec; } /* Calculate the instance-specific bounding box */ calcbboxinst(newinst); return newinst; } /*--------------------------------------------------------------*/ /* Deal with object reads: Create a new object and prepare for */ /* reading. The library number is passed as "mode". */ /*--------------------------------------------------------------*/ objectptr *new_library_object(short mode, char *name, objlistptr *retlist) { objlistptr newdef, redef = NULL; objectptr *newobject, *libobj; objectptr *curlib = (mode == FONTLIB) ? xobjs.fontlib.library : xobjs.userlibs[mode - LIBRARY].library; short *libobjects = (mode == FONTLIB) ? &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number; int i, j; curlib = (objectptr *) realloc(curlib, (*libobjects + 1) * sizeof(objectptr)); if (mode == FONTLIB) xobjs.fontlib.library = curlib; else xobjs.userlibs[mode - LIBRARY].library = curlib; /* initial 1-pointer allocations */ newobject = curlib + (*libobjects); *newobject = (objectptr) malloc(sizeof(object)); initmem(*newobject); /* check that this object is not already in list of objects */ if (mode == FONTLIB) { for (libobj = xobjs.fontlib.library; libobj != xobjs.fontlib.library + xobjs.fontlib.number; libobj++) { /* This font character may be a redefinition of another */ if (!objnamecmp(name, (*libobj)->name)) { newdef = (objlistptr) malloc(sizeof(objlist)); newdef->libno = FONTLIB; newdef->thisobject = *libobj; newdef->next = redef; redef = newdef; } } } else { for (i = 0; i < xobjs.numlibs; i++) { for (j = 0; j < xobjs.userlibs[i].number; j++) { libobj = xobjs.userlibs[i].library + j; /* This object may be a redefinition of another object */ if (!objnamecmp(name, (*libobj)->name)) { newdef = (objlistptr) malloc(sizeof(objlist)); newdef->libno = i + LIBRARY; newdef->thisobject = *libobj; newdef->next = redef; redef = newdef; } } } } (*libobjects)++; sprintf((*newobject)->name, "%s", name); /* initmem() initialized schemtype to PRIMARY; change it. */ (*newobject)->schemtype = SYMBOL; *retlist = redef; return newobject; } /*--------------------------------------------------------------*/ /* do an exhaustive comparison between a new object and any */ /* object having the same name. If they are the same, destroy */ /* the duplicate. If different, rename the original one. */ /*--------------------------------------------------------------*/ Boolean library_object_unique(short mode, objectptr newobject, objlistptr redef) { Boolean is_unique = True; objlistptr newdef; short *libobjects = (mode == FONTLIB) ? &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number; if (redef == NULL) return is_unique; /* No name conflicts; object is okay as-is */ for (newdef = redef; newdef != NULL; newdef = newdef->next) { /* Must make sure that default parameter values are */ /* plugged into the original object! */ opsubstitute(newdef->thisobject, NULL); if (objcompare(newobject, newdef->thisobject) == True) { addalias(newdef->thisobject, newobject->name); /* If the new object has declared an association to a */ /* schematic, transfer it to the original, and make */ /* sure that the page points to the object which will */ /* be left, not the one which will be destroyed. */ if (newobject->symschem != NULL) { newdef->thisobject->symschem = newobject->symschem; newdef->thisobject->symschem->symschem = newdef->thisobject; } reset(newobject, DESTROY); (*libobjects)--; is_unique = False; break; } /* Not the same object, but has the same name. This can't */ /* happen within the same input file, so the name of the */ /* original object can safely be altered. */ else if (!strcmp(newobject->name, newdef->thisobject->name)) { /* Replacement---for project management, allow the library */ /* master version to take precedence over a local version. */ if ((xobjs.do_replace != -1) && ((xobjs.do_replace == -2) || (xobjs.do_replace == newdef->libno - LIBRARY))) { reset(newobject, DESTROY); (*libobjects)--; is_unique = False; } else checkname(newdef->thisobject); break; } } for (; (newdef = redef->next); redef = newdef) free(redef); free(redef); return is_unique; } /*--------------------------------------------------------------*/ /* Add an instance of the object to the library's instance list */ /*--------------------------------------------------------------*/ void add_object_to_library(short mode, objectptr newobject) { objinstptr libinst; if (mode == FONTLIB) return; libinst = addtoinstlist(mode - LIBRARY, newobject, FALSE); calcbboxvalues(libinst, (genericptr *)NULL); /* Center the view of the object in its instance */ centerview(libinst); } /*--------------------------------------------------------------*/ /* Continuation Line --- add memory to "buffer" as necessary. */ /* Add a space character to the current text in "buffer" and */ /* return a pointer to the new end-of-text. */ /*--------------------------------------------------------------*/ char *continueline(char **buffer) { char *lineptr; int bufsize; for (lineptr = *buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++); if (*lineptr == '\n') *lineptr++ = ' '; bufsize = (int)(lineptr - (*buffer)) + 256; *buffer = (char *)realloc((*buffer), bufsize * sizeof(char)); return ((*buffer) + (bufsize - 256)); } /*--------------------------------------------------------------*/ /* Read image data out of the Setup block of the input */ /* We assume that width and height have been parsed from the */ /* "imagedata" line and the file pointer is at the next line. */ /*--------------------------------------------------------------*/ void readimagedata(FILE *ps, int width, int height) { char temp[150], ascbuf[6]; int x, y, q, r, g, b, ilen; char *pptr; Imagedata *iptr; Boolean do_flate = FALSE, do_ascii = FALSE; u_char *filtbuf, *flatebuf; union { u_char b[4]; u_long i; } pixel; iptr = addnewimage(NULL, width, height); /* Read the image data */ fgets(temp, 149, ps); if (strstr(temp, "ASCII85Decode") != NULL) do_ascii = TRUE; #ifdef HAVE_LIBZ if (strstr(temp, "FlateDecode") != NULL) do_flate = TRUE; #else if (strstr(temp, "FlateDecode") != NULL) Fprintf(stderr, "Error: Don't know how to Flate decode!" " Get zlib and recompile xcircuit!\n"); #endif while (strstr(temp, "ReusableStreamDecode") == NULL) fgets(temp, 149, ps); /* Additional piped filter lines */ fgets(temp, 149, ps); /* Initial data line */ q = 0; pptr = temp; ilen = 3 * width * height; filtbuf = (u_char *)malloc(ilen + 4); if (!do_ascii) { /* ASCIIHexDecode algorithm */ q = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { sscanf(pptr, "%02x%02x%02x", &r, &g, &b); filtbuf[q++] = (u_char)r; filtbuf[q++] = (u_char)g; filtbuf[q++] = (u_char)b; pptr += 6; if (*pptr == '\n') { fgets(temp, 149, ps); pptr = temp; } } } } else { /* ASCII85Decode algorithm */ while (1) { ascbuf[0] = *pptr; pptr++; if (ascbuf[0] == '~') break; else if (ascbuf[0] == 'z') { for (y = 0; y < 5; y++) ascbuf[y] = '\0'; } else { for (y = 1; y < 5; y++) { if (*pptr == '\n') { fgets(temp, 149, ps); pptr = temp; } ascbuf[y] = *pptr; if (ascbuf[y] == '~') { for (; y < 5; y++) { ascbuf[y] = '!'; q--; } break; } else pptr++; } for (y = 0; y < 5; y++) ascbuf[y] -= '!'; } if (*pptr == '\n') { fgets(temp, 149, ps); pptr = temp; } /* Decode from ASCII85 to binary */ pixel.i = ascbuf[4] + ascbuf[3] * 85 + ascbuf[2] * 7225 + ascbuf[1] * 614125 + ascbuf[0] * 52200625; for (y = 0; y < 4; y++) { filtbuf[q + y] = pixel.b[3 - y]; } q += 4; if (q >= ilen) break; } } /* Extra decoding goes here */ #ifdef HAVE_LIBZ if (do_flate) { flatebuf = (char *)malloc(ilen); large_inflate(filtbuf, q, &flatebuf, ilen); free(filtbuf); } else #endif flatebuf = filtbuf; pixel.b[3] = 0; q = 0; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { pixel.b[2] = flatebuf[q++]; pixel.b[1] = flatebuf[q++]; pixel.b[0] = flatebuf[q++]; XPutPixel(iptr->image, x, y, pixel.i); } } free(flatebuf); fgets(temp, 149, ps); /* definition line */ fgets(temp, 149, ps); /* pick up name of image from here */ for (pptr = temp; !isspace(*pptr); pptr++); *pptr = '\0'; iptr->filename = strdup(temp + 1); for (x = 0; x < 5; x++) fgets(temp, 149, ps); /* skip image dictionary */ } /*--------------------------------------------------------------*/ /* Read an object (page) from a file into xcircuit */ /*--------------------------------------------------------------*/ Boolean objectread(FILE *ps, objectptr localdata, short offx, short offy, short mode, char *retstr, int ccolor) { char *temp, *buffer, keyword[80]; short tmpfont = -1; float tmpscale = 0.0; objectptr *libobj; int curcolor = ccolor; char *colorkey = NULL; int i, j, k; objinstptr *newinst; /* path-handling variables */ pathptr *newpath; XPoint startpoint; keyword[0] = '\0'; buffer = (char *)malloc(256 * sizeof(char)); temp = buffer; for(;;) { char *lineptr, *keyptr; if (fgets(temp, 255, ps) == NULL) { if (strcmp(keyword, "restore")) { Wprintf("Error: end of file."); *retstr = '\0'; } break; } temp = buffer; /* because PostScript is a stack language, we will scan from the end */ for (lineptr = buffer; (*lineptr != '\n') && (*lineptr != '\0'); lineptr++); if (lineptr != buffer) { /* ignore any blank lines */ for (keyptr = lineptr - 1; isspace(*keyptr) && keyptr != buffer; keyptr--); for (; !isspace(*keyptr) && keyptr != buffer; keyptr--); sscanf(keyptr, "%79s", keyword); if (!strcmp(keyword, "showpage")) { strncpy(retstr, buffer, 150); retstr[149] = '\0'; free(buffer); /* If we have just read a schematic that is attached */ /* to a symbol, check all of the pin labels in the symbol */ /* to see if they correspond to pin names in the schematic. */ /* The other way around (pin in schematic with no */ /* corresponding name in the symbol) is not an error. */ if (areastruct.schemon && localdata->symschem != NULL) { genericptr *pgen, *lgen; labelptr plab, lcmp; for (pgen = localdata->symschem->plist; pgen < localdata-> symschem->plist + localdata->symschem->parts; pgen++) { if (IS_LABEL(*pgen)) { plab = TOLABEL(pgen); if (plab->pin == LOCAL) { for (lgen = localdata->plist; lgen < localdata->plist + localdata->parts; lgen++) { if (IS_LABEL(*lgen)) { lcmp = TOLABEL(lgen); if (lcmp->pin == LOCAL) if (!stringcomprelaxed(lcmp->string, plab->string, areastruct.topinstance)) break; } } if (lgen == localdata->plist + localdata->parts) { char *pch = textprint(plab->string, areastruct.topinstance); Fprintf(stderr, "Warning: Unattached pin \"%s\" in " "symbol %s\n", pch, localdata->symschem->name); free(pch); } } } } } return False; /* end of page */ } /* make a color change, adding the color if necessary */ else if (!strcmp(keyword, "scb")) { float red, green, blue; if (sscanf(buffer, "%f %f %f", &red, &green, &blue) == 3) { curcolor = rgb_alloccolor((int)(red * 65535), (int)(green * 65535), (int)(blue * 65535)); addnewcolorentry(curcolor); colorkey = NULL; } else { char tmpkey[30]; oparamptr ops; sscanf(buffer, "%29s", tmpkey); ops = match_param(localdata, tmpkey); if ((ops != NULL) && (ops->which == P_COLOR)) { colorkey = ops->key; curcolor = ops->parameter.ivalue; } } } /* end the color change, returning to default */ else if (!strcmp(keyword, "sce")) { curcolor = ccolor; colorkey = NULL; } /* begin a path constructor */ else if (!strcmp(keyword, "beginpath")) { NEW_PATH(newpath, localdata); (*newpath)->plist = (genericptr *)malloc(sizeof(genericptr)); (*newpath)->parts = 0; (*newpath)->color = curcolor; (*newpath)->passed = NULL; lineptr = varpscan(localdata, buffer, &startpoint.x, (genericptr)*newpath, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &startpoint.y, (genericptr)*newpath, 0, offy, P_POSITION_Y); std_eparam((genericptr)(*newpath), colorkey); } /* end the path constructor */ else if (!strcmp(keyword, "endpath")) { lineptr = varscan(localdata, buffer, &(*newpath)->style, (genericptr)*newpath, P_STYLE); lineptr = varfscan(localdata, lineptr, &(*newpath)->width, (genericptr)*newpath, P_LINEWIDTH); if ((*newpath)->parts > 0) { localdata->parts++; } else { /* in case of an empty path */ free((*newpath)->plist); free(*newpath); } newpath = NULL; } /* read path parts */ else if (!strcmp(keyword, "polyc")) { polyptr *newpoly; pointlist newpoints; short tmpnum; NEW_POLY(newpoly, (*newpath)); (*newpath)->parts++; for (--keyptr; *keyptr == ' '; keyptr--); for (; *keyptr != ' '; keyptr--); sscanf(keyptr, "%hd", &tmpnum); (*newpoly)->number = tmpnum + 1; (*newpoly)->width = 1.0; (*newpoly)->style = UNCLOSED; (*newpoly)->color = curcolor; (*newpoly)->passed = NULL; (*newpoly)->points = (pointlist) malloc((*newpoly)->number * sizeof(XPoint)); lineptr = buffer; for (newpoints = (*newpoly)->points + (*newpoly)->number - 1; newpoints > (*newpoly)->points; newpoints--) { lineptr = varpscan(localdata, lineptr, &newpoints->x, (genericptr)*newpoly, newpoints - (*newpoly)->points, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &newpoints->y, (genericptr)*newpoly, newpoints - (*newpoly)->points, offy, P_POSITION_Y); } newpoints->x = startpoint.x; newpoints->y = startpoint.y; startpoint.x = (newpoints + (*newpoly)->number - 1)->x; startpoint.y = (newpoints + (*newpoly)->number - 1)->y; } else if (!strcmp(keyword, "arc") || !strcmp(keyword, "arcn")) { arcptr *newarc; NEW_ARC(newarc, (*newpath)); (*newpath)->parts++; (*newarc)->width = 1.0; (*newarc)->style = UNCLOSED; (*newarc)->color = curcolor; (*newarc)->passed = NULL; lineptr = varpscan(localdata, buffer, &(*newarc)->position.x, (genericptr)*newarc, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y, (genericptr)*newarc, 0, offy, P_POSITION_Y); lineptr = varscan(localdata, lineptr, &(*newarc)->radius, (genericptr)*newarc, P_RADIUS); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1, (genericptr)*newarc, P_ANGLE1); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2, (genericptr)*newarc, P_ANGLE2); (*newarc)->yaxis = (*newarc)->radius; if (!strcmp(keyword, "arcn")) { float tmpang = (*newarc)->angle1; (*newarc)->radius = -((*newarc)->radius); (*newarc)->angle1 = (*newarc)->angle2; (*newarc)->angle2 = tmpang; } calcarc(*newarc); startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x; startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y; } else if (!strcmp(keyword, "pellip") || !strcmp(keyword, "nellip")) { arcptr *newarc; NEW_ARC(newarc, (*newpath)); (*newpath)->parts++; (*newarc)->width = 1.0; (*newarc)->style = UNCLOSED; (*newarc)->color = curcolor; (*newarc)->passed = NULL; lineptr = varpscan(localdata, buffer, &(*newarc)->position.x, (genericptr)*newarc, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y, (genericptr)*newarc, 0, offy, P_POSITION_Y); lineptr = varscan(localdata, lineptr, &(*newarc)->radius, (genericptr)*newarc, P_RADIUS); lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis, (genericptr)*newarc, P_MINOR_AXIS); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1, (genericptr)*newarc, P_ANGLE1); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2, (genericptr)*newarc, P_ANGLE2); if (!strcmp(keyword, "nellip")) { float tmpang = (*newarc)->angle1; (*newarc)->radius = -((*newarc)->radius); (*newarc)->angle1 = (*newarc)->angle2; (*newarc)->angle2 = tmpang; } calcarc(*newarc); startpoint.x = (short)(*newarc)->points[(*newarc)->number - 1].x; startpoint.y = (short)(*newarc)->points[(*newarc)->number - 1].y; } else if (!strcmp(keyword, "curveto")) { splineptr *newspline; NEW_SPLINE(newspline, (*newpath)); (*newpath)->parts++; (*newspline)->passed = NULL; (*newspline)->width = 1.0; (*newspline)->style = UNCLOSED; (*newspline)->color = curcolor; lineptr = varpscan(localdata, buffer, &(*newspline)->ctrl[1].x, (genericptr)*newspline, 1, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].y, (genericptr)*newspline, 1, offy, P_POSITION_Y); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].x, (genericptr)*newspline, 2, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].y, (genericptr)*newspline, 2, offy, P_POSITION_Y); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].x, (genericptr)*newspline, 3, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].y, (genericptr)*newspline, 3, offy, P_POSITION_Y); (*newspline)->ctrl[0].x = startpoint.x; (*newspline)->ctrl[0].y = startpoint.y; calcspline(*newspline); startpoint.x = (*newspline)->ctrl[3].x; startpoint.y = (*newspline)->ctrl[3].y; } /* read arcs */ else if (!strcmp(keyword, "xcarc")) { arcptr *newarc; NEW_ARC(newarc, localdata); (*newarc)->color = curcolor; (*newarc)->passed = NULL; /* backward compatibility */ if (version < 1.5) { sscanf(buffer, "%hd %hd %hd %f %f %f %hd", &(*newarc)->position.x, &(*newarc)->position.y, &(*newarc)->radius, &(*newarc)->angle1, &(*newarc)->angle2, &(*newarc)->width, &(*newarc)->style); (*newarc)->position.x -= offx; (*newarc)->position.y -= offy; } else { lineptr = varscan(localdata, buffer, &(*newarc)->style, (genericptr)*newarc, P_STYLE); lineptr = varfscan(localdata, lineptr, &(*newarc)->width, (genericptr)*newarc, P_LINEWIDTH); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x, (genericptr)*newarc, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y, (genericptr)*newarc, 0, offy, P_POSITION_Y); lineptr = varscan(localdata, lineptr, &(*newarc)->radius, (genericptr)*newarc, P_RADIUS); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1, (genericptr)*newarc, P_ANGLE1); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2, (genericptr)*newarc, P_ANGLE2); } (*newarc)->yaxis = (*newarc)->radius; calcarc(*newarc); localdata->parts++; std_eparam((genericptr)(*newarc), colorkey); } /* read ellipses */ else if (!strcmp(keyword, "ellipse")) { arcptr *newarc; NEW_ARC(newarc, localdata); (*newarc)->color = curcolor; (*newarc)->passed = NULL; lineptr = varscan(localdata, buffer, &(*newarc)->style, (genericptr)*newarc, P_STYLE); lineptr = varfscan(localdata, lineptr, &(*newarc)->width, (genericptr)*newarc, P_LINEWIDTH); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.x, (genericptr)*newarc, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newarc)->position.y, (genericptr)*newarc, 0, offy, P_POSITION_Y); lineptr = varscan(localdata, lineptr, &(*newarc)->radius, (genericptr)*newarc, P_RADIUS); lineptr = varscan(localdata, lineptr, &(*newarc)->yaxis, (genericptr)*newarc, P_MINOR_AXIS); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle1, (genericptr)*newarc, P_ANGLE1); lineptr = varfscan(localdata, lineptr, &(*newarc)->angle2, (genericptr)*newarc, P_ANGLE2); calcarc(*newarc); localdata->parts++; std_eparam((genericptr)(*newarc), colorkey); } /* read polygons */ /* (and wires---backward compatibility for v1.5 and earlier) */ else if (!strcmp(keyword, "polygon") || !strcmp(keyword, "wire")) { polyptr *newpoly; pointlist newpoints; NEW_POLY(newpoly, localdata); lineptr = buffer; (*newpoly)->passed = NULL; if (!strcmp(keyword, "wire")) { (*newpoly)->number = 2; (*newpoly)->width = 1.0; (*newpoly)->style = UNCLOSED; } else { /* backward compatibility */ if (version < 1.5) { for (--keyptr; *keyptr == ' '; keyptr--); for (; *keyptr != ' '; keyptr--); sscanf(keyptr, "%hd", &(*newpoly)->style); for (--keyptr; *keyptr == ' '; keyptr--); for (; *keyptr != ' '; keyptr--); sscanf(keyptr, "%f", &(*newpoly)->width); } for (--keyptr; *keyptr == ' '; keyptr--); for (; *keyptr != ' '; keyptr--); sscanf(keyptr, "%hd", &(*newpoly)->number); if (version >= 1.5) { lineptr = varscan(localdata, lineptr, &(*newpoly)->style, (genericptr)*newpoly, P_STYLE); lineptr = varfscan(localdata, lineptr, &(*newpoly)->width, (genericptr)*newpoly, P_LINEWIDTH); } } if ((*newpoly)->style & BBOX) (*newpoly)->color = BBOXCOLOR; else (*newpoly)->color = curcolor; (*newpoly)->points = (pointlist) malloc((*newpoly)->number * sizeof(XPoint)); for (newpoints = (*newpoly)->points; newpoints < (*newpoly)->points + (*newpoly)->number; newpoints++) { lineptr = varpscan(localdata, lineptr, &newpoints->x, (genericptr)*newpoly, newpoints - (*newpoly)->points, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &newpoints->y, (genericptr)*newpoly, newpoints - (*newpoly)->points, offy, P_POSITION_Y); } localdata->parts++; std_eparam((genericptr)(*newpoly), colorkey); } /* read spline curves */ else if (!strcmp(keyword, "spline")) { splineptr *newspline; NEW_SPLINE(newspline, localdata); (*newspline)->color = curcolor; (*newspline)->passed = NULL; /* backward compatibility */ if (version < 1.5) { sscanf(buffer, "%f %hd %hd %hd %hd %hd %hd %hd %hd %hd", &(*newspline)->width, &(*newspline)->ctrl[1].x, &(*newspline)->ctrl[1].y, &(*newspline)->ctrl[2].x, &(*newspline)->ctrl[2].y, &(*newspline)->ctrl[3].x, &(*newspline)->ctrl[3].y, &(*newspline)->ctrl[0].x, &(*newspline)->ctrl[0].y, &(*newspline)->style); (*newspline)->ctrl[1].x -= offx; (*newspline)->ctrl[2].x -= offx; (*newspline)->ctrl[0].x -= offx; (*newspline)->ctrl[3].x -= offx; (*newspline)->ctrl[1].y -= offy; (*newspline)->ctrl[2].y -= offy; (*newspline)->ctrl[3].y -= offy; (*newspline)->ctrl[0].y -= offy; } else { lineptr = varscan(localdata, buffer, &(*newspline)->style, (genericptr)*newspline, P_STYLE); lineptr = varfscan(localdata, lineptr, &(*newspline)->width, (genericptr)*newspline, P_LINEWIDTH); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].x, (genericptr)*newspline, 1, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[1].y, (genericptr)*newspline, 1, offy, P_POSITION_Y); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].x, (genericptr)*newspline, 2, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[2].y, (genericptr)*newspline, 2, offy, P_POSITION_Y); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].x, (genericptr)*newspline, 3, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[3].y, (genericptr)*newspline, 3, offy, P_POSITION_Y); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].x, (genericptr)*newspline, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newspline)->ctrl[0].y, (genericptr)*newspline, 0, offy, P_POSITION_Y); } localdata->parts++; calcspline(*newspline); std_eparam((genericptr)(*newspline), colorkey); } /* read graphics image instances */ else if (!strcmp(keyword, "graphic")) { graphicptr *newgp; Imagedata *img; NEW_GRAPHIC(newgp, localdata); (*newgp)->color = curcolor; (*newgp)->passed = NULL; (*newgp)->clipmask = (Pixmap)NULL; (*newgp)->target = NULL; (*newgp)->valid = FALSE; lineptr = buffer + 1; for (i = 0; i < xobjs.images; i++) { img = xobjs.imagelist + i; if (!strncmp(img->filename, lineptr, strlen(img->filename))) { (*newgp)->source = img->image; break; } } lineptr += strlen(img->filename) + 1; lineptr = varfscan(localdata, lineptr, &(*newgp)->scale, (genericptr)*newgp, P_SCALE); lineptr = varscan(localdata, lineptr, &(*newgp)->rotation, (genericptr)*newgp, P_ROTATION); lineptr = varpscan(localdata, lineptr, &(*newgp)->position.x, (genericptr)*newgp, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newgp)->position.y, (genericptr)*newgp, 0, offy, P_POSITION_Y); localdata->parts++; std_eparam((genericptr)(*newgp), colorkey); } /* read labels */ else if (!strcmp(keyword, "fontset")) { /* old style */ char tmpstring[100]; int i; sscanf(buffer, "%f %*c%99s", &tmpscale, tmpstring); for (i = 0; i < fontcount; i++) if (!strcmp(tmpstring, fonts[i].psname)) { tmpfont = i; break; } if (i == fontcount) i = 0; /* Why bother with anything fancy? */ } else if (!strcmp(keyword, "label") || !strcmp(keyword, "pinlabel") || !strcmp(keyword, "pinglobal") || !strcmp(keyword, "infolabel")) { labelptr *newlabel; stringpart *firstscale, *firstfont; NEW_LABEL(newlabel, localdata); (*newlabel)->color = curcolor; (*newlabel)->string = NULL; (*newlabel)->passed = NULL; /* scan backwards to get the number of substrings */ lineptr = keyptr - 1; for (i = 0; i < ((version < 2.25) ? 5 : 6); i++) { for (; *lineptr == ' '; lineptr--); for (; *lineptr != ' '; lineptr--); } if ((strchr(lineptr, '.') != NULL) && (version < 2.25)) { Fprintf(stderr, "Error: File version claims to be %2.1f," " but has version %2.1f labels\n", version, PROG_VERSION); Fprintf(stderr, "Attempting to resolve problem by updating version.\n"); version = PROG_VERSION; for (; *lineptr == ' '; lineptr--); for (; *lineptr != ' '; lineptr--); } /* no. segments is ignored---may be a derived quantity, anyway */ if (version < 2.25) { sscanf(lineptr, "%*s %hd %hd %hd %hd", &(*newlabel)->justify, &(*newlabel)->rotation, &(*newlabel)->position.x, &(*newlabel)->position.y); (*newlabel)->position.x -= offx; (*newlabel)->position.y -= offy; *lineptr = '\0'; } else { *lineptr++ = '\0'; lineptr = advancetoken(lineptr); /* skip string token */ lineptr = varscan(localdata, lineptr, &(*newlabel)->justify, (genericptr)*newlabel, P_JUSTIFY); lineptr = varscan(localdata, lineptr, &(*newlabel)->rotation, (genericptr)*newlabel, P_ROTATION); lineptr = varfscan(localdata, lineptr, &(*newlabel)->scale, (genericptr)*newlabel, P_SCALE); lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.x, (genericptr)*newlabel, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newlabel)->position.y, (genericptr)*newlabel, 0, offy, P_POSITION_Y); } if (version < 2.4) (*newlabel)->rotation = -(*newlabel)->rotation; while ((*newlabel)->rotation < 0) (*newlabel)->rotation += 360; (*newlabel)->pin = False; if (strcmp(keyword, "label")) { /* all the schematic types */ /* enable schematic capture if it is not already on. */ if (!areastruct.schemon) doxschema(NULL, 0, NULL); if (!strcmp(keyword, "pinlabel")) (*newlabel)->pin = LOCAL; else if (!strcmp(keyword, "pinglobal")) (*newlabel)->pin = GLOBAL; else if (!strcmp(keyword, "infolabel")) { /* Do not turn top-level pages into symbols! */ /* Info labels on schematics are treated differently. */ if (localdata != topobject) localdata->schemtype = FUNDAMENTAL; (*newlabel)->pin = INFO; if (curcolor == DEFAULTCOLOR) (*newlabel)->color = INFOLABELCOLOR; } } lineptr = buffer; /* back to beginning of string */ if (!strncmp(lineptr, "mark", 4)) lineptr += 4; readlabel(localdata, lineptr, &(*newlabel)->string); if (version < 2.25) { /* Switch 1st scale designator to overall font scale */ firstscale = (*newlabel)->string->nextpart; if (firstscale->type != FONT_SCALE) { if (tmpscale != 0.0) (*newlabel)->scale = 0.0; else (*newlabel)->scale = 1.0; } else { (*newlabel)->scale = firstscale->data.scale; deletestring(firstscale, &((*newlabel)->string), areastruct.topinstance); } } firstfont = (*newlabel)->string; if (firstfont->type != FONT_NAME) { if (tmpfont == -1) { Fprintf(stderr, "Error: Label with no font designator?\n"); tmpfont = 0; } firstfont = makesegment(&((*newlabel)->string), (*newlabel)->string); firstfont->type = FONT_NAME; firstfont->data.font = tmpfont; } cleanuplabel(&(*newlabel)->string); localdata->parts++; std_eparam((genericptr)(*newlabel), colorkey); } /* read symbol-to-schematic connection */ else if (!strcmp(keyword, "is_schematic")) { char tempstr[50]; for (lineptr = buffer; *lineptr == ' '; lineptr++); sscanf(++lineptr, "%49s", tempstr); checksym(localdata, tempstr); } /* read bounding box (font files only) */ else if (!strcmp(keyword, "bbox")) { for (lineptr = buffer; *lineptr == ' '; lineptr++); if (*lineptr != '%') { Wprintf("Illegal bbox."); free(buffer); *retstr = '\0'; return True; } sscanf(++lineptr, "%hd %hd %hd %hd", &localdata->bbox.lowerleft.x, &localdata->bbox.lowerleft.y, &localdata->bbox.width, &localdata->bbox.height); } /* read "hidden" attribute */ else if (!strcmp(keyword, "hidden")) { localdata->hidden = True; } /* read "libinst" special instance of a library part */ else if (!strcmp(keyword, "libinst")) { /* Read backwards from keyword to find name of object instanced. */ for (lineptr = keyptr; *lineptr != '/' && lineptr > buffer; lineptr--); sscanf(++lineptr, "%79s", keyword); new_library_instance(mode - LIBRARY, keyword, buffer); } /* read objects */ else if (!strcmp(keyword, "{")) { /* This is an object definition */ objlistptr redef; objectptr *newobject; for (lineptr = buffer; *lineptr == ' '; lineptr++); if (*lineptr++ != '/') { /* This may be part of a label. . . treat as a continuation line */ temp = continueline(&buffer); continue; } sscanf(lineptr, "%79s", keyword); newobject = new_library_object(mode, keyword, &redef); if (objectread(ps, *newobject, 0, 0, mode, retstr, curcolor) == True) { strncpy(retstr, buffer, 150); retstr[149] = '\0'; free(buffer); return True; } else { if (library_object_unique(mode, *newobject, redef)) add_object_to_library(mode, *newobject); } } else if (!strcmp(keyword, "def")) { strncpy(retstr, buffer, 150); retstr[149] = '\0'; free (buffer); return False; /* end of object def or end of object library */ } else if (!strcmp(keyword, "loadfontencoding")) { /* Deprecated, but retained for backward compatibility. */ /* Load from script, .xcircuitrc, or command line instead. */ for (lineptr = buffer; *lineptr != '%'; lineptr++); sscanf (lineptr + 1, "%149s", _STR); if (*(lineptr + 1) != '%') loadfontfile(_STR); } else if (!strcmp(keyword, "loadlibrary")) { /* Deprecated, but retained for backward compatibility */ /* Load from script, .xcircuitrc, or command line instead. */ int ilib, tlib; for (lineptr = buffer; *lineptr != '%'; lineptr++); sscanf (++lineptr, "%149s", _STR); while (isspace(*lineptr)) lineptr++; while (!isspace(*++lineptr)); while (isspace(*++lineptr)); if (sscanf (lineptr, "%d", &ilib) > 0) { while ((ilib - 2 + LIBRARY) > xobjs.numlibs) { tlib = createlibrary(FALSE); if (tlib != xobjs.numlibs - 2 + LIBRARY) { ilib = tlib; break; } } mode = ilib - 1 + LIBRARY; } loadlibrary(mode); } else if (!strcmp(keyword, "beginparm")) { /* parameterized object */ short tmpnum, i; for (--keyptr; *keyptr == ' '; keyptr--); for (; isdigit(*keyptr) && (keyptr >= buffer); keyptr--); sscanf(keyptr, "%hd", &tmpnum); lineptr = buffer; while (isspace(*lineptr)) lineptr++; if (tmpnum < 256) { /* read parameter defaults in order */ stringpart *newpart; oparamptr newops; for (i = 0; i < tmpnum; i++) { oparamptr newops; newops = (oparamptr)malloc(sizeof(oparam)); newops->next = localdata->params; localdata->params = newops; newops->key = (char *)malloc(6); sprintf(newops->key, "v%d", i + 1); if (*lineptr == '(' || *lineptr == '{') { /* type is XC_STRING */ char *linetmp, csave; newops->parameter.string = NULL; /* get simple substring or set of substrings and commands */ linetmp = find_delimiter(lineptr); csave = *(++linetmp); *linetmp = '\0'; if (*lineptr == '{') lineptr++; readlabel(localdata, lineptr, &(newops->parameter.string)); /* Add the ending part to the parameter string */ newpart = makesegment(&(newops->parameter.string), NULL); newpart->type = PARAM_END; newpart->data.string = (u_char *)NULL; newops->type = (u_char)XC_STRING; newops->which = P_SUBSTRING; /* Fprintf(stdout, "Parameter %d to object %s defaults " "to string \"%s\"\n", i + 1, localdata->name, ops->parameter.string); */ *linetmp = csave; lineptr = linetmp; while (isspace(*lineptr)) lineptr++; } else { /* type is assumed to be XC_FLOAT */ newops->type = (u_char)XC_FLOAT; sscanf(lineptr, "%g", &newops->parameter.fvalue); /* Fprintf(stdout, "Parameter %s to object %s defaults to " "value %g\n", newops->key, localdata->name, newops->parameter.fvalue); */ lineptr = advancetoken(lineptr); } } } } else if (!strcmp(keyword, "trivial")) { localdata->schemtype = TRIVIAL; if (!areastruct.schemon) doxschema(NULL, 0, NULL); } else if (!strcmp(keyword, "begingate")) { localdata->params = NULL; /* read dictionary of parameter key:value pairs */ readparams(NULL, NULL, localdata, buffer); } else if (!strcmp(keyword, "%%Trailer")) break; else if (!strcmp(keyword, "EndLib")) break; else if (!strcmp(keyword, "restore")); /* handled at top */ else if (!strcmp(keyword, "grestore")); /* ignore */ else if (!strcmp(keyword, "endgate")); /* also ignore */ else if (!strcmp(keyword, "xyarray")); /* ignore for now */ else { char *tmpptr; Boolean found = False; /* First, make sure this is not a general comment line */ /* Return if we have a page boundary */ /* Read an image if this is an imagedata line. */ for (tmpptr = buffer; isspace(*tmpptr); tmpptr++); if (*tmpptr == '%') { if (strstr(buffer, "%%Page:") == tmpptr) { strncpy(retstr, buffer, 150); retstr[149] = '\0'; free (buffer); return True; } else if (strstr(buffer, "%imagedata") == tmpptr) { int width, height; sscanf(buffer, "%*s %d %d", &width, &height); readimagedata(ps, width, height); } continue; } /* (Assume that this line calls an object instance) */ /* Double loop through user libraries */ for (k = 0; k < ((mode == FONTLIB) ? 1 : xobjs.numlibs); k++) { for (j = 0; j < ((mode == FONTLIB) ? xobjs.fontlib.number : xobjs.userlibs[k].number); j++) { libobj = (mode == FONTLIB) ? xobjs.fontlib.library + j : xobjs.userlibs[k].library + j; if (!objnamecmp(keyword, (*libobj)->name)) { /* If the name is not exactly the same (appended underscores) */ /* check if the name is on the list of aliases. */ if (strcmp(keyword, (*libobj)->name)) { Boolean is_alias = False; aliasptr ckalias = aliastop; slistptr sref; for (; ckalias != NULL; ckalias = ckalias->next) { if (ckalias->baseobj == (*libobj)) { sref = ckalias->aliases; for (; sref != NULL; sref = sref->next) { if (!strcmp(keyword, sref->alias)) { is_alias = True; break; } } if (is_alias) break; } } if (!is_alias) continue; } found = True; NEW_OBJINST(newinst, localdata); localdata->parts++; (*newinst)->thisobject = *libobj; (*newinst)->color = curcolor; (*newinst)->params = NULL; (*newinst)->passed = NULL; (*newinst)->bbox.lowerleft.x = (*libobj)->bbox.lowerleft.x; (*newinst)->bbox.lowerleft.y = (*libobj)->bbox.lowerleft.y; (*newinst)->bbox.width = (*libobj)->bbox.width; (*newinst)->bbox.height = (*libobj)->bbox.height; (*newinst)->schembbox = NULL; lineptr = varfscan(localdata, buffer, &(*newinst)->scale, (genericptr)*newinst, P_SCALE); lineptr = varscan(localdata, lineptr, &(*newinst)->rotation, (genericptr)*newinst, P_ROTATION); lineptr = varpscan(localdata, lineptr, &(*newinst)->position.x, (genericptr)*newinst, 0, offx, P_POSITION_X); lineptr = varpscan(localdata, lineptr, &(*newinst)->position.y, (genericptr)*newinst, 0, offy, P_POSITION_Y); /* Negative rotations = flip in x in version 2.3.6 and */ /* earlier. Later versions don't allow negative rotation */ if (version < 2.4) { if ((*newinst)->rotation < 0) { (*newinst)->scale = -((*newinst)->scale); (*newinst)->rotation++; } (*newinst)->rotation = -(*newinst)->rotation; } while ((*newinst)->rotation > 360) (*newinst)->rotation -= 360; while ((*newinst)->rotation < 0) (*newinst)->rotation += 360; std_eparam((genericptr)(*newinst), colorkey); /* Does this instance contain parameters? */ readparams(localdata, *newinst, *libobj, buffer); calcbboxinst(*newinst); break; } /* if !strcmp */ } /* for j loop */ if (found) break; } /* for k loop */ if (!found) /* will assume that we have a continuation line */ temp = continueline(&buffer); } } } strncpy(retstr, buffer, 150); retstr[149] = '\0'; free(buffer); return True; } /*------------------------*/ /* Save a PostScript file */ /*------------------------*/ #ifdef TCL_WRAPPER void setfile(char *filename, int mode) { /* see if name has been changed in the buffer */ if (strcmp(xobjs.pagelist[areastruct.page]->filename, filename)) { Wprintf("Changing name of edit file."); free(xobjs.pagelist[areastruct.page]->filename); xobjs.pagelist[areastruct.page]->filename = strdup(filename); } if (strstr(xobjs.pagelist[areastruct.page]->filename, "Page ") != NULL) { Wprintf("Warning: Enter a new name."); if (beeper) XBell(dpy, 100); } else { savefile(mode); if (beeper) XBell(dpy, 100); } } #else /* !TCL_WRAPPER */ void setfile(xcWidget button, xcWidget fnamewidget, caddr_t clientdata) { /* see if name has been changed in the buffer */ sprintf(_STR2, "%.249s", (char *)XwTextCopyBuffer(fnamewidget)); if (strcmp(xobjs.pagelist[areastruct.page]->filename, _STR2)) { Wprintf("Changing name of edit file."); free(xobjs.pagelist[areastruct.page]->filename); xobjs.pagelist[areastruct.page]->filename = strdup(_STR2); } if (strstr(xobjs.pagelist[areastruct.page]->filename, "Page ") != NULL) { Wprintf("Warning: Enter a new name."); if (beeper) XBell(dpy, 100); } else { Arg wargs[1]; xcWidget db, di; savefile(CURRENT_PAGE); /* Change "close" button to read "done" */ di = xcParent(button); db = XtNameToWidget(di, "Close"); XtSetArg(wargs[0], XtNlabel, " Done "); XtSetValues(db, wargs, 1); if (beeper) XBell(dpy, 100); } } #endif /* TCL_WRAPPER */ /*--------------------------------------------------------------*/ /* Update number of changes for an object and initiate a temp */ /* file save if total number of unsaved changes exceeds 20. */ /*--------------------------------------------------------------*/ void incr_changes(objectptr thisobj) { /* It is assumed that empty pages are meant to be that way */ /* and are not to be saved, so changes are marked as zero. */ if (thisobj->parts == 0) { thisobj->changes = 0; return; } thisobj->changes++; xobjs.new_changes++; if (xobjs.new_changes > MAXCHANGES) { #ifdef TCL_WRAPPER savetemp(NULL); #else savetemp(NULL, NULL); #endif xobjs.new_changes = 0; /* reset the count */ } } /*--------------------------------------------------------------*/ /* tempfile save */ /*--------------------------------------------------------------*/ #ifdef TCL_WRAPPER xcTimeOutProc savetemp(caddr_t clientdata) { if (xobjs.timeout_id == NULL) { /* Not called by timeout callback */ xcRemoveTimeOut(xobjs.timeout_id); } xobjs.timeout_id = NULL; #else xcTimeOutProc savetemp(caddr_t clientdata, xcIntervalId *id) { if (id == NULL) /* Not called by timeout callback */ xcRemoveTimeOut(xobjs.timeout_id); #endif /* First see if there are any unsaved changes in the file. */ /* If not, then just reset the counter and continue. */ if (countchanges(NULL) > 0) { if (xobjs.tempfile == NULL) { int fd; char *template = (char *)malloc(10 + strlen(xobjs.tempdir)); sprintf(template, "%s/XCXXXXXX", xobjs.tempdir); #ifdef _MSC_VER fd = mktemp(template); #else fd = mkstemp(template); #endif if (fd == -1) { Fprintf(stderr, "Error generating file for savetemp\n"); free(template); return NULL; } close(fd); xobjs.tempfile = strdup(template); free(template); } savefile(ALL_PAGES); } xobjs.timeout_id = xcAddTimeOut(app, (u_long)60000 * xobjs.save_interval, (xcTimeOutProc)savetemp, NULL); return NULL; } /*----------------------------------------------------------------------*/ /* Set all objects in the list "wroteobjs" as having no unsaved changes */ /*----------------------------------------------------------------------*/ void setassaved(objectptr *wroteobjs, short written) { int i; for (i = 0; i < written; i++) (*(wroteobjs + i))->changes = 0; } /*---------------------------------------------------------------*/ /* Save indicated library. If libno is 0, save current page if */ /* the current page is a library. If not, save the user library */ /*---------------------------------------------------------------*/ void savelibpopup(xcWidget button, pointertype libno, caddr_t nulldata) { char *bconf; buttonsave *savebutton; int ilib = (int)libno; if (ilib == 0) ilib = is_library(topobject); if (ilib < 0) ilib = xobjs.numlibs - 1; if ((ilib = is_library(topobject)) < 0) ilib = xobjs.numlibs - 1; if (xobjs.userlibs[ilib].number == 0) { Wprintf("No objects in library to save."); return; } #ifndef TCL_WRAPPER savebutton = (buttonsave *)malloc(sizeof(buttonsave)); getgeneric(savebutton, button, savelibpopup, Number(ilib)); popupprompt(button, "Enter name for library:", "\0", savelibrary, savebutton, NULL); #endif } /*---------------------------------------------------------*/ void savelibrary(xcWidget w, int ilib) { FILE *ps; char outname[250], *outptr; objectptr *wroteobjs, *libptr, libobjptr, *optr, depobj; genericptr *gptr; liblistptr spec; short written; char *uname = NULL; char *hostname = NULL; #ifndef _MSC_VER struct passwd *mypwentry = NULL; #endif int i; sscanf(_STR2, "%249s", outname); if ((outptr = strrchr(outname, '/')) == NULL) outptr = outname; if (strchr(outptr, '.') == NULL) sprintf(outname, "%s.lps", _STR2); xc_tilde_expand(outname); ps = fopen(outname, "w"); if (ps == NULL) { Wprintf("Can't open PS file."); return; } fprintf(ps, "%%! PostScript set of library objects for XCircuit\n"); fprintf(ps, "%% Version: %2.1f\n", version); fprintf(ps, "%% Library name is: %s\n", _STR2); #ifdef _MSC_VER uname = getenv((const char *)"USERNAME"); #else uname = getenv((const char *)"USER"); if (uname != NULL) mypwentry = getpwnam(uname); #endif /* Check for both $HOST and $HOSTNAME environment variables. Thanks */ /* to frankie liu for this fix. */ if ((hostname = getenv((const char *)"HOSTNAME")) == NULL) if ((hostname = getenv((const char *)"HOST")) == NULL) { if (gethostname(_STR, 149) != 0) hostname = uname; else hostname = _STR; } #ifndef _MSC_VER if (mypwentry != NULL) fprintf(ps, "%% Author: %s <%s@%s>\n", mypwentry->pw_gecos, uname, hostname); #endif fprintf(ps, "%%\n\n"); /* Print lists of object dependencies, where they exist. */ /* Note that collectdepend() will always return the object itself as */ /* the last entry in the list, so we should ignore that one. */ wroteobjs = (objectptr *) malloc(sizeof(objectptr)); for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) { if (!spec->virtual) { written = 0; libobjptr = spec->thisinst->thisobject; /* Search for all object definitions instantiated in this object, */ /* and add them to the dependency list (non-recursive). */ for (gptr = libobjptr->plist; gptr < libobjptr->plist + libobjptr->parts; gptr++) { if (IS_OBJINST(*gptr)) { depobj = TOOBJINST(gptr)->thisobject; /* Search among the list of objects already collected. */ /* If this object has been previously, then ignore it. */ /* Otherwise, update the list of object dependencies */ for (optr = wroteobjs; optr < wroteobjs + written; optr++) if (*optr == depobj) break; if (optr == wroteobjs + written) { wroteobjs = (objectptr *)realloc(wroteobjs, (written + 1) * sizeof(objectptr)); *(wroteobjs + written) = depobj; written++; } } } if (written > 0) { fprintf(ps, "%% Depend %s", libobjptr->name); for (i = 0; i < written; i++) { depobj = *(wroteobjs + i); fprintf(ps, " %s", depobj->name); } fprintf(ps, "\n"); } } } fprintf(ps, "\n%% XCircuitLib library objects\n"); /* list of library objects already written */ wroteobjs = (objectptr *)realloc(wroteobjs, sizeof(objectptr)); written = 0; /* write all of the object definitions used, bottom up, with virtual instances */ /* in the correct placement. */ for (spec = xobjs.userlibs[ilib].instlist; spec != NULL; spec = spec->next) { if (!spec->virtual) { printobjects(ps, spec->thisinst->thisobject, &wroteobjs, &written, DEFAULTCOLOR); } else { if ((spec->thisinst->scale != 1.0) || (spec->thisinst->rotation != 0)) { fprintf(ps, "%3.2f %d ", spec->thisinst->scale, spec->thisinst->rotation); } printparams(ps, spec->thisinst, 0); fprintf(ps, "/%s libinst\n", spec->thisinst->thisobject->name); if ((spec->next != NULL) && (!(spec->next->virtual))) fprintf(ps, "\n"); } } setassaved(wroteobjs, written); xobjs.new_changes = countchanges(NULL); /* and the postlog */ fprintf(ps, "\n%% EndLib\n"); fclose(ps); sprintf(_STR, "Library %s saved.", outname); Wprintf(_STR); free(wroteobjs); } /*----------------------------------------------------------------------*/ /* Recursive routine to search the object hierarchy for fonts used */ /*----------------------------------------------------------------------*/ void findfonts(objectptr writepage, short *fontsused) { genericptr *dfp; stringpart *chp; int findex; for (dfp = writepage->plist; dfp < writepage->plist + writepage->parts; dfp++) { if (IS_LABEL(*dfp)) { for (chp = TOLABEL(dfp)->string; chp != NULL; chp = chp->nextpart) { if (chp->type == FONT_NAME) { findex = chp->data.font; if (fontsused[findex] == 0) { fontsused[findex] = 0x8000 | fonts[findex].flags; } } } } else if (IS_OBJINST(*dfp)) { findfonts(TOOBJINST(dfp)->thisobject, fontsused); } } } /*----------------------------------------------------------------------*/ /* Main file saving routine */ /*----------------------------------------------------------------------*/ void savefile(short mode) { FILE *ps, *pro; char outname[150], temp[150], prologue[150], *fname, *fptr, ascbuf[6]; u_char decodebuf[6]; short written, fontsused[256], i, page, curpage, multipage; short savepage, stcount, *pagelist, *glist; objectptr *wroteobjs; objinstptr writepage; int findex, j; time_t tdate; char *tmp_s; if (mode != ALL_PAGES) { /* doubly-protected file write: protect against errors during file write */ fname = xobjs.pagelist[areastruct.page]->filename; sprintf(outname, "%s~", fname); rename(fname, outname); } else { /* doubly-protected backup: protect against errors during file write */ sprintf(outname, "%sB", xobjs.tempfile); rename(xobjs.tempfile, outname); fname = xobjs.tempfile; } if ((fptr = strrchr(fname, '/')) == NULL) fptr = fname; if ((mode != ALL_PAGES) && (strchr(fptr, '.') == NULL)) sprintf(outname, "%s.ps", fname); else sprintf(outname, "%s", fname); xc_tilde_expand(outname); ps = fopen(outname, "w"); if (ps == NULL) { Wprintf("Can't open PS file."); return; } /* calculate the bounding box of the drawing */ /* find all the schematics which are subcircuits of the top-level-page */ /* circuit and give them the same filename, so that they will be saved */ /* together as a set. */ if ((areastruct.schemon) && (mode != NO_SUBCIRCUITS)) { Wprintf("Gathering all subcircuits. . ."); collectsubschems(areastruct.page); } /* Check for multiple-page output: get the number of pages; */ /* ignore empty pages. */ multipage = 0; savepage = areastruct.page; if (mode == NO_SUBCIRCUITS) pagelist = pagetotals(areastruct.page, INDEPENDENT); else if (mode == ALL_PAGES) pagelist = pagetotals(areastruct.page, ALL_PAGES); else pagelist = pagetotals(areastruct.page, TOTAL_PAGES); for (page = 0; page < xobjs.pages; page++) if (pagelist[page] > 0) multipage++; if (multipage == 0) { Wprintf("Panic: could not find this page in page list!"); free (pagelist); fclose(ps); return; } /* Print the PostScript DSC Document Header */ fprintf(ps, "%%!PS-Adobe-3.0"); if (multipage == 1 && !(xobjs.pagelist[areastruct.page]->pmode & 1)) fprintf(ps, " EPSF-3.0\n"); else fprintf(ps, "\n"); fprintf(ps, "%%%%Title: %s\n", xobjs.pagelist[areastruct.page]->filename); fprintf(ps, "%%%%Creator: XCircuit v%2.1f rev%d\n", PROG_VERSION, PROG_REVISION); tdate = time(NULL); fprintf(ps, "%%%%CreationDate: %s", asctime(localtime(&tdate))); fprintf(ps, "%%%%Pages: %d\n", multipage); /* This is just a default value; each bounding box is declared per */ /* page by the DSC "PageBoundingBox" keyword. */ /* However, encapsulated files adjust the bounding box to center on */ /* the object, instead of centering the object on the page. */ if (multipage == 1 && !(xobjs.pagelist[areastruct.page]->pmode & 1)) { objectptr thisobj = xobjs.pagelist[areastruct.page]->pageinst->thisobject; float psscale = getpsscale(xobjs.pagelist[areastruct.page]->outscale, areastruct.page); /* The top-level bounding box determines the size of an encapsulated */ /* drawing, regardless of the PageBoundingBox numbers. Therefore, */ /* we size this to bound just the picture by closing up the 1" (72 */ /* PostScript units) margins, except for a tiny sliver of a margin */ /* (4 PostScript units) which covers a bit of sloppiness in the font */ /* measurements. */ fprintf(ps, "%%%%BoundingBox: 68 68 %d %d\n", (int)((float)thisobj->bbox.width * psscale) + 76, (int)((float)thisobj->bbox.height * psscale) + 76); } else if (xobjs.pagelist[0]->coordstyle == CM) fprintf(ps, "%%%%BoundingBox: 0 0 595 812\n"); /* A4 default */ else fprintf(ps, "%%%%BoundingBox: 0 0 612 792\n"); /* letter default */ for (i = 0; i <= fontcount; i++) fontsused[i] = 0; fprintf(ps, "%%%%DocumentNeededResources: "); stcount = 27; /* find all of the fonts used in this document */ /* log all fonts which are native PostScript */ for (curpage = 0; curpage < xobjs.pages; curpage++) if (pagelist[curpage] > 0) { writepage = xobjs.pagelist[curpage]->pageinst; findfonts(writepage->thisobject, fontsused); } for (i = 0; i <= fontcount; i++) { if (fontsused[i] & 0x8000) if ((fonts[i].flags & 0x8018) == 0x0) { stcount += strlen(fonts[i].psname) + 6; if (stcount > OUTPUTWIDTH) { stcount = strlen(fonts[i].psname) + 6; fprintf(ps, "\n%%%%+ "); } fprintf(ps, "font %s ", fonts[i].psname); } } fprintf(ps, "\n%%%%EndComments\n"); tmp_s = getenv((const char *)"XCIRCUIT_LIB_DIR"); if (tmp_s != NULL) { sprintf(prologue, "%s/%s", tmp_s, PROLOGUE_FILE); pro = fopen(prologue, "r"); } else pro = NULL; if (pro == NULL) { sprintf(prologue, "%s/%s", PROLOGUE_DIR, PROLOGUE_FILE); pro = fopen(prologue, "r"); if (pro == NULL) { sprintf(prologue, "%s", PROLOGUE_FILE); pro = fopen(prologue, "r"); if (pro == NULL) { Wprintf("Can't open prolog."); free(pagelist); fclose(ps); return; } } } /* write the prolog to the output */ for(;;) { if (fgets(temp, 149, pro) == NULL) break; fputs(temp, ps); } fclose(pro); /* Special font handling */ for (findex = 0; findex < fontcount; findex++) { /* Derived font slant */ if ((fontsused[findex] & 0x032) == 0x032) fprintf(ps, "/%s /%s .167 fontslant\n\n", fonts[findex].psname, fonts[findex].family); /* Derived ISO-Latin1 encoding */ if ((fontsused[findex] & 0xf80) == 0x100) { char *fontorig = NULL; short i; /* find the original standard-encoded font (can be itself) */ for (i = 0; i < fontcount; i++) { if (i == findex) continue; if (!strcmp(fonts[i].family, fonts[findex].family) && ((fonts[i].flags & 0x03) == (fonts[findex].flags & 0x03))) fontorig = fonts[i].psname; if (fontorig == NULL) fontorig = fonts[findex].psname; } fprintf(ps, "/%s findfont dup length dict begin\n", fontorig); fprintf(ps, "{1 index /FID ne {def} {pop pop} ifelse} forall\n"); fprintf(ps, "/Encoding ISOLatin1Encoding def currentdict end\n"); fprintf(ps, "/%s exch definefont pop\n\n", fonts[findex].psname); } /* To do: ISO-Latin2 encoding */ if ((fontsused[findex] & 0xf80) == 0x180) { } /* To do: Special encoding */ if ((fontsused[findex] & 0xf80) == 0x80) { } /* To do: Vectored (drawn) font */ if (fontsused[findex] & 0x8) { } } /* List of objects already written */ wroteobjs = (objectptr *) malloc (sizeof(objectptr)); written = 0; fprintf(ps, "%% XCircuit output starts here.\n\n"); fprintf(ps, "%%%%BeginSetup\n\n"); /* Write out all of the images used */ glist = collect_graphics(pagelist); for (i = 0; i < xobjs.images; i++) { Imagedata *img = xobjs.imagelist + i; int ilen, flen, k, m = 0, n, q = 0; u_char *filtbuf, *flatebuf; Boolean lastpix = FALSE; union { u_long i; u_char b[4]; } pixel; if (glist[i] == 0) continue; fprintf(ps, "%%imagedata %d %d\n", img->image->width, img->image->height); fprintf(ps, "currentfile /ASCII85Decode filter "); #ifdef HAVE_LIBZ fprintf(ps, "/FlateDecode filter\n"); #endif fprintf(ps, "/ReusableStreamDecode filter\n"); /* creating a stream buffer is wasteful if we're just using ASCII85 */ /* decoding but is a must for compression filters. */ ilen = 3 * img->image->width * img->image->height; filtbuf = (u_char *)malloc(ilen + 4); q = 0; for (j = 0; j < img->image->height; j++) { for (k = 0; k < img->image->width; k++) { pixel.i = XGetPixel(img->image, k, j); filtbuf[q++] = (u_char)pixel.b[2]; filtbuf[q++] = (u_char)pixel.b[1]; filtbuf[q++] = (u_char)pixel.b[0]; } } for (j = 0; j < 4; j++) filtbuf[q++] = 0; /* Extra encoding goes here */ #ifdef HAVE_LIBZ flen = ilen * 2; flatebuf = (char *)malloc(flen); ilen = large_deflate(flatebuf, flen, filtbuf, ilen); free(filtbuf); #else flatebuf = filtbuf; #endif ascbuf[5] = '\0'; for (j = 0; j < ilen; j += 4) { if ((j + 4) > ilen) lastpix = TRUE; if (!lastpix && (flatebuf[j] + flatebuf[j + 1] + flatebuf[j + 2] + flatebuf[j + 3] == 0)) { fprintf(ps, "z"); m++; } else { for (n = 0; n < 4; n++) pixel.b[3 - n] = flatebuf[j + n]; ascbuf[0] = '!' + (pixel.i / 52200625); pixel.i %= 52200625; ascbuf[1] = '!' + (pixel.i / 614125); pixel.i %= 614125; ascbuf[2] = '!' + (pixel.i / 7225); pixel.i %= 7225; ascbuf[3] = '!' + (pixel.i / 85); pixel.i %= 85; ascbuf[4] = '!' + pixel.i; if (lastpix) for (n = 0; n < ilen + 1 - j; n++) fprintf(ps, "%c", ascbuf[n]); else fprintf(ps, "%5s", ascbuf); m += 5; } if (m > 75) { fprintf(ps, "\n"); m = 0; } } fprintf(ps, "~>\n"); free(flatebuf); fprintf(ps, "/%sdata exch def\n", img->filename); fprintf(ps, "/%s <<\n", img->filename); fprintf(ps, " /ImageType 1 /Width %d /Height %d /BitsPerComponent 8\n", img->image->width, img->image->height); fprintf(ps, " /MultipleDataSources false\n"); fprintf(ps, " /Decode [0 1 0 1 0 1]\n"); fprintf(ps, " /ImageMatrix [1 0 0 -1 %d %d]\n", (img->image->width >> 1), (img->image->height >> 1)); fprintf(ps, " /DataSource %sdata >> def\n\n", img->filename); } free(glist); for (curpage = 0; curpage < xobjs.pages; curpage++) { if (pagelist[curpage] == 0) continue; /* Write all of the object definitions used, bottom up */ printrefobjects(ps, xobjs.pagelist[curpage]->pageinst->thisobject, &wroteobjs, &written); } fprintf(ps, "\n%%%%EndSetup\n\n"); page = 0; for (curpage = 0; curpage < xobjs.pages; curpage++) { if (pagelist[curpage] == 0) continue; /* Print the page header, all elements in the page, and page trailer */ printpageobject(ps, xobjs.pagelist[curpage]->pageinst->thisobject, ++page, curpage); /* For crash recovery, log the filename for each page */ if (mode == ALL_PAGES) fprintf(ps, "%% %s is_filename\n", xobjs.pagelist[curpage]->filename); fprintf(ps, "\n"); fflush(ps); } /* For crash recovery, save all objects that have been edited but are */ /* not in the list of objects already saved. */ if (mode == ALL_PAGES) { int i, j, k; objectptr thisobj; for (i = 0; i < xobjs.numlibs; i++) { for (j = 0; j < xobjs.userlibs[i].number; j++) { thisobj = *(xobjs.userlibs[i].library + j); if (thisobj->changes > 0 ) { for (k = 0; k < written; k++) if (thisobj == *(wroteobjs + k)) break; if (k == written) printobjects(ps, thisobj, &wroteobjs, &written, DEFAULTCOLOR); } } } } else { /* No unsaved changes in these objects */ setassaved(wroteobjs, written); for (i = 0; i < xobjs.pages; i++) if (pagelist[i] > 0) xobjs.pagelist[i]->pageinst->thisobject->changes = 0; xobjs.new_changes = countchanges(NULL); } /* Free allocated memory */ free((char *)pagelist); free((char *)wroteobjs); /* Done! */ fprintf(ps, "%%%%Trailer\n"); fprintf(ps, "XCIRCsave restore\n"); fprintf(ps, "%%%%EOF\n"); fclose(ps); sprintf(_STR, "File %s saved (%d page%s).", fname, multipage, (multipage > 1 ? "s" : "")); Wprintf(_STR); /* Remove the temporary redundant backup */ if (mode == ALL_PAGES) { sprintf(outname, "%sB", xobjs.tempfile); unlink(outname); } else if (!xobjs.retain_backup) { /* Remove the backup file */ sprintf(outname, "%s~", fname); unlink(outname); } /* Write LATEX strings, if any are present */ TopDoLatex(); } /*----------------------------------------------------------------------*/ /* Given a color index, print the R, G, B values */ /*----------------------------------------------------------------------*/ int printRGBvalues(char *tstr, int index, const char *postfix) { int i; for (i = 0; i < number_colors; i++) { if (colorlist[i].color.pixel == index) { sprintf(tstr, "%4.3f %4.3f %4.3f %s", (float)colorlist[i].color.red / 65535, (float)colorlist[i].color.green / 65535, (float)colorlist[i].color.blue / 65535, postfix); return 0; } } /* The program can reach this point for any color which is */ /* not listed in the table. This should not happen. */ return -1; } /*----------------------------------------------------*/ /* Write string to PostScript string, ignoring NO_OPs */ /*----------------------------------------------------*/ char *nosprint(char *sptr) { int qtmp, slen = 100; u_char *pptr, *qptr, *bptr; bptr = (u_char *)malloc(slen); /* initial length 100 */ qptr = bptr; *qptr++ = '('; /* Includes extended character set (non-ASCII) */ for (pptr = sptr; pptr && *pptr != '\0'; pptr++) { /* Ensure enough space for the string, including everything */ /* following the "for" loop */ qtmp = qptr - bptr; if (qtmp + 7 >= slen) { slen += 7; bptr = (char *)realloc(bptr, slen); qptr = bptr + qtmp; } /* Deal with non-printable characters and parentheses */ if (*pptr > (char)126) { sprintf(qptr, "\\%3o", (int)(*pptr)); qptr += 4; } else { if ((*pptr == '(') || (*pptr == ')') || (*pptr == '\\')) *qptr++ = '\\'; *qptr++ = *pptr; } } if (qptr == bptr + 1) { /* Empty string gets a NULL result, not "()" */ qptr--; } else { *qptr++ = ')'; *qptr++ = ' '; } *qptr++ = '\0'; return (char *)bptr; } /*--------------------------------------------------------------*/ /* Write label segments to the output (in reverse order) */ /*--------------------------------------------------------------*/ short writelabel(FILE *ps, stringpart *chrtop, short *stcount) { short i, segs = 0; stringpart *chrptr; char **ostr = (char **)malloc(sizeof(char *)); char *tmpstr; float lastscale = 1.0; int lastfont = -1; /* Write segments into string array, in forward order */ for (chrptr = chrtop; chrptr != NULL; chrptr = chrptr->nextpart) { ostr = (char **)realloc(ostr, (segs + 1) * sizeof(char *)); if (chrtop->type == PARAM_END) { /* NULL parameter is empty string */ ostr[segs] = (char *)malloc(4); strcpy(ostr[segs], "() "); } else { tmpstr = writesegment(chrptr, &lastscale, &lastfont); if (tmpstr[0] != '\0') ostr[segs] = tmpstr; else segs--; } segs++; } /* Write string array to output in reverse order */ for (i = segs - 1; i >= 0; i--) { dostcount(ps, stcount, strlen(ostr[i])); fputs(ostr[i], ps); free(ostr[i]); } free(ostr); return segs; } /*--------------------------------------------------------------*/ /* Write a single label segment to the output */ /* (Recursive, so we can write segments in the reverse order) */ /*--------------------------------------------------------------*/ char *writesegment(stringpart *chrptr, float *lastscale, int *lastfont) { int type = chrptr->type; char *retstr; switch(type) { case PARAM_START: sprintf(_STR, "%s ", chrptr->data.string); break; case PARAM_END: _STR[0] = '\0'; chrptr->nextpart = NULL; break; case SUBSCRIPT: sprintf(_STR, "{ss} "); break; case SUPERSCRIPT: sprintf(_STR, "{Ss} "); break; case NORMALSCRIPT: *lastscale = 1.0; sprintf(_STR, "{ns} "); break; case UNDERLINE: sprintf(_STR, "{ul} "); break; case OVERLINE: sprintf(_STR, "{ol} "); break; case NOLINE: sprintf(_STR, "{} "); break; case HALFSPACE: sprintf(_STR, "{hS} "); break; case QTRSPACE: sprintf(_STR, "{qS} "); break; case RETURN: *lastscale = 1.0; sprintf(_STR, "{CR} "); break; case TABSTOP: sprintf(_STR, "{Ts} "); break; case TABFORWARD: sprintf(_STR, "{Tf} "); break; case TABBACKWARD: sprintf(_STR, "{Tb} "); break; case FONT_NAME: if (*lastscale == 1.0) sprintf(_STR, "{/%s cf} ", fonts[chrptr->data.font].psname); else sprintf(_STR, "{/%s %5.3f cf} ", fonts[chrptr->data.font].psname, *lastscale); *lastfont = chrptr->data.font; break; case FONT_SCALE: if (*lastfont == -1) { Fprintf(stderr, "Warning: Font may not be the one that was intended.\n"); *lastfont = 0; } *lastscale = chrptr->data.scale; sprintf(_STR, "{/%s %5.3f cf} ", fonts[*lastfont].psname, *lastscale); break; case FONT_COLOR: strcpy(_STR, "{"); if (chrptr->data.color == DEFAULTCOLOR) strcat(_STR, "sce} "); else if (printRGBvalues(_STR + 1, colorlist[chrptr->data.color].color.pixel, "scb} ") < 0) strcat(_STR, "sce} "); break; case KERN: sprintf(_STR, "{%d %d Kn} ", chrptr->data.kern[0], chrptr->data.kern[1]); break; case TEXT_STRING: /* Everything except TEXT_STRING will always fit in the _STR fixed-length character array. */ return nosprint(chrptr->data.string); } retstr = (char *)malloc(1 + strlen(_STR)); strcpy(retstr, _STR); return retstr; } /*--------------------------------------------------------------*/ /* Routine to write all the label segments as stored in _STR */ /*--------------------------------------------------------------*/ int writelabelsegs(FILE *ps, short *stcount, stringpart *chrptr) { Boolean ismultipart = ((chrptr->nextpart != NULL) && (chrptr->nextpart->type != PARAM_END)) ? True : False; int segs; /* If there is only one part, but it is not a string or the */ /* end of a parameter (empty parameter), then set multipart */ /* anyway so we get the double brace {{ }}. */ if ((!ismultipart) && (chrptr->type != TEXT_STRING) && (chrptr->type != PARAM_END)) ismultipart = True; /* nextpart is not NULL if there are multiple parts to the string */ if (ismultipart) { fprintf(ps, "{"); (*stcount)++; } segs = writelabel(ps, chrptr, stcount); if (ismultipart) { fprintf(ps, "} "); (*stcount) += 2; } return segs; } /*--------------------------------------------------------------*/ /* Write the dictionary of parameters belonging to an object */ /*--------------------------------------------------------------*/ void printobjectparams(FILE *ps, objectptr localdata) { int segs; short stcount; oparamptr ops; char *ps_expr; float fp; /* Check for parameters and default values */ if (localdata->params == NULL) return; fprintf(ps, "<<"); stcount = 2; for (ops = localdata->params; ops != NULL; ops = ops->next) { fprintf(ps, "/%s ", ops->key); dostcount (ps, &stcount, strlen(ops->key) + 2); switch (ops->type) { case XC_EXPR: ps_expr = evaluate_expr(ops, NULL); if (ops->which == P_SUBSTRING || ops->which == P_EXPRESSION) { dostcount(ps, &stcount, 3 + strlen(ps_expr)); fputs("(", ps); fputs(ps_expr, ps); fputs(") ", ps); } else if (sscanf(ps_expr, "%g", &fp) == 1) { dostcount(ps, &stcount, 1 + strlen(ps_expr)); fputs(ps_expr, ps); fputs(" ", ps); } else { /* Expression evaluates to error in object */ dostcount(ps, &stcount, 2); fputs("0 ", ps); } dostcount(ps, &stcount, 7 + strlen(ops->parameter.expr)); fputs("(", ps); fputs(ops->parameter.expr, ps); fputs(") pop ", ps); free(ps_expr); break; case XC_STRING: segs = writelabelsegs(ps, &stcount, ops->parameter.string); if (segs == 0) { /* When writing object parameters, we cannot allow a */ /* NULL value. Instead, print an empty string (). */ dostcount(ps, &stcount, 3); fputs("() ", ps); } break; case XC_INT: sprintf(_STR, "%d ", ops->parameter.ivalue); dostcount(ps, &stcount, strlen(_STR)); fputs(_STR, ps); break; case XC_FLOAT: sprintf(_STR, "%g ", ops->parameter.fvalue); dostcount(ps, &stcount, strlen(_STR)); fputs(_STR, ps); break; } } fprintf(ps, ">> "); dostcount (ps, &stcount, 3); } /*--------------------------------------------------------------*/ /* Write the list of parameters belonging to an object instance */ /*--------------------------------------------------------------*/ short printparams(FILE *ps, objinstptr sinst, short stcount) { short i, iend, loccount; oparamptr ops, objops; eparamptr epp; char *ps_expr; Boolean done; /* If any expression results are being held in the instance, */ /* remove them first. Note that free_instance_param mucks */ /* with the loop variable so we iterate until clean. */ done = False; while (!done) { done = True; for (ops = sinst->params; ops != NULL; ops = ops->next) { if (ops->type != XC_EXPR) { objops = match_param(sinst->thisobject, ops->key); if (objops && (objops->type == XC_EXPR)) { free_instance_param(sinst, ops); done = False; break; } } } } if (sinst->params == NULL) return stcount; fprintf(ps, "<<"); /* begin PostScript dictionary */ loccount = stcount + 2; for (ops = sinst->params; ops != NULL; ops = ops->next) { fprintf(ps, "/%s ", ops->key); dostcount(ps, &loccount, strlen(ops->key) + 2); /* Check for indirect parameter references */ for (epp = sinst->passed; epp != NULL; epp = epp->next) { if (epp->pdata.refkey != NULL) { if (!strcmp(epp->pdata.refkey, ops->key)) { sprintf(_STR, "%s ", epp->key); dostcount(ps, &loccount, strlen(_STR)); fputs(_STR, ps); break; } } } if (epp == NULL) { /* No indirection */ switch (ops->type) { case XC_STRING: objops = match_param(sinst->thisobject, ops->key); if (objops->type != XC_EXPR) writelabelsegs(ps, &loccount, ops->parameter.string); break; case XC_EXPR: ps_expr = evaluate_expr(ops, sinst); dostcount(ps, &loccount, 3 + strlen(ps_expr)); fputs("(", ps); fputs(ps_expr, ps); fputs(") ", ps); dostcount(ps, &loccount, 3 + strlen(ops->parameter.expr)); fputs("(", ps); fputs(ops->parameter.expr, ps); fputs(") pop ", ps); free(ps_expr); break; case XC_INT: sprintf(_STR, "%d ", ops->parameter.ivalue); dostcount(ps, &loccount, strlen(_STR)); fputs(_STR, ps); break; case XC_FLOAT: sprintf(_STR, "%g ", ops->parameter.fvalue); dostcount(ps, &loccount, strlen(_STR)); fputs(_STR, ps); break; } } } fprintf(ps, ">> "); /* end PostScript dictionary */ return loccount + 3; } /*------------------------------------------------------------------*/ /* Macro for point output (calls varpcheck() on x and y components) */ /*------------------------------------------------------------------*/ #define xyvarcheck(z, n, t) \ varpcheck(ps, z.x, localdata, n, &stcount, *t, P_POSITION_X); \ varpcheck(ps, z.y, localdata, n, &stcount, *t, P_POSITION_Y) /*--------------------------------------------------------------*/ /* Main routine for writing the contents of a single object to */ /* output file "ps". */ /*--------------------------------------------------------------*/ void printOneObject(FILE *ps, objectptr localdata, int ccolor) { int i, curcolor = ccolor; genericptr *savegen, *pgen; objinstptr sobj; graphicptr sg; Imagedata *img; pointlist savept; short stcount; short segs; Boolean has_param; /* first, get a total count of all objects and give warning if large */ if ((is_page(localdata) == -1) && (localdata->parts > 255)) { sprintf(_STR, "Warning: \"%s\" may exceed printer's PS limit for definitions", localdata->name); Wprintf(_STR); } for (savegen = localdata->plist; savegen < localdata->plist + localdata->parts; savegen++) { /* change current color if different */ if ((*savegen)->color != curcolor) { if ((curcolor = (*savegen)->color) == DEFAULTCOLOR) fprintf(ps, "sce\n"); else { if (printRGBvalues(_STR, (*savegen)->color, "scb\n") < 0) { fprintf(ps, "sce\n"); curcolor = DEFAULTCOLOR; } else fputs(_STR, ps); } } stcount = 0; switch(ELEMENTTYPE(*savegen)) { case(POLYGON): varcheck(ps, TOPOLY(savegen)->style, localdata, &stcount, *savegen, P_STYLE); varfcheck(ps, TOPOLY(savegen)->width, localdata, &stcount, *savegen, P_LINEWIDTH); for (savept = TOPOLY(savegen)->points; savept < TOPOLY(savegen)-> points + TOPOLY(savegen)->number; savept++) { varpcheck(ps, savept->x, localdata, savept - TOPOLY(savegen)->points, &stcount, *savegen, P_POSITION_X); varpcheck(ps, savept->y, localdata, savept - TOPOLY(savegen)->points, &stcount, *savegen, P_POSITION_Y); } sprintf(_STR, "%hd polygon\n", TOPOLY(savegen)->number); dostcount (ps, &stcount, strlen(_STR)); fputs(_STR, ps); break; case(PATH): pgen = TOPATH(savegen)->plist; switch(ELEMENTTYPE(*pgen)) { case ARC: varpcheck(ps, (short)TOARC(pgen)->points[0].x, localdata, 0, &stcount, *pgen, P_POSITION_X); varpcheck(ps, (short)TOARC(pgen)->points[0].y, localdata, 0, &stcount, *pgen, P_POSITION_Y); break; case POLYGON: xyvarcheck(TOPOLY(pgen)->points[0], 0, pgen); break; case SPLINE: varpcheck(ps, (short)TOSPLINE(pgen)->ctrl[0].x, localdata, 0, &stcount, *pgen, P_POSITION_X); varpcheck(ps, (short)TOSPLINE(pgen)->ctrl[0].y, localdata, 0, &stcount, *pgen, P_POSITION_Y); break; } dostcount(ps, &stcount, 9); fprintf(ps, "beginpath\n"); for (pgen = TOPATH(savegen)->plist; pgen < TOPATH(savegen)->plist + TOPATH(savegen)->parts; pgen++) { switch(ELEMENTTYPE(*pgen)) { case ARC: if (abs(TOARC(pgen)->radius) == TOARC(pgen)->yaxis) { xyvarcheck(TOARC(pgen)->position, 0, pgen); varcheck(ps, abs(TOARC(pgen)->radius), localdata, &stcount, *pgen, P_RADIUS); if (TOARC(pgen)->radius < 0) { varfcheck(ps, TOARC(pgen)->angle2, localdata, &stcount, *pgen, P_ANGLE2); varfcheck(ps, TOARC(pgen)->angle1, localdata, &stcount, *pgen, P_ANGLE1); fprintf(ps, " arcn\n"); } else { varfcheck(ps, TOARC(pgen)->angle1, localdata, &stcount, *pgen, P_ANGLE1); varfcheck(ps, TOARC(pgen)->angle2, localdata, &stcount, *pgen, P_ANGLE2); fprintf(ps, " arc\n"); } } else { xyvarcheck(TOARC(pgen)->position, 0, pgen); varcheck(ps, abs(TOARC(pgen)->radius), localdata, &stcount, *pgen, P_RADIUS); varcheck(ps, TOARC(pgen)->yaxis, localdata, &stcount, *pgen, P_MINOR_AXIS); if (TOARC(pgen)->radius < 0) { varfcheck(ps, TOARC(pgen)->angle2, localdata, &stcount, *pgen, P_ANGLE2); varfcheck(ps, TOARC(pgen)->angle1, localdata, &stcount, *pgen, P_ANGLE1); fprintf(ps, " nellip\n"); } else { varfcheck(ps, TOARC(pgen)->angle1, localdata, &stcount, *pgen, P_ANGLE1); varfcheck(ps, TOARC(pgen)->angle2, localdata, &stcount, *pgen, P_ANGLE2); fprintf(ps, " pellip\n"); } } break; case POLYGON: for (savept = TOPOLY(pgen)->points + TOPOLY(pgen)->number - 1; savept > TOPOLY(pgen)->points; savept--) { varpcheck(ps, savept->x, localdata, savept - TOPOLY(pgen)->points, &stcount, *pgen, P_POSITION_X); varpcheck(ps, savept->y, localdata, savept - TOPOLY(pgen)->points, &stcount, *pgen, P_POSITION_Y); } sprintf(_STR, "%hd polyc\n", TOPOLY(pgen)->number - 1); dostcount (ps, &stcount, strlen(_STR)); fputs(_STR, ps); break; case SPLINE: xyvarcheck(TOSPLINE(pgen)->ctrl[1], 1, pgen); xyvarcheck(TOSPLINE(pgen)->ctrl[2], 2, pgen); xyvarcheck(TOSPLINE(pgen)->ctrl[3], 3, pgen); fprintf(ps, "curveto\n"); break; } } varcheck(ps, TOPATH(savegen)->style, localdata, &stcount, *savegen, P_STYLE); varfcheck(ps, TOPATH(savegen)->width, localdata, &stcount, *savegen, P_LINEWIDTH); fprintf(ps, "endpath\n"); break; case(SPLINE): varcheck(ps, TOSPLINE(savegen)->style, localdata, &stcount, *savegen, P_STYLE); varfcheck(ps, TOSPLINE(savegen)->width, localdata, &stcount, *savegen, P_LINEWIDTH); xyvarcheck(TOSPLINE(savegen)->ctrl[1], 1, savegen); xyvarcheck(TOSPLINE(savegen)->ctrl[2], 2, savegen); xyvarcheck(TOSPLINE(savegen)->ctrl[3], 3, savegen); xyvarcheck(TOSPLINE(savegen)->ctrl[0], 0, savegen); fprintf(ps, "spline\n"); break; case(ARC): varcheck(ps, TOARC(savegen)->style, localdata, &stcount, *savegen, P_STYLE); varfcheck(ps, TOARC(savegen)->width, localdata, &stcount, *savegen, P_LINEWIDTH); xyvarcheck(TOARC(savegen)->position, 0, savegen); varcheck(ps, abs(TOARC(savegen)->radius), localdata, &stcount, *savegen, P_RADIUS); if (abs(TOARC(savegen)->radius) == TOARC(savegen)->yaxis) { varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount, *savegen, P_ANGLE1); varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount, *savegen, P_ANGLE2); fprintf(ps, "xcarc\n"); } else { varcheck(ps, abs(TOARC(savegen)->yaxis), localdata, &stcount, *savegen, P_MINOR_AXIS); varfcheck(ps, TOARC(savegen)->angle1, localdata, &stcount, *savegen, P_ANGLE1); varfcheck(ps, TOARC(savegen)->angle2, localdata, &stcount, *savegen, P_ANGLE2); fprintf(ps, "ellipse\n"); } break; case(OBJINST): sobj = TOOBJINST(savegen); varfcheck(ps, sobj->scale, localdata, &stcount, *savegen, P_SCALE); varcheck(ps, sobj->rotation, localdata, &stcount, *savegen, P_ROTATION); xyvarcheck(sobj->position, 0, savegen); stcount = printparams(ps, sobj, stcount); fprintf(ps, "%s\n", sobj->thisobject->name); break; case(GRAPHIC): sg = TOGRAPHIC(savegen); for (i = 0; i < xobjs.images; i++) { img = xobjs.imagelist + i; if (img->image == sg->source) break; } fprintf(ps, "/%s ", img->filename); stcount += (2 + strlen(img->filename)); varfcheck(ps, sg->scale, localdata, &stcount, *savegen, P_SCALE); varcheck(ps, sg->rotation, localdata, &stcount, *savegen, P_ROTATION); xyvarcheck(sg->position, 0, savegen); fprintf(ps, "graphic\n"); break; case(LABEL): /* strip schematic pins and labels if schematic capture is off */ if (TOLABEL(savegen)->pin && !areastruct.schemon) break; /* Don't save temporary labels from schematic capture system */ else if (TOLABEL(savegen)->string->type != FONT_NAME) break; /* Check for parameter --- must use "mark" to count # segments */ has_param = hasparameter(TOLABEL(savegen)); if (has_param) { fprintf(ps, "mark "); stcount += 5; } segs = writelabel(ps, TOLABEL(savegen)->string, &stcount); if (segs > 0) { if (has_param) sprintf(_STR, "ctmk "); else sprintf(_STR, "%hd ", segs); dostcount(ps, &stcount, strlen(_STR)); fputs(_STR, ps); varcheck(ps, TOLABEL(savegen)->justify, localdata, &stcount, *savegen, P_JUSTIFY); varcheck(ps, TOLABEL(savegen)->rotation, localdata, &stcount, *savegen, P_ROTATION); varfcheck(ps, TOLABEL(savegen)->scale, localdata, &stcount, *savegen, P_SCALE); xyvarcheck(TOLABEL(savegen)->position, 0, savegen); if (areastruct.schemon) { switch(TOLABEL(savegen)->pin) { case LOCAL: strcpy(_STR, "pinlabel\n"); break; case GLOBAL: strcpy(_STR, "pinglobal\n"); break; case INFO: strcpy(_STR, "infolabel\n"); break; default: strcpy(_STR, "label\n"); } } else strcpy(_STR, "label\n"); dostcount(ps, &stcount, strlen(_STR)); fputs(_STR, ps); } break; } } } /*----------------------------------------------------------------------*/ /* Recursive routine to print out the library objects used in this */ /* drawing, starting at the bottom of the object hierarchy so that each */ /* object is defined before it is called. A list of objects already */ /* written is maintained so that no object is written twice. */ /* */ /* When object "localdata" is not a top-level page, call this routine */ /* with mpage=-1 (simpler than checking whether localdata is a page). */ /*----------------------------------------------------------------------*/ void printobjects(FILE *ps, objectptr localdata, objectptr **wrotelist, short *written, int ccolor) { genericptr *gptr; objectptr *optr; oparamptr ops; int curcolor = ccolor; /* Search among the list of objects already written to the output */ /* If this object has been written previously, then we ignore it. */ for (optr = *wrotelist; optr < *wrotelist + *written; optr++) if (*optr == localdata) return; /* If this page is a schematic, write out the definiton of any symbol */ /* attached to it, because that symbol may not be used anywhere else. */ if (localdata->symschem && (localdata->schemtype == PRIMARY)) printobjects(ps, localdata->symschem, wrotelist, written, curcolor); /* Search for all object definitions instantiated in this object, */ /* and (recursively) print them to the output. */ for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++) if (IS_OBJINST(*gptr)) printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written, curcolor); /* Update the list of objects already written to the output */ *wrotelist = (objectptr *)realloc(*wrotelist, (*written + 1) * sizeof(objectptr)); *(*wrotelist + *written) = localdata; (*written)++; fprintf(ps, "/%s {\n", localdata->name); /* No longer writes "bbox" record */ if (localdata->hidden == True) fprintf(ps, "%% hidden\n"); /* For symbols with schematics, and "trivial" schematics */ if (areastruct.schemon) { if (localdata->symschem != NULL) fprintf(ps, "%% %s is_schematic\n", localdata->symschem->name); else if (localdata->schemtype == TRIVIAL) fprintf(ps, "%% trivial\n"); } printobjectparams(ps, localdata); fprintf(ps, "begingate\n"); /* Write all the elements in order */ printOneObject(ps, localdata, curcolor); /* Write object (gate) trailer */ fprintf(ps, "endgate\n} def\n\n"); } /*--------------------------------------------------------------*/ /* Print a page header followed by everything in the page. */ /* this routine assumes that all objects used by the page have */ /* already been handled and written to the output. */ /* */ /* "page" is the page number, counting consecutively from one. */ /* "mpage" is the page number in xcircuit's pagelist structure. */ /*--------------------------------------------------------------*/ void printpageobject(FILE *ps, objectptr localdata, short page, short mpage) { genericptr *gptr; XPoint origin, corner; objinstptr writepage; int width, height; float psnorm, psscale; float xmargin, ymargin; char *rootptr; polyptr framebox; /* Output page header information */ rootptr = strrchr(xobjs.pagelist[mpage]->filename, '/'); if (rootptr == NULL) rootptr = xobjs.pagelist[mpage]->filename; else rootptr++; writepage = xobjs.pagelist[mpage]->pageinst; psnorm = xobjs.pagelist[mpage]->outscale; psscale = getpsscale(psnorm, mpage); /* Determine the margins (offset of drawing from page corner) */ /* If a bounding box has been declared in the drawing, it is */ /* centered on the page. Otherwise, the drawing itself is */ /* centered on the page. If encapsulated, the bounding box */ /* encompasses only the object itself. */ width = toplevelwidth(writepage, &origin.x); height = toplevelheight(writepage, &origin.y); corner.x = origin.x + width; corner.y = origin.y + height; if (xobjs.pagelist[mpage]->pmode & 1) { /* full page */ if (xobjs.pagelist[mpage]->orient == 90) { xmargin = (xobjs.pagelist[mpage]->pagesize.x - ((float)height * psscale)) / 2; ymargin = (xobjs.pagelist[mpage]->pagesize.y - ((float)width * psscale)) / 2; } else { xmargin = (xobjs.pagelist[mpage]->pagesize.x - ((float)width * psscale)) / 2; ymargin = (xobjs.pagelist[mpage]->pagesize.y - ((float)height * psscale)) / 2; } /* If a framebox is declared, then we adjust the page to be */ /* centered on the framebox by translating through the */ /* difference between the object center and the framebox */ /* center. */ if ((framebox = checkforbbox(localdata)) != NULL) { int i, fcentx = 0, fcenty = 0; for (i = 0; i < framebox->number; i++) { fcentx += framebox->points[i].x; fcenty += framebox->points[i].y; } fcentx /= framebox->number; fcenty /= framebox->number; xmargin += psscale * (float)(origin.x + (width >> 1) - fcentx); ymargin += psscale * (float)(origin.y + (height >> 1) - fcenty); } } else { /* encapsulated --- should have 1" border so that any */ /* drawing passed directly to a printer will not clip */ xmargin = ymargin = 72; } /* If the page label is just the root name of the file, or has been left */ /* as "Page n" or "Page_n", just do the normal page numbering. Otherwise, */ /* write out the page label explicitly. */ if ((!strcmp(rootptr, localdata->name)) || (strchr(localdata->name, ' ') != NULL) || (strstr(localdata->name, "Page_") != NULL)) fprintf (ps, "%%%%Page: %d %d\n", page, page); else fprintf (ps, "%%%%Page: %s %d\n", localdata->name, page); if (xobjs.pagelist[mpage]->orient == 90) fprintf (ps, "%%%%PageOrientation: Landscape\n"); else fprintf (ps, "%%%%PageOrientation: Portrait\n"); if (xobjs.pagelist[mpage]->pmode & 1) /* full page */ fprintf(ps, "%%%%PageBoundingBox: 0 0 %d %d\n", xobjs.pagelist[mpage]->pagesize.x, xobjs.pagelist[mpage]->pagesize.y); /* Encapsulated files do not get a PageBoundingBox line */ /* fprintf(ps, "%%%%PageBoundingBox: 0 0 %d %d\n", (int)((float)width * psscale) + 114, (int)((float)height * psscale) + 114); */ fprintf (ps, "/pgsave save def bop\n"); /* Top-page definitions */ if (localdata->params != NULL) { printobjectparams(ps, localdata); fprintf(ps, "begin\n"); } if (localdata->symschem != NULL) { if (is_page(localdata->symschem) == -1) fprintf(ps, "%% %s is_symbol\n", localdata->symschem->name); else fprintf(ps, "%% %s is_primary\n", localdata->symschem->name); } /* Extend bounding box around schematic pins */ extendschembbox(xobjs.pagelist[mpage]->pageinst, &origin, &corner); if (xobjs.pagelist[mpage]->drawingscale.x != 1 || xobjs.pagelist[mpage]->drawingscale.y != 1) fprintf(ps, "%% %hd:%hd drawingscale\n", xobjs.pagelist[mpage]->drawingscale.x, xobjs.pagelist[mpage]->drawingscale.y); if (xobjs.pagelist[mpage]->gridspace != 32 || xobjs.pagelist[mpage]->snapspace != 16) fprintf(ps, "%% %4.2f %4.2f gridspace\n", xobjs.pagelist[mpage]->gridspace, xobjs.pagelist[mpage]->snapspace); if (xobjs.pagelist[mpage]->background.name != (char *)NULL) { float iscale = (xobjs.pagelist[mpage]->coordstyle == CM) ? CMSCALE : INCHSCALE; if (xobjs.pagelist[mpage]->orient == 90) fprintf(ps, "%5.4f %d %d 90 psinsertion\n", psnorm, (int)(ymargin - xmargin), -((int)((float)(corner.y - origin.y) * psscale) + (int)(xmargin + ymargin))); else fprintf(ps, "%5.4f %d %d 0 psinsertion\n", psnorm, (int)(xmargin / psscale) - origin.x, (int)(ymargin / psscale) - origin.y); savebackground(ps, xobjs.pagelist[mpage]->background.name); fprintf(ps, "\nend_insert\n"); } if (xobjs.pagelist[mpage]->orient == 90) fprintf(ps, "90 rotate %d %d translate\n", (int)(ymargin - xmargin), -((int)((float)(corner.y - origin.y) * psscale) + (int)(xmargin + ymargin))); fprintf(ps, "%5.4f ", psnorm); switch(xobjs.pagelist[mpage]->coordstyle) { case CM: fprintf(ps, "cmscale\n"); break; default: fprintf(ps, "inchscale\n"); break; } /* Final scale and translation */ fprintf(ps, "%5.4f setlinewidth %d %d translate\n\n", 1.3 * xobjs.pagelist[mpage]->wirewidth, (int)(xmargin / psscale) - origin.x, (int)(ymargin / psscale) - origin.y); /* Output all the elements in the page */ printOneObject(ps, localdata, DEFAULTCOLOR); /* Page trailer */ if (localdata->params != NULL) fprintf(ps, "end "); fprintf(ps, "pgsave restore showpage\n"); } /*--------------------------------------------------------------*/ /* Print objects referenced from a particular page. These get */ /* bundled together at the beginning of the output file under */ /* the DSC "Setup" section, so that the PostScript */ /* interpreter knows that these definitions may be used by any */ /* page. This prevents ghostscript from producing an error */ /* when backing up in a multi-page document. */ /*--------------------------------------------------------------*/ void printrefobjects(FILE *ps, objectptr localdata, objectptr **wrotelist, short *written) { genericptr *gptr; /* If this page is a schematic, write out the definiton of any symbol */ /* attached to it, because that symbol may not be used anywhere else. */ if (localdata->symschem && (localdata->schemtype == PRIMARY)) printobjects(ps, localdata->symschem, wrotelist, written, DEFAULTCOLOR); /* Search for all object definitions instantiated on the page and */ /* write them to the output. */ for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++) if (IS_OBJINST(*gptr)) printobjects(ps, TOOBJINST(gptr)->thisobject, wrotelist, written, DEFAULTCOLOR); } /*----------------------------------------------------------------------*/