Logo Search packages:      
Sourcecode: xcircuit version File versions

files.c

/*-----------------------------------------------------------------------*/
/* files.c --- file handling routines for xcircuit                 */
/* Copyright (c) 2002  Tim Edwards, Johns Hopkins University             */
/*-----------------------------------------------------------------------*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <pwd.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#ifdef TCL_WRAPPER 
#include <tk.h>
#else
#include "Xw/TextEdit.h"   /* for XwTextCopyBuffer() */
#endif

/*------------------------------------------------------------------------*/
/* Local includes                                                         */
/*------------------------------------------------------------------------*/

#include "colordefs.h"
#include "xcircuit.h"

/*----------------------------------------------------------------------*/
/* Function prototype declarations                                      */
/*----------------------------------------------------------------------*/
#include "prototypes.h"

/*------------------------------------------------------------------------*/
/* 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

/* type checks */

#define IS_POLYGON(a)   ((*a)->type == POLYGON)
#define IS_LABEL(a)     ((*a)->type == LABEL)
#define IS_OBJINST(a)   ((*a)->type == OBJECT)
#define IS_ARC(a) ((*a)->type == ARC)
#define IS_SPLINE(a)    ((*a)->type == SPLINE)
#define IS_PATH(a)      ((*a)->type == PATH)

/*------------------------------------------------------------------------*/
/* 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      */
/*------------------------------------------------------*/

void ridnewline(char *sptr)
{
   char *tstrp;

   for (tstrp = sptr; *tstrp != '\0' && *tstrp != '\n'; tstrp++);
   if (*tstrp == '\n') *tstrp = '\0';
}

/*------------------------------------------------------*/
/* Free memory allocated to a label string            */
/*------------------------------------------------------*/

void freelabel(stringpart *string)
{
   stringpart *strptr = string, *tmpptr;

   while (strptr != NULL) {
      if (strptr->type == TEXT_STRING)
         free(strptr->data.string);
      tmpptr = strptr->nextpart;
      free(strptr);
      strptr = tmpptr;
   }
}

/*------------------------------------------------------*/
/* Free memory for a single object              */
/*------------------------------------------------------*/

void free_single(genericptr *genobj)
{
   int i;
   objinstptr geninst;

   if (IS_POLYGON(genobj)) free(((polyptr)(*genobj))->points);
   else if (IS_LABEL(genobj)) freelabel(((labelptr)(*genobj))->string);
   else if (IS_PATH(genobj)) free(((pathptr)(*genobj))->plist);
   else if (IS_OBJINST(genobj)) {
      geninst = TOOBJINST(genobj);
      if (geninst->params != NULL) {
         for (i = 0; i < geninst->thisobject->num_params; i++) {
          if (geninst->params[i] != NULL) {
             if (geninst->params[i]->type == XC_STRING)
              freelabel(geninst->params[i]->parameter.string);
             free (geninst->params[i]);
          }
       }
         free(geninst->params);
      }
   }
   free(*genobj);
}

/*---------------------------------------------------------*/
/* Reset an object structure by freeing all alloc'd memory */
/*---------------------------------------------------------*/

