/*
  open questions:

     who keeps track of where the browser has been:  ie, is this
        a browser over the entire file?  or over a page?
	should there be a browser that knows how to display a single
	page and then a parent that knows how to display an entire file?
     and what about this font_table, anyways?

     * make endpos and startpos longs
     * find .psfonts bug
     * add height and width to resource list
     * make sure all bug fixes from postps are in here
     change default for fullDocument to False
     postps version should have info page as last page
     * how to deal with errorStateFlag
     should do an fclose on each widget entrance
     find the height and width bug
     how to do spacebar and carriage return in translations
     goto bugs
*/



/************************************************************

  ScribePSBrowser.C

    Mark S. Ackerman
    MIT/Project Athena
    

    7/11/87  MSA  Release previewer for testing
    7/12/87  MSA  Made show() also set current x since Scribe does in
                  fact use that PostScript featur

    7/27/87  1    MSA     Converted into browser.c with public and private
                           entry points
    7/28/87  2    MSA     Added in linked list storage of state info
                           for multiple browsers
    8/4/87   3    MSA     Included buttons for nextpage, backpage, and
                           exit
    8/18/87  4    MSA	 
    8/19/87  5    MSA     Made any non-private globals statics, 
                          added parent_window as an argument to start_browser, 
		          added internal_buttons as an argument to 
			   start_browser
    8/21/87  5b   MSA     Changed so exposure events do not cause the screen
                           to be cleared
			  Changed so trailer will be ignored if necessary
    8/25/87  6a   MSA     Many changes:
                           Installed dink_table and dink_buffer to keep track
			    of locations of strings.  Therefore, changed
			    init_page to initialize these tables.
			   show() changed to set dink tables
			   Changed expose event handling to refresh screen
			    from dink_table and dink_buffer instead of parsing
			    again
			   Changed browser_event() to recopy contents of
			    changed globals (or possibily changed globals)
			    back into the browser_rec structure
    9/16/87  6b   MSA     Installed get_last_dinkinfo to get last
                            string font and (x,y) box.
			  Slight modification to show() to fudge around
			    problems with screen fonts being different
			    than text fonts -- show() now uses the
			    x position from postscript as a clue if
			    the font changes to avoid overlapping strings.
			   (This should be changed to be done a flag
			    passed into browser_start.  Also, this
			    blank space between words should depend on
			    the fontsize.)
    1/16/88  6c   MSA      Most Scribe5.0 macros implemented.
    5/27/88  7a   MSA      Converted to X11.  Bug on redisplay.
    8/16/88  8a   MSA      Converted to widget. 

**************************************************************/


#include <stdio.h>
#include <math.h>
#include <ctype.h>
#include <X11/Xlib.h>     
#include <X11/Xutil.h>
#include <X11/StringDefs.h>
#include <X11/IntrinsicP.h>
#include "SPBrowserP.h"


#define MAX_TOKEN 256              /* the longest possible token coming
				      in from Scribe */
#define DEFAULT_FONT 0
#define DEFAULT_FONTNAME   "/Helvetica"

#define FALSE 0
#define TRUE  1

#define EOS  (char)0

long show_page();
char *getenv();

/*****************************************************

  INSTANCE VARIABLES BEING TREATED AS GLOBALS

  Hey, this was non-widget code before!  Actually,
  on the page display, not having to do the referencing
  should be faster, too.

******************************************************/
static Display *the_display;
static GC the_GC;
static FILE *fp;  /* state */
static long page[MAXPAGE]; /* state */
static Window the_window;  /* state */
static ScribePSBrowserWidget the_widget;
static int curpage;  /* state */
static long endpos; /* state */

static struct _dink_table_rec dink_table[MAX_DINKS];

static char *next_dink_buffer;
static int next_dink_index;
static char dink_buffer[MAX_DINK_BUFFER];

static double pixels_per_vertical_inch; /* global state */
static double pixels_per_horz_inch;     /* global state */

static int cur_x;    /* current x coordinate */
static int cur_y;    /* current y coordinate */

#define MAX_FONTNAME 32
#define NFONTS  50
#define N_DEFAULT_TABLE 22

struct _font_table {
  char fontname[MAX_FONTNAME];
  int fontsize;
  char fontfile[MAX_FONTNAME];
  Font xfont;
  XFontStruct *xfontstruct;
};

static struct _font_table font_table[NFONTS] = 
{
  {"/Helvetica",10,"helvetica-medium10",0,0},
  {"/Helvetica",12,"helvetica-medium12",0,0},
  {"/Helvetica",13,"helvetica-medium14",0,0}, /* well... there isn't a 13 */
  {"/Helvetica-Bold",10,"helvetica-bold10",0,0},
  {"/Helvetica-Bold",12,"helvetica-bold12",0,0},
  {"/Helvetica-Bold",13,"helvetica-bold14",0,0},
  {"/Helvetica-Bold",15,"helvetica-bold14",0,0},
  {"/Helvetica-Oblique",10,"helvetica-oblique10",0,0},
  {"/Helvetica-Oblique",12,"helvetica-oblique12",0,0},
  {"/Helvetica-Oblique",13,"helvetica-oblique14",0,0},
  {"/Times-Roman",10,"times-roman10",0,0},
  {"/Times-Roman",12,"times-roman12",0,0},
  {"/Times-Roman",13,"times-roman14",0,0}, /* well... there isn't a 13 */
  {"/Times-Bold",10,"times-bold10",0,0},
  {"/Times-Bold",12,"times-bold12",0,0},
  {"/Times-Bold",13,"times-bold14",0,0},
  {"/Times-Bold",18,"times-bold18",0,0},
  {"/Times-Italic",10,"times-italic10",0,0},
  {"/Times-Italic",12,"times-italic12",0,0},
  {"/Times-Italic",13,"times-italic14",0,0},
  {"/Symbol",7,"symbol-medium10",0,0},
  {"/Symbol",10,"symbol-medium10",0,0},
};

static int cur_fontsize = 0;
static int cur_font;
static char cur_fontname[MAX_FONTNAME] = {"/Helvetica"};

static int n_actual_fonts;
/****************************************************************
 *
 * Full class record constant
 *
 ****************************************************************/

/* Private Data */

static char defaultTranslations[] =
  "<Btn1Up>:     browser_nextpage() \n\
   <Btn2Up>:     browser_backpage() \n\
   <Btn3Up>:     browser_exit() \n\
   <Key>B:        browser_backpage() \n\
      Shift<Key>B:      browser_backpage() \n\
      Ctrl<Key>V:       browser_nextpage() \n\
      Meta<Key>V:       browser_backpage() \n\
      Ctrl<Key>C:       browser_exit() \n\
<Key>: browser_listen() \n\
Shift<Key>: browser_listen() \n";



