/* dvireader.c - 18:19 GMT +10:00 Wed 18 Aug 1993 - modifier Geoffrey Tobin */

/* From input file "../include/globals.p" */


#include "config.h"
#include "globals.h"

#include "files.h"
#include "dvireader.h"
#include "screenio.h"
#include "fontreader.h"
#include "options.h"


/* Variables Exported, via "dvireader.h". */

int DVImag;
int totalpages, totalfonts, totalrules;
int currDVIpage;
TeXcounters currTeXpage;
ruleinfo * rulelist, * ruletail;
fontinfo * fontlist, * currfont;
specialinfo * speciallist;
boolean pageempty;
int minhp, minvp, maxhp, maxvp;


/*******************************************************************************
   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 maxstacksize    100   /* maximum stack size for state values   */

/*  According to the DVI Driver Standard, Level 0, version 0.5,
    section 2.6.2, max_drift should depend on the output device:

    2  if a "device unit" <= 0.005 in (0.127 mm),
    1  if a "device unit" >  0.005 in (0.127 mm)
                      but <= 0.01 in (0.254 mm),
    0  if a "device unit" >  0.01 in (0.254 mm).

    So, the value given here for max_drift implies a device with
    a resolution of at least 200 dpi.

    Note:  max_drift is a number of pixels.
*/

#define max_drift        2   /* prevent hh & vv from drifting too far */


/*******************************************************************************
   DECLARATIONS FOR ACCESSING BYTES IN A DVI FILE

   A DVI file is a stream of 8-bit bytes.  Once opened, we can access any byte
   by setting DVIoffset to the required position and then calling 
GetDVIByte.
*/

Static int DVIfile;        /* DVI file descriptor                  */
Static int DVIoffset;      /* current byte offset in DVIfile       */
Static int currDVIbuff;    /* starting byte offset in buffer       */
Static buffer DVIbuffer;   /* input buffer                         */
Static int postpostid;     /* offset of postpost's id 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.
*/

Static int curreop;   /* position of eop byte of current page   */
Static int currbop;   /* position of bop byte of current page   */
Static int lastbop;   /* position of last bop byte              */
Static int prevbop;   /* position of bop byte of previous page;
                         note that prevbop of first page = -1   */

Static unsigned char DVIcommand;  /* holds next DVI command     */

Static int maxstack;   /* max pushes over pops in DVI file      */
Static int num = 0;    /* DVI numerator                         */
Static int den = 0;    /* DVI denominator                       */
Static double xconv = 0.0;   /* converts DVI units to X pixels        */
Static double yconv = 0.0;   /* converts DVI units to Y pixels        */
Static int h, v;       /* current pos on page in DVI units      */
Static int w, x;       /* horizontal increments in DVI units    */
Static int y, z;       /* vertical increments in DVI units      */
Static int hh, vv;     /* h and v in pixels (approx)            */
Static int hhh, vvv;   /* h and v rounded to nearest pixel      */
Static int hstack[maxstacksize], vstack[maxstacksize];
    /* push down stacks for state values     */
Static int wstack[maxstacksize], xstack[maxstacksize],
           ystack[maxstacksize], zstack[maxstacksize],
           hhstack[maxstacksize], vvstack[maxstacksize];
Static int stackpos;
/* stacks empty when stackpos = 0, i.e., top of stacks = stackpos - 1 */
Static int fontspace;   /* used in DoRight and DoDown            */


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

/* Here are the functions used to get byte/s from DVIfile.
   They are essentially the same as those used in DVITYPE.
*/

Static int GetDVIByte (VOID)
{
  /* Returns the value (unsigned) of the byte at DVIoffset and
     advances DVIoffset for the next GetDVIByte.
  */

  extern int currDVIbuff, DVIfile, DVIoffset;

  int Result, buffstart, result;

  /* bufflen is a #define-d constant in "files.h" */

  buffstart = DVIoffset / bufflen * bufflen;   /* 0, bufflen, 2*bufflen... */
  if (buffstart != currDVIbuff)
  {
    currDVIbuff = buffstart;
    result = lseek (DVIfile, buffstart, 0);

    /* DEBUG
       IF result <> buffstart THEN BEGIN
          writeln ('Lseek failed in GetDVIByte!'); RestoreTerminal; EXIT (1);
       END;
     GUBED */

    if (result != buffstart)
    {
      MesgString ("lseek failed in GetDVIByte!");
      MesgLine();

      RestoreTerminal();
      exit (1);
    }
    result = read (DVIfile, DVIbuffer, bufflen);

    /* DEBUG
       IF result = -1 THEN BEGIN
          writeln ('Read failed in GetDVIByte!'); RestoreTerminal; EXIT (1);
       END;
     GUBED */

    if (result == -1)
    {
      MesgString ("read failed in GetDVIByte!");
      MesgLine();

      RestoreTerminal();
      exit (1);
    }
  }
  Result = DVIbuffer[DVIoffset - buffstart];
  DVIoffset++;
  return Result;
}
/* GetDVIByte */

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

Static int SignedDVIByte (VOID)
{
  /* the next byte, signed */
  int b;

  b = GetDVIByte();
  if (b < 128)
    return b;
  else
    return (b - 256);
}
/* SignedDVIByte */

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

Static int GetTwoDVIBytes (VOID)
{
  /* the next 2 bytes, unsigned */
  int a, b;

  a = GetDVIByte();
  b = GetDVIByte();
  return (a * 256 + b);
}
/* GetTwoDVIBytes */

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

Static int SignedDVIPair (VOID)
{
  /* the next 2 bytes, signed */
  int a, b;

  a = GetDVIByte();
  b = GetDVIByte();
  if (a < 128)
    return (a * 256 + b);
  else
    return ((a - 256) * 256 + b);
}
/* SignedDVIPair */

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

Static int GetThreeDVIBytes (VOID)
{
  /* the next 3 bytes, unsigned */
  int a, b, c;

  a = GetDVIByte();
  b = GetDVIByte();
  c = GetDVIByte();
  return ((a * 256 + b) * 256 + c);
}
/* GetThreeDVIBytes */

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

Static int SignedDVITrio (VOID)
{
  /* the next 3 bytes, signed */
  int a, b, c;

  a = GetDVIByte();
  b = GetDVIByte();
  c = GetDVIByte();
  if (a < 128)
    return ((a * 256 + b) * 256 + c);
  else
    return (((a - 256) * 256 + b) * 256 + c);
}
/* SignedDVIOqTrio */


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