void reset(objectptr localdata, short mode)
{
   short i;

#ifdef SCHEMA
   if (localdata->netlist != NULL)
      destroynets(localdata);
#endif

   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(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);

   destroyselects();

   /* 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->num_params = 0;

   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;

#ifdef SCHEMA
   localdata->highlight.netid = 0;
   localdata->highlight.thisinst = NULL;
   localdata->schemtype = SCHEMATIC;
   localdata->symschem = NULL;
   localdata->netlist = NULL;
   localdata->portlist = NULL;
   localdata->calllist = NULL;
   localdata->valid = False;
   localdata->traversed = False;
   localdata->devname = NULL;
#endif
}

/*--------------------------------------------------------------*/
/* Exhaustively compare the contents of two objects and return  */
/* true if equivalent, false if not.                        */
/*--------------------------------------------------------------*/

Boolean elemcompare(genericptr *compgen, genericptr *gchk)
{
   Boolean bres;
   switch((*compgen)->type) {
      case(ARC):
       bres = (TOARC(compgen)->position.x == TOARC(gchk)->position.x &&
            TOARC(compgen)->position.y == TOARC(gchk)->position.y &&
          TOARC(compgen)->num_params == TOARC(gchk)->num_params &&  /* more! */
          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)->num_params == TOSPLINE(gchk)->num_params &&  /* more! */
          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)->num_params == TOPOLY(gchk)->num_params && /* more! */
             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((*compgen)->type) {
       case(OBJECT):{
          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->num_params == oldobj->num_params &&     /* more! */
                  newobj->thisobject == oldobj->thisobject &&
                  newobj->thisobject->num_params
                        == oldobj->thisobject->num_params); /* more! */
          } 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)->num_params == TOLABEL(gchk)->num_params &&
#ifdef SCHEMA
                  ((TOLABEL(compgen)->pin == TOLABEL(gchk)->pin) ||
                        !areastruct.schemon) &&
#endif
                  !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)->num_params == TOPATH(gchk)->num_params &&
                  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 */

   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) {
      if (obja->num_params != objb->num_params) return False;
      else {
       short i;
       for (i = 0; i < obja->num_params; i++) {
          oparamptr opsa = *(obja->params + i);
          oparamptr opsb = *(objb->params + i);
          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_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;

#ifdef SCHEMA
   /* 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;
#endif

   return(True);
}

/*------------------------*/
/* scale renormalization  */
/*------------------------*/

float getpsscale(float value, short page)
{
   if (xobjs.pagelist[page]->coordstyle == FRAC_INCH ||
       xobjs.pagelist[page]->coordstyle == DEC_INCH)
      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 (e.g., "v1", "v2", etc.).          */
/*----------------------------------------------------------------------*/

void varpcheck(FILE *ps, short value, objectptr localdata, int addin,
      int pointno, short *stptr, genericptr thiselem, u_char which)
{
   oparamptr ops;
   int i, paramno;
   eparamptr epp;
   Boolean done = False;

   if (thiselem->passed != NULL) {
      for (i = 0; i < thiselem->num_params; i++) {
       epp = thiselem->passed + i;
       if (epp->pointno != pointno) continue;
       paramno = epp->paramno;
       ops = localdata->params[paramno];
       if (ops != NULL) {
          if (ops->which == which) {
             sprintf(_STR, "v%d ", paramno + 1);
             done = True;
             break;
          }
       }
      }
   }

   if (!done)
      sprintf(_STR, "%d ", (int)value + addin);

   dostcount (ps, stptr, strlen(_STR));
   fputs(_STR, ps);
}

/*----------------------------------------------------------------------*/
/* like varpcheck(), but without offset to add in                 */
/*----------------------------------------------------------------------*/

void varcheck(FILE *ps, short value, objectptr localdata,
      short *stptr, genericptr thiselem, u_char which)
{
   varpcheck(ps, value, localdata, 0, 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;
   int i, paramno;
   eparamptr epp;
   Boolean done = False;

   if (thiselem->passed != NULL) {
      for (i = 0; i < thiselem->num_params; i++) {
       epp = thiselem->passed + i;
       paramno = epp->paramno;
       ops = localdata->params[paramno];
       if (ops != NULL) {
          if (ops->which == which) {
             sprintf(_STR, "v%d ", paramno + 1);
             done = True;
             break;
          }
       }
      }
   }

   if (!done)
      sprintf(_STR, "%3.2f ", value);

   dostcount (ps, stptr, strlen(_STR));
   fputs(_STR, ps);
}

/*-------------------------------------------------------*/
/* Load a PostScript or Python (interpreter script) file */
/*-------------------------------------------------------*/

void getfile(xcWidget button, pointertype mode, caddr_t nulldata)
{
   buttonsave *savebutton;
   static void ((*runprog[LOAD_MODES])()) =
      {startloadfile, importfile, loadbackground, execscript, crashrecover};
   static char *substr[LOAD_MODES] = 
      {"load", "import", "render", "execute", "recover"};
   char *promptstr = NULL;

   if (is_page(topobject) == -1) {
      Wprintf("Can only read file into top-level page!");
      return;
   }
   else if ((int)mode >= LOAD_MODES) {
      Wprintf("Unknown mode passed to routine getfile()\n");
      return;
   }
   savebutton = (buttonsave *)malloc(sizeof(buttonsave));
   getgeneric(savebutton, button, getfile, (void *)mode);
   if ((int)mode == 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, runprog[(int)mode], savebutton, NULL);
      if (cfile) free(cfile);
   }
   else {
      promptstr = (char *)malloc(18 + strlen(substr[(int)mode]));
      sprintf(promptstr, "Select file to %s:", substr[(int)mode]);
      popupprompt(button, promptstr, "\0", runprog[(int)mode], savebutton,
            ((int)mode == SCRIPT) ?
#ifdef HAVE_PYTHON
            "py"
#else
            ""
#endif
            : "ps");
   }
   free(promptstr);
}

/*--------------------------------------------------------------*/
/* Tilde ('~') expansion in file name (stored in _STR)            */
/*--------------------------------------------------------------*/

Boolean xc_tilde_expand(char *filename)
{
   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;
} 

/*---------------------------------------------------------*/

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();
      loadlibrary(ilib);
      /* if (ilib == tlib) zoomview(NULL, NULL, NULL); */
   }
   if (lflag)
      lflag = False;
   else
      ilib = createlibrary();
   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);
      }
   }
}

/*------------------------------------------------------*/
/* 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 inname[150], temp[150], keyword[30], percentc;

   xc_tilde_expand(_STR);
   sscanf(_STR, "%149s", inname);

   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", _STR);
         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, _STR);
                ps = fopen(inname, "r");
                if (ps == NULL) {
             sprintf(inname, "%s/%s.lps", tmp_s, _STR);
             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, _STR);
             ps = fopen(inname, "r");
             if (ps == NULL) {
                sprintf(inname, "%s/fonts/%s.lps", tmp_s, _STR);
                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, _STR);
                ps = fopen(inname, "r");
                if (ps == NULL) {
             sprintf(inname, "%s/%s.lps", BUILTINS_DIR, _STR);
             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, _STR);
             ps = fopen(inname, "r");
             if (ps == NULL) {
                sprintf(inname, "%s/fonts/%s.lps", BUILTINS_DIR, _STR);
                ps = fopen(inname, "r");
             }
             if (ps == NULL) Fprintf(stdout, "%s not found.\n", inname);
          }
          if (ps == NULL) {
               Wprintf("No library file found.");
               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.");
         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);

   /* Fprintf(stdout, "Loaded library %s\n", inname); */
   sprintf(_STR, "Loaded library %s", inname);
   Wprintf(_STR);

   version = PROG_VERSION;
}

/*---------------------------------------------------------*/

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);

#ifdef SCHEMA
   setsymschem();
#endif
}