#define offset(field) XtOffset(ScribePSBrowserWidget, field)
static XtResource resources[] = { 
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
       offset(scribePSBrowser.foreground), XtRString, "Black"},
    {XtNfilename, XtCFilename, XtRString, sizeof(String),
       offset(scribePSBrowser.filename), XtRString, ""},
    {XtNcursor, XtCCursor, XtRCursor, sizeof(Cursor),
       offset(simple.cursor), XtRString, "opendot"},
    {XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), 
       offset(scribePSBrowser.callbacks), XtRCallback, (caddr_t)NULL},
    {XtNstartPosition, XtCStartPosition, XtRLong, sizeof(long), 
       offset(scribePSBrowser.startpos), XtRString, "0L"},
    {XtNendPosition, XtCEndPosition, XtRLong, sizeof(long), 
       offset(scribePSBrowser.endpos), XtRString, "0L"},
    {XtNfullDocument, XtCFullDocument, XtRBoolean, sizeof(Boolean),
       offset(scribePSBrowser.fullDocument), XtRString, "True"},
    {XtNheightInInches, XtCHeightInInches, XtRFloat, sizeof(float),
       offset(scribePSBrowser.height_in_inches), XtRString, "11.0"},
    {XtNwidthInInches, XtCWidthInInches, XtRFloat, sizeof(float),
       offset(scribePSBrowser.width_in_inches), XtRString, "8.5"},
         /**** CHANGE NAMES TO xInInches *****/
    
};
#undef offset


XtActionProc *BrowserNextpage();
XtActionProc *BrowserBackpage();
XtActionProc *BrowserExit();
XtActionProc *BrowserNull();
XtActionProc *BrowserListenAndGoto();

static XtActionsRec actionsList[] =
{
  {"browser_nextpage",		BrowserNextpage},
  {"browser_backpage",		BrowserBackpage},
  {"browser_exit",		BrowserExit},
  {"browser_listen",            BrowserListenAndGoto},
  {"browser_null",              BrowserNull},
};

  /* ...ClassData must be initialized at compile time.  Must
     initialize all substructures.  (Actually, last two here
     need not be initialized since not used.)
  */


XtInitProc Initialize();
XtExposeProc Redisplay();
static void ClassInitialize();

XtSetValuesFunc SetValues()
{
}


ScribePSBrowserClassRec scribePSBrowserClassRec = {
  {
    (WidgetClass) &simpleClassRec,	/* superclass		  */	
    "ScribePSBrowser",			/* class_name		  */
    sizeof(ScribePSBrowserRec),		/* size			  */
    ClassInitialize,		        /* class_initialize	  */
    NULL,				/* class_part_initialize  */
    FALSE,				/* class_inited		  */
    Initialize,				/* initialize		  */
    NULL,				/* initialize_hook	  */
    XtInheritRealize,			/* realize		  */
    actionsList,			/* actions		  */
    XtNumber(actionsList),		/* num_actions		  */
    resources,				/* resources		  */
    XtNumber(resources),		/* resource_count	  */
    NULLQUARK,				/* xrm_class		  */
    FALSE,				/* compress_motion	  */
    TRUE,				/* compress_exposure	  */
    TRUE,				/* compress_enterleave    */
    FALSE,				/* visible_interest	  */
    NULL,				/* destroy		  */
    XtInheritResize,			/* resize		  */
    Redisplay,				/* expose		  */
    SetValues,				/* set_values		  */
    NULL,				/* set_values_hook	  */
    XtInheritSetValuesAlmost,		/* set_values_almost	  */
    NULL,				/* get_values_hook	  */
    NULL,				/* accept_focus		  */
    XtVersion,				/* version		  */
    NULL,				/* callback_private	  */
    defaultTranslations,		/* tm_table		  */
    XtInheritQueryGeometry,		/* query_geometry	  */
  },  /* CoreClass fields initialization */
  {
    0,   /* field not used */
  },    /* SimpleClass fields initialization */
  {
    False,                              /* fonts inited */
  },  /* ScribePSBrowserClass fields initialization */
};

  /* for public consumption */
WidgetClass scribePSBrowserWidgetClass = 
  (WidgetClass) &scribePSBrowserClassRec;



/**********************************************************

  GLOBAL INITIALIZATION ENTRYPOINT

**********************************************************/


static void CvtStringToFloat();
static void CvtStringToLong();


static void ClassInitialize(wc)  /* global initialization */
     WidgetClass *wc;
{   /***CHECK THIS ****/
  ScribePSBrowserWidgetClass pbc = (ScribePSBrowserWidgetClass) wc;

    XtAddConverter( XtRString, XtRFloat, CvtStringToFloat, NULL, 0 );
    XtAddConverter( XtRString, XtRLong, CvtStringToLong, NULL, 0);

} /* ClassInitialize */

static void CvtStringToFloat(args, num_args, fromVal, toVal)
    XrmValuePtr *args;		/* unused */
    Cardinal	*num_args;	/* unused */
    XrmValuePtr fromVal;
    XrmValuePtr toVal;
{
    char  *s = (char *) fromVal->addr;
    static float temp;

    if (s == NULL) 
      {
	toVal->size = 0;
	toVal->addr = NULL;
	return;
      }

    temp = atof(s);

    toVal->size = sizeof(float);
    toVal->addr = (caddr_t) &temp;

};

static void CvtStringToLong(args, num_args, fromVal, toVal)
    XrmValuePtr *args;		/* unused */
    Cardinal	*num_args;	/* unused */
    XrmValuePtr fromVal;
    XrmValuePtr toVal;
{
    char  *s = (char *) fromVal->addr;
    static long temp;

    if (s == NULL) 
      {
	toVal->size = 0;
	toVal->addr = NULL;
	return;
      }

    temp = atol(s);

    toVal->size = sizeof(long);
    toVal->addr = (caddr_t) &temp;
};