Static int SignedDVIQuad (VOID)
{
  /* the next 4 bytes, signed */
  int w;
  byte3(w) = GetDVIByte();
  byte2(w) = GetDVIByte();
  byte1(w) = GetDVIByte();
  byte0(w) = GetDVIByte();
  return (w);
}
/* SignedDVIQuad */

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

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

  extern int DVIoffset, postpostid;
  extern int lastbop, num, den, DVImag, maxstack, totalpages;

  int postamblepos, postamble, pagehtplusdp, pagewidth;

  DVIoffset = postpostid - 4;
  postamblepos = SignedDVIQuad();   /* get post_post's postamble ptr */
  DVIoffset = postamblepos;
  postamble = GetDVIByte();
  lastbop = SignedDVIQuad();
  num = SignedDVIQuad();
  den = SignedDVIQuad();
  DVImag = SignedDVIQuad();
  pagehtplusdp = SignedDVIQuad();
  pagewidth = SignedDVIQuad();
  maxstack = SignedDVIPair();
  totalpages = SignedDVIPair();
  if (maxstack <= maxstacksize)
    return;

  MesgString ("Stack capacity exceeded!");
  MesgLine();

  RestoreTerminal();
  exit (1);
  /* now we don't need to test for stack overflow in DoPush */
}
/* ProcessPostamble */

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

Static Void ProcessFontDefs (VOID)
{
  /* 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.)
     Since ProcessPostamble ended by reading the totalpages parameter, the
     next GetDVIByte should return nop or first fntdef.
  */

  extern int totalfonts;
  extern fontinfo * fontlist;

  int f, c, s, d, a, l;   /* hold fntdef parameters */
  int i;
  Char ch;   /* for getting farea and fname */
  string farea, fname;   /* a and l bytes long respectively */

  totalfonts = 0;   /* number of nodes in font list */
  fontlist = (fontinfo *) 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;
      }
      c = SignedDVIQuad();   /* checksum; ignore it */
      s = SignedDVIQuad();   /* scaled size */
      d = SignedDVIQuad();   /* design size */
      a = GetDVIByte();   /* length of font area */
      l = GetDVIByte();   /* length of font name */

      /* read and store font area */
      for (i = 0; i < a; i++)
      {
        ch = GetDVIByte();
        if (i < maxfontspec)
          farea[i] = ch;
      }
      farea[i <= maxfontspec ? i: maxfontspec] = '\0';

      /* read and store font name */
      for (i = 0; i < l; i++)
      {
        ch = GetDVIByte();
        if (i < maxfontspec)
          fname[i] = ch;
      }

      fname[i <= maxfontspec ? i: maxfontspec] = '\0';

      currfont = (fontinfo *) Malloc (sizeof (fontinfo));
      currfont->fontused = false;
      currfont->fontnum = f;
      currfont->scaledsize = s;
      currfont->designsize = d;
      strncpy (currfont->fontarea, farea, maxstring);
      currfont->fontarea[maxstring] = '\0';  /* to be careful */
      currfont->fontarealen = a;
      strncpy (currfont->fontname, fname, maxstring);
      currfont->fontname[maxstring] = '\0';  /* to be careful */
      currfont->fontnamelen = l;
      currfont->fontspec[0] = '\0';
      currfont->fontspeclen = 0;   /* fontspec is built in FontReader */
      currfont->fontexists = false; /* becomes TRUE if fontspec can be opened */
      currfont->totalchars = 0;

      /* first node allocated in DoFont */
      currfont->charlist = (charinfo *) NULL;

      /* nodes are added to tail of char list */
      currfont->chartail = (charinfo *) NULL;

      /* allocated once per font; see DoFont */
      currfont->pixelptr = (_REC_pixeltable *) NULL;

      currfont->nextfont = fontlist;
      fontlist = currfont;   /* add new font to head of list */
      totalfonts++;
    }
    else if (DVIcommand != nop)
    {
      if (DVIcommand != postpost)
      {
        /* gt - hope outstring has room enough - it should have. */
        string outstring;
        sprintf (outstring,
          "Unexpected DVI command in postamble = %d", DVIcommand);
        MesgString (outstring);
        MesgLine();

        RestoreTerminal();
        exit (1);
      }
      /* we have reached end of postamble */
    }
  } while (DVIcommand != postpost);

  /* nop commands can occur between DVI commands */
}
/* ProcessFontDefs */

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

#ifdef __STDC__
Void OpenDVIFile (Char * name)
#else
Void OpenDVIFile (name)
    Char * name;
#endif
{
  /* If the given file can be opened and is a valid TeX82 DVI file then
     the following global variables are initialized:

        DVImag      := magnification value stored in DVI file (TeX's \mag)
        totalpages  := total number of pages in DVI file
        currDVIpage := 0      (and remains so until a page is selected)
        currTeXpage := ten 0s (ditto)
        totalfonts  := total number of fonts in DVI file (= nodes in font list)
        fontlist->  (nodes are added to head of list)
           fontused   := FALSE
           fontnum    := internal DVI font number
           scaledsize := scaled size of font (in DVI units)
           designsize := design size of font (in DVI units)
           fontarea   := a string of min (fontarealen, maxfontspec) characters
           fontname   := a string of min (fontnamelen, maxfontspec) characters
           fontspec   := a null string (fontspeclen := 0)
           fontexists := FALSE
           totalchars := 0
           charlist   := NIL
           chartail   := NIL
           pixelptr   := NIL
           nextfont   := next node in font list (if not NIL)
  */

  extern int currDVIpage;
  extern TeXcounters currTeXpage;
  extern int currDVIbuff;
  extern int DVIfile;
  extern int DVIoffset, postpostid;


  currDVIbuff = -1;   /* impossible value for first GetDVIByte */

  DVIfile = open (name, O_RDONLY, 0);

  if (DVIfile >= 0)
  {
    int i;

    if (logfile != NULL)
      fprintf (logfile, "Opened DVI file `%s'\n", DVIname);
  
    /* get offset of last DVI byte */
    DVIoffset = lseek (DVIfile, 0, 2);
    if (DVIoffset == -1)
    {
      MesgString ("Failed to find end of file!");
      MesgLine();

      RestoreTerminal();
      exit (1);
    }
    if (DVIoffset == 0)
    {
      MesgString ("DVI file is empty!");
      MesgLine();

      RestoreTerminal();
      exit (1);
    }

    /* DEBUG
       writeln ('total bytes = ', DVIoffset:1);
     GUBED */

    fprintf (logfile, "Total DVI bytes = %d\n", DVIoffset);
    fflush (logfile);

    DVIoffset--;

    /* In the following, DVIoffset == 1 signifies the start of the file. */

    /* skip any NULs */
    while (GetDVIByte() == 0  &&  DVIoffset != 1)
    {
      DVIoffset -= 2;   /* GetDVIByte increments */
    }
    DVIoffset--;

    /* skip 223s */
    while (GetDVIByte() == 223  &&  DVIoffset != 1)
    {
      DVIoffset -= 2;   /* GetDVIByte increments */
    }

    DVIoffset--;
    postpostid = DVIoffset;   /* remember offset of id byte */
    if (GetDVIByte() != 2)
    {
      MesgString ("Not a valid DVI file!");
      MesgLine();

      RestoreTerminal();
      exit (1);

      /*NOTREACHED*/
      return;
    }
    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;
    
    return;
  }
  else
  {
    /* gt - I suppose this is correct for length */
    int length = strlen (name);
    /* gt - hope outstr has sufficient room */
    string outstr;
    sprintf (outstr, "Couldn't open DVI file: `%.*s'\n", length, name);
    MesgString (outstr);
    MesgLine();

    RestoreTerminal();
    exit (1);
  }
}
/* OpenDVIFile */

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

