/* Original Author:         Andrew Trevorrow
   Implementation: Modula-2 under VAX/UNIX 4.2 BSD
   Date Started:   June, 1986

   Description:
   Implements routines and data structures for use in a TeX82 DVI translator.
   Much of the code in DVIReader is based on DVITYPE 2.7 by Donald Knuth.
   DVITYPE is a program for verifying a DVI file and also serves as a model
   for other DVI-reading programs.  See the "TeXWARE" manual by Knuth for a
   complete description of DVITYPE and the format of DVI files.
   For efficiency reasons we assume the given DVI file is formatted correctly;
   it is the job of DVITYPE to diagnose bad DVI files.

   This version converted to C and ported to BSD and System V UNIX by
   some chaps at Kernel Technology up to September 1989.  Contact
   mjh@uk.co.kernel (Mark J. Hewitt) with bug fixes etc.

   Involved were:	Mark J. Hewitt
   			Dave Dixon
			Marc Hadley
*/

#include <math.h>
#ifdef USG
#include <string.h>
#else
#include <strings.h>
#endif /* USG */

#include "def.h"
#include "dvireader.h"
#include "dvitovdu.h"
#include "screenio.h"

static char *sccsid[] = "@(#)dvireader.c	1.1";

void ProcessPostamble ();
void ProcessFontDefs ();
void ReadFirstBop ();
void SkipFntdef ();
void ReadNextBop ();
void ReadBopParameters ();
short   CurrMatchesNew ();
void InitStateValues ();
void InitPage ();
void DoSetChar ();
void DoPutChar ();
void DoPush ();
void DoPop ();
void DoRight ();
void DoDown ();
void DoSetRule ();
void DoPutRule ();
void DoFont ();

#ifdef DEBUG
int Debug = 1;				/* Set != 0 for loads of debugging */
#endif /* DEBUG */

/*******************************************************************************
   DECLARATIONS FOR RANDOMLY ACCESSING A DVI FILE

   A DVI file is considered to be a stream of 8-bit bytes, ending with at
   least 4 223-bytes and having a total length divisible by 4.
   We will be randomly positioning to DVI bytes using MoveToDVIByte
   and then reading bytes via GetDVIByte, SignedDVIByte etc.
*/

/*******************************************************************************
   DECLARATIONS FOR INTERPRETING A DVI PAGE

   The commands between the BOP and EOP bytes for a particular page need to be
   translated (based on the method used by DVITYPE) before we can determine the
   the position and shape of all rules on that page, as well as the position
   of all characters and which fonts they belong to.
*/

 /* Use symbolic names for the opcodes of DVI commands:                     */
#define   SETCHAR0  0		/* setchar1..setchar127 = 1..127              */
#define   SET1      128		/* set2,set3,set4 = 129,130,131               */
#define   SETRULE   132
#define   PUT1      133		/* put2,put3,put4 = 134,135,136               */
#define   PUTRULE   137
#define   NOP       138
#define   BOP       139
#define   EOP       140
#define   PUSH      141
#define   POP       142
#define   RIGHT1    143
#define   W0        147
#define   X0        152
#define   DOWN1     157
#define   Y0        161
#define   Z0        166
#define   RIGHT2    144
#define   W1        148
#define   X1        153
#define   DOWN2     158
#define   Y1        162
#define   Z1        167
#define   RIGHT3    145
#define   W2        149
#define   X2        154
#define   DOWN3     159
#define   Y2        163
#define   Z2        168
#define   RIGHT4    146
#define   W3        150
#define   X3        155
#define   DOWN4     160
#define   Y3        164
#define   Z3        169
#define   W4        151
#define   X4        156
#define   Y4        165
#define   Z4        170
#define   FNTNUM0   171		/* FNTNUM1..FNTNUM63 = 172..234               */
#define   FNT1      235		/* fnt2,fnt3,fnt4 = 236,237,238               */
#define   XXX1      239		/* xxx2,xxx3,xxx4 = 240,241,242               */
#define   FNTDEF1   243		/* FNTDEF2,FNTDEF3,FNTDEF4 = 244,245,246      */
#define   PRE       247
#define   POST      248
#define   POSTPOST  249

 /* undefined commands = 250..255 */

#define   MAXINT    2147483647	/* SYSDEP: 2^31 - 1               */
#define   MAXSTACKSIZE  100	/* maxstack size for state values */
#define   MAXDRIFT      2	/* prevent hh & vv from drifting  */

short   DVIcommand;		/* holds next DVI command         */

unsigned int
              maxstack,		/* max pushes over pops in DVI file      */
              num,		/* DVI numerator                         */
              den;		/* DVI denominator                       */

float
        conv;			/* converts DVI units to pixels          */

int
      h, v,			/* current pos on page in DVI units      */
      w, x,			/* horizontal increments in DVI units    */
      y, z,			/* vertical increments in DVI units      */
      hh, vv,			/* h and v in pixels (approx)            */
      hhh, vvv;			/* h and v rounded to nearest pixel      */

typedef int stackarr[MAXSTACKSIZE + 1];

stackarr			/* push down stacks for state values   */
hstack, vstack,
wstack, xstack,
ystack, zstack,
hhstack, vvstack;

int   stackpos;			/* stacks empty when stackpos = 0, i.e., top of
				   stacks = stackpos - 1    */
int   fontspace;		/* used in DoRight()and DoDown()           */
ruleinfoptr thisrule;		/* temporary pointer to node in rulelist */
charinfoptr thischarptr;	/* temporary pointer to node in charlist */

FILE * DVIfile;
unsigned int  DVIoffset;	/* offset of next byte to be read */
unsigned int  postamblepos;	/* offset of postamble byte */

/*******************************************************************************
   DECLARATIONS FOR GETTING TO A DVI PAGE

   The user can select a particular page by specifying a DVI page
   number (from 1 to totalpages), or a TeX page number (based on the
   values of \count0,\count1,...,\count9), or simply requesting the next page
   in the DVI file (which depends on whether we are ascending or not).
   We will often need to follow the DVI backpointers to locate the BOP byte
   of a selected page.
*/

unsigned int
              curreop,		/* position of EOP byte of current page    */
              currbop,		/* position of bop byte of current page    */
              lastbop;		/* position of last BOP byte               */

int   prevbop;			/* position of BOP byte of previous page; note that
				   prevbop of first page = -1       */

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