XtInitProc Initialize(request,w)
     Widget request;
     Widget w;
{
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;
  char msg[128];
  Screen *the_screen;
  int i;

  the_display = XtDisplay(w);
  the_widget = pbw;
  pbw->scribePSBrowser.firstFlag = True;
  pbw->scribePSBrowser.errorStateFlag = False; 

  if ((pbw->scribePSBrowser.fp = fopen(pbw->scribePSBrowser.filename,"r"))
      == NULL)
    {
      sprintf(msg,"Couldn't open %s.  browser abandoned.(SPB)\n",
		pbw->scribePSBrowser.filename);
      PutSeriousWarnMsg(msg);
      return;
    }

     /* gotta have the display before you can load fonts, so we've
	got to do for the instances */
  if (!(((ScribePSBrowserClassRec *)(pbw->core.widget_class))->
	scribePSBrowser_class.fonts_inited))
    {
      if (init_fonts() < 0)     
	{
	  PutSeriousWarnMsg("Browser unable to initialize fonts\n");
	  return;
	}
      ((ScribePSBrowserClassRec *)(pbw->core.widget_class))->
	scribePSBrowser_class.fonts_inited =
	True;
    }



  if (pbw->scribePSBrowser.endpos == 0L)
    {
      fseek(pbw->scribePSBrowser.fp, 0L, 2);
      pbw->scribePSBrowser.endpos = ftell(pbw->scribePSBrowser.fp);
    }
  fseek(pbw->scribePSBrowser.fp, pbw->scribePSBrowser.startpos, 0);

     /* isolate all of the X initialization */

  the_screen = XtScreen(w);  /* to make Saber happy */

  pbw->scribePSBrowser.the_GC = XCreateGC(the_display,
					  RootWindowOfScreen(the_screen),
					  None, (XGCValues *)NULL);
  determine_conversions(w);

/*
  pbw->scribePSBrowser.height_in_inches = 11.0;
  pbw->scribePSBrowser.width_in_inches = 8.5;
*/

  pbw->core.height = (int) (pbw->scribePSBrowser.height_in_inches
			    * pixels_per_horz_inch);
  pbw->core.width = (int)(pbw->scribePSBrowser.width_in_inches
			  * pixels_per_horz_inch);
  
  XSetFont(the_display,pbw->scribePSBrowser.the_GC,
	   font_table[DEFAULT_FONT].xfont);

  pbw->scribePSBrowser.nextGotoChar = 0;
  pbw->scribePSBrowser.gotoBuffer[0] = EOS;
  pbw->scribePSBrowser.next_dink_buffer = dink_buffer;
    /* set a couple of globals */
  pbw->scribePSBrowser.curpage = 0;
  for (i=0;i<MAXPAGE;i++)
    pbw->scribePSBrowser.page[i] = 0L;
  if (pbw->scribePSBrowser.fullDocument == True)
     if ((pbw->scribePSBrowser.page[curpage] = 
	  ignore_PS_header(pbw->scribePSBrowser.fp)) < 0L) 
       {
	 clear_page();
	 return;
       }
     else;
  else
    pbw->scribePSBrowser.page[pbw->scribePSBrowser.curpage] = 
      pbw->scribePSBrowser.startpos;
  pbw->scribePSBrowser.next_dink_index = -1;

}




static XtExposeProc Redisplay(w, event, region)
    Widget w;
    XEvent *event;		/* unused */
    Region region;		/* unused */
{
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;


  if (pbw->scribePSBrowser.errorStateFlag)
    {
      clear_page();
      return;
    }
  browser_uncurl(w);    
	  if (pbw->scribePSBrowser.firstFlag)
	    { 
	      clear_page();  /* probably not necessary */
	      if ((page[++curpage] = show_page(page[curpage])) < 0L)
		return;
	      pbw->scribePSBrowser.firstFlag = FALSE;
	    }
	  else
	    {
	      /* no clear page on expose events */
	      if (show_page(page[--curpage]) < 0L)
		return;
	      curpage++;  /* was up that high before */
	    }


  browser_curl(w);
}
/*********************************************************

  BROWSER STARTUP ENTRYPOINT

*********************************************************/



  

static void BrowserGoto(w,wantedPage)
     Widget w;
     int wantedPage;
{
  char msg[128];
  char linebuffer[MAX_TOKEN];
  long nextpage_loc;
  Boolean firstLine;

  firstLine = True;
  if (wantedPage > MAXPAGE)
    {
      sprintf(msg,"Request to go to page %d exceeds maximum pages\n",
	      wantedPage);
      PutWarnMsg(msg);
      return;
    }
  browser_uncurl(w);
  
    /* decrease wantedPage since the user's setting it as a count
       and here I'm using it as an index */
  if (page[--wantedPage] != 0)
    curpage = wantedPage;
  else
    {
        /* should set curpageto lastPage or equivalent  if 
	   curpage isn't lastpage seen */
      if (fseek(fp,page[curpage],0) == -1)
      {
	PutSeriousWarnMsg("Cannot read.  Browser abandoned.\n");
	return;
      }
  
        /* the check for firstLine is a hack that results
	   from the following:  This looks for the %Page:
	   comment.  Show_page() stops right before the
	   %Page: comment.  Therefore the page count gets
	   all screwed up. */
      while (fgets(linebuffer,MAX_TOKEN,fp) != NULL)
	{
	  /*printf("%s",linebuffer);*/
	  if (linebuffer[0] == '%') /* ignore comments */
	    {
	      if (strncmp("%%Page:",linebuffer,7) == 0)
		  if (!firstLine)
		    {
		      page[++curpage] = ftell(fp);
		      if (curpage == wantedPage)
			break;
		    }
		  else
		    firstLine = False;
	      else
		firstLine = False;
	    }
	}
      if (curpage != wantedPage)
	{
	  PutWarnMsg("Cannot go to page.  Beyond end of document.\n");
	  curpage = 0;
	  return;
	}
    }

  clear_page();
  if ((nextpage_loc = show_page(page[curpage])) < 0L)
    return;
  if (++curpage > MAXPAGE)
      curpage = 0;
  else
    page[curpage] = nextpage_loc;

  browser_curl(w);
}


XtActionProc *BrowserExit(w)
     Widget w;
{

  PutWarnMsg("Request to delete browser ignored\n");
}



    /* this nonsense should go away and get replaced with passed
       parameters */

browser_curl(w)
     Widget w;
{
  int i;

  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;

  /*cur_browser->browser_window = the_window;*/
  for (i=0;i<MAXPAGE;i++)
    pbw->scribePSBrowser.page[i] = page[i];
  pbw->scribePSBrowser.curpage = curpage;
  /*cur_browser->endpos = endpos;*/
  /*cur_browser->browser_file = fp;*/
  for (i=0;i<=next_dink_index;i++)
    pbw->scribePSBrowser.dink_table[i] = dink_table[i];
  strcpy(pbw->scribePSBrowser.dink_buffer,dink_buffer);
  pbw->scribePSBrowser.next_dink_index = next_dink_index;
  pbw->scribePSBrowser.next_dink_buffer = next_dink_buffer;

  return(0);
}

browser_uncurl(w)
     Widget w;
{
  int i;
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;

  the_window = XtWindow(w);
  the_display = XtDisplay(w);
  the_widget = pbw;
  the_GC = pbw->scribePSBrowser.the_GC;
  /*  cur_browser->browser_window = the_window; */
  for (i=0;i<MAXPAGE;i++)
    page[i] = pbw->scribePSBrowser.page[i];
  curpage = pbw->scribePSBrowser.curpage;
  endpos = pbw->scribePSBrowser.endpos;
  fp =  pbw->scribePSBrowser.fp;


  next_dink_index = pbw->scribePSBrowser.next_dink_index;
  for (i=0;i<=next_dink_index;i++)
    dink_table[i] = pbw->scribePSBrowser.dink_table[i];
  strcpy(dink_buffer,pbw->scribePSBrowser.dink_buffer);
  next_dink_buffer = pbw->scribePSBrowser.next_dink_buffer;
}

   /* For translation overriding */