#ifdef __STDC__
Static Void SkipFntdef (int which)
#else
Static Void SkipFntdef (which)
    int which;
#endif
{
  /* Read past a fntdef command without interpreting it. */

  int dummy, a, l, i;

  switch (which)   /* which = DVIcommand - fntdef1 */
  {
  case 0:
    dummy = GetDVIByte();
    break;

  case 1:
    dummy = GetTwoDVIBytes();
    break;

  case 2:
    dummy = GetThreeDVIBytes();
    break;

  case 3:
    dummy = SignedDVIQuad();
    break;
  }
  dummy = SignedDVIQuad();
  dummy = SignedDVIQuad();
  dummy = SignedDVIQuad();

  a = GetDVIByte();   /* length of directory */
  l = GetDVIByte();   /* length of font name */
  for (i = 1; i <= l + a; i++)
    dummy = GetDVIByte();
}
/* SkipFntdef */

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

Static Void ReadFirstBop (VOID)
{

  /* Read first bop by skipping past preamble; update currbop and currDVIpage. */
  int k, i, dummy;

  DVIoffset = 14;   /* position of preamble's k parameter */
  k = GetDVIByte();   /* length of x parameter */
  for (i = 1; i <= k; i++)   /* skip preamble comment */
    dummy = GetDVIByte();
  do
  {   /* skip any nops and fntdefs */
    DVIcommand = GetDVIByte();
    if (DVIcommand != nop && DVIcommand != bop)
    {
      if (DVIcommand >= fntdef1 && DVIcommand <= fntdef1 + 3)
        SkipFntdef (DVIcommand - fntdef1);
      else
      {
        MesgLine();
        MesgString ("Unexpected DVI command before first bop=");
        MesgInt (DVIcommand);
        MesgLine();

        RestoreTerminal();
        exit (1);
      }
    }
    /* do nothing */
  } while (DVIcommand != bop);
  currbop = DVIoffset - 1;   /* position in DVI file of first bop */
  currDVIpage = 1;
}
/* ReadFirstBop */

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

Static Void ReadNextBop (VOID)
{
  /* 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.
  */

  /* skip any nops and fntdefs */
  do
  {
    DVIcommand = GetDVIByte();
    if (DVIcommand != nop && DVIcommand != bop)
    {
      if (DVIcommand >= fntdef1 && DVIcommand <= fntdef1 + 3)
        SkipFntdef (DVIcommand - fntdef1);
      else
      {
        MesgLine();
        MesgString ("Unexpected DVI command between eop and bop=");
        MesgInt (DVIcommand);
        MesgLine();

        RestoreTerminal();
        exit (1);
      }
    }
    /* do nothing */
  } while (DVIcommand != bop);
  currbop = DVIoffset - 1;   /* position in DVI file of this bop */
  currDVIpage++;
}
/* ReadNextBop */

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

Static Void ReadBopParameters (VOID)
{

  /* 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.
  */
  int i;

  for (i = 0; i <= 9; i++)
    currTeXpage[i] = SignedDVIQuad();
  prevbop = SignedDVIQuad();   /* position of previous bop in DVI file */
}
/* ReadBopParameters */

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

#ifdef __STDC__
Void MoveToNextPage (boolean ascending)
#else
Void MoveToNextPage (ascending)
    boolean ascending;
#endif
{
  /* MoveToNextPage will select the next page, depending on the current page
     and the specified direction.  If the value of currDVIpage is 0 (set in
     OpenDVIFile), then MoveToNextPage will select the first page if ascending is
     TRUE and the last page if ascending is FALSE.  If currDVIpage is > 0 then
     MoveToNextPage will select currDVIpage+1 if ascending (unless currDVIpage =
     totalpages, in which case it does nothing), or currDVIpage-1 if descending
     (unless currDVIpage = 0).

     Before calling InterpretPage, DVItoVDU must position DVIReader to the
     desired page by calling one of the MoveTo... routines.
     The global variables updated if a page is located by any MoveTo... call are:
        currDVIpage := the current DVI page (1..totalpages)
        currTeXpage := the ten TeX counter values stored with this page
     Note that currDVIpage is initially 0 until one of these routines succeeds.
  */

  extern int currDVIpage;
  extern int DVIoffset;
  extern int totalpages;
  extern int currbop, lastbop, prevbop;

  /* keep within limits of document */
  if ((currDVIpage != 1 && !ascending)
   || (currDVIpage != totalpages && ascending))
  {
    if (currDVIpage == 0)  /* special value */
    {
      /* we haven't processed a page yet */
      if (ascending)   /* get first page */
        ReadFirstBop();
      else
      {  /* get last page */
        currbop = lastbop;
        DVIoffset = 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;
        DVIoffset = currbop + 1;   /* move to byte after previous bop */
        currDVIpage--;
      }
    }
    ReadBopParameters();   /* update currTeXpage and prevbop */
  }
}
/* MoveToNextPage */

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

#ifdef __STDC__
Void MoveToDVIPage (int n)
#else
Void MoveToDVIPage (n)
    int n;
#endif
{
  /* Move to nth DVI page; n should be in 1..totalpages. */

  extern int totalpages;
  extern int currDVIpage;
  extern int currbop, lastbop, prevbop;

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

  if (n == 1)
  {
    /* Note that this test must come before next test 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;
    }
    /* if n = currDVIpage we'll just move back to currbop */
    /* n is now <= currDVIpage so search by following backpointers */
    while (true)
    {
      if (n == currDVIpage)
      {
        DVIoffset = currbop + 1;   /* move to byte after currbop */
        break;  /* escape from "infinite" loop */
      }
      DVIoffset = currbop + 41;   /* move to location of backpointer */
      currbop = SignedDVIQuad();   /* get location of previous page */
      currDVIpage--;
    } /* while */
  } /* if */
  ReadBopParameters();   /* update currTeXpage and prevbop */
_L999: ;
}
/* MoveToDVIPage */

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

#ifdef __STDC__
Static boolean CurrMatchesNew (TeXpageinfo * newTeXpage)
#else
Static boolean CurrMatchesNew (newTeXpage)
    TeXpageinfo * newTeXpage;