/*------------------------------------------------------*/
/* 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);
}

/*--------------------------------------------------------------*/
/* 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.                 */
/*--------------------------------------------------------------*/

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;

   /* 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

   sscanf(_STR, "%149s", inname);
   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 (!strcmp(pdchar, "%BoundingBox:")) {
          sscanf(temp, "%*s %hd %hd %hd %hd", &offx, &offy,
            &(pagesize.x), &(pagesize.y));
          pagesize.x += offx;
          pagesize.y += offy;
       }
      }
#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);

      /* 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;
       }
      }
#ifdef SCHEMA
      else topobject->valid = False;
#endif

      /* read to the "scale" line, picking up inch/cm type, drawing */
      /* scale, and grid/snapspace along the way                */

      for(;;) {
         if (strstr(temp, "offsets") != NULL) {
            sscanf(temp, "%c %hd %hd %*s", &percentc, &offx, &offy);
            if(percentc != '%') {
               Wprintf("Something wrong in offsets line.");
               offx = offy = 0;
            }
       }

       if (strstr(temp, "drawingscale") != NULL)
          sscanf(temp, "%*c %hd:%hd %*s",
            &xobjs.pagelist[areastruct.page]->drawingscale.x,
            &xobjs.pagelist[areastruct.page]->drawingscale.y);
#ifdef SCHEMA
       else if (strstr(temp, "is_symbol") != NULL) {
          sscanf(temp, "%*c %49s", teststr);
          checkschem(topobject, teststr);
       }
#endif
       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);
       }

       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.                */
   objectread(ps, topobject, 0, 0, USERLIB, temp, DEFAULTCOLOR);
   cleanupaliases(USERLIB);

   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;
}

/*------------------------------------------------------*/
/* 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));
}

/*------------------------------------------------------*/
/* Find matching parenthesis, bracket, or brace       */
/* Don't count when backslash character '\' is in front */
/*------------------------------------------------------*/