void OpenDVIFile (filespec)
char  filespec[];
{
/* if the given file can be opened and is a valid TeX82 DVI file then we
   process the postamble and initialize currDVIpage and currTeXpage.
*/
  unsigned int  i;

  DVIfile = fopen (filespec, "r");/* SYSDEP: open for reading */
  if (DVIfile != NULL)
  {
  /* try to move to last DVI byte */
    if (fseek (DVIfile, -1, 2) < 0)
    {
    /* SYSDEP: true if empty file */
      DVIErrorRoutine (DVIempty);
    }
    else
    {
    /* we are positioned at last byte, so skip back over 223-bytes */
      for (;;)
      {
	i = (int) (fgetc (DVIfile));/* fgetc advances to next byte */
	if (i != 223)
	  break;

      /* if i==223 then move back 2 bytes and try again */
	if (fseek (DVIfile, -2, 1) < 0)
	{
	  DVIErrorRoutine (DVIbadid);/* file only has 223-bytes! */
	}
      }
      if (i != 2)
      {
#ifdef DEBUG
	(void) printf ("Bad DVI file! id byte = %d\n", i);
#endif /* DEBUG */
	DVIErrorRoutine (DVIbadid);/* not a valid TeX82 DVI file */
      }
      else
      {
      /* move back to 1st byte of postamble pointer */
	if (fseek (DVIfile, -5, 1) < 0)
	{
	  DVIErrorRoutine (DVIbadid);/* file too short! */
	}
	DVIoffset = 0;		/* not really; just playing safe */
	postamblepos = SignedDVIQuad ();

      /* NB. From now on we use MoveToDVIByte()rather than explicit fseek. DVIoffset
         is reset only in MoveToDVIByte()and incremented only in the various Get
         routines. */

	ProcessPostamble ();	/* get DVImag, totalpages, etc */
	ProcessFontDefs ();	/* build and initialize font list */
	currDVIpage = 0;	/* we haven't processed a page yet */
	for (i = 0; i <= 9; i++)
	{
	  currTeXpage[i] = 0;
	}
      }
    }
  }
  else
  {
    DVIErrorRoutine (DVIunopened);/* given file could not be opened */
  }
}

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

void MoveToDVIByte (offset)
unsigned int  offset;
{
/* Set current position in DVIfile to given byte offset from start of file,
   where first byte is at offset 0.
*/

  if (fseek (DVIfile, (long) offset, 0) < 0)
  {
    WriteString ("fseek failed in MoveToDVIByte()");
    WriteLn ();
    RestoreTerminal ();
    exitprog (1);
  }
  DVIoffset = offset;		/* SYSDEP: next fgetc will read this byte */
}

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

/* Here are the functions used to get byte/s from DVIfile. */

int   NoSignExtend (fp, n)	/* return n byte quantity from file fd */
register  FILE * fp;		/* file pointer    */
register int  n;		/* number of bytes */
{
  register int  xnum;		/* number being constructed */

  xnum = 0;
  while (n--)
  {
    xnum <<= 8;
    xnum |= getc (fp);
  }
  return (xnum);
}


int   SignExtend (fp, n)	/* return n byte quantity from file fd */
register  FILE * fp;		/* file pointer    */
register int  n;		/* number of bytes */
{
  int   n1;			/* number of bytes	    */
  register int  xnum;		/* number being constructed */

  xnum = getc (fp); /* get first (high-order) byte */
  n1 = n--;
  while (n--)
	  {
    xnum <<= 8;
    xnum |= getc (fp);
  }

 /* NOTE: This code assumes that the right-shift is an arithmetic, rather than
    logical, shift which will propagate the sign bit right.   According to Kernighan
    and Ritchie, this is compiler dependent! */

  xnum <<= 32 - 8 * n1;
  xnum >>= 32 - 8 * n1;		/* sign extend */

#ifdef DEBUG
  if (Debug)
  {
    (void) fprintf (stderr, "\tSignExtend(fp,%d)=%X\n", n1, xnum);
  }
#endif /* DEBUG */
  return (xnum);
}


int   GetDVIByte ()
{
/* Return the value (unsigned) of the next byte and advance. */

    DVIoffset++;
    return (NoSignExtend (DVIfile, 1));
}

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

int   SignedDVIByte ()
{
    DVIoffset++;
    return (SignExtend (DVIfile, 1));
}

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

int   GetTwoDVIBytes ()		/* the next 2 bytes, unsigned */
{
    DVIoffset += 2;
    return (NoSignExtend (DVIfile, 2));
}

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

int   SignedDVIPair ()		/* the next 2 bytes, signed */
{
    
    DVIoffset += 2;
    return (SignExtend (DVIfile, 2));
}

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

int   GetThreeDVIBytes ()	/* the next 3 bytes, unsigned */
{
    DVIoffset += 3;
    return (NoSignExtend (DVIfile, 3));
}

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

int   SignedDVITrio ()		/* the next 3 bytes, signed */
{
    DVIoffset += 3;
    return (SignExtend (DVIfile, 3));
}


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

int   SignedDVIQuad ()		/* the next 4 bytes, signed */
{
        DVIoffset += 4;
	return (SignExtend (DVIfile, 4));
}

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

void ProcessPostamble ()
{
/* Having successfully opened the DVI file, we move to the postamble
   and initialize these global variables:
   lastbop, num, den, DVImag, maxstack, totalpages.
   The font definitions are read by ProcessFontDefs()
*/

  MoveToDVIByte (postamblepos);	/* DVIoffset is now properly initialized */
  (void) GetDVIByte ();			/* Ignore postamble */
  lastbop = SignedDVIQuad ();
  num = SignedDVIQuad ();
  den = SignedDVIQuad ();
  DVImag = SignedDVIQuad ();
  (void) SignedDVIQuad ();		/* Ignore pagehtplusdp */
  (void) SignedDVIQuad ();		/* Ignore pagewidth */
  maxstack = SignedDVIPair ();
  totalpages = SignedDVIPair ();
  if (maxstack > MAXSTACKSIZE)
  {

#ifdef DEBUG
    (void) printf ("Stack capacity exceeded!   maxstack = %d", maxstack);
    (void) printf (" but maxstacksize only = %d\n", MAXSTACKSIZE);
    RestoreTerminal ();
    exitprog (1);
#endif /* DEBUG */

    DVIErrorRoutine (DVIstackoverflow);
  /* now we don't need to test for stack overflow in DoPush() */
  }
#ifdef DEBUG 
   /* (void) printf("postamble opcode     = %d\n",postamble); */
   (void) printf("postion of last BOP  = %d\n",lastbop);
   (void) printf("num                  = %d\n",num);
   (void) printf("den                  = %d\n",den);
   (void) printf("DVI mag              = %d\n",DVImag);
   /* (void) printf("ht+dp of tallest page= %d\n",pagehtplusdp); */
   /* (void) printf("width of widest page = %d\n",pagewidth); */
   (void) printf("max stack depth      = %d\n",maxstack);
   (void) printf("total # of pages     = %d\n",totalpages);
#endif /* DEBUG */
}

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