XtActionProc *BrowserNull(w)
     Widget w;
{
  return;
}

#define KEY_STR_LENGTH 5

XtActionProc *BrowserListenAndGoto(w,event)
     Widget w;
     XEvent *event;
{
  char str[KEY_STR_LENGTH];
  int i;
  XComposeStatus compose_status;  
  int wantedPage;
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;

  /* don't bother to uncurl and curl */
  if ((i = XLookupString(event, str, KEY_STR_LENGTH, NULL, 
			&compose_status)) > 0)
    {
      if (i == 1 && (str[0] == '\x07f' || str[0] == '\b') && 
	  (pbw->scribePSBrowser.nextGotoChar > 0)) /* backspace */
	pbw->scribePSBrowser.gotoBuffer[--(pbw->scribePSBrowser.nextGotoChar)]
	  = EOS;
      else
	{
	  str[i] = EOS;
	  if (strlen(pbw->scribePSBrowser.gotoBuffer)+i > MAX_GOTO_BUFFER)
	    {
	      PutWarnMsg("Too many characters for a goto\n");
	      pbw->scribePSBrowser.gotoBuffer[0] = EOS;
	      pbw->scribePSBrowser.nextGotoChar = 0;
	      return;
	    }
	  strcat(&(pbw->scribePSBrowser.gotoBuffer[
		pbw->scribePSBrowser.nextGotoChar]),str);
	  pbw->scribePSBrowser.nextGotoChar += i;
	}
    }
  else
    return;

      /* Do the goto on a \n */
      /* and on a '\r', whatever that is */
      /* but avoid going to the first page if the user accidentally
	 hits the carriage return */
  if ((str[i-1] == '\n' || str[i-1] == '\r') && 
      (strlen(pbw->scribePSBrowser.gotoBuffer) > 1)) 
    {
      wantedPage = atoi(pbw->scribePSBrowser.gotoBuffer);
      pbw->scribePSBrowser.gotoBuffer[0] = EOS;
      pbw->scribePSBrowser.nextGotoChar = 0;
      BrowserGoto(w,wantedPage);
    }
}

#undef KEY_STR_LENGTH

XtActionProc *BrowserNextpage(w)
     Widget w;
{
  long nextpage_loc;
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;


  if (pbw->scribePSBrowser.errorStateFlag)
    {
      clear_page();
      return;
    }
  browser_uncurl(w);
  next_dink_buffer = dink_buffer;

  if (page[curpage] >= endpos) 
    curpage = 0;
  clear_page();
  if ((nextpage_loc = show_page(page[curpage])) < 0L)
    return;
  if (++curpage > MAXPAGE)
    curpage = 0;
  else
    page[curpage] = nextpage_loc;

  browser_curl(w);
}

XtActionProc *BrowserBackpage(w)
     Widget w;
{
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;

  if (pbw->scribePSBrowser.errorStateFlag)
    {
      clear_page();
      return;
    }

  browser_uncurl(w);
  next_dink_buffer = dink_buffer;

  if (curpage < 2) /* don't redisplay if first page */
    return;
  curpage = curpage - 2;
  clear_page();
  if (show_page(page[curpage]) < 0L)
    return;
  curpage++;
  browser_curl(w);
}

XtActionProc *BrowserExpose(w)
     Widget w;
{
  ScribePSBrowserWidget pbw = (ScribePSBrowserWidget) w;


  if (pbw->scribePSBrowser.errorStateFlag)
    {
      clear_page();
      return;
    }
  browser_uncurl(w);
	  if (pbw->scribePSBrowser.firstFlag)
	    { 
	      clear_page();  /* probably not necessary */
	      if ((page[++curpage] = show_page(page[curpage])) < 0L)
		return;
	      pbw->scribePSBrowser.firstFlag = FALSE;
	    }
	  else
	    {
	      /* no clear page on expose events */
	      if (show_page(page[--curpage]) < 0L)
		return;
	      curpage++;  /* was up that high before */
	    }

  browser_curl(w);
}




/**************************************************************

  Internal routines to handle the tables to search for a word hit

***************************************************************/

static init_dink_table()
{
  next_dink_buffer= dink_buffer;
  next_dink_index = -1;   /* a ++ instruction will start 'er at 0 */
  return(0);
}

static put_dink_index(x,y,font_table_index,string,length,
		      upper_y,lower_y,right_x)
     int x;
     int y;
     int font_table_index;
     char *string;
     int length;
     int upper_y;  /* see show() for description */
     int lower_y;
     int right_x;
{
  char *temp_ptr;

  if (++next_dink_index >= MAX_DINKS)
    {
      PutWarnMsg("Too many words in browser.  Continuing.\n");
      return(-1);
    }

  dink_table[next_dink_index].x = x;
  dink_table[next_dink_index].y = y;
  dink_table[next_dink_index].font_table_index = font_table_index;
  dink_table[next_dink_index].length = length;
  dink_table[next_dink_index].upper_y = upper_y;
  dink_table[next_dink_index].lower_y = lower_y;
  dink_table[next_dink_index].right_x = right_x;
  dink_table[next_dink_index].string_ptr = next_dink_buffer;

  temp_ptr=next_dink_buffer+length;  /* note only last string in table
					will retain its eos */

  if (temp_ptr > dink_buffer+MAX_DINK_BUFFER)
    {
      next_dink_buffer--;
      PutWarnMsg("Too many characters in browser.  Continuing\n");
      return(-2);
    }

  strcpy(next_dink_buffer,string);
  next_dink_buffer = temp_ptr;
  return(0);
}

static void get_last_dinkinfo(x,y,font,right_x)
     int *x;
     int *y;
     int *font;
     int *right_x;
{

  *x = dink_table[next_dink_index].x;
  *y = dink_table[next_dink_index].y;
  *font = dink_table[next_dink_index].font_table_index;
  *right_x = dink_table[next_dink_index].right_x;
}

/******************************************************************

  THE X ROUTINES

     All the X routines have been isolated here to facilitate
     movement between X10 and X11.  (The visual courseware group
     requires X10).

******************************************************************/

static PutWarnMsg(msg)
     char * msg;
{
  XtWarning(msg);
}

static PutSeriousWarnMsg(msg)
     char *msg;
{
  XtWarning(msg);
  the_widget->scribePSBrowser.errorStateFlag = True;
}

static clear_page()
{
  XClearWindow(XtDisplay((Widget)the_widget),XtWindow((Widget)the_widget));
}

#define MM_PER_INCH 25.40  /* approximately, for now */