u_char *find_match(u_char *fstring)
{
   int count = 1;

   u_char *search = fstring; 
   u_char source = *fstring;
   u_char target;

   switch(source) {
      case '(':  target = ')'; break;
      case '[':  target = ']'; break;
      case '{':  target = '}'; break;
      default:   target = 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, areastruct.topinstance);
          }
          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;
   int paramno;
   char *endptr, *segptr = lineptr;
   stringpart *newpart;

   while (*segptr != '\0') {  /* Look through all segments */

      while (isspace(*segptr) && (*segptr != '\0')) segptr++;

      if (*segptr == '(' || *segptr == '{') {
         endptr = find_match(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 (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, "sce")) {  /* revert to default color */
            newpart->type = FONT_COLOR;
          newpart->data.color = DEFAULTCOLOR;
         }
         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 v1, v2, v3,...      */
      /* The parameter (default and/or substitution value) is     */
      /* assumed to exist.                            */

      else {

         if (sscanf(segptr + 1, "%d", &paramno) == 1) {
          newpart->type = PARAM_START;
          newpart->data.paramno = paramno - 1;

          /* check for compatibility between the parameter value and    */
          /* the number of parameters and parameter type.         */

          if (localdata->num_params < paramno) {
             Fprintf(stderr, "readlabel() error:  Parameter %d exceeds declared "
                "number of parameters (%d)!\n", paramno, localdata->num_params);
             deletestring(newpart, strhead, areastruct.topinstance);
          }
          else if ((localdata->params[paramno - 1])->type != XC_STRING) {
             Fprintf(stderr, "readlabel() error:  Parameter %d is not a string "
                  "but was called as one!\n", paramno);
             deletestring(newpart, strhead, areastruct.topinstance);
          }

            /* Fprintf(stdout, "Parameter %d called from object %s\n",  */
          /* paramno,   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 */
/*---------------------------------------------------*/

void readparams(objinstptr newinst, objectptr libobj, char *buffer)
{
   char *arrayptr, *endptr;
   int i;

   if ((arrayptr = strchr(buffer, '[')) != NULL) {
      newinst->params = (oparamptr *)malloc(
            libobj->num_params * sizeof(oparamptr));
      endptr = find_match(arrayptr);

      /* move to next non-space token after opening bracket */
      arrayptr++;
      while (isspace(*arrayptr) && *arrayptr != '\0') arrayptr++;

      for (i = 0; i < libobj->num_params; i++) {
       if (*arrayptr == '\0' || arrayptr == endptr)
          newinst->params[i] = NULL;
       else {
          newinst->params[i] = (oparamptr)malloc(sizeof(oparam));

          /* Fill in "which" entry from the object default */
          newinst->params[i]->which = libobj->params[i]->which;

          if (*arrayptr == '(' || *arrayptr == '{') { 
             char *substrend, csave;
             stringpart *endpart;

             /* type XC_STRING */

             newinst->params[i]->type = XC_STRING;
             newinst->params[i]->which = P_SUBSTRING;
             newinst->params[i]->parameter.string = NULL;
             substrend = find_match(arrayptr);
             csave = *(++substrend);
             *substrend = '\0';
             if (*arrayptr == '{') arrayptr++;
             readlabel(libobj, arrayptr,
                  &(newinst->params[i]->parameter.string));

             *substrend = csave;
             arrayptr = substrend;
             while (isspace(*arrayptr) && *arrayptr != '\0')
              arrayptr++;

             /* Append a PARAM_END to the parameter string */

             endpart = makesegment(&(newinst->params[i]->parameter.string),
                        NULL);
             endpart->type = PARAM_END;
          }
          else {

             /* type XC_FLOAT or XC_INT */

             oparamptr ops;
             if (newinst->thisobject->params == NULL)
                Fprintf(stdout, "Error: parameter list does not exist!\n");
             else {
              ops = newinst->thisobject->params[i];
              if (ops == NULL)
                 Fprintf(stdout, "Error: parameter does not exist!\n");
              else {
                 newinst->params[i]->type = ops->type;
                 if (ops->type == XC_FLOAT) {
                  sscanf(arrayptr, "%f",
                        &(newinst->params[i]->parameter.fvalue));
                  /* Fprintf(stdout, "Object %s called with parameter "
                        "%d value %3.2f\n", newinst->thisobject->name,
                        i + 1, newinst->params[i]->parameter.fvalue); */
                 }
                 else if (ops->type == XC_INT) {
                  sscanf(arrayptr, "%d",
                        &(newinst->params[i]->parameter.ivalue));
                  /* Fprintf(stdout, "Object %s called with parameter "
                        "%d value %d\n", newinst->thisobject->name,
                        i + 1, newinst->params[i]->parameter.ivalue); */
                 }
                 else
                    Fprintf(stderr, "Error: unknown parameter type!\n");
              }
             }
             arrayptr = advancetoken(arrayptr);
          }
       }
      }
   }

   /* Calculate the unique bounding box for the instance */

   if (libobj->num_params != 0) {
      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;
   int paramno;
   eparamptr epp;
   char *varchr;

   if (sscanf(lineptr, "%hd", hvalue) != 1) {
      if ((varchr = strchr(lineptr, 'v')) != NULL) {
         if (sscanf(varchr + 1, "%d", &paramno) == 1) {
            ops = localdata->params[paramno - 1];
          thiselem->num_params++;
          if (thiselem->passed == NULL)
             thiselem->passed = (eparamptr)malloc(sizeof(eparam));
          else
             thiselem->passed = (eparamptr)realloc(thiselem->passed,
                  thiselem->num_params * sizeof(eparam));
          epp = thiselem->passed + thiselem->num_params - 1;
          epp->paramno = paramno - 1;
          epp->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 has no default?");
          }
       }
       else 
          Wprintf("Error in reading parameter point substitution");
      }
      else
       Wprintf("Error in point specification");
   }

   *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;
   int paramno;
   eparamptr epp;
   char *varchr;

   if (sscanf(lineptr, "%f", fvalue) != 1) {
      if ((varchr = strchr(lineptr, 'v')) != NULL) {
         if (sscanf(varchr + 1, "%d", &paramno) == 1) {
            ops = localdata->params[paramno - 1];
          thiselem->num_params++;
          if (thiselem->passed == NULL)
             thiselem->passed = (eparamptr)malloc(sizeof(eparam));
          else
             thiselem->passed = (eparamptr)realloc(thiselem->passed,
                  thiselem->num_params * sizeof(eparam));
          epp = thiselem->passed + thiselem->num_params - 1;
          epp->paramno = paramno - 1;
          epp->pointno = 0;
          if (ops != NULL) {
             ops->which = which;
             *fvalue = ops->parameter.fvalue;
          }
          else
             Fprintf(stderr, "Error: no parameter defined!\n");
       }
       else 
          Wprintf("Error in reading parameter point substitution");
      }
      else
       Wprintf("Error in point specification");
   }

   /* 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 = OBJECT;
   objectdefaults(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;
}

/*--------------------------------------------------------------*/
/* 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 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;
   int i, j, k;
   objinstptr *newinst, newobjinst;

   /* 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);
          return False;  /* end of page */
       }

       /* make a color change, adding the color if necessary */

       else if (!strcmp(keyword, "scb")) {
          float red, green, blue;
          sscanf(buffer, "%f %f %f", &red, &green, &blue);
          curcolor = rgb_alloccolor((int)(red * 65535), (int)(green * 65535),
            (int)(blue * 65535)); 
          addnewcolorentry(curcolor);
       }

       /* end the color change, returning to default */

       else if (!strcmp(keyword, "sce")) curcolor = ccolor;

       /* 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)->num_params = 0;
          (*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);
       }

       /* 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)->num_params = 0;
          (*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)->num_params = 0;
          (*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)->num_params = 0;
          (*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)->num_params = 0;
          (*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)->num_params = 0;
          (*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++;
         }

       /* read ellipses */

         else if (!strcmp(keyword, "ellipse")) {
          arcptr *newarc;
       
          NEW_ARC(newarc, localdata);

          (*newarc)->color = curcolor;
          (*newarc)->num_params = 0;
          (*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++;
         }

         /* 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)->num_params = 0;
          (*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++;
         }

       /* read spline curves */

         else if (!strcmp(keyword, "spline")) {
            splineptr *newspline;

          NEW_SPLINE(newspline, localdata);
          (*newspline)->color = curcolor;
          (*newspline)->num_params = 0;
          (*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);
         }

         /* 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)->num_params = 0;
          (*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;

#ifdef SCHEMA
          (*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")) {
              localdata->schemtype = FUNDAMENTAL;
              (*newlabel)->pin = INFO;
              if (curcolor == DEFAULTCOLOR)
                 (*newlabel)->color = INFOLABELCOLOR;
             }
          }
#endif
          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);

#ifdef SCHEMA

          /* link string to the one that corresponds to this one */
          /* in the symbol, if it exists.                */

          if (areastruct.schemon && localdata->symschem != NULL &&
              (*newlabel)->pin && ((*newlabel)->pin != INFO)) {
             genericptr *pgen;
             for (pgen = localdata->symschem->plist; pgen < localdata->
                  symschem->plist + localdata->symschem->parts; pgen++) {
              if ((*pgen)->type == LABEL)
                 if (!stringcomp(TOLABEL(pgen)->string, (*newlabel)->string))
                  break;
             }
             if (pgen == localdata->symschem->plist + localdata->symschem->parts)
              Wprintf("Error:  Unattached pin");
          }
#endif

          localdata->parts++;
         }

       /* read symbol-to-schematic connection */

#ifdef SCHEMA
       else if (!strcmp(keyword, "is_schematic")) {
          char tempstr[50];
          for (lineptr = buffer; *lineptr == ' '; lineptr++);
          sscanf(++lineptr, "%49s", tempstr);
          checksym(localdata, tempstr);
       }
#endif

         /* 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 = buffer; *lineptr != '/' && *lineptr != '\0';
                  lineptr++);
          sscanf(++lineptr, "%79s", keyword);

          /* Find the library containing the object, add a record to its */
          /* instlist list, and create a new instance and add it to the  */
          /* record.                                           */

          for (i = 0; i < xobjs.numlibs; i++) {
             for (j = 0; j < xobjs.userlibs[i].number; j++) {
              libobj = xobjs.userlibs[i].library + j;
                if (!strcmp(keyword, (*libobj)->name)) {
                 newobjinst = addtoinstlist(i, *libobj, TRUE);
                 lineptr = buffer;
                 while (isspace(*lineptr)) lineptr++;

                 /* May declare instanced scale and rotation first */
                 if (*lineptr != '[') {
                      lineptr = varfscan(localdata, lineptr,
                              &newobjinst->scale,
                              (genericptr)newobjinst, P_SCALE);
                          lineptr = varscan(localdata, lineptr,
                              &newobjinst->rotation,
                              (genericptr)newobjinst, P_ROTATION);
                 }
                 readparams(newobjinst, *libobj, lineptr);
                 break;
              }
             }
             if (j != xobjs.userlibs[i].number) break;
          }
       }

         /* read objects */

         else if (!strcmp(keyword, "{")) {  /* This is an object definition */
          objlistptr newdef, redef = NULL;
          objectptr *newobject;
          objectptr *curlib = (mode == FONTLIB) ?
            xobjs.fontlib.library : xobjs.userlibs[mode - LIBRARY].library;
          short *libobjects = (mode == FONTLIB) ?
            &xobjs.fontlib.number : &xobjs.userlibs[mode - LIBRARY].number;

          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);

          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(keyword, (*libobj)->name)) {
                 newdef = (objlistptr) malloc(sizeof(objlist));
                 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(keyword, (*libobj)->name)) {
                    newdef = (objlistptr) malloc(sizeof(objlist));
                    newdef->thisobject = *libobj;
                    newdef->next = redef;
                    redef = newdef;
                 }
                }
             }
          }

            (*libobjects)++;
          sprintf((*newobject)->name, "%s", keyword);