void ProcessFontDefs ()
{
/* Read()the FNTDEF commands in the postamble (FNTDEF commands in the DVI
   pages will be skipped) and store the information in the font list.
   (Note that complete fontspecs are NOT built here because DVIReader does not
   want to know about the format or naming conventions of font files;
   the client module must handle all this in its PixelTableRoutine.)
   Since ProcessPostamble()ended by reading the totalpages parameter, the
   next GetDVIByte should return NOP or first FNTDEF.
*/

  int   f, s, d, a, l, i;	/* hold FNTDEF parameters */
  char  ch;			/* for getting farea and fname */
  fontstring farea, fname;	/* a and l bytes long respectively */

  totalfonts = 0;		/* number of nodes in font list */
  fontlist = NULL;
  do
  {
    DVIcommand = GetDVIByte ();
    if ((DVIcommand >= FNTDEF1) && (DVIcommand <= FNTDEF1 + 3))
    {
      switch (DVIcommand - FNTDEF1)
      {
	case 0: 
	  f = GetDVIByte ();
	  break;
	case 1: 
	  f = GetTwoDVIBytes ();
	  break;
	case 2: 
	  f = GetThreeDVIBytes ();
	  break;
	case 3: 
	  f = SignedDVIQuad ();
	  break;
      }
      (void) SignedDVIQuad ();	/* checksum; ignore it */
      s = SignedDVIQuad ();	/* scaled size */
      d = SignedDVIQuad ();	/* design size */
      a = GetDVIByte ();	/* length of font area */
      l = GetDVIByte ();	/* length of font name */
      (void) strcpy (farea, "");
      for (i = 0; i <= a - 1; i++)/* read and store font area */
      {
	ch = (char) (GetDVIByte ());
	if (i < MAXFONTSPEC)
	  farea[i] = ch;
      }
      (void) strcpy (fname, "");
      for (i = 0; i <= l - 1; i++)/* read and store font name */
      {
	ch = (char) (GetDVIByte ());
	if (i < MAXFONTSPEC)
	  fname[i] = ch;
      }
      currfont = (fontinfoptr) malloc (sizeof (struct fontinfo));
      {
	currfont->fontused = FALSE;
	currfont->fontnum = f;
	currfont->scaledsize = s;
	currfont->designsize = d;
	(void) strcpy (currfont->fontarea, farea);
	currfont->fontarealen = (unsigned) a;
	(void) strcpy (currfont->fontname, fname);
	currfont->fontnamelen = (unsigned) l;
	(void) strcpy (currfont->fontspec, "");
	currfont->fontspeclen = 0;/* fontspec has to be built by client */
	currfont->totalchars = 0;
	currfont->charlist = NULL;/* first node allocated in DoFont() */
	currfont->chartail = NULL;/* nodes are added to tail of char list */
	currfont->pixelptr = NULL;/* allocated once per font; see DoFont() */
	currfont->nextfont = fontlist;
      }
      fontlist = currfont;	/* add new font to head of list */
      totalfonts++;
    }
    else
      if (DVIcommand == NOP)
      {
      /* NOP commands can occur between DVI commands */
      }
      else
	if (DVIcommand == POSTPOST)
	{
	/* we have reached end of postamble */
	}
	else
	{
#ifdef DEBUG
	  (void) printf ("Unexpected DVI command in postamble = %d\n", DVIcommand);
	  RestoreTerminal ();
	  exitprog (1);
#endif	/* GUBED */
	  DVIErrorRoutine (DVIcatastrophe);
	}
  }
  while (DVIcommand != POSTPOST);
}


void MoveToNextPage (ascending)
short   ascending;
{
/* Move to next DVI page; whether we can will depend on the current DVI page
   and if we are ascending or descending.
*/

  if ((currDVIpage == 1) && (!ascending))
    return;			/* do nothing */
  else
    if ((currDVIpage == totalpages) && ascending)
      return;			/* do nothing */
    else
      if (currDVIpage == 0)
      {
      /* we haven't processed a page yet */
	if (ascending)
	{
	/* get first page */
	  ReadFirstBop ();
	}
	else			/* get last page */
	{
	  currbop = lastbop;
	  MoveToDVIByte (currbop + 1);
	  currDVIpage = totalpages;
	}
      }
      else
      {
	if (ascending)
	{
	/* currently positioned after EOP of currDVIpage, so get next BOP */
	  ReadNextBop ();
	}
	else
	{
	/* move to BOP pointed to by currbop's backpointer */
	  currbop = prevbop;
	  MoveToDVIByte (currbop + 1);/* move to byte after previous BOP */
	  currDVIpage--;
	}
      }
  ReadBopParameters ();		/* update currTeXpage and prevbop */
}

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

void ReadFirstBop ()
{
/* Read()first BOP by skipping past preamble; update currbop and currDVIpage. */

  unsigned int  k, i;

  MoveToDVIByte (14);		/* position of preamble's k parameter */
  k = GetDVIByte ();		/* length of x parameter */
  for (i = 1; i <= k; i++)
    (void) GetDVIByte ();	/* skip preamble comment */

  do				/* skip any NOPs and FNTDEFs */
  {
    DVIcommand = GetDVIByte ();
    if ((DVIcommand == NOP) || (DVIcommand == BOP))
    {
    /* do nothing */
    }
    else
      if ((DVIcommand >= FNTDEF1) && (DVIcommand <= FNTDEF1 + 3))

	SkipFntdef ((unsigned) DVIcommand - FNTDEF1);
      else
      {
#ifdef DEBUG
	  (void) printf ("\nUnexpected DVI command before first BOP = %d\n", DVIcommand);
	  RestoreTerminal ();
	  exitprog (1);
#endif /* DEBUG */
	  
	  DVIErrorRoutine (DVIcatastrophe);
      }
  }
  while (DVIcommand != BOP);
  currbop = DVIoffset - 1;	/* position in DVI file of first BOP */
  currDVIpage = 1;
}

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

void SkipFntdef (which)
unsigned int  which;
{
/* Read()past a FNTDEF command without interpreting it. */

  unsigned int a, l, i;

  switch (which)		/* which = DVIcommand - FNTDEF1 */
  {
    case 0: 
      (void) GetDVIByte ();
      break;
    case 1: 
      (void) GetTwoDVIBytes ();
      break;
    case 2: 
      (void) GetThreeDVIBytes ();
      break;
    case 3: 
      (void) SignedDVIQuad ();
      break;
  }
  (void) SignedDVIQuad ();
  (void) SignedDVIQuad ();
  (void) SignedDVIQuad ();
  a = GetDVIByte ();		/* length of directory */
  l = GetDVIByte ();		/* length of font name */

  for (i = 0; i <= l + a - 1; i++)
    (void) GetDVIByte ();
}

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