#endif
{
  /* Return TRUE iff currTeXpage matches newTeXpage. */

  extern TeXcounters currTeXpage;

  boolean Result;
  int i, FORLIM;

  Result = true;
  FORLIM = newTeXpage->lastvalue;
  for (i = 0; i <= FORLIM; i++)
  {
    if (newTeXpage->present[i])
    {
      if (newTeXpage->value[i] != currTeXpage[i])
        Result = false;
    }
  }
  return Result;
}
/* CurrMatchesNew */

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

#ifdef __STDC__
boolean MoveToTeXPage (TeXpageinfo * newTeXpage)
#else
boolean MoveToTeXPage (newTeXpage)
    TeXpageinfo * newTeXpage;
#endif
{
  /* MoveToTeXPage will search for the lowest DVI page matching the given
     TeX page specification.

     (TeX stores the values of \count0,\count1, ..., \count9 with every DVI
      page.  Plain TeX uses \count0 to control page numbering.)

     newTeXpage is a VAR parameter only for efficiency; it won't be changed.
     The value array stores the requested counter values, the present array
     indicates which counters are relevant and lastvalue indicates
     the position (0..9) of the last relevant counter.

     DVItoVDU converts a more friendly representation
     of a TeX page request into the TeXpageinfo format.  For example,
     [2..5] would be converted to:     and [] would be converted to:
     value     = [2,?,5,?,?,?,?,?,?,?]     value     = [?,?,?,?,?,?,?,?,?,?]
     present   = [T,F,T,?,?,?,?,?,?,?]     present   = [F,?,?,?,?,?,?,?,?,?]
     lastvalue =      2                    lastvalue =  0
     MoveToTeXPage returns TRUE iff the requested TeX page is located.
  */

  extern int totalpages;
  extern int currDVIpage;
  extern int DVIoffset;
  extern int currbop, lastbop, prevbop;

  boolean Result;

  int savecurrbop, savecurrDVIpage, nextbop, i;
  boolean atleastone;

  /* save away current page and DVI position */
  savecurrDVIpage = currDVIpage;
  if (currDVIpage != 0)   /* only if we've processed a page */
    savecurrbop = currbop;
  /* note that curreop is saved in last InterpretPage */
  /* search backwards through all DVI pages for lowest matching page */
  atleastone = false;
  nextbop = lastbop;
  for (i = totalpages; i >= 1; i--)
  {
    DVIoffset = 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;
      DVIoffset = currbop + 1;
      ReadBopParameters();   /* restore currTeXpage and prevbop */
      DVIoffset = curreop + 1;
      /* we should now be after the eop byte of the original page */
    }
    Result = false;
    goto _L999;
  }
  DVIoffset = currbop + 1;
  ReadBopParameters();   /* update currTeXpage and prevbop */
  Result = true;
_L999:
  return Result;

  /* we found lowest matching page */
}
/* MoveToTeXPage */

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

#ifdef __STDC__
Void SetConversionFactors (double xres, double yres, double magnification)
#else
Void SetConversionFactors (xres, yres, magnification)
    double xres;  double yres;  double magnification;
#endif
/* Arguments:
      xres:  horizontal resolution of output device in dots per inch.
      yres:  vertical resolution.
      magnification:  magnification of document;  1 means normal size.
*/
{
  /* This routine must be called before the first InterpretPage call.
     DVIReader needs to know the resolution and magnification values
     before it attempts to convert DVI units into pixel values.
  */

  /* num, den are written in ProcessPostAmble */
  extern int num, den;

  /* GT - The "254000.0" corresponds to ten thousand units per mm. */
  /* Refer to TUG's DVI Driver standard. */

  xconv = ( num / 254000.0 * xres / den * magnification );
  yconv = ( num / 254000.0 * yres / den * magnification );
}
/* SetConversionFactors */

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

#ifdef __STDC__
Inline Static int Sign (double d)
#else
Static int Sign (d)
  double d;
#endif
{
    return (d < 0.0 ? -1 : 1);
}
/* Sign */

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

Static Void InitStateValues (VOID)
{
  /* Initialize state values and stack. */

  /* This is the only place in "dvireader.c" */
  /* where hoffset, voffset appear.          */
  /* The offsets are set in "options.c". */
  /* The other extern variables are Static in "dvireader.c". */

  extern int hoffset, voffset;  /* page offsets in X & Y pixels */
  extern int hh, vv;            /* current position in X & Y pixels */
  extern double xconv, yconv;   /* numbers of X & Y pixels in one DVI unit */
  extern int h, v;              /* current position in DVI units */
  extern int w, x, y, z;        /* movement in DVI units */
  extern int stackpos;
  extern int fontspace;

  double hox, voy;  /* offsets divided by conversion factors */

  hox = hoffset / xconv;
  voy = voffset / yconv;

  hh = hoffset;   /* 0 if no horizontal shift specified */
  vv = voffset;   /* 0 if no vertical shift specified */
  h = Sign (hox) * (int) ((hox >= 0.0 ? hox : -hox) + 0.5);
  v = Sign (voy) * (int) ((voy >= 0.0 ? voy : -voy) + 0.5);
  w = 0;
  x = 0;
  y = 0;
  z = 0;
  stackpos = 0;
  fontspace = 0;   /* for DoRight and DoDown before a DoFont call */
}
/* InitStateValues */

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

Static Void InitPage (VOID)
{
  /* Initialize page so that there are no fonts, chars, rules, specials. */

  pageempty = true;  /* gt - assumed until a char or rule is found */

  /* page edges will change if there is at least one char or rule on page */

  /* gt - these shouldn't be necessary, but to be on the safe side ... */
  minhp = 0;
  minvp = 0;
  maxhp = 0;
  maxvp = 0;

  currfont = fontlist;
  while (currfont != (fontinfo *) NULL)
  {
    if (currfont->fontused)  /* only reset those fonts used in last page */
    {
      currfont->fontused = false;

      /* deallocate char list completely; DoFont will allocate first node */
      currfont->totalchars = 0;
      while (currfont->charlist != (charinfo *) NULL)
      {
        charinfo * thischar;   /* temporary pointer to node in charlist */
        thischar = currfont->charlist;
        currfont->charlist = thischar->nextchar;
        Free (thischar, charinfo);
      }
      currfont->chartail = (charinfo *) NULL;
      /* pixel table remains allocated */
    }
    currfont = currfont->nextfont;
  }
  currfont = (fontinfo *) NULL; /* current font's undefined at start of page */

  /* deallocate rule information except for one node (for DoSet/PutRule) */
  totalrules = 0;
  while (rulelist != ruletail)
  {
    ruleinfo * thisrule;   /* temporary pointer to node in rulelist */
    thisrule = rulelist;
    rulelist = thisrule->nextrule;
    Free (thisrule, ruleinfo);
  }
  rulelist->rulecount = 0;   /* no rules in this node */
  rulelist->nextrule = (ruleinfo *) NULL;

  /* deallocate \special information */
  while (speciallist != (specialinfo *) NULL)
  {
    specialinfo * thisspecial;   /* temporary ptr to node in speciallist */
    thisspecial = speciallist;
    speciallist = speciallist->nextspecial;
    Free (thisspecial, specialinfo);
  }
}
/* InitPage */

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