#ifdef SCHEMA
          /* initmem() initialized schemtype to SCHEMATIC;  change it. */
          (*newobject)->schemtype = SYMBOL;
#endif

          if (objectread(ps, *newobject, 0, 0, mode, retstr, curcolor) == True) {
               strncpy(retstr, buffer, 150);
               retstr[149] = '\0';
             free(buffer);
             return True;
            }

          /* do an exhaustive comparison between this object      */
          /* and any object having the same name. If they are */
          /* the same, destroy the duplicate.  If different,      */
          /* rename the original one.                       */

          else if (redef != NULL) {
             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);
#ifdef SCHEMA
                 /* 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;
                 }
#endif
                 reset(*newobject, DESTROY);
                 (*libobjects)--;
                 newobject = NULL;
                 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)) {
                 checkname(newdef->thisobject);
                 break;
              }
             }
             for (; (newdef = redef->next); redef = newdef)
              free(redef);
             free(redef);
             redef = NULL;
          }

          /* Add an instance of the object to the library's instance list */

          if ((mode != FONTLIB) && (newobject != NULL)) {
             objinstptr libinst;

             libinst = addtoinstlist(mode - LIBRARY, *newobject, FALSE);
             calcbboxvalues(libinst, (genericptr *)NULL);

             /* Center the view of the object in its instance */
             centerview(libinst);
          }
         }
         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();
              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;

             localdata->num_params = (u_char) tmpnum;
             localdata->params = (oparamptr *)malloc(tmpnum * sizeof(oparamptr));
             for (i = 0; i < tmpnum; i++) {
              oparamptr ops;
              *(localdata->params + i) = (oparamptr)malloc(sizeof(oparam));
              ops = *(localdata->params + i);
              if (*lineptr == '(' || *lineptr == '{') {  /* type is XC_STRING */
                 char *linetmp, csave;
                 
                 ops->parameter.string = NULL;

                 /* get simple substring or set of substrings and commands */
                 linetmp = find_match(lineptr);
                 csave = *(++linetmp);
                 *linetmp = '\0';
                 if (*lineptr == '{') lineptr++;
                 readlabel(localdata, lineptr, &(ops->parameter.string));

                 /* Add the ending part to the parameter string */
                 newpart = makesegment(&(ops->parameter.string), NULL);  
                 newpart->type = PARAM_END;

                 ops->type = (u_char)XC_STRING;
                 ops->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 */
                 ops->type = (u_char)XC_FLOAT;
                   sscanf(lineptr, "%f", &ops->parameter.fvalue);
                 /* Fprintf(stdout, "Parameter %d to object %s defaults to "
                        "value %3.2f\n", i + 1, localdata->name,
                        ops->parameter.fvalue); */
                 lineptr = advancetoken(lineptr);
              }
             }
          }
       }