void ReadNextBop ()
{
/* We are currently positioned after an EOP byte which we know is not the
   last.  This routine positions us after the next BOP byte and updates
   currbop and currDVIpage.
*/

  do				/* skip any NOPs and FNTDEFs */
  {
    DVIcommand = GetDVIByte ();
    if ((DVIcommand == NOP) || (DVIcommand == BOP))
    {
    /* do nothing */
    }
    else
      if ((DVIcommand >= FNTDEF1) && (DVIcommand <= FNTDEF1 + 3))

	SkipFntdef ((unsigned) DVIcommand - FNTDEF1);
      else
      {
#ifdef DEBUG
	  WriteString ("\nUnexpected DVI command between EOP and BOP = %d\n"
		       ,DVIcommand);
	  RestoreTerminal ();
	  exitprog (1);
#endif /* DEBUG */
	  
	DVIErrorRoutine (DVIcatastrophe);
      }
  }
  while (DVIcommand != BOP);
  currbop = DVIoffset - 1;	/* position in DVI file of this BOP */
  currDVIpage++;
}

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

void ReadBopParameters ()
{
/* We should now be positioned after the BOP of desired page, so read
   the 10 TeX counters and update currTeXpage and prevbop.
   At the end of this routine we will be at the byte after currbop's parameters
   and ready to InterpretPage()
*/
  unsigned int  i;

  for (i = 0; i <= 9; i++)
    currTeXpage[i] = SignedDVIQuad ();

  prevbop = SignedDVIQuad ();	/* position of previous BOP in DVI file */
}

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

void MoveToDVIPage (n)

unsigned int  n;
{
/* Move to nth DVI page; n should be in 1..totalpages. */

  if ((n < 1) || (n > totalpages))
    return;			/* do nothing */

  if (n == 1)
  {
  /*  Note that this test must come before next if(so that we avoid any
      problems when currDVIpage initially = 0. */
    ReadFirstBop ();
  }
  else
    if (n == currDVIpage + 1)
    {
      ReadNextBop ();
    }
    else
    {
      if (n < currDVIpage)
      {
	currbop = prevbop;	/* start searching backwards from previous page */
	currDVIpage--;
      }
      else
	if (n > currDVIpage)
	{
	  currbop = lastbop;	/* start searching backwards from last page */
	  currDVIpage = totalpages;
	}

    /* n = currDVIpage so we'll just move back to currbop */

    /* n is now <= currDVIpage so search by following backpointers */
      for (;;)
      {
	if (n == currDVIpage)
	{
	  MoveToDVIByte (currbop + 1);/* move to byte after currbop */
	  break;
	}
	else
	{
	  MoveToDVIByte (currbop + 41);/* move to location of backpointer */
	  currbop = SignedDVIQuad ();/* get location of previous page */
	  currDVIpage--;
	}
      }
    }
  ReadBopParameters ();		/* update currTeXpage and prevbop */
}

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

short   MoveToTeXPage (newTeXpage)/* in */
struct TeXpageinfo *newTeXpage;
{
/* Return TRUE iff the given TeX page exists.
   If so then we move to the lowest matching page.
*/
  unsigned int  savecurrbop, savecurrDVIpage, i;
  int   nextbop;
  short   atleastone;

/* save away current page and DVI position */

  savecurrDVIpage = currDVIpage;
  if (currDVIpage != 0)		/* only if we've processed a page */
  /* note that curreop is saved in last InterpretPage() */
    savecurrbop = currbop;

/* search backwards through all DVI pages for lowest matching page */
  atleastone = FALSE;
  nextbop = lastbop;
  for (i = totalpages; i >= 1; i--)
  {
    MoveToDVIByte ((unsigned) (nextbop + 1));
    ReadBopParameters ();	/* update currTeXpage and prevbop */
    if (CurrMatchesNew (newTeXpage))
    {
      currbop = nextbop;
      currDVIpage = i;
      atleastone = TRUE;
    }
    nextbop = prevbop;
  }
  if (!atleastone)		/* no match, so restore currDVIpage */
  {
    currDVIpage = savecurrDVIpage;
    if (currDVIpage != 0)	/* restore page and positioning info */
    {
      currbop = savecurrbop;
      MoveToDVIByte (currbop + 1);
      ReadBopParameters ();	/* restore currTeXpage and prevbop */
      MoveToDVIByte (curreop + 1);
    /* we should now be after the EOP byte of the original page */
    }
    return (FALSE);
  }
  else				/* we found lowest matching page */
  {
    MoveToDVIByte (currbop + 1);
  }
  ReadBopParameters ();		/* update currTeXpage and prevbop */
  return (TRUE);
}

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

short   CurrMatchesNew (newTeXpage)

struct TeXpageinfo *newTeXpage;
{

/* Return TRUE iff currTeXpage matches newTeXpage. */

  short   i;

  {
    for (i = 0; i <= newTeXpage->lastvalue; i++)
    {
      if (newTeXpage->present[i] && (newTeXpage->value[i] != currTeXpage[i]))
	return (FALSE);
    }
  }
  return (TRUE);
}



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

void SetConversionFactor (localresolution, magnification)

unsigned int  localresolution, magnification;
{

/* Client module must help calculate conv, the number of pixels per DVI unit. */

  conv = (float) (num) / 254000.0 * (float) (localresolution) / (float) (den) *
    (float) (magnification) / 1000.0;
}

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