#ifdef __STDC__
Inline int XPixelRound (int DVIunits)
#else
Inline int XPixelRound (DVIunits)
    int DVIunits;
#endif
{
  /* Return the nearest number of X pixels in the given DVI X dimension. */

  /* According to the DVI Driver Standard, Level 0, draft 0.05, */
  /* section 2.6.2, "Changes in position due to characters and  */
  /* rules",                                                    */
  /* pixel_round (n) = sign (Kn) * floor (abs (Kn) + 0.5),      */
  /* where sign (i) = -1 if i < 0 and = 1 if i >= 0,            */
  /* and K is xconv for horizontal and yconv for vertical       */
  /* dimensions.                                                */

  double Kn = xconv * (double) DVIunits;
  return (Sign (Kn) * (int) ((Kn >= 0.0 ? Kn : -Kn) + 0.5));
}
/* XPixelRound */

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

#ifdef __STDC__
Inline int YPixelRound (int DVIunits)
#else
Inline int YPixelRound (DVIunits)
    int DVIunits;
#endif
{
  /* Return the nearest number of Y pixels in the given DVI Y dimension. */

  /* According to the DVI Driver Standard, Level 0, draft 0.05, */
  /* section 2.6.2, "Changes in position due to characters and  */
  /* rules",                                                    */
  /* pixel_round (n) = sign (Kn) * floor (abs (Kn) + 0.5),      */
  /* where sign (i) = -1 if i < 0 and = 1 if i >= 0,            */
  /* and K is xconv for horizontal and yconv for vertical       */
  /* dimensions.                                                */

  double Kn = yconv * (double) DVIunits;
  return (Sign (Kn) * (int) ((Kn >= 0.0 ? Kn : -Kn) + 0.5));
}
/* YPixelRound */

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

#ifdef __STDC__
Static Void DoSetChar (int ch)
#else
Static Void DoSetChar (ch)
    int ch;
#endif
{
  /* Add char info to current chartable, update our horizontal
     position on the page and check the page edges.
  */

  if (ch > maxTeXchar)
  { /* ignore ch */
    string codestr;
    sprintf (codestr, "%d", ch);
    MesgString ("Unknown character (code ");
    MesgString (codestr);
    MesgString (") from ");
    MesgString (currfont->fontspec);
  }
  else /* ch is valid */
  {
    charinfo * char_info = currfont->chartail;   /* may be new chartable */
    _REC_chartable * char_tab = (_REC_chartable *) NULL;

    _REC_pixeltable * pix_tab = (_REC_pixeltable *) NULL;

    if (char_info->charcount == chartablesize)
    {
      char_info->nextchar = (charinfo *) Malloc (sizeof (charinfo));
          /* add new node to end of char list */
      char_info->nextchar->charcount = 0;   /* reset charcount */
      char_info->nextchar->nextchar = (charinfo *) NULL;
      currfont->chartail = char_info->nextchar;
    }

    /* allocate a new chartable */

    char_info = currfont->chartail;
    char_tab = &char_info->chartable[char_info->charcount];
    char_tab->hp = hh;
    char_tab->vp = vv;
    char_tab->code = ch;

    pix_tab = &currfont->pixelptr[ch];

    /* do page edges increase? */

    if (pageempty ||  hh - pix_tab->xo < minhp)
    {
      minhp = hh - pix_tab->xo;
    }
    if (pageempty ||  vv - pix_tab->yo < minvp)
      minvp = vv - pix_tab->yo;
    if (pageempty ||  hh + pix_tab->wd - pix_tab->xo - 1 > maxhp)
      maxhp = hh + pix_tab->wd - pix_tab->xo - 1;
    if (pageempty ||  vv + pix_tab->ht - pix_tab->yo - 1 > maxvp)
      maxvp = vv + pix_tab->ht - pix_tab->yo - 1;

    /* the above checks ensure that page edges include all black
       pixels in glyph, but we also want to include reference point */

    if (pageempty ||  hh < minhp)
      minhp = hh;
    if (pageempty ||  vv < minvp)
      minvp = vv;
    if (pageempty ||  hh > maxhp)
      maxhp = hh;
    if (pageempty ||  vv > maxvp)
      maxvp = vv;

    /* add DVI width calculated in PixelTableRoutine */
    h += pix_tab->dwidth;

    /* add pixel width calculated in PixelTableRoutine */
    hh += pix_tab->pwidth;

    /* use hhh and max_drift to prevent hh drifting too far from h */

    hhh = XPixelRound (h);

    if (abs (hhh - hh) > max_drift)
    {
      /* Adjust hh to be just within the allowed tolerance from h.  */
      hh = hhh + (hhh > hh ? -max_drift : max_drift);
    }

    currfont->totalchars++;
    char_info->charcount++;
    pageempty = false;
  }
}
/* DoSetChar */

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

#ifdef __STDC__
Static Void DoPutChar (int ch)
#else
Static Void DoPutChar (ch)
    int ch;
#endif
{
  /* 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)
  { /* ignore ch */
    string codestr;
    sprintf (codestr, "%d", ch);
    MesgString ("Unknown character (code ");
    MesgString (codestr);
    MesgString (") from ");
    MesgString (currfont->fontspec);
  }
  else  /* ch is valid */
  {
    charinfo * char_info = currfont->chartail;   /* may be new chartable */
    _REC_chartable * char_tab = (_REC_chartable *) NULL;
    _REC_pixeltable * pix_tab = (_REC_pixeltable *) NULL;

    if (char_info->charcount == chartablesize)
    {
      char_info->nextchar = (charinfo *) Malloc (sizeof (charinfo));
          /* add new node to end of char list */
      char_info->nextchar->charcount = 0;   /* reset charcount */
      char_info->nextchar->nextchar = (charinfo *) NULL;
      currfont->chartail = char_info->nextchar;
    }

    /* allocate a new chartable */

    char_info = currfont->chartail;
    char_tab = &char_info->chartable[char_info->charcount];
    char_tab->hp = hh;
    char_tab->vp = vv;
    char_tab->code = ch;
    pix_tab = &currfont->pixelptr[ch];

    /* do page edges increase? */

    if (pageempty ||  hh - pix_tab->xo < minhp)
      minhp = hh - pix_tab->xo;
    if (pageempty ||  vv - pix_tab->yo < minvp)
      minvp = vv - pix_tab->yo;
    if (pageempty ||  hh + pix_tab->wd - pix_tab->xo - 1 > maxhp)
      maxhp = hh + pix_tab->wd - pix_tab->xo - 1;
    if (pageempty ||  vv + pix_tab->ht - pix_tab->yo - 1 > maxvp)
      maxvp = vv + pix_tab->ht - pix_tab->yo - 1;

    /* the above checks ensure that page edges include all black
       pixels in glyph, but we also want to include reference point */

    if (pageempty ||  hh < minhp)
      minhp = hh;
    if (pageempty ||  vv < minvp)
      minvp = vv;
    if (pageempty ||  hh > maxhp)
      maxhp = hh;
    if (pageempty ||  vv > maxvp)
      maxvp = vv;

    currfont->totalchars++;
    char_info->charcount++;
    pageempty = false;
  }
}
/* DoPutChar */

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

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

  extern int stackpos;

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

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