#ifdef SCHEMA
       else if (!strcmp(keyword, "trivial")) {
          localdata->schemtype = TRIVIAL;
          if (!areastruct.schemon) doxschema(NULL, 0, NULL);
       }
#endif
         else if (!strcmp(keyword, "begingate")) {
          localdata->num_params = (u_char) 0;
          localdata->params = (oparamptr *)NULL;
       }
         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 */
          /* and return if we have a page boundary             */

          for (tmpptr = buffer; isspace(*tmpptr); tmpptr++);
          if (*tmpptr == '%') {
             if (strstr(buffer, "%%Page:") == tmpptr) {
                  strncpy(retstr, buffer, 150);
                  retstr[149] = '\0';
              free (buffer);
              return True;
             }
             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)->num_params = 0;
                 (*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;
#ifdef SCHEMA
                 (*newinst)->schembbox = NULL;
#endif

                   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;

                 calcbboxinst(*newinst);

                 /* Does this instance contain parameters? */
                 readparams(*newinst, *libobj, buffer);
                 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);

         fd = mkstemp(template);
         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, (void *)ilib);
   popupprompt(button, "Enter name for library:", "\0", savelibrary,
      savebutton, NULL);
#endif
}

/*---------------------------------------------------------*/

void savelibrary(xcWidget w, int ilib)
{
   FILE *ps;
   char outname[150], *outptr;
   objectptr *wroteobjs, *libptr;
   liblistptr spec;
   short written = 0;
   char *uname = NULL;
   char *hostname = NULL;
   struct passwd *mypwentry = NULL;

   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);
   uname = getenv((const char *)"USER");
   if (uname != NULL) mypwentry = getpwnam(uname);

   /* Check for both $HOST and $HOSTNAME environment variables.  Thanks */
   /* to frankie liu <frankliu@Stanford.EDU> 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;
      }

   if (mypwentry != NULL)
      fprintf(ps, "%%  Author: %s <%s@%s>\n", mypwentry->pw_gecos, uname,
            hostname);
   fprintf(ps, "%%\n\n%% XCircuitLib library objects\n");

   /* list of library objects already written */

   wroteobjs = (objectptr *) malloc (sizeof(objectptr));

   /* 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, 0.0, 0.0, 0.0, 0, 0,
                  &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;
   short written = 0, fontsused[256], i, page, curpage, multipage;
   short savepage, stcount, *pagelist;
   objectptr *wroteobjs;
   objinstptr writepage;
   int findex;
   float psscale, psnorm;
   float xmargin = 72.0, ymargin = 72.0;
   int bboxx, bboxy, width, height;
   time_t tdate;
   char *tmp_s;

   if (mode != ALL_PAGES)
      fname = xobjs.pagelist[areastruct.page]->filename;
   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 */

#ifdef SCHEMA
   width = toplevelwidth(areastruct.topinstance);
   height = toplevelheight(areastruct.topinstance);
#else
   width = topobject->bbox.width;
   height = topobject->bbox.height;
#endif

   /* find margins */

   psscale = getpsscale(xobjs.pagelist[areastruct.page]->outscale, areastruct.page);
   if (xobjs.pagelist[areastruct.page]->pmode & 1) {
      if (xobjs.pagelist[areastruct.page]->orient == 90) {
       xmargin = (xobjs.pagelist[areastruct.page]->pagesize.x -
            ((float)height * psscale)) / 2;
       ymargin = (xobjs.pagelist[areastruct.page]->pagesize.y -
            ((float)width * psscale)) / 2;
      }
      else {
         xmargin = (xobjs.pagelist[areastruct.page]->pagesize.x -
            ((float)width * psscale)) / 2;
         ymargin = (xobjs.pagelist[areastruct.page]->pagesize.y -
            ((float)height * psscale)) / 2;
      }
   }