void InterpretPage ()
{

/* When this routine is called we are positioned after the bytes of a BOP
   command (i.e., at currbop + 45).  At the end of this routine we will be
   positioned after the EOP byte for the current page.  In between we carry
   out the important task of translating the DVI description of this page
   and filling in the various data structures exported by DVIReader.
*/
  int   param;

  InitStateValues ();
  InitPage ();
  do
  {
    DVIcommand = GetDVIByte ();
  /* For efficiency reasons the most frequent commands should be tested 1st. The
     following order is the result of frequency testing on "typical" DVI files. Note
     that the most frequent commands in the DVI file generated by VAX/VMS TeX 1.3
     for the Dec. 1983 LaTeX manual were: <SET1, W0, RIGHT3, PUSH/POP, X0, W3, Y0,
     FNTNUM25, RIGHT2, FNTNUM31, DOWN3, X2, RIGHT4, W2, X3, DOWN4, Z0, FNTNUM8,
     SETRULE, Y3, etc. */
    if (DVIcommand < SET1)
      DoSetChar ((unsigned) DVIcommand);/* 0..127 */
    else
      if (DVIcommand == W0)
	DoRight (w);
      else
	if (DVIcommand == RIGHT3)
	  DoRight (SignedDVITrio ());
	else
	  if (DVIcommand == PUSH)
	    DoPush ();
	  else
	    if (DVIcommand == POP)
	      DoPop ();
	    else
	      if (DVIcommand == X0)
		DoRight (x);
	      else
		if (DVIcommand == W3)
		{
		  w = SignedDVITrio ();
		  DoRight (w);
		}
		else
		  if (DVIcommand == Y0)
		    DoDown (y);

		  else
		    if ((DVIcommand > Z4) && (DVIcommand < FNT1))
		    /* FNTNUM0..FNTNUM63 */
		      DoFont (DVIcommand - FNTNUM0);

  /* catch all the remaining movement commands */
		    else
		      if ((DVIcommand > POP) && (DVIcommand < FNTNUM0))
		      {
		      /* SYSDEP: compiler did not like if(with too many
		         if(branches!!! */
			switch (DVIcommand)
			{
			  case RIGHT2: 
			    DoRight (SignedDVIPair ());
			    break;
			  case RIGHT4: 
			    DoRight (SignedDVIQuad ());
			    break;
			  case X2: 
			    x = SignedDVIPair ();
			    DoRight (x);
			    break;
			  case X3: 
			    x = SignedDVITrio ();
			    DoRight (x);
			    break;
			  case DOWN3: 
			    DoDown (SignedDVITrio ());
			    break;
			  case DOWN4: 
			    DoDown (SignedDVIQuad ());
			    break;
			  case W2: 
			    w = SignedDVIPair ();
			    DoRight (w);
			    break;
			  case Z0: 
			    DoDown (z);
			    break;
			  case Y3: 
			    y = SignedDVITrio ();
			    DoDown (y);
			    break;
			  case Z3: 
			    z = SignedDVITrio ();
			    DoDown (z);
			    break;
			  case DOWN2: 
			    DoDown (SignedDVIPair ());
			    break;
			  /* the next DVI commands are used very rarely (by TeX 1.3
			     at least) */
			  case W1: 
			    w = SignedDVIByte ();
			    DoRight (w);
			    break;
			  case W4: 
			    w = SignedDVIQuad ();
			    DoRight (w);
			    break;
			  case X1: 
			    x = SignedDVIByte ();
			    DoRight (x);
			    break;
			  case X4: 
			    x = SignedDVIQuad ();
			    DoRight (x);
			    break;
			  case Y1: 
			    y = SignedDVIByte ();
			    DoDown (y);
			    break;
			  case Y2: 
			    y = SignedDVIPair ();
			    DoDown (y);
			    break;
			  case Y4: 
			    y = SignedDVIQuad ();
			    DoDown (y);
			    break;
			  case Z1: 
			    z = SignedDVIByte ();
			    DoDown (z);
			    break;
			  case Z2: 
			    z = SignedDVIPair ();
			    DoDown (z);
			    break;
			  case Z4: 
			    z = SignedDVIQuad ();
			    DoDown (z);
			    break;
			  case RIGHT1: 
			    DoRight (SignedDVIByte ());
			    break;
			  case DOWN1: 
			    DoDown (SignedDVIByte ());
			    break;
			  default: 
			    {
#ifdef DEBUG				/* we've missed a movement command */
				(void) printf ("\nBug in InterpretPage()\n");
				RestoreTerminal ();
				exitprog (1);
#endif /* DEBUG */
			      DVIErrorRoutine (DVIcatastrophe);
			      break;
			    }
			}
		      }
		      else
			if (DVIcommand == SETRULE)
			{
			  int   width, height;

			  height = SignedDVIQuad ();
			  width = SignedDVIQuad ();
			  DoSetRule (height, width);
			}
			else
			  if (DVIcommand == PUTRULE)
			  {
			    int   width, height;

			    height = SignedDVIQuad ();
			    width = SignedDVIQuad ();
			    DoPutRule (height, width);
			  }
			  else
			    if ((DVIcommand >= PUT1) && (DVIcommand <= PUT1 + 3))

			      switch (DVIcommand - PUT1)
			      {
				case 0: 
				  DoPutChar ((unsigned) GetDVIByte ());
				  break;
				case 1: 
				  DoPutChar ((unsigned) GetTwoDVIBytes ());
				  break;
				case 2: 
				  DoPutChar ((unsigned) GetThreeDVIBytes ());
				  break;
				case 3: 
				  DoPutChar ((unsigned) SignedDVIQuad ());
				  break;
			      }

			    else
			      if ((DVIcommand >= SET1) && (DVIcommand <= SET1 + 3))

				switch (DVIcommand - SET1)
				{
				  case 0: 
				    DoSetChar ((unsigned) GetDVIByte ());
				    break;
				  case 1: 
				    DoSetChar ((unsigned) GetTwoDVIBytes ());
				    break;
				  case 2: 
				    DoSetChar ((unsigned) GetThreeDVIBytes ());
				    break;
				  case 3: 
				    DoSetChar ((unsigned) SignedDVIQuad ());
				    break;
				}

			      else
				if ((DVIcommand >= FNT1) && (DVIcommand <= FNT1 + 3))

				  switch (DVIcommand - FNT1)
				  {
				    case 0: 
				      DoFont (GetDVIByte ());
				      break;
				    case 1: 
				      DoFont (GetTwoDVIBytes ());
				      break;
				    case 2: 
				      DoFont (GetThreeDVIBytes ());
				      break;
				    case 3: 
				      DoFont (SignedDVIQuad ());
				      break;
				  }

				else
				  if ((DVIcommand >= XXX1) && (DVIcommand <= XXX1 + 3))
				  {
				    switch (DVIcommand - XXX1)
				    {
				      case 0: 
					param = GetDVIByte ();
					break;
				      case 1: 
					param = GetTwoDVIBytes ();
					break;
				      case 2: 
					param = GetThreeDVIBytes ();
					break;
				      case 3: 
					param = SignedDVIQuad ();
					break;
				    }
				  /* Pass number of bytes and byte grabbing function
				     for client to use. We use IgnoreSpecial()if
				     client does not assign a procedure. */
				    SpecialRoutine (param, GetDVIByte);

				  /* skip FNTDEF command since we've got this info
				     from postamble */
				  }
				  else
				    if ((DVIcommand >= FNTDEF1) && (DVIcommand <= FNTDEF1 + 3))
				      SkipFntdef ((unsigned) (DVIcommand - FNTDEF1));
				    else
				      if (DVIcommand == NOP);/* do nothing */
				      else
					if (DVIcommand == EOP);/* do nothing */
					else
					{
#ifdef DEBUG
					    (void) printf ("Unexpected DVI command while interpreting page = %d\n", DVIcommand);
					    RestoreTerminal ();
					    exitprog (1);
#endif /* DEBUG */

					  DVIErrorRoutine (DVIcatastrophe);
					}
  }
  while (DVIcommand != EOP);
/* save position of EOP byte for use in MoveToTeXPage */
  curreop = DVIoffset - 1;
  if (stackpos != 0)
  {
#ifdef DEBUG
    (void) printf ("Stack not empty at EOP!\n");
    RestoreTerminal ();
    exitprog (1);
#endif /* DEBUG */

    DVIErrorRoutine (DVIcatastrophe);
  }
  pageempty = ((minhp == MAXINT) && (minvp == MAXINT) &&
      (maxhp == -MAXINT) && (maxvp == -MAXINT));/* InitPage()values */
}

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