static int determine_conversions(w)
     Widget w;
{
  Screen *the_screen;

  /****** See comment in moveto() about conversions *****/

  the_screen = XtScreen(w);

/*
  pixels_per_vertical_inch = (double) DisplayHeight(the_display,the_screen) /
                             (double) DisplayHeightMM(the_display,the_screen) *
			     (double) MM_PER_INCH;

  pixels_per_horz_inch = (double) DisplayWidth(the_display,the_screen) /
                         (double) DisplayWidthMM(the_display,the_screen) *
			 (double) MM_PER_INCH;
*/
  pixels_per_vertical_inch = (double) the_screen->height /
                             (double) the_screen->mheight *
			     (double) MM_PER_INCH;
  pixels_per_horz_inch = (double) the_screen->width / 
                         (double) the_screen->mwidth * (double) MM_PER_INCH;
  return(1);
}

#undef MM_PER_INCH


/****************************************************************

  FONT INITIALIZATION ROUTINES

****************************************************************/



init_fonts()
{
  int i;
  FILE *fontfp;
  char junk[MAX_TOKEN];
  char filename[128];
  char *home;
  
  strcpy(filename,".psfonts");
  for (i=0;i<2;i++)
    {
      if ((fontfp = fopen(filename,"r")) != NULL)
	break;
      if ((home = getenv("HOME")) != NULL)
	{
	  strcpy(filename,home);
	  strcat(filename,"/");
	  strcat(filename,".psfonts");
	}
    }
     
  if (i >= 2)
    {
      /* Just quietly use the default fonts */
      /*PutWarnMsg("Can't open .psfonts.  Using default bindings.\n");*/
      /*** PUT IN MOVE FROM DEFAULTS TO INSTANCE STRUCTURE ***/
      n_actual_fonts = N_DEFAULT_TABLE;
    }
  else
    {
      if (fscanf(fontfp,"%s %d\n",junk,&n_actual_fonts) == EOF)
	{
	  PutWarnMsg("Missing number of fonts in .psfonts.");
	  return(-3);
	}
      if (n_actual_fonts > NFONTS)
	{
	  PutWarnMsg("Too many fonts in .psfonts.  ");
	  sprintf(junk,"Accepting %d.\n",NFONTS); 
	  PutWarnMsg(junk);
	  n_actual_fonts = NFONTS;
	}
      for (i=0;i<n_actual_fonts;i++)
	{
	  if (fscanf(fontfp,"%31s %d %31s\n",font_table[i].fontname,
	       &(font_table[i].fontsize),font_table[i].fontfile) == EOF)
	    {
	      strcpy(junk, "Missing entry in .psfonts file.  Continuing.");
	      PutWarnMsg(junk);
	      n_actual_fonts = --i;
	      break;
	    }
	  font_table[i].xfont = NULL;
	  font_table[i].xfontstruct = NULL;
	}
    }


  for (i=0;i<n_actual_fonts;i++)
    if ((font_table[i].xfontstruct = 
	 XLoadQueryFont(the_display,font_table[i].fontfile))
	== NULL)
    {
      if (i==DEFAULT_FONT)
	{
	  sprintf(junk, 
		"Couldn't open default font %s.  Using Fixed in browser.\n",
		 font_table[i].fontfile);
	  PutWarnMsg(junk);
	  strcpy(font_table[i].fontfile,"fixed");
	  if ((font_table[i].xfontstruct = XLoadQueryFont(the_display,
							  "fixed")) == NULL)
	    {
	      PutSeriousWarnMsg("Cannot open Fixed.  Browser abandoned.\n");
	    }
	  break;
	}
      else
	{
	  sprintf(junk, "Could not open font %s.  ",
		 font_table[i].fontfile);
	  strcat(junk,"Using default in browser\n");
	  PutWarnMsg(junk);
	  font_table[i].xfontstruct = font_table[DEFAULT_FONT].xfontstruct;
	  font_table[i].xfont = (font_table[DEFAULT_FONT].xfontstruct)->fid;
	}
    }
  else
    font_table[i].xfont = (font_table[i].xfontstruct)->fid;

  cur_font = DEFAULT_FONT;

  return(1);
}

            /***************************************************
	     *                                                 *
	     *  The only change from here down in moving from  *
	     *  postps to widget code is that fp was added     *
	     *  as a parameter to ignore_PS_header().          *
	     *                                                 *
	     **************************************************/

put_end_msg()
{
  static char *str1 = {"END OF FILE"};
  static char *str2 = {"Press Right Button to Exit"};
  int len1;
  int len2;
  int x,y,width,height,border_width,depth;
  Window root;

    /* Write out a message after the last page has been shown. */
  len1 = XTextWidth(font_table[DEFAULT_FONT].xfontstruct,
		    str1,strlen(str1));
  len2 = XTextWidth(font_table[DEFAULT_FONT].xfontstruct,
		    str2, strlen(str2));
  XGetGeometry(the_display,the_window,&root,&x,&y,&width,&height,
	       &border_width,&depth);
  XDrawString(the_display,the_window,the_GC,(width-len1)/2,
	      height/5,str1,strlen(str1));
  XDrawString(the_display,the_window,the_GC,(width-len2)/2,
	      height/5*3,str2,strlen(str2));
  return(0);
}
  
  


/**************************************************************

    postscript routines that call X

      These routines implement postscript commands.  They all
      call X routines.

**************************************************************/



setfont()
{
  int i;

  for (i=0;i<n_actual_fonts;i++)
    if (strcmp(cur_fontname,font_table[i].fontname) == 0)
      if (font_table[i].fontsize == cur_fontsize)
	break;

  if (i >= n_actual_fonts)
    {
      printf("sysmsg:  could not find font %s %d.",cur_fontname,
	     cur_fontsize);
      printf("   using default font...\n");
      i = DEFAULT_FONT;
    }

  cur_font = i;
  XSetFont(the_display,the_GC,font_table[cur_font].xfont);
  
  return(1);
}



show()
{
  char string[MAX_TOKEN];

  if (pop_token(string) < 0)
    return(-1);
  XDrawString(the_display,the_window,the_GC,cur_x,cur_y,string,strlen(string));
  cur_x = cur_x + XTextWidth(font_table[cur_font].xfontstruct,
			     string,strlen(string));
  return(1);
}

setlinewidth()
{
  char token[MAX_TOKEN];
  int width;
  
  if (pop_token(token) < 0)
    return(-1);

  width = atoi(token);
  if (width < 0)
    {
      printf("sysmsg:   illegal linewidth.  resetting to 1\n");
      width = 1;
    }

  /* It doesn't matter.  I don't want to transform the width anyways for now */
  width = 1;

  XSetLineAttributes(the_display,the_GC,width,LineSolid,CapButt,JoinMiter);
  return(1);
}