#ifdef SCHEMA
   /* 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);
   }
#endif

   /* 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\n", PROG_VERSION);
   tdate = time(NULL);
   fprintf(ps, "%%%%CreationDate: %s", asctime(localtime(&tdate)));
   fprintf(ps, "%%%%Pages: %d\n", multipage);

   if (xobjs.pagelist[areastruct.page]->orient == 90) {
      bboxx = (int)((float)height * psscale) + (int)xmargin + 4;
      bboxy = (int)((float)width * psscale) + (int)ymargin + 4;
   }
   else {
      bboxx = (int)((float)width * psscale) + (int)xmargin + 4;
      bboxy = (int)((float)height * psscale) + (int)ymargin + 4;
   }
   /* in order to reconstruct the page size, it is necessary that the   */
   /* bounding box extent + margin = page size.  This of course assumes */
   /* that the picture is centered on the page so margins are equal.    */

   if (xobjs.pagelist[areastruct.page]->pmode & 1) 
      fprintf(ps, "%%%%BoundingBox: %d %d %d %d\n",
         xobjs.pagelist[areastruct.page]->pagesize.x - bboxx,
         xobjs.pagelist[areastruct.page]->pagesize.y - bboxy, bboxx, bboxy);
   else
      fprintf(ps, "%%%%BoundingBox: %d %d %d %d\n", (int)xmargin - 4,
         (int)ymargin - 4, bboxx, bboxy);
   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) {
      }
   }

   fprintf(ps, "%% XCircuit output starts here.\n\n");

   /* list of objects already written---maximum possible = # of builtins */

   wroteobjs = (objectptr *) malloc (sizeof(objectptr));

   page = 0;
   for (curpage = 0; curpage < xobjs.pages; curpage++) {
      if (pagelist[curpage] == 0) continue;

      writepage = xobjs.pagelist[curpage]->pageinst;

#ifdef SCHEMA
      width = toplevelwidth(writepage);
      height = toplevelheight(writepage);
#else
      width = writepage->thisobject->bbox.width;
      height = writepage->thisobject->bbox.height;
#endif

      psnorm = xobjs.pagelist[curpage]->outscale;
      psscale = getpsscale(psnorm, curpage);
      if (xobjs.pagelist[curpage]->pmode & 1) {
         if (xobjs.pagelist[curpage]->orient == 90) {
          xmargin = (xobjs.pagelist[curpage]->pagesize.x -
            ((float)height * psscale)) / 2;
          ymargin = (xobjs.pagelist[curpage]->pagesize.y -
            ((float)width * psscale)) / 2;
         }
         else {
            xmargin = (xobjs.pagelist[curpage]->pagesize.x -
            ((float)width * psscale)) / 2;
            ymargin = (xobjs.pagelist[curpage]->pagesize.y -
            ((float)height * psscale)) / 2;
         }
      }

      /* Write all of the object definitions used, bottom up */

      printobjects(ps, writepage->thisobject, psnorm, xmargin, ymargin, ++page,
          curpage, &wroteobjs, &written, DEFAULTCOLOR);
      fprintf(ps, "pgsave restore showpage\n");

      /* 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, 0.0, 0.0, 0.0, 0, 0, &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);
   }
}

/*----------------------------------------------------------------------*/
/* 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(3);
       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, "v%d ", chrptr->data.paramno + 1);
       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      */
/*--------------------------------------------------------------*/

void writelabelsegs(FILE *ps, short *stcount, stringpart *chrptr)
{
   Boolean ismultipart = ((chrptr->nextpart != NULL) &&
         (chrptr->nextpart->type != PARAM_END)) ? True : False;

   /* nextpart is not NULL if there are multiple parts to the string */
   if (ismultipart) {
      fprintf(ps, "{");
      (*stcount)++;
   }
   writelabel(ps, chrptr, stcount);

   if (ismultipart) {
      fprintf(ps, "}");
      (*stcount)++;
   }
}

/*--------------------------------------------------------------*/
/* Write the list of parameters belonging to an object instance */
/*--------------------------------------------------------------*/

short printparams(FILE *ps, objinstptr sobj, short stcount)
{
   short i, iend, loccount;
   stringpart *tmpptr;

   if (sobj->params == NULL) return stcount;

   loccount = stcount;

   for (iend = sobj->thisobject->num_params; iend > 0; iend--)
      if (sobj->params[iend - 1] != NULL) break;

   if (iend) {
      fprintf(ps, "[");
      loccount++;
   }
   for (i = 0; i < iend; i++) {
      switch (sobj->thisobject->params[i]->type) {
       case XC_STRING:
          if (sobj->params[i] == NULL)     /* use the default */
             tmpptr = sobj->thisobject->params[i]->parameter.string;   
          else
             tmpptr = sobj->params[i]->parameter.string;
          writelabelsegs(ps, &loccount, tmpptr);
          break;
       case XC_INT:
          if (sobj->params[i] == NULL) {   /* use the default */
             sprintf(_STR, "%d ",
                     sobj->thisobject->params[i]->parameter.ivalue);
          }
          else {
             sprintf(_STR, "%d ", sobj->params[i]->parameter.ivalue);
          }
          dostcount(ps, &loccount, strlen(_STR));
          fputs(_STR, ps);
          break;
       case XC_FLOAT:
          if (sobj->params[i] == NULL) {   /* use the default */
             sprintf(_STR, "%3.2f ",
                     sobj->thisobject->params[i]->parameter.fvalue);
          }
          else {
             sprintf(_STR, "%3.2f ", sobj->params[i]->parameter.fvalue);
          }
          dostcount(ps, &loccount, strlen(_STR));
          fputs(_STR, ps);
          break;
      }
   }
   if (iend) {
      fprintf(ps, "] ");
      loccount += 2;
   }
   return loccount;
}

/*------------------------------------------------------------------*/
/* Macro for point output (calls varpcheck() on x and y components) */
/*------------------------------------------------------------------*/

#define xyvarcheck(z, n, t) \
    varpcheck(ps, z.x, localdata, xaddin, n, &stcount, *t, P_POSITION_X); \
    varpcheck(ps, z.y, localdata, yaddin, n, &stcount, *t, P_POSITION_Y)
  
/*--------------------------------------------------------------------------*/
/* 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.                     */
/*--------------------------------------------------------------------------*/

void printobjects(FILE *ps, objectptr localdata, float psnorm, float xmargin,
      float ymargin, short page, short mpage, objectptr **wrotelist,
      short *written, int ccolor)
{
   genericptr *gptr, *savegen;
   objectptr *optr;
   pointlist savept;
   Boolean already;
   short stcount;
   int xaddin, yaddin;
   int curcolor = ccolor;
   float psscale = getpsscale(psnorm, mpage);
   XPoint origin, corner;

   /* first, get a total count of all objects and give warning if large */

   if (localdata->parts > 255) {
      sprintf(_STR, "Warning: \"%s\" may exceed printer's PS limit for definitions",
          localdata->name);
      Wprintf(_STR);
   }
         
   /* search for all object definitions needed */

   for (gptr = localdata->plist; gptr < localdata->plist + localdata->parts; gptr++)
      if (IS_OBJINST(gptr))
         printobjects(ps, TOOBJINST(gptr)->thisobject, 0.0, 0.0, 0.0, 0, 0,
            wrotelist, written, curcolor);

   if (psscale != 0) {  /* on topmost object of page */
      char *rootptr = strrchr(xobjs.pagelist[mpage]->filename, '/');
      if (rootptr == NULL) rootptr = xobjs.pagelist[mpage]->filename;
      else rootptr++;

      /* 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");
     
      fprintf (ps, "/pgsave save def bop\n");

      origin = localdata->bbox.lowerleft;
      corner.x = origin.x + localdata->bbox.width;
      corner.y = origin.y + localdata->bbox.height;

#ifdef SCHEMA
      if (localdata->symschem != NULL) {
       fprintf(ps, "%% %s is_symbol\n", localdata->symschem->name);
      }

      /* For a page, extend bounding box around schematic pins */
      extendschembbox(xobjs.pagelist[mpage]->pageinst, &origin, &corner);
#endif

      xaddin = (int)(xmargin / psscale) - origin.x;
      yaddin = (int)(ymargin / psscale) - origin.y;
      fprintf(ps, "%% %hd %hd offsets\n", xaddin, yaddin);

      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)((float)xaddin * iscale * psnorm) + (int)(ymargin - xmargin),
              (int)((float)yaddin * iscale * psnorm) -
              ((int)((float)(corner.y - origin.y) * psscale) +
              (int)(xmargin + ymargin)));
       else
          fprintf(ps, "%5.4f %d %d 0 psinsertion\n", psnorm,
              (int)((float)xaddin * iscale * psnorm),
              (int)((float)yaddin * iscale * psnorm));
       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 FRAC_INCH: case DEC_INCH:
          fprintf(ps, "inchscale\n");
          break;
       case CM:
          fprintf(ps, "cmscale\n");
          break;
      };
      fprintf(ps, "%5.4f setlinewidth\n\n", 1.3 *
                 xobjs.pagelist[mpage]->wirewidth);
   }

   /* search among the list of objects already written to the output */

   already = False;
   for (optr = *wrotelist; optr < *wrotelist + *written; optr++)
      if (*optr == localdata) {
        already = True;
        break;
      }

   if (!already) {

      /* update the list of objects already written to the output */
      if (psscale == 0) {
       *wrotelist = (objectptr *)realloc(*wrotelist, (*written + 1) * 
            sizeof(objectptr));
         *(*wrotelist + *written) = localdata;
         (*written)++;
      }

      if (psscale == 0) {
       fprintf(ps, "/%s {\n", localdata->name);
       /* No longer writes "bbox" record */
       if (localdata->hidden == True) fprintf(ps, "%% hidden\n");

#ifdef SCHEMA
       /* 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");
         }
#endif

       /* Check for parameters and default values */
         if ((localdata->params != NULL) && (localdata->num_params > 0)) {
          short i;
          stcount = 0;
          for (i = 0; i < localdata->num_params; i++) {
             switch (localdata->params[i]->type) {
              case XC_STRING:
                 writelabelsegs(ps, &stcount,
                        localdata->params[i]->parameter.string);
                 fprintf(ps, " ");
                       stcount++;
                 break;
              case XC_INT:
                 fprintf(ps, "%d ", localdata->params[i]->parameter.ivalue);
                       stcount += 5;
                 break;
              case XC_FLOAT:
                 fprintf(ps, "%3.2f ", localdata->params[i]->parameter.fvalue);
                       stcount += 5;
                 break;
             }
          }

          sprintf(_STR, "%d beginparm\n", localdata->num_params);
          dostcount (ps, &stcount, strlen(_STR));
          fputs(_STR, ps);
       }
       else 
          fprintf(ps, "begingate\n");
      }

      if (psscale == 0) {
       xaddin = yaddin = 0;
      }

      /* write all the elements in order */

      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((*savegen)->type) {

          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, xaddin,
                  savept - TOPOLY(savegen)->points, &stcount, *savegen,
                  P_POSITION_X);
              varpcheck(ps, savept->y, localdata, yaddin,
                  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): {
             genericptr *pgen;
             pgen = TOPATH(savegen)->plist;
             switch((*pgen)->type) {
              case ARC:
                 varpcheck(ps, (short)TOARC(pgen)->points[0].x, localdata,
                        xaddin, 0, &stcount, *pgen, P_POSITION_X);
                 varpcheck(ps, (short)TOARC(pgen)->points[0].y, localdata,
                        yaddin, 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,
                        xaddin, 0, &stcount, *pgen, P_POSITION_X);
                 varpcheck(ps, (short)TOSPLINE(pgen)->ctrl[0].y, localdata,
                        yaddin, 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((*pgen)->type) {
                 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, xaddin,
                        savept - TOPOLY(pgen)->points, &stcount, *pgen,
                        P_POSITION_X);
                     varpcheck(ps, savept->y, localdata, yaddin,
                        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(OBJECT):
             {objinstptr 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", TOOBJINST(savegen)->thisobject->name);
             }break;
            
          case(LABEL):
             {short segs;
             Boolean has_param = False;

#ifdef SCHEMA
             /* 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;
#endif

             /* 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);

#ifdef SCHEMA
              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");
#else
              strcpy(_STR, "label\n");
#endif
              dostcount(ps, &stcount, strlen(_STR));
                fputs(_STR, ps);
             }
             }break;
         }

      }

      /* If this is not the top level, end the object definition */

      if (psscale == 0) fprintf(ps, "endgate\n} def\n\n");

   }
}

/*----------------------------------------------------------------------*/

Generated by  Doxygen 1.6.0   Back to index