void InitStateValues ()
{
/* Set state values to 0 and stack empty. */

  h = 0;
  v = 0;
  w = 0;
  x = 0;
  y = 0;
  z = 0;
  hh = 0;
  vv = 0;
  stackpos = 0;
  fontspace = 0;		/* for DoRight()and DoDown()before a DoFont()call */
}

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

void InitPage ()
{
/* Initialize()page structures so there are no fonts, no chars and no rules. */
/* page edges will change if there is at least one char or rule on page */

  minhp = MAXINT;
  minvp = MAXINT;
  maxhp = -MAXINT;
  maxvp = -MAXINT;
  currfont = fontlist;
  while (currfont != NULL)
  {
    {
      if (currfont->fontused)
      {
      /* only reset those fonts used in last page */
	currfont->fontused = FALSE;
	currfont->totalchars = 0;
      /* deallocate char list completely; DoFont()will allocate first node */
	while (currfont->charlist != NULL)
	{
	  thischarptr = currfont->charlist;
	  currfont->charlist = thischarptr->nextchar;
	  free (thischarptr);
	}
	currfont->chartail = NULL;
      /* pixel table remains allocated */
      }
      currfont = currfont->nextfont;
    }
  }
  currfont = NULL;		/* current font is undefined at start of page */
  totalrules = 0;
/* deallocate rule information except for one node (for DoSet/PutRule) */
  while (rulelist != ruletail)
  {
    thisrule = rulelist;
    rulelist = thisrule->nextrule;
    free (thisrule);
  }
  rulelist->rulecount = 0;	/* no rules in this node */
  rulelist->nextrule = NULL;
}

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

void DoSetChar (ch)
unsigned int  ch;
{

/* Add char info to current chartable, update our horizontal
   position on the page and check the page edges.
*/

  {
    if (ch > MAXTEXCHAR)
    {
      DVIErrorRoutine (DVIbadchar);
      return;			/* ignore ch */
    }
    {
      charinfoptr ct = currfont->chartail;
      if (ct->charcount == CHARTABLESIZE)/* allocate a new chartable */
      {
	ct->nextchar = (charinfoptr) malloc (sizeof (struct charinfo));
      /* add new node to end of char list */
	ct->nextchar->charcount = 0;/* reset charcount */
	ct->nextchar->nextchar = NULL;
	currfont->chartail = ct->nextchar;
      }
    }
    {
      charinfoptr ct = currfont->chartail;
    /* may be new chartable */
      {
	ct->chartable[ct->charcount].hp = hh;
	ct->chartable[ct->charcount].vp = vv;
	ct->chartable[ct->charcount].code = ch;
      }
      {
	pixeltableptr pp = currfont->pixelptr;
      /* do page edges increase? */

#ifdef DEBUG
	(void) printf( "hh = %d, vv = %d, ch = %c, pp[ch].yo = %d\n",
	       hh, vv, ch, pp[ch].yo );
#endif /* DEBUG */

	if ((vv - pp[ch].yo) < minvp)
	  minvp = vv - pp[ch].yo;
	if ((hh - pp[ch].xo) < minhp)
	  minhp = hh - pp[ch].xo;
	if ((vv + (pp[ch].ht - pp[ch].yo - 1)) > maxvp)
	  maxvp = vv + (pp[ch].ht - pp[ch].yo - 1);
	if ((hh + (pp[ch].wd - pp[ch].xo - 1)) > maxhp)
	  maxhp = hh + (pp[ch].wd - pp[ch].xo - 1);
      /* the above checks ensure that page edges include all black pixels in glyph,
         but we also want to include reference point */
	if (hh < minhp)
	  minhp = hh;
	if (vv < minvp)
	  minvp = vv;
	if (hh > maxhp)
	  maxhp = hh;
	if (vv > maxvp)
	  maxvp = vv;

      /* add pixel width calculated in PixelTableRoutine */
	hh += pp[ch].pwidth;

      /* use hhh and MAXDRIFT to prevent hh drifting too far from h */
	hhh = PixelRound (h + pp[ch].dwidth);
	if (ABS (hhh - hh) > MAXDRIFT)
	{
	  if (hhh > hh)
	    hh = hhh - MAXDRIFT;
	  else
	    hh = hhh + MAXDRIFT;
	}
      /* add DVI width calculated in PixelTableRoutine */
	h += pp[ch].dwidth;
      }
      currfont->totalchars++;
      ct->charcount++;
    }
  }
}

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

void DoPutChar (ch)
unsigned int  ch;
{
/* Exactly the same as DoSetChar() but we DON'T update the horizontal
   position on the page.  (We still have to check page edges.)
*/

  {
    if (ch > MAXTEXCHAR)
    {
      DVIErrorRoutine (DVIbadchar);
      return;			/* ignore ch */
    }
    {
      charinfoptr ct = currfont->chartail;
      if (ct->charcount == CHARTABLESIZE)/* allocate a new chartable */
      {
	ct->nextchar = (charinfoptr) malloc (sizeof (struct charinfo));
      /* add new node to end of char list */
	ct->nextchar->charcount = 0;/* reset charcount */
	ct->nextchar->nextchar = NULL;
	currfont->chartail = ct->nextchar;
      }
    }
    {
      charinfoptr ct = currfont->chartail;
    /* may be new chartable */
      {
	ct->chartable[ct->charcount].hp = hh;
	ct->chartable[ct->charcount].vp = vv;
	ct->chartable[ct->charcount].code = ch;
      }
      {
	pixeltableptr pp = currfont->pixelptr;
      /* do page edges increase? */
	if (vv - pp[ch].yo < minvp)
	  minvp = vv - pp[ch].yo;
	if (hh - pp[ch].xo < minhp)
	  minhp = hh - pp[ch].xo;
	if (vv + (pp[ch].ht - pp[ch].yo - 1) > maxvp)
	  maxvp = vv + (pp[ch].ht - pp[ch].yo - 1);
	if (hh + (pp[ch].wd - pp[ch].xo - 1) > maxhp)
	  maxhp = hh + (pp[ch].wd - pp[ch].xo - 1);

      /* the above checks ensure that page edges include all black pixels in glyph,
         but we also want to include reference point */
	if (hh < minhp)
	  minhp = hh;
	if (vv < minvp)
	  minvp = vv;
	if (hh > maxhp)
	  maxhp = hh;
	if (vv > maxvp)
	  maxvp = vv;
      }
      currfont->totalchars++;
      ct->charcount++;
    }
  }
}

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