Static Void DoPop (VOID)
{
  /* Pop state values from top of stack. */

  extern int stackpos;

  /* DEBUG
     IF stackpos = 0 THEN BEGIN
        WriteLine; WriteString ('Stack empty!'); WriteLine;
        RestoreTerminal; EXIT (1);
     END;
   GUBED */

  if (stackpos == 0)
  {
    MesgLine();
    MesgString ("Stack empty!");
    MesgLine();

    RestoreTerminal();
    exit (1);
  }
  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];
}
/* DoPop */

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

#ifdef __STDC__
Static Void DoRight (int amount)
#else
Static Void DoRight (amount)
    int amount;
#endif
{
  /* 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.
  */

  h += amount;

  /* GT - Wrong?  DVI Driver Standard specifies:
       "if (processor uses TFM)
          word_space = space - space_shrink;
        else
          quad = magnification * (design size of font);
          word_space = 0.2 * quad;
        fi;

        back_space = 0.9 * quad;

        if (amount < word_space && amount > -back_space)".

     So,     TFM -> space, space_shrink;
            else -> design size.

     Now, how do we learn those quantities?
  */

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

    /* use hhh and max_drift to prevent hh drifting too far from h */

    hhh = XPixelRound (h);
    if (abs (hhh - hh) > max_drift)
    {
      /* Adjust hh to be just within the allowed tolerance from h.  */
      hh = hhh + (hhh > hh ? -max_drift : max_drift);
    }
  }
  else
  {
    hh = XPixelRound (h);
  }
}
/* DoRight */

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

#ifdef __STDC__
Static Void DoDown (int amount)
#else
Static Void DoDown (amount)
    int amount;
#endif
{
  /* 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.
  */

  v += amount;

  /* GT - Wrong?  DVI Driver Standard specifies:
         "if (abs (y) < 0.8 * quad)".
  */

  if (abs (amount) < fontspace * 5)
  {
    vv += YPixelRound (amount);

    /* use vvv and max_drift to prevent vv drifting too far from v */

    vvv = YPixelRound (v);
    if (abs (vvv - vv) > max_drift)
    {
      /* Adjust vv to be just within the allowed tolerance from v.  */
      vv = vvv + (vvv > vv ? -max_drift : max_drift);
    }
  }
  else
  {
    vv = YPixelRound (v);
  }
}
/* DoDown */

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

#ifdef __STDC__
Static int RulePixels (double conv, int DVIunits)
#else
Static int RulePixels (conv, DVIunits)
    double conv;  int DVIunits;