rlineto()
{
  char x_token[MAX_TOKEN];
  char y_token[MAX_TOKEN];
  double scribe_y;
  double scribe_x;
  int pt2_x;
  int pt2_y;

  if (pop_token(y_token) < 0)
    return(-1);
  scribe_y = (double) atoi(y_token);
  pt2_y = (int) rint(scribe_y / 7200.0 * pixels_per_vertical_inch) + cur_y;

  if (pop_token(x_token) < 0)
    return(-2);
  scribe_x = (double)atoi(x_token);
  pt2_x = (int) rint(scribe_x / 7200.0 * pixels_per_horz_inch) + cur_x;
  
  XDrawLine(the_display,the_window,the_GC,cur_x,cur_y,pt2_x,pt2_y);
  
  cur_x = pt2_x;
  cur_y = pt2_y;
  
  return(1);
}
lineto()
{
  char x_token[MAX_TOKEN];
  char y_token[MAX_TOKEN];
  double scribe_y;
  double scribe_x;
  int pt2_x;
  int pt2_y;

  if (pop_token(y_token) < 0)
    return(-1);
  scribe_y = (double) atoi(y_token);
  pt2_y = (int) rint(scribe_y / 7200.0 * pixels_per_vertical_inch);

  if (pop_token(x_token) < 0)
    return(-2);
  scribe_x = (double)atoi(x_token);
  pt2_x = (int) rint(scribe_x / 7200.0 * pixels_per_horz_inch);

  
  XDrawLine(the_display,the_window,the_GC,cur_x,cur_y,pt2_x,pt2_y);
  
  cur_x = pt2_x;
  cur_y = pt2_y;
  
  return(1);
}


/*****************************************************************

  POSTSCRIPT comands that don't call X

*****************************************************************/

moveto()
{
  char x_token[MAX_TOKEN];
  char y_token[MAX_TOKEN];
  
  double scribe_y;
  double scribe_x;

    /* 
       The following is a pain-in-the-ass.  Scribe's way of looking at the
       world is to place the origin at the upper left corner of the page
       which is great.  But, it also multiplies the number of pixels by
       100 for some reason.  To convert from pixels on the page to pixels
       on the screen, we unfortunately have to do the following conversion:

           Scribe dimensions * screen size in pixels * mm/inch
	   ----------------------------------------------------
        	 100.0 * 72.0 * screen size in mm

       which gives us

           (Scribe pixels) * (screeen pixels) * mm/inch
	   ----------------------------------------------------
           (Scribe pixels/PS pixels) * (PS pixels/inch) * mm

       which leaves us with:

            screen pixels

       Unfortunately, this is necessary considering that X gives us
       screen size in mm and screen pixels only.  PostScript and Scribe,
       but of course, use inches.

       The conversion factor for the screen is computed in setX().

     */
  if (pop_token(y_token) < 0)
    return(-1);
    /* Note: This is atoi to avoid erroneous conversions since string will
       always be an integer. */
  scribe_y = (double) atoi(y_token);
  cur_y = (int) rint(scribe_y / 7200.0 * pixels_per_vertical_inch);
  if (pop_token(x_token) < 0)
    return(-2);
  scribe_x = (double)atoi(x_token);
  cur_x = (int) rint(scribe_x / 7200.0 * pixels_per_horz_inch);

#ifdef FOR_DEBUG
  printf("debug: (x,y) are (%d,%d)\n",cur_x,cur_y);
#endif
  return(1);
}

rmoveto()
{
  char x_token[MAX_TOKEN];
  char y_token[MAX_TOKEN];
  
  double scribe_y;
  double scribe_x;

  if (pop_token(y_token) < 0)
    return(-1);

  scribe_y = (double) atoi(y_token);
  cur_y = (int) rint(scribe_y / 7200.0 * pixels_per_vertical_inch) + cur_y;

  if (pop_token(x_token) < 0)
    return(-2);
  scribe_x = (double)atoi(x_token);
  cur_x = (int) rint(scribe_x / 7200.0 * pixels_per_horz_inch) + cur_x;

#ifdef FOR_DEBUG
  printf("debug: (x,y) are (%d,%d)\n",cur_x,cur_y);
#endif
  return(1);
}

scalefont()
{
  char fontsize_token[MAX_TOKEN];

  if (pop_token(fontsize_token) < 0)
    return(-2);

  cur_fontsize = atoi(fontsize_token);

  return(1);
}


findfont()
{
  char fontname_token[MAX_TOKEN];

  if (pop_token(fontname_token) < 0)
    return(-1);

  if (strlen(fontname_token) >= MAX_FONTNAME)
    {
      printf("sysmsg:  fontname %s too long.  substituting default...\n",
	     fontname_token);
      strcpy(cur_fontname,DEFAULT_FONTNAME);
    }
  else
    strcpy(cur_fontname,fontname_token);

  return(1);
}

exch()  /* 11/9/87 */
{
  char token1[MAX_TOKEN];
  char token2[MAX_TOKEN];
  
  if (pop_token(token1) < 0)
    return(-1);
  if (pop_token(token2) < 0)
    return(-2);
  if (push_token(token1) < 0)
    return(-3);
  if (push_token(token2) < 0)
    return(-4);
  return(0);
}

dup()  /* 11/9/87 */
{
  char token1[MAX_TOKEN];
  
  if (pop_token(token1) < 0)
    return(-1);
  if (push_token(token1) < 0)
    return(-3);
  if (push_token(token1) < 0)
    return(-4);
  return(0);
}


neg()
{
  char token1[MAX_TOKEN];
  double r;

  if (pop_token(token1) < 0)
    return(-1);
  r = atof(token1);
  r = -r;
  sprintf(token1,"%lf6.2",r);
  if (push_token(token1) < 0)
    return(-4);
  return(0);
}


/*********************************************************************

  Scribe macros

*********************************************************************/
UL_proc_1()
{
  char token[MAX_TOKEN];
  double r;

  if (pop_token(token) < 0)
    return(-1);
  if (push_token(token) < 0)  /* same as dup if second used for divide below */
    return(-2);
  r = atof(token);
  r = r/2.0;                  /* 2.0 div */
  if (push_token("0.0") < 0)  /* 0.0 exch */
    return(-3);
  sprintf(token,"%f\n",(float)r); /* result of div */
  if (push_token(token) < 0)
    return(-4);
  return(1);
}

XM_proc()
{
  char x_token[MAX_TOKEN];
  double scribe_x;

  /* This routine resets only the the x coordinate */

  if (pop_token(x_token) < 0)
    return(-1);

  scribe_x = (double)atoi(x_token);
  cur_x = (int) rint(scribe_x / 7200.0 * pixels_per_horz_inch);
  return(1);
}

LH_proc()
{
  if (moveto() < 0) 
    return(-1);
  if (setlinewidth() < 0)
    return(-2);
  if (push_token("0.0") < 0)
    return(-5);
  if (rlineto() < 0)
    return(-6);
  return(0);
}

LV_proc()
{
  if (moveto() < 0) 
    return(-1);
  if (setlinewidth() < 0)
    return(-2);
  if (push_token("0.0") < 0)
    return(-3);
  if (exch() < 0)
    return(-4);
  if (rlineto() < 0)
    return(-6);
  return(0);
}