int   PixelRound (DVIunits)
int   DVIunits;
{

/* Return the nearest number of pixels in the given DVI dimension. */

  if (DVIunits > 0)
    return ((int) (conv * (float) (DVIunits) + 0.5));
  else
    return (-(int) (conv * (float) (ABS (DVIunits)) + 0.5));

}

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

void DoPush ()
{
/* Push state values onto stack.
   No need to test for stack overflow since we compare maxstack and
   MAXSTACKSIZE in ProcessPostamble()
*/

  hstack[stackpos] = h;
  vstack[stackpos] = v;
  wstack[stackpos] = w;
  xstack[stackpos] = x;
  ystack[stackpos] = y;
  zstack[stackpos] = z;
  hhstack[stackpos] = hh;
  vvstack[stackpos] = vv;
  stackpos++;			/* stackpos = next vacant position in stacks */
}

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

void DoPop ()
{
/* Pop state values from top of stack. */

#ifdef DEBUG
  if (stackpos == 0)
  {
    (void) printf ("Stack empty!\n");
    RestoreTerminal ();
    exitprog (1);
  }
#endif /* GUBED */

  stackpos--;
  h = hstack[stackpos];
  v = vstack[stackpos];
  w = wstack[stackpos];
  x = xstack[stackpos];
  y = ystack[stackpos];
  z = zstack[stackpos];
  hh = hhstack[stackpos];
  vv = vvstack[stackpos];
}

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

void DoRight (amount)
int   amount;
{
/* Move the reference point horizontally by given amount (usually +ve).
   When the amount is small, like a kern, hh changes
   by rounding the amount; but when the amount is large, hh changes by rounding
   the true position h so that accumulated rounding errors disappear.
*/

  if ((amount < fontspace) && (amount > (-4 * fontspace)))
  {
    hh = hh + PixelRound (amount);

  /* use hhh and MAXDRIFT to prevent hh drifting too far from h */
    hhh = PixelRound (h + amount);
    if (ABS (hhh - hh) > MAXDRIFT)
    {
      if (hhh > hh)
	hh = hhh - MAXDRIFT;
      else
	hh = hhh + MAXDRIFT;
    }
  }
  else
    hh = PixelRound (h + amount);

  h = h + amount;
}

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

void DoDown (amount)
int   amount;
{
/* Move the reference point vertically by given amount (usually +ve).
   Rounding is done similarly to DoRight()but with the threshold between
   small and large amounts increased by a factor of 5.
*/

  if (ABS (amount) < (5 * fontspace))
  {
    vv = vv + PixelRound (amount);
  /* use vvv and MAXDRIFT to prevent vv drifting too far from v */
    vvv = PixelRound (v + amount);
    if (ABS (vvv - vv) > MAXDRIFT)
      if (vvv > vv)
	vv = vvv - MAXDRIFT;
      else
	vv = vvv + MAXDRIFT;
  }
  else
    vv = PixelRound (v + amount);

  v = v + amount;
}

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

void DoSetRule (height, width)
int   height, width;
{
/* Add rule information to current ruletable, update page edges, h and hh
   (but only if width and height are > 0).
*/

  if ((height > 0) && (width > 0))
  {
    {
      if (ruletail->rulecount == RULETABLESIZE)/* allocate a new ruletable */
      {
	ruletail->nextrule = (ruleinfoptr) malloc (sizeof (struct ruleinfo));
      /* add new node to end of rule list */
	ruletail->nextrule->rulecount = 0;/* reset rulecount */
	ruletail->nextrule->nextrule = NULL;
	ruletail = ruletail->nextrule;
      }
    }
    {				/* may be new ruletable */
      {
	ruletail->ruletable[ruletail->rulecount].hp = hh;
	ruletail->ruletable[ruletail->rulecount].vp = vv;
	ruletail->ruletable[ruletail->rulecount].wd = RulePixels (width);
	ruletail->ruletable[ruletail->rulecount].ht = RulePixels (height);

      /* do page edges increase? */
	if ((vv - (ruletail->ruletable[ruletail->rulecount].ht - 1)) < minvp)
	  minvp = vv - (ruletail->ruletable[ruletail->rulecount].ht - 1);
	if ((hh + (ruletail->ruletable[ruletail->rulecount].wd - 1)) > maxhp)
	  maxhp = hh + (ruletail->ruletable[ruletail->rulecount].wd - 1);

      /* ref pt of rule is bottom left black pixel */
	if (vv > maxvp)
	  maxvp = vv;
	if (hh < minhp)
	  minhp = hh;
	hh = hh + ruletail->ruletable[ruletail->rulecount].wd;

      /* use hhh and MAXDRIFT to prevent hh drifting too far from h */
	hhh = PixelRound (h + width);
	if (ABS (hhh - hh) > MAXDRIFT)

	  if (hhh > hh)

	    hh = hhh - MAXDRIFT;
	  else
	    hh = hhh + MAXDRIFT;


	h = h + width;
      }
      totalrules++;
      ruletail->rulecount++;
    }
  }
}

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

void DoPutRule (height, width)
int   height, width;
{
/* Exactly the same as DoSetRule() but we DON'T update the horizontal
   position on the page.  (We still have to check page edges.)
*/

  if ((height > 0) && (width > 0))
  {
    {
      if (ruletail->rulecount == RULETABLESIZE)/* allocate a new ruletable */
      {
	ruletail->nextrule = (ruleinfoptr) malloc (sizeof (struct ruleinfo));
      /* add new node to end of rule list */
	ruletail->nextrule->rulecount = 0;/* reset rulecount */
	ruletail->nextrule->nextrule = NULL;
	ruletail = ruletail->nextrule;
      }
    }
    {				/* may be new ruletable */
      {
	ruletail->ruletable[ruletail->rulecount].hp = hh;
	ruletail->ruletable[ruletail->rulecount].vp = vv;
	ruletail->ruletable[ruletail->rulecount].wd = RulePixels (width);
	ruletail->ruletable[ruletail->rulecount].ht = RulePixels (height);

      /* do page edges increase? */
	if ((vv - (ruletail->ruletable[ruletail->rulecount].ht - 1)) < minvp)
	  minvp = vv - (ruletail->ruletable[ruletail->rulecount].ht - 1);
	if ((hh + (ruletail->ruletable[ruletail->rulecount].wd - 1)) > maxhp)
	  maxhp = hh + (ruletail->ruletable[ruletail->rulecount].wd - 1);

      /* ref pt of rule is bottom left black pixel */
	if (vv > maxvp)
	  maxvp = vv;
	if (hh < minhp)
	  minhp = hh;
      }
      totalrules++;
      ruletail->rulecount++;
    }
  }
}

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