#endif
{
  /* Return the number of pixels in the given height or width of a rule
     using the method recommended in DVITYPE.
  */

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

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

#ifdef __STDC__
Static Void DoSetRule (int height, int width)
#else
Static Void DoSetRule (height, width)
    int height; int width;
#endif
{
  /* Add rule information to current ruletable, update page edges, h and hh
     (but only if width and height are > 0).
  */

  _REC_ruletable * ruletab = (_REC_ruletable *) NULL;

  if (height <= 0 || width <= 0)
    return;

  /* may be new ruletable */
  if (ruletail->rulecount == ruletablesize)
  {
    ruletail->nextrule = (ruleinfo *) Malloc (sizeof (ruleinfo));
        /* add new node to end of rule list */
    ruletail->nextrule->rulecount = 0;   /* reset rulecount */
    ruletail->nextrule->nextrule = (ruleinfo *) NULL;
    ruletail = ruletail->nextrule;
  }

  /* allocate a new ruletable */
  ruletab = &ruletail->ruletable[ruletail->rulecount];
  ruletab->hp = hh;
  ruletab->vp = vv;
  ruletab->wd = RulePixels (xconv, width);
  ruletab->ht = RulePixels (yconv, height);

  /* do page edges increase? */

  /* ref pt of rule is bottom left black pixel */

  if (pageempty ||  vv > maxvp)
    maxvp = vv;
  if (pageempty ||  hh < minhp)
    minhp = hh;

  if (pageempty ||  vv - ruletab->ht + 1 < minvp)
    minvp = vv - ruletab->ht + 1;
  if (pageempty ||  hh + ruletab->wd - 1 > maxhp)
    maxhp = hh + ruletab->wd - 1;

  hh += ruletab->wd;
  h += width;

  /* use hhh and max_drift to prevent hh drifting too far from h */

  hhh = XPixelRound (h);

  if (abs (hhh - hh) > max_drift)
  {
    /* Adjust hh to be just within the allowed tolerance from h.  */
    hh = hhh + (hhh > hh ? -max_drift : max_drift);
  }

  totalrules++;
  ruletail->rulecount++;
  pageempty = false;
}
/* DoSetRule */

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

#ifdef __STDC__
Static Void DoPutRule (int height, int width)
#else
Static Void DoPutRule (height, width)
    int height; int width;
#endif
{

  /* Exactly the same as DoSetRule, but we DON'T update the horizontal
     position on the page.  (We still have to check page edges.)
  */
  _REC_ruletable * ruletab;

  if (height <= 0 || width <= 0)
    return;

  /* may be new ruletable */
  if (ruletail->rulecount == ruletablesize)
  {
    ruletail->nextrule = (ruleinfo *) Malloc (sizeof (ruleinfo));
        /* add new node to end of rule list */
    ruletail->nextrule->rulecount = 0;   /* reset rulecount */
    ruletail->nextrule->nextrule = (ruleinfo *) NULL;
    ruletail = ruletail->nextrule;
  }

  /* allocate a new ruletable */
  ruletab = &ruletail->ruletable[ruletail->rulecount];
  ruletab->hp = hh;
  ruletab->vp = vv;
  ruletab->wd = RulePixels (xconv, width);
  ruletab->ht = RulePixels (yconv, height);

  /* do page edges increase? */

  /* ref pt of rule is bottom left black pixel */

  if (pageempty ||  hh < minhp)
    minhp = hh;
  if (pageempty ||  vv > maxvp)
    maxvp = vv;

  if (pageempty ||  hh + ruletab->wd - 1 > maxhp)
    maxhp = hh + ruletab->wd - 1;
  if (pageempty ||  vv - ruletab->ht + 1 < minvp)
    minvp = vv - ruletab->ht + 1;

  totalrules++;
  ruletail->rulecount++;
  pageempty = false;
}
/* DoPutRule */

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

#ifdef __STDC__
Static Void DoFont (int externf)
#else
Static Void DoFont (externf)
    int externf;
#endif
{
  /* Search font list for externf, setting currfont and fontspace.
     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 routine to fill it in.
  */

  extern fontinfo * currfont, * fontlist;

  charinfo * char_info = (charinfo *) NULL;

  currfont = fontlist;
  while (currfont != (fontinfo *) NULL  &&  currfont->fontnum != externf)
  {
    currfont = currfont->nextfont;
  }

  /* DEBUG
     IF currfont = NIL THEN BEGIN
        WriteLine; WriteString ('Failed to find font #'); WriteInt (externf);
        WriteLine; RestoreTerminal; EXIT (1);
     END;
   GUBED */

  if (currfont == NULL)
  {
    MesgLine();
    MesgString ("Failed to find font #");
    MesgInt (externf);
    MesgLine();

    RestoreTerminal();
    exit (1);
  }

  if (!currfont->fontused)
  {
    currfont->fontused = true;
    currfont->charlist = (charinfo *) Malloc (sizeof (charinfo));
        /* allocate first chartable */
    char_info = currfont->charlist;
    char_info->charcount = 0;   /* for DoSet/PutChar */
    char_info->nextchar = (charinfo *) NULL;   /* this node is also last */
    currfont->chartail = currfont->charlist;
    if (currfont->pixelptr == (_REC_pixeltable *) NULL)
    {  /* first time we've seen this font */
      currfont->pixelptr = (_REC_pixeltable *) Malloc (sizeof (pixeltable));
      PixelTableRoutine (currfont);
    }
  }
  /* do nothing since we've already used this font on this page */

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

}
/* DoFont */

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

#ifdef __STDC__
Static Void DoSpecial (int hpos, int vpos, int totalbytes)
#else
Static Void DoSpecial (hpos, vpos, totalbytes)
    int hpos; int vpos; int totalbytes;
#endif
{
  /* in */

  /* DVIReader has seen a \special command while interpreting the current page.
     It will pass the current page position and number of bytes in the command.
     We save the info away in speciallist for later use by the main program.
  */
  int i;
  specialinfo * temp;

  temp = (specialinfo *) Malloc (sizeof (specialinfo));
  for (i = 0; i < totalbytes; i++)
  {
    int temp1;
    temp1 = GetDVIByte();
    if (i < maxspeciallen)
      temp->special[i] = temp1;
  }
  temp->special[i <= maxspeciallen ? i: maxspeciallen] = '\0';

  temp->hp = hpos;
  temp->vp = vpos;
  temp->nextspecial = speciallist;
  speciallist = temp;   /* add new info to head of list */
}
/* DoSpecial */

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

Void InterpretPage (VOID)
{
  /* 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 following global data structures:

        totalrules := number of rules on page
        rulelist-> (nodes are added to tail of rule list)
           rulecount  := number of rules in this ruletable
           ruletable[0..rulecount-1].
              hp, vp  := reference point of a rule
              wd, ht  := pixel dimensions of a rule (both > 0)
           nextrule   := next node in rule list (if not NIL)
        ruletail   := pointer to last node in rule list

        speciallist-> (nodes are added to head of this list)
           hp, vp      := reference point on page
           special     := \special bytes
           nextspecial := next node in list (if not NIL)

        fontlist->
           (the following fontinfo is relevant only if fontused is TRUE)
           totalchars := number of chars on page from this font
           charlist-> (nodes are added to tail of char list)
              charcount  := number of chars in this chartable
              chartable[0..charcount-1].
                 hp, vp  := reference point of a character
                 code    := TeX character code (and index into pixel table)
              nextchar   := next node in char list (if not NIL)
           chartail   := pointer to last node in char list
           pixelptr^[0..maxTeXchar].
              (filled in by FontReader's PixelTableRoutine)
              wd, ht  := glyph width and height in pixels
              xo, yo  := offsets from the character's reference point
              dwidth  := advance width in DVI units
              pwidth  := advance width in pixels
              mapadr  := offset in fontspec of the glyph's bitmap info
              bitmap  := NIL
           nextfont   := next node in font list (if not NIL)

        pageempty  := TRUE iff the page has no rules and no characters
        minhp, minvp, maxhp, maxvp
                   := the edges of the page (undefined if pageempty is TRUE)
                      They define the smallest rectangle containing all black
                      pixels on the page;(minhp,minvp) is the top left corner.

     Reference points for rules and characters are stored as a pair of
     horizontal and vertical pixel coordinates.  The point (0,0) is assumed
     to be the pixel 1 inch in from the top and left edges of an imaginary sheet
     of paper.  Horizontal coordinates increase to the right and vertical
     coordinates increase down the paper.
     The numbers of pixels per inch in the X and Y directions are defined by
     the xres and yres resolution parameters given to SetConversionFactors.
  */

  int param, ht, wd;

  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 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)   /* 0..127 */
      DoSetChar (DVIcommand);
    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)
    {
      /* catch all the remaining movement commands */
      DoFont (DVIcommand - fntnum0);
    }
    else if (DVIcommand > pop && DVIcommand < fntnum0)
    {
      if (DVIcommand == right2)
        DoRight (SignedDVIPair());
      else if (DVIcommand == right4)
        DoRight (SignedDVIQuad());
      else if (DVIcommand == x2)
      {
        x = SignedDVIPair();
        DoRight (x);
      }
      else if (DVIcommand == x3)
      {
        x = SignedDVITrio();
        DoRight (x);
      }
      else if (DVIcommand == down3)
        DoDown (SignedDVITrio());
      else if (DVIcommand == down4)
        DoDown (SignedDVIQuad());
      else if (DVIcommand == w2)
      {
        w = SignedDVIPair();
        DoRight (w);
      }
      else if (DVIcommand == z0)
        DoDown (z);
      else if (DVIcommand == y3)
      {
        y = SignedDVITrio();
        DoDown (y);
      }
      else if (DVIcommand == z3)
      {
        z = SignedDVITrio();
        DoDown (z);
      }
      else if (DVIcommand == down2)
      {
        /* the next DVI commands are used very rarely (by TeX 1.3 at least) */
        DoDown (SignedDVIPair());
      }
      else if (DVIcommand == w1)
      {
        w = SignedDVIByte();
        DoRight (w);
      }
      else if (DVIcommand == w4)
      {
        w = SignedDVIQuad();
        DoRight (w);
      }
      else if (DVIcommand == x1)
      {
        x = SignedDVIByte();
        DoRight (x);
      }
      else if (DVIcommand == x4)
      {
        x = SignedDVIQuad();
        DoRight (x);
      }
      else if (DVIcommand == y1_)
      {
        y = SignedDVIByte();
        DoDown (y);
      }
      else if (DVIcommand == y2)
      {
        y = SignedDVIPair();
        DoDown (y);
      }
      else if (DVIcommand == y4)
      {
        y = SignedDVIQuad();
        DoDown (y);
      }
      else if (DVIcommand == z1)
      {
        z = SignedDVIByte();
        DoDown (z);
      }
      else if (DVIcommand == z2)
      {
        z = SignedDVIPair();
        DoDown (z);
      }
      else if (DVIcommand == z4)
      {
        z = SignedDVIQuad();
        DoDown (z);
      }
      else if (DVIcommand == right1)
        DoRight (SignedDVIByte());
      else if (DVIcommand == down1)
        DoDown (SignedDVIByte());
      else
      {
        MesgLine();
        MesgString ("Bug in InterpretPage!");
        MesgLine();

        RestoreTerminal();
        exit (1);
      }
    }
    else if (DVIcommand == setrule)
    {
      ht = SignedDVIQuad();
      wd = SignedDVIQuad();
      DoSetRule (ht, wd);
    }
    else if (DVIcommand == putrule)
    {
      ht = SignedDVIQuad();
      wd = SignedDVIQuad();
      DoPutRule (ht, wd);
    }
    else if (DVIcommand >= put1 && DVIcommand <= put1 + 3)
    {
      switch (DVIcommand - put1)
      {
      case 0:
        DoPutChar (GetDVIByte());
        break;

      case 1:
        DoPutChar (GetTwoDVIBytes());
        break;

      case 2:
        DoPutChar (GetThreeDVIBytes());
        break;

      case 3:
        DoPutChar (SignedDVIQuad());
        break;
      }

    }
    else if (DVIcommand >= set1 && DVIcommand <= set1 + 3)
    {
      switch (DVIcommand - set1)
      {
      case 0:
        DoSetChar (GetDVIByte());
        break;

      case 1:
        DoSetChar (GetTwoDVIBytes());
        break;

      case 2:
        DoSetChar (GetThreeDVIBytes());
        break;

      case 3:
        DoSetChar (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 current pixel position and number of bytes */
      DoSpecial (hh, vv, param);
    }
    else if (DVIcommand >= fntdef1 && DVIcommand <= fntdef1 + 3)
      SkipFntdef (DVIcommand - fntdef1);
    else if (DVIcommand != nop)
    {
      if (DVIcommand != eop)
      {   /* do nothing */
        MesgLine();
        MesgString ("Unexpected DVI command while interpreting page=");
        MesgInt (DVIcommand);
        MesgLine();

        RestoreTerminal();
        exit (1);
      }
    }
  } while (DVIcommand != eop);

  /* save position of eop byte for use in MoveToTeXPage */
  curreop = DVIoffset - 1;

  if (stackpos != 0)
  {
    MesgLine();
    MesgString ("Stack not empty at eop!");
    MesgLine();

    RestoreTerminal();
    exit (1);
  }

      /* InitPage values */


  /* fntnum0..fntnum63 */


  /* skip fntdef command since we've got this info from postamble */

  /* do nothing */
}
/* InterpretPage */

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