BX_proc()
{
  char start_x[MAX_TOKEN];
  char start_y[MAX_TOKEN];

  if (pop_token(start_y) < 0)
    return(-20);
  if (pop_token(start_x) < 0)
    return(-21);
  if (push_token(start_x) < 0)
    return(-22);
  if (push_token(start_y) < 0)
    return(-23);

  if (moveto() < 0) 
    return(-1);
  if (setlinewidth() < 0)
    return(-2);
  if (exch() < 0)
    return(-3);
  if (dup() < 0)
    return(-4);
  if (push_token("0.0") < 0)
    return(-5);
  if (rlineto() < 0)
    return(-6);
  if (exch() < 0)
    return(-7);
  if (push_token("0.0") < 0)
    return(-8);
  if (exch() < 0)
    return(-9);
  if (neg() < 0)
    return(-10);
  if (rlineto() < 0)
    return(-11);
  if (neg() < 0)
    return(-12);
  if (push_token("0.0") < 0)
    return(-13);
  if (rlineto() < 0)
    return(-14);
  if (push_token(start_x) < 0)
    return(-15);
  if (push_token(start_y) < 0)
    return(-16);
  if (lineto() < 0)
    return(-17);
  return(0);
}

BX2_proc()  /* ignore for now */
{
  char token[MAX_TOKEN];
  if (pop_token(token) < 0)
    return(-1);
  return(0);
}

W_proc()
{
  char x_add[MAX_TOKEN];
  int dx;

  /* This implements a Scribe macro that does microjustification on
     blanks in a string.  I'm just ignoring the microjustification for
     now */

  if (pop_token(x_add) < 0)
    return(-2);
  dx = atoi(x_add);
  /* ignore for now */
  if (show() < 0)
    return(-1);
  return(1);
}


long ignore_PS_header(fp)
     FILE *fp;
{
  char linebuffer[MAX_TOKEN];
  int flag;
  long fpos;

  flag = FALSE;

  while (fgets(linebuffer,MAX_TOKEN,fp) != NULL)
    {
#ifdef FOR_DEBUG
      printf("DEBUG: got: %s",linebuffer);
#endif
      if (strcmp(linebuffer,"%%EndProlog\n") == 0)
	{
	  flag = TRUE;
	  break;
	}
    }
  if (!flag || fgets(linebuffer,MAX_TOKEN,fp) == NULL)
    {
      printf("sysmsg:  This must be PostScript output for a Scribe file!\n");
      printf("sysmsg:  If this was a Scribe file, it stopped before");
      printf(" end of header\n");
      return(-1L);
    }
 
  fpos = ftell(fp);

  return(fpos);
}


long find_end()
{
  long endpos;

  if (fseek(fp,0L,2) == -1)
    {
      printf("sysmsg:  cannot get to end of file.  cannot continue....\n");
      return(-2);
    }
  endpos = ftell(fp);
  rewind(fp);
  
  return(endpos);
}


 
char *command_str[] = {"ES","AF","SF","SH","MT","BS","SI","W","XM","SS","UL",
			 "BX","BX1","BX2","LH","LV","PB","PE"};
#define NCOMMANDS     18
#define COMMAND_ES  0
#define COMMAND_AF  1
#define COMMAND_SF  2
#define COMMAND_SH  3
#define COMMAND_MT  4
#define COMMAND_BS  5
#define COMMAND_SI  6
#define COMMAND_W   7
#define COMMAND_XM  8
#define COMMAND_SS  9
#define COMMAND_UL  10
#define COMMAND_BX  11
#define COMMAND_BX1 12
#define COMMAND_BX2 13
#define COMMAND_LH  14
#define COMMAND_LV  15
#define COMMAND_PB  16
#define COMMAND_PE  17

int picture_flag = FALSE;

long show_page(from_pos)
     long from_pos;
{
  long fpos;
  char linebuffer[MAX_TOKEN];
  int showpage_flag;
  int count;
  char token[MAX_TOKEN];
  int command;
  int debug;


  if (fseek(fp,from_pos,0) == -1)
    {
      printf("sysmsg:  improper seek %lx.  trying to continue.\n",from_pos);
      return(-7L);
    }
  
  showpage_flag = FALSE;
  count = 0;
/*
  clear_page();
*/
  while (fgets(linebuffer,MAX_TOKEN,fp) != NULL)
    {
      /*printf("%s",linebuffer);*/
      if (linebuffer[0] == '%') /* ignore comments */
	{
	  if (strcmp("%%Trailer\n",linebuffer) == 0)
	    {
	      put_end_msg();
	      return(endpos+1L);
	    }
	  else
	    continue;
	}
      count++;
      reinit_parser();
        /* check for return codes on parser < 0 */
      while ((parser(linebuffer,token) != TRUE) && !showpage_flag)
	{
	  if ((command = check_str(token,command_str,NCOMMANDS)) < 0)
	    /* if it's not a command, then push it onto the token stack 
	       unless it's during a picture command (in which we eat it) */
	    if (picture_flag)
	      ;
	    else if (push_token(token) < 0)
	      return(-8L);
	    else
	      ;
	  else
	    switch (command) /* the Scribe macros */
	      {
	      case COMMAND_ES:
		showpage_flag = TRUE;
#ifdef FOR_DEBUG
		if (nextstack() != 0)
		  debug = 1;  /* for debugging */
#endif
		break;
	      case COMMAND_AF:
		if (findfont() < 0)
		  return(-100L);
		if (scalefont() < 0)
		  return(-101L);
		if (setfont() < 0)
		  return(-102L);
		break;
	      case COMMAND_SF:
		if (findfont() < 0)
		  return(-200L);
		if (setfont() < 0)
		  return(-201L);
		break;
	      case COMMAND_SS:
		if (scalefont() < 0)
		  return(-300L);
		if (setfont() < 0)
		  return(-301L);
		break;
	      case COMMAND_SH:
		if (show() < 0)
		  return(-3L);
		break;
	      case COMMAND_W:
		if (W_proc() < 0)
		  return(-11L);
		break;
	      case COMMAND_MT:
		if (moveto() < 0)
		  return(-4L);
		break;
	      case COMMAND_BS:
		/* Ignore, already built into the structure of this
		   program. */
		break;
	      case COMMAND_SI:
		/* Ignore, already built into the structure of this
		   program.  But, command takes one argument, so pop
		   off stack. */
		if (pop_token(token) < 0)
		  return(-5L);
		break;
	      case COMMAND_XM:
		if (XM_proc() < 0)
		  return(-20L);
		break;
	      case COMMAND_UL:
		if (moveto() < 0)
		  return(-1000L);  
		if (UL_proc_1() < 0) /* dup 2.0 div 0.0 exch */
		  return(-1001L);
		if (rmoveto() < 0)
		  return(-1002L);
		if (setlinewidth() < 0)
		  return(-1003L);
		if (push_token("0.0") < 0)
		  return(-1004L);
		if (rlineto() < 0) /* rlineto stroke */
		  return(-1005L);
		break;
	      case COMMAND_BX:
		if (BX_proc() < 0)
		  return(-1100L);
		break;
	      case COMMAND_BX1:
		break;
	      case COMMAND_BX2:
		if (BX2_proc() < 0)
		  return(-1200L);
		break;
	      case COMMAND_LH:
		if (LH_proc() < 0)
		  return(-1300L);
		break;
	      case COMMAND_LV:
		if (LV_proc() < 0)
		  return(-1400L);
		break;
	      case COMMAND_PB:
		picture_flag = TRUE;
		break;
	      case COMMAND_PE:
		picture_flag = FALSE;
		break;

	      default:
		printf("sysmsg:  wow!  unknown command %x.  continuing...\n",
		       command);
		break;
	      }
	}
      if (showpage_flag)
	break;

    }


  fpos = ftell(fp);
  if (showpage_flag)
     return(fpos);
  else
    if (count)
      {
	printf("sysmsg:  missing end of page.  continuing...\n");
	return(fpos);
      }
    else
      {
#ifdef FOR_DEBUG
	printf("DEBUG:  shouldn't have gotten here:  END OF FILE\n");
#endif
	return(-6L);
      }
}