int   RulePixels (DVIunits)
int   DVIunits;
{
/* Return the number of pixels in the given height or width of a rule
   using the method recommended in DVITYPE.
*/
  int   n;

  n = (int) (conv * (float) (DVIunits));
  if ((float) (n) < conv * (float) (DVIunits))
    return (n + 1);
  else
    return (n);
}

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

void DoFont (externf)
int   externf;
{
/* Search font list for externf;
   If this is the first time we've seen this font (on current page) then
   we need to allocate the first chartable.
   If this is the first time we've seen this font used at all then we
   allocate a pixeltable and call client routine to fill it in.
*/

  currfont = fontlist;
  while ((currfont != NULL) && (currfont->fontnum != externf))
    currfont = currfont->nextfont;

  if (currfont == NULL)
  {
#ifdef DEBUG
    (void) printf ("\n\n currfont == NULL\n");
    RestoreTerminal ();
    exitprog (1);
#endif  /* GUBED */

    DVIErrorRoutine (DVIcatastrophe);
  }
  {
    if (currfont->fontused)
    {
    /* do nothing since we've already used this font on this page */
    }
    else
    {
      currfont->fontused = TRUE;
      currfont->charlist = (charinfoptr) malloc (sizeof (struct charinfo));
      if(currfont->charlist == NULL)
	  DVIErrorRoutine(DVIcatastrophe);
      
    /* allocate first chartable */
      {
	charinfoptr cp = currfont->charlist;
	cp->charcount = 0;	/* for DoSet/PutChar */
	cp->nextchar = NULL;	/* this node is also last */
      }
      currfont->chartail = currfont->charlist;
      if (currfont->pixelptr == NULL)
      /* first time we've seen this font requested */
      {
	currfont->pixelptr = (pixeltableptr) malloc (sizeof (struct pixtabstr) * (MAXTEXCHAR + 1));
	PixelTableRoutine ();	/* call client routine to build pixel table */
      }
    }
    fontspace = currfont->scaledsize / 6;
  /* See DVITYPE; a 3-unit thin space. Note that a thin space is 1/6 of a quad,
     where a quad is 1 em in the current font and usually equals the design size. */
  }
}

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

Void IgnoreSpecial (specialbytes, GetNextByte)/* in */
GetByteFunction GetNextByte;
int   specialbytes;
{
/* Default SpecialRoutine simply skips bytes given to TeX's \special command. */

  int   i;

  for (i = 1; i <= specialbytes; i++)
    (void) GetNextByte ();
}

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

void SortFonts (unusedlist)
fontinfoptr * unusedlist;
{				/* out */

/* Sort fontlist in order of ascending totalchars.
   Fonts with least characters can then be accessed first.
   Since the number of fonts used on a typical page is quite small, a simple
   sorting algorithm should be good enough.  Note that unused fonts are moved
   to the end of the list and unusedlist points to the first such node.
*/

  fontinfoptr newfontlist, prevfont, largest, prevlargest;
  unsigned int  mostchars;

  newfontlist = NULL;

/* go thru fontlist once and move all unused fonts to head of newfontlist */
  prevfont = NULL;
  currfont = fontlist;
  while (currfont != NULL)
  {
    {
      if (currfont->fontused)
      {
	prevfont = currfont;	/* remember previous node */
	currfont = currfont->nextfont;
      }
      else
      {
      /* move node from fontlist to head of newfontlist and don't change prevfont */
	if (prevfont == NULL)
	{
	  fontlist = currfont->nextfont;/* remove first node in fontlist */
	  currfont->nextfont = newfontlist;
	  newfontlist = currfont;
	  currfont = fontlist;
	}
	else
	{
	  prevfont->nextfont = currfont->nextfont;
	  currfont->nextfont = newfontlist;
	  newfontlist = currfont;
	  currfont = prevfont->nextfont;
	}
      }
    }
  }
/* unusedlist will be last unused font moved to newfontlist.  It will be NULL
   if either fontlist is NULL or all fonts are used.
*/

  *unusedlist = newfontlist;

/* Now go thru fontlist repeatedly moving node with max totalchars to
   head of newfontlist until fontlist is exhausted.
*/
  while (fontlist != NULL)
  {
    prevfont = NULL;
    currfont = fontlist;
    prevlargest = NULL;
    largest = fontlist;
    mostchars = 0;
    while (currfont != NULL)	/* search for largest totalchars */
    {
      {
	if (currfont->totalchars > mostchars)
	{
	  prevlargest = prevfont;
	  largest = currfont;
	  mostchars = currfont->totalchars;
	}
	prevfont = currfont;
	currfont = currfont->nextfont;
      }
    }
  /* move largest node from fontlist to head of newfontlist */
    {
      if (prevlargest == NULL)
	fontlist = largest->nextfont;/* remove first node in fontlist */
      else
	prevlargest->nextfont = largest->nextfont;

      largest->nextfont = newfontlist;
      newfontlist = largest;
    }
  }
  fontlist = newfontlist;	/* used fonts now sorted and unused fonts at end */
}

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

void CloseDVIFile ()
{

/* Close the currently open DVI file and deallocate dynamic data structures. */

  (void) fclose (DVIfile);
  while (fontlist != NULL)
  {
    currfont = fontlist;
    {
      while (currfont->charlist != NULL)/* deallocate char list */
      {
	thischarptr = currfont->charlist;
	currfont->charlist = thischarptr->nextchar;
	free (thischarptr);
      }
      if (currfont->pixelptr != NULL)
	free (currfont->pixelptr);/* deallocate pixel table */

      fontlist = currfont->nextfont;
    }
    free (currfont);		/* deallocate font information */
  }
/* Deallocate rule information except for one node (in case client opens
   another DVI file).
*/
  while (rulelist != ruletail)
  {
    thisrule = rulelist;
    rulelist = thisrule->nextrule;
    (void) free (thisrule);
  }
}

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

void InitReader ()
{
  rulelist = (ruleinfoptr) malloc (sizeof (struct ruleinfo));

 /* for first InitPage() */

  ruletail = rulelist;			/* ditto */
  fontlist = NULL;			/* safer for CloseDVIFile() */
  SpecialRoutine = IgnoreSpecial;	/* client can reassign */
}