#ifdef __STDC__
Void SortFonts (fontinfo ** unusedlist)
#else
Void SortFonts (unusedlist)
    fontinfo ** unusedlist;
#endif
{
  /* 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.
     DVItoVDU need only process fonts up to (but excluding) unusedlist
     and does not have to worry about checking the fontused flag.
     If unusedlist is NIL then either 1) all fonts are used on the current page
     or 2) fontlist is also NIL (totalfonts = 0).
  */
  fontinfo * newfontlist, * prevfont, * largest, * prevlargest;
  int mostchars;

  newfontlist = (fontinfo *) NULL;
  /* go thru fontlist once and move all unused fonts to head of newfontlist */
  prevfont = (fontinfo *) NULL;
  currfont = fontlist;
  while (currfont != (fontinfo *) NULL)
  {
    if (currfont->fontused)
    {
      prevfont = currfont;   /* remember previous node */
      currfont = currfont->nextfont;
      continue;
    }
    /* move node from fontlist to head of newfontlist
       and don't change prevfont
    */
    if (prevfont == (fontinfo *) 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 NIL
     if either fontlist is NIL 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 != (fontinfo *) NULL)
  {
    prevfont = (fontinfo *) NULL;
    currfont = fontlist;
    prevlargest = (fontinfo *) NULL;
    largest = fontlist;
    mostchars = 0;

    /* search for largest totalchars */
    while (currfont != (fontinfo *) NULL)
    {
      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 == (fontinfo *) 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 */
}
/* SortFonts */

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

Void CloseDVIFile (VOID)
{
  /* Close the currently open DVI file,
     and deallocate dynamic data structures. */

  int result;

  result = close (DVIfile);
  while (fontlist != (fontinfo *) NULL)
  {
    currfont = fontlist;
    while (currfont->charlist != (charinfo *) NULL)
    {
      charinfo * thischar;   /* temporary pointer to node in charlist */
      thischar = currfont->charlist;
      currfont->charlist = thischar->nextchar;
      Free (thischar, charinfo);  /* deallocate char list */
    }
    if (currfont->pixelptr != (_REC_pixeltable *) NULL)
      Free (currfont->pixelptr, _REC_pixeltable);  /* deallocate pixel table */
    fontlist = currfont->nextfont;
    Free (currfont, fontinfo);  /* deallocate font information */
  }
  /* Deallocate rule information except for one node
     (in case DVItoVDU ever opens another DVI file).
  */
  while (rulelist != ruletail)
  {
    ruleinfo * thisrule;   /* temporary pointer to node in rulelist */
    thisrule = rulelist;
    rulelist = thisrule->nextrule;
    Free (thisrule, ruleinfo);
  }
  while (speciallist != (specialinfo *) NULL)
  {
    specialinfo * thisspecial;   /* temporary ptr to node in speciallist */
    thisspecial = speciallist;
    speciallist = speciallist->nextspecial;
    Free (thisspecial, specialinfo);
  }
}
/* CloseDVIFile */

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

Void InitDVIReader (VOID)
{
  totalrules = 0;
  rulelist = (ruleinfo *) Malloc (sizeof (ruleinfo));   /* for first InitPage */
  ruletail = rulelist;   /* ditto */
  speciallist = (specialinfo *) NULL;   /* ditto */
  fontlist = (fontinfo *) NULL;   /* safer for CloseDVIFile */
}
/* InitDVIReader */

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

/* end dvireader.c */