/******************************************************************

  The POSTSCRIPT LINE PARSER for a line of input.

  For a true postscript parser, must add ] to the parser list.

******************************************************************/

int lpos;

reinit_parser()
{
  lpos = 0;
}

int parser(line,token)
     char *line;
     char *token;
{
  /* returns TRUE when eos, FALSE otherwise */
  int tpos;
  int strflag;
  char c;

  tpos = 0;
  strflag = FALSE;

  while (line[lpos] == ' ' || line[lpos] == '\n' || line[lpos] == '\t')
    lpos++;

  if (line[lpos] == '(')
    {
      strflag = TRUE;
      lpos ++;
    }

  while ((c = line[lpos]) != EOS)
    {
      if (strflag)
	switch (c)
	  {
	  case ')':
	  /*case ']':*/
	    lpos++;
	    token[tpos] = EOS;
	    return(FALSE);

	  case '\\':
	    /* Note1:  If it's in the middle of a string, then it will be
	       \ddd where ddd is the ascii version of the character */
	    ++lpos;
	    switch (line[lpos])
	      {
	      case 'n':
		token[tpos++] = '\n';
		break;
	      case 'r':
		token[tpos++] = '\r';
		break;
	      case 't':
		token[tpos++] = '\t';
		break;
	      case 'b':
		token[tpos++] = '\b';
		break;
	      case 'f':
		/* formfeed -- ignore */
		break;
	      case '\\':
		token[tpos++] = '\\';
		break;
	      case '(':
		token[tpos++] = '(';
		break;
	      case ')':
		token[tpos++] = ')';
		break;
	      case '\n':
		/* PostScript defines as ignore */
		break;
	      default:
		if (isdigit(line[lpos]))
		  {
		    sscanf(&line[lpos],"%3o",&token[tpos++]); 
		    lpos = lpos + 3;
		  }
		else
		  {
		    printf("sysmsg:  unknown string following \\: %x",
			   line[lpos]);
		    printf("  cannot continue....\n");
		    return(-4);
		  }
		break;
	      }
	    break;
	  case '\n':  /* should this just keep going assuming the
			 newline is treated like white space? */
	    printf("sysmsg:  broken token in parser.  cannot continue...\n");
	    return(-2);
	  default:
	    token[tpos++] = line[lpos++];
	  }
      else
	switch (*(line+lpos))
	  {
	  case '[':
	  case '(':
	    token[tpos]=EOS;
	    return(FALSE);
	  case '\n':
	  case '\t':
	  case ' ':
	    lpos++;
	    token[tpos]=EOS;
	    return(FALSE);
	  default:
	    /* Note:  if it's a \, then it's part of the token */
	    token[tpos++] = line[lpos++];
	    
	  }
    }

    /* If there's nothing left on the line, signal line is over */
  if (tpos==0)
    return(TRUE);
  else
    {
      printf("sysmsg:  broken token in parser.  cannot continue...\n");
      return(-3);
    }
}

check_str(string, array, nstrings)
     char *string;
     char *array[];
     int nstrings;
{
  int i;
  for (i=0;i<nstrings;i++)
    if (strcmp(string,array[i]) == 0)
      return(i);
  return(-1);
}





/*************************************************************************

         POSTSCRIPT STACK OPERATIONS

*************************************************************************/

#define MAX_STACK  40
#define MAX_STACK_BUFFER  MAX_TOKEN*MAX_STACK

char buf[MAX_STACK_BUFFER];  /* buffer for tokens on stack */
char *stack[MAX_STACK];      /* location of tokens in the buf */
int nextloc = 0;             /* next available stack location */
char *nextbufpos = buf;      /* next available buffer location : init to &buf */


pop_token(token)
     char *token;
{
  char *startloc;
  int len;
  int i;

  if (--nextloc < 0)
    {
      printf("sysmsg:  postscript stack underflow.  cannot continue...\n");
      return(-4);
    }

  startloc = stack[nextloc];
  if (startloc < buf || startloc >= (buf + MAX_STACK_BUFFER))
    {
      printf("sysmsg:  postscript stack corrupted.  cannot continue...\n");
      return(-1);
    }
  
  nextbufpos = startloc;  /* reset next_buffer_pos */
  len = strlen(startloc);
  if (len > MAX_TOKEN)
    {
      printf("sysmsg:  postscript stack buffer corrupted.  ");
      printf("cannot continue...\n");
      return(-2);
    }
  strcpy(token,startloc);
  *startloc = (char)0;
/*
  for (i=0;i<len;i++)
    *(token++) = *(startloc++);
  */
  return(1);
}



push_token(token)
     char *token;
{
  int len;
  int i;
  char *lastpos;

  stack[nextloc] = nextbufpos;
  if (++nextloc >= MAX_STACK)
    {
      printf("sysmsg:  postscript stack overflow.\n");
#ifdef  FOR_DEBUG
      printf("DEBUG:\n");
      for (i=0;i<nextloc;i++)
	printf("%s ",stack[i]);
#endif
      --nextloc;
      return(-2);
    }
  if (strlen(token) > MAX_TOKEN)
    {
      printf("sysmsg:  postscript token too long.  truncating...\n");
      token[MAX_TOKEN-1] = EOS;
    }
  strcpy(nextbufpos,token);

  if (nextbufpos + strlen(token) + 1>= buf + MAX_STACK_BUFFER)
    {
     printf("sysmsg: postscript stack buffer overflow.  cannot continue...\n");
      return(-3);
    }
  nextbufpos = nextbufpos + strlen(token) + 1;

  return(1);
}

static int nextstack() /* for debugging */
{
  return(nextloc);
}
