DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T i

⟦b656be621⟧ TextFile

    Length: 33440 (0x82a0)
    Types: TextFile
    Names: »ivd2dvi.c«

Derivation

└─⟦060c9c824⟧ Bits:30007080 DKUUG TeX 2/12/89
    └─⟦this⟧ »./DVIware/ivd2dvi/ivd2dvi.c« 
└─⟦52210d11f⟧ Bits:30007239 EUUGD2: TeX 3 1992-12
    └─⟦af5ba6c8e⟧ »unix3.0/DVIWARE.tar.Z« 
        └─⟦ca79c7339⟧ 
            └─⟦this⟧ »DVIware/ivd2dvi/ivd2dvi.c« 

TextFile

/*
 *  ivd2dvi.
 *  Copyright 1988 by Larry Denenberg.  May be freely distributed as long
 *  as this notice is retained.
 *  
 *  This is a general discussion of ivd2dvi, assuming familiarity with
 *  the format of DVI files and with the semantics of the reflection
 *  commands (see Knuth and Mackay, "Mixing right-to-left text with
 *  left-to-right text" in TUGboat volume 8 number 1).  Notation: let
 *  "BR" stand for the BEGIN_REFLECT command, let "ER" stand for
 *  END_REFLECT, let "a" stand for SET_097 (i.e.  "typeset character 97,"
 *  usually lowercase a), and similarly for the other lowercase letters.
 *  
 *  The simplest idea would be to rearrange reflected text somehow so
 *  that the right thing happens.  Is it enough to reverse the commands?
 *  Certainly if we see "BR a b c ER" we can replace it with "c b a".
 *  But this works only so long as the reflected segment sticks to a
 *  small subset of DVI commands; "BR a FONT_00 b ER" can't in general be
 *  changed to "b FONT_00 a".  We also get in trouble with commands that
 *  change the DVI registers (not to mention PUSH and POP!).
 *  
 *  Some of these difficulties could be overcome with more clever
 *  rearrangement.  Unfortunately, there are situations that can't be
 *  handled by any such scheme:  for example, you can't typeset the
 *  sequence "BR PUSH i POP w ER" without knowing about the widths of
 *  "i" and "w".  The PUTn commands also lead to unsolvable problems.
 *  So we abandon this approach and resign ourselves to reading width
 *  data from TFM files.
 *  
 *  Here's the method we use:  we read through the input DVI file,
 *  copying DVI commands to the output file.  When we see a BR we stop
 *  copying and simply read the input looking for the matching ER.
 *  During this scan we keep track of the total width of the commands
 *  between the BR and ER.  (Each character contributes its width as
 *  revealed by the TFM file, horizontal motions contribute their
 *  width, vertical motions are ignored, etc.)  This procedure is
 *  called *simulating*; at its end we've just read an ER and we know
 *  the width of the segment between the BR and the ER.  Call this
 *  width T.  Note that nothing at all is output during simulation.
 *  
 *  Now we move right (in the output file) by T; of course we do so by
 *  shipping out a RIGHTn command.  Next, we make a second pass over the
 *  commands between the BR and the ER; this time, we typeset them
 *  "backwards."  For example, to SET an "a" we first move left by the
 *  width of an "a", and lay down the "a" without moving---this is the
 *  inverse of laying down the "a" and then moving right.  If the command
 *  is any sort of horizontal motion we negate the distance before moving.
 *  Reflection applies only to horizontal motion, however; vertical
 *  motion commands, font changes, font definitions, and so forth, are
 *  copied unchanged.  (For the precise way in which we reverse, see the
 *  documentation for the individual DVI commands below, especially
 *  /SetChar/ and /SetString/.)
 *
 *  When we see the ER for the second time, the horizontal location in
 *  the output file should be the same as it was when we first saw the BR
 *  (since we've processed all the same commands, only backwards).  So we
 *  once again output a command to move right by T, thus moving to the
 *  place where we typeset the first command after the BR.  We're now
 *  done translating this reflected segment and we continue normally.
 *  
 *  What happens when reflections are nested?  Well, it's easy.  If we
 *  encounter a BR during a simulation, we basically ignore it!  We're
 *  trying to find the total width of the reflected segment; the fact
 *  that part of the segment is further reflected doesn't matter.  (We do
 *  have to be certain to skip over the inner ER so that we match only
 *  the ER we're looking for.)  Later, when we're no longer simulating
 *  but "typesetting backwards," we'll encounter the BR of the inner
 *  reflection.  At that point it gets its own simulation set up, and we
 *  do the whole two-pass business again on the inner segment; of course,
 *  the second pass is now a "forwards" pass, at the end of which we
 *  continue the "backwards" pass over the outer segment.
 *  
 *  All of this can be nested to any depth.  We keep track of what we're
 *  doing with the variable /State/ which takes on the following values:
 *      LTYPESETTING  Typesetting normally, left-to-right
 *      RTYPESETTING  Typesetting "backwards," right-to-left
 *      SIMULATING    First pass over text to be reflected
 *     >SIMULATING    Inside nested begin_reflect/end_reflect pairs
 *  Values of /State/ greater than SIMULATING are used to keep track of
 *  the nesting depth of BR/ER pairs.  While we're simulating, we simply
 *  increment /State/ when we see BR and decrement it when we see ER.
 *  We switch from simulating to typesetting only when we see ER while
 *  /State/ is exactly equal to SIMULATING.  Note carefully that we're
 *  never simulating on behalf of more than one BR/ER pair; there's never
 *  more than one segment whose width we're measuring.
 *  
 *  So the procedure upon hitting a BR in the general case is as follows:
 *  if /State/ equals or exceeds SIMULATING, just increment /State/.
 *  Otherwise set /State/ to SIMULATING and start the first pass.  The
 *  actions necessary at ER are a bit more complex: if /State/ is
 *  *greater* than SIMULATING, just decrement it.  If /State/ is equal to
 *  SIMULATING, we've just finished a simulation: output the motion
 *  command as above, switch to the direction opposite to the one we were
 *  in when we hit the BR, and reset the input file to the point just
 *  after the BR, thus beginning the second pass.  If /State/ is
 *  LTYPESETTING or RTYPESETTING, we've just finished the second pass
 *  over a reflection:  invert /State/, and output the second long motion
 *  command as described above.  Of course, there's lots of things to
 *  save and restore here; details are in the code below.
 *  
 *  Even when we're not simulating we have to keep track of certain
 *  values.  The current font is an obvious example: we have to remember
 *  what it is, because if we start a simulation we'll have to measure
 *  its characters.  We also keep track of the horizontal motion
 *  parameters: if a W0 appears in reflected text we must know the
 *  current value of W, which may have been set before the simulation
 *  began.  Therefore we keep variables in which we store the values, and
 *  a stack to model all pushes and pops in the input file.  The same
 *  stack is used to store values over a simulation; no conflict can
 *  occur.  On the other hand, the values of the vertical motion
 *  parameters never concern us.
 *  
 *  One final twist.  Suppose we encounter the following sequence: "BR
 *  <set W to d> a W0 b W0 c W0 ER" Now if we weren't clever, each W0
 *  would have to be replaced by a longer RIGHTn command, because when
 *  we're typesetting backwards a W0 translates to a rightward motion by
 *  -d, not d.  It's better to set W to -d to begin with; then we can use
 *  W0 inside the reflection and save considerable space.  So we negate
 *  the parameter of Wn commands encountered while RTYPESETTING.  But as
 *  a consequence, the input file may disagree with the output file over
 *  the current value of W.  So we must keep two separate variables,
 *  /WInput/ and /WOutput/, that record the two values.  These two
 *  variables always are equal in absolute value, but we can't compute
 *  from the state whether they're equal or not (because reflections may
 *  start and end independently of changes to W).  More details are given
 *  at /SetWandMoveForward/.  All of this applies to X as well.  With
 *  more work we could do further optimization, catching (e.g.)  cases
 *  like "<set W to d> BR a W0 b W0 ER" in which W0 will not be used as
 *  our scheme stands.
 *  
 *  Jacques Goldberg first suggested the possibility of a dvi-ivd to dvi
 *  processor.  Please send comments, suggestions, and bug reports to
 *  larry@bbn.com or larry@harvard.edu.
 *  
 */


#include <stdio.h>
#include "global.h"
#include "commands.h"

/*  Global variables and procedures defined in auxiliary.c.  */
extern font *CurFont, *FindFont();
extern void Initializations(), BadDVIAbort(), MaxPushLevelOutput();
extern void PushWrite(), PopWrite();
extern void PushWX(), PopXW(), PushDeltaH(), PopDeltaH();
extern void FontDefine(), CopyFontDefinition();

/*  Global variables and procedures defined in io.c.  */
extern unsigned BufSize, SignedBytes();
extern long BytesOutput, CopyWord();
extern long ReadSigned(), ReadUnsigned(), CharWidth(), CopyUnsigned();
extern void WriteByte(), WriteString(), WriteNumber(), WriteWord();
extern void CopyNBytes(), SkipNBytes();
extern void RereadLastByte(), ResetFilePosition();
extern unsigned_byte CopyByte(), ReadByte(), ReadCommand();
extern unsigned_byte *ReadFilePosition();


/* Global variables defined here */
char   *ProgramName;		/* argv[0], used in error messages	*/
boolean	VerboseOutput = FALSE;	/* v flag: report page progress		*/
boolean	ExactOutput = FALSE;	/* X flag: try not to change input file	*/
long	PrevPagePointer = -1;	/* output file location of previous BOP	*/
int	State;			/* current direction or simulation depth*/
long	WInput, WOutput;	/* value of W in input and output files	*/
long	XInput, XOutput;	/* value of X in input and output files	*/
long	DeltaH;			/* total size of reflected segment	*/
int	SavedState;		/* state at start of current simulation	*/
font   *SavedFont;		/* font at start of current simulation	*/
unsigned_byte *SavedPosition;	/* file position at start of simulation	*/
unsigned_byte  CurCommand;	/* DVI command under consideration	*/

#define REVERSE(STATE)	(LTYPESETTING + RTYPESETTING - STATE)


/* Procedures defined in this file, in order of definition */
void main(),Arguments(),FileArgument(),Preliminaries(),MainLoop();
void SetChar(),SetRule(),SetString(),SetFont();
void BeginPage(),EndPage(),BegReflect(),EndReflect();
void MoveForward(),SetWandMoveForward(),SetXandMoveForward();
void Postliminaries(),CopyParametrizedCommand();




/*
 *  Main procedure, whose function is self-documenting.  The only way
 *  ivd2dvi can terminate normally is through the /exit/ here.
 */
void
main(ignore, argv)
int ignore;
char *argv[];
{
  Arguments(argv);
  Initializations();
  Preliminaries();
  MainLoop();
  Postliminaries();
  exit(0);
}



/*
 *  Standard argument processing.  Don't worry if a flag is given more
 *  than once, but allow at most one filename.  We also allow forms like
 *  "-Xv", or even "-Xbv 1024" since the argument following any -b is
 *  taken as the new buffer size.
 */
void
Arguments(argv)
char *argv[];
{
  char *arg;
  boolean seenfile = FALSE;
  int newbufsize;

  ProgramName = argv[0];
  for (arg = *++argv; arg; arg = *++argv)
    if (*arg == '-')
      while (*++arg)
	switch(*arg) {
	  case 'X':
	    ExactOutput = TRUE;
	    break;
	  case 'v':
	    VerboseOutput = TRUE;
	    break;
	  case 'b':
	    if (!*++argv) {
              fprintf(stderr, "%s: missing buffer size, -b ignored\n",
			      ProgramName);
	      return;
	    } else {
	      newbufsize = atoi(*argv);
	      if (newbufsize == 0)
		fprintf(stderr, "%s: illegal buffer size %s ignored\n",
				ProgramName, *argv);
	      else
		BufSize = newbufsize;
	    }
	    break;
	  default:
	    fprintf(stderr, "%s: illegal flag %c ignored\n",
			    ProgramName, *arg);
	}
    else if (seenfile)
      fprintf(stderr, "%s: superflous filename %s ignored\n",
		      ProgramName, arg);
    else {
      seenfile = TRUE;
      FileArgument(arg);
    }
}



/*
 *  Process a file argument.  Try to open the file.  If we can't, and if
 *  the name has no period after its rightmost slash, append ".dvi" and
 *  try again.  In either case we're reopening standard input, so that
 *  we can read the input DVI file from /stdin/ whether or not there's a
 *  filename on the command line.
 */
void
FileArgument(filename)
char *filename;
{
  char buf[MAXFILENAMESIZE], *p;

  if (freopen(filename, "r", stdin) != NULL) return;
  p = rindex(filename, '/');
  if (*p == '\0') p = filename;
  p = index(p, '.');
  if (*p != '\0') {
    fprintf(stderr, "%s: Can't open %s\n", ProgramName, filename);
    exit(1);
  } else {
    (void) sprintf(buf, "%s.dvi", filename);
    if (freopen(buf, "r", stdin) != NULL) return;
    fprintf(stderr, "%s: Can't open %s nor %s\n",
		    ProgramName, filename, buf);
    exit(1);
  }
}



/*
 *  Process the preamble.  We mostly just copy it through untouched,
 *  except that we check the first two bytes for correctness and update
 *  the comment string to say ivd2dvi was here.  (Don't update the
 *  comment string if the -X flag was used nor when the new comment
 *  wouldn't fit.)
 */
void
Preliminaries()
{
  static char *comment = "; postprocessed by ivd2dvi";
  unsigned comlength;

  if (CopyByte() != PRE) BadDVIAbort("no preamble");
  if (CopyByte() != DVIVERSION)
    BadDVIAbort("wrong DVI version in preamble");
  CopyNBytes(12L);
  comlength = ReadByte();
  if (!ExactOutput && (comlength < 256 - strlen(comment))) {
    WriteByte(comlength + strlen(comment));
    CopyNBytes((long) comlength);
    WriteString(comment);
  } else {
    WriteByte(comlength);
    CopyNBytes((long) comlength);
  }
}




/*
 *  The main loop.  Read a command, switch on it, and continue doing so
 *  forever.  The only way out of this routine is when /CurCommand/ is
 *  POST.  Most cases of the main switch are simply procedure calls; the
 *  rest are documented case-by-case.  We first handle the most common
 *  case, for top speed:  if /CurCommand/ is SET_000 through SET_127 and
 *  we're typesetting normally, we need do nothing but write it into the
 *  output.  If we're simulating or typesetting backwards, /SetString/
 *  will handle it.  Other commands are handled inside the switch.  Note:
 *  the first test relies on the assumption that SET_000 is 0, which we
 *  really shouldn't do, but then again it *is* the test for the most
 *  common case!   Expressions like "CurCommand - W1 + 1" return the
 *  number of bytes in the first parameter of commands with four forms.
 */

#define SETTING TRUE

void
MainLoop()
{
  while (TRUE) {

    CurCommand = ReadCommand();

    if (CurCommand <= SETC_127) {
      if (State == LTYPESETTING) WriteByte(CurCommand); else SetString();
      continue;
    }

    switch (CurCommand) {

      case SET1: case SET2: case SET3: case SET4:
	SetChar(CurCommand - SET1 + 1,  SETTING);
	break;

      case PUT1: case PUT2: case PUT3: case PUT4:
	SetChar(CurCommand - PUT1 + 1, !SETTING);
	break;

      case SET_RULE: case PUT_RULE:
	SetRule();
	break;

      case NOP:
	if (ExactOutput) WriteByte(NOP);
	break;

      case BOP:
	BeginPage();
	break;

      case EOP:
	EndPage();
	break;


/*
 *  When we see a PUSH/POP we save/restore the horizontal parameters.
 *  If we're simulating, we also have to save/restore /DeltaH/, since
 *  stuff that happens between the PUSH and the POP has no effect on
 *  the size of the reflected segment.  (If we're not simulating, we
 *  don't care at all about /DeltaH/ and there's no sense saving it.)
 *  Note that the stack discipline works out as long as we restore
 *  in the opposite order that we save, since PUSH/POP must nest with
 *  respect to reflection---so we can't have a PUSH while simulating
 *  that matches a POP while not simulating.  Oh yeah, I almost forgot:
 *  if we're *not* simulating, we must write the command to the output!
 */
      case PUSH:
	PushWX();
	if (State < SIMULATING) PushWrite(); else PushDeltaH();
	break;

      case POP:
	if (State < SIMULATING) PopWrite(); else PopDeltaH();
	PopXW();
	break;

      case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4:
	MoveForward(ReadSigned(CurCommand - RIGHT1 + 1));
	break;


/*
 *  Move forward by W.  It's up to /MoveForward/ whether "forward" means
 *  left or right (or neither, if we're just simulating).  The amount to
 *  move is of course the *input* file's idea of the current W.
 */
      case W0:
	MoveForward(WInput);
	break;

      case W1: case W2: case W3: case W4:
	SetWandMoveForward(ReadSigned(CurCommand - W1 + 1));
	break;


/*
 *  Move forward by X.  Please see the comments for W0, above.
 */
      case X0:
	MoveForward(XInput);
	break;

      case X1: case X2: case X3: case X4:
	SetXandMoveForward(ReadSigned(CurCommand - X1 + 1));
	break;


/*
 *  We don't especially care about vertical motion, so if we're only
 *  simulating there's nothing to do.  Otherwise, copy the command and
 *  its parameters to the output file.
 */
      case DOWN1: case DOWN2: case DOWN3: case DOWN4:
	if (State < SIMULATING)
	  CopyParametrizedCommand(CurCommand - DOWN1 + 1);
	break;

      case Y0: case Y1: case Y2: case Y3: case Y4:
	if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Y0);
	break;

      case Z0: case Z1: case Z2: case Z3: case Z4:
	if (State < SIMULATING) CopyParametrizedCommand(CurCommand - Z0);
	break;


/*
 *  We must keep track of all font changes, simulating or not.  So call
 *  /SetFont/ on the selected font number, and if we're not simulating
 *  copy the whole command to the output file.
 */
      case FNT1: case FNT2: case FNT3: case FNT4:
        if (State >= SIMULATING)
	  SetFont(ReadUnsigned(CurCommand - FNT1 + 1));
	else {
	  WriteByte(CurCommand);
	  SetFont(CopyUnsigned(CurCommand - FNT1 + 1));
	}
	break;


/*
 *  Skip past if simulating, otherwise copy out the whole command.
 */
      case XXX1: case XXX2: case XXX3: case XXX4:
	if (State >= SIMULATING)
	  SkipNBytes(ReadUnsigned(CurCommand - XXX1 + 1));
	else {
	  WriteByte(CurCommand);
	  CopyNBytes(CopyUnsigned(CurCommand - XXX1 + 1));
	}
	break;

      case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
	FontDefine(CurCommand - FNT_DEF1 + 1);
	break;

      case PRE:
	BadDVIAbort("unexpected PRE");
	break;


/*
 *  Shouldn't see the postamble while simulating or reversing; otherwise
 *  it's time to quit.  This is the only normal way out of /MainLoop/.
 */
      case POST:
	if (State != LTYPESETTING) BadDVIAbort("unexpected POST");
	return;

      case POST_POST:
	BadDVIAbort("unexpected POST_POST");
	break;

      case BEG_REFLECT:
	BegReflect();
	break;

      case END_REFLECT:
	EndReflect();
	break;


/*
 *  The only commands left, besides illegal ones, are the one-byte font
 *  selection commands.  Copy them to the output file unless simulating.
 *  Then call /SetFont/ to note the change of font.
 */
      default:
	if (CurCommand >= FONT_00 && CurCommand <= FONT_63) {
	  if (State < SIMULATING) WriteByte(CurCommand);
	  SetFont((long) CurCommand - FONT_00);
	} else
	  BadDVIAbort("unrecognized command");

      }		/* end of "switch (CurCommand) { ... }"	*/
   }	   /* end of "while (TRUE) { ... }"	*/
}	/* end of procedure MainLoop */




/*
 *  Set a single character as specified by a SETn or PUTn command.
 *  (SET_nnn commands don't come here; they go to /SetString/.)
 *  /setting/ tells whether it was a SET or a PUT, and /bytes/ gives the
 *  parameter length.  If we're typesetting normally we just copy out the
 *  command and parameter, and if we're simulating it's enough to add the
 *  width of the desired character to the running total.  If we're
 *  typesetting in reverse there's more to do: first move "forward"
 *  (left, in this case) by the width of the character.  Only then do we
 *  typeset the character.  Moreover, if the original command was a SET,
 *  we PUT the character since we've already moved.  And if the original
 *  command was a PUT, we SET the character---which has the effect of
 *  moving back to the right, for a net motion of zero.
 */
void
SetChar(bytes, setting)
unsigned bytes;
boolean setting;
{
  long charnumber;

  switch (State) {
    case LTYPESETTING:
      CopyParametrizedCommand(bytes);
      break;
    case RTYPESETTING:
      charnumber = ReadUnsigned(bytes);
      MoveForward(CharWidth(charnumber));
      if (setting)
	WriteByte(PUT1 + bytes - 1);
      else
	WriteByte(SET1 + bytes - 1);
      WriteNumber(charnumber, bytes);
      break;
    default:	/* State >= SIMULATING */
      DeltaH += CharWidth(ReadUnsigned(bytes));
  }
}



/*
 *  Same idea as /SetChar/, just above.  If LTYPESETTING just copy, if
 *  simulating just accumulate the width.  If RTYPESETTING first move
 *  left and then interchange SET and PUT.
 */
void
SetRule()
{
  long height, width;

  switch (State) {
    case LTYPESETTING:
      CopyParametrizedCommand(8);
      break;
    case RTYPESETTING:
      height = ReadSigned(4);
      width = ReadSigned(4);
      MoveForward(width);
      if (CurCommand == SET_RULE) WriteByte(PUT_RULE);
	else WriteByte(SET_RULE);
      WriteWord(height);
      WriteWord(width);
      break;
    default:	/* State >= SIMULATING */
      SkipNBytes(4L);
      width = ReadSigned(4);
      if (CurCommand == SET_RULE) DeltaH += width;
  }
}



/*
 *  Handle a SET_nnn command.  This routine is called only while
 *  simulating or typesetting in reverse (the normal case is handled
 *  directly in /MainLoop/ for speed).
 *
 *  If we're just simulating there's nothing to do except add the width
 *  of the character to the running total.  The RTYPESETTING case is the
 *  interesting one.  Here's the story:  We could typeset backwards
 *  as we do in /SetChar/ above:  move left, then PUT (since all these
 *  commands are variants of SET).  But that would mean that every one
 *  of these common one-byte commands would translate into around five
 *  bytes.  So instead of handling a single command, this routine handles
 *  /CurCommand/ and all subsequent SET_nnn commands at once.  We first
 *  continue to read the input until we hit a command that is *not* a
 *  SET_nnn.  As we do so, we accumulate the total width (call it T) of
 *  all the SET_nnn commands that we've read.  Finally, we write the
 *  following instructions into the output file:  (a) move forward---that
 *  is, left---by T,  (b) PUSH, to save the new value of H,  (c) typeset
 *  all the characters we've seen *backwards*,  (d) POP to get back to
 *  the place where we started emitting characters.  This does the trick.
 *
 *  A few notes:  We must back up the input after we're done since that
 *  first non-SET_nnn command must be reconsidered by /MainLoop/.  We
 *  rely on the fact that /ReadFilePosition/ gives us a pointer into a
 *  buffer from which we can rattle off /count/ characters;  this is true
 *  since we must be rescanning input after a simulation.  Finally, if
 *  there's only a single SET_nnn character to typeset we save a byte by
 *  replacing steps (b), (c) and (d) with a simple PUT1.
 *
 *  This routine relies on the fact that SET_nnn in fact has value nnn,
 *  which, stylistically, is absolutely indefensible.
 */
void
SetString()
{
  unsigned_byte *p;
  unsigned_byte nextcommand;
  long totalwidth;
  int count;

  if (State >= SIMULATING)
    DeltaH += CharWidth((long) CurCommand);
  else {		/* State == RTYPESETTING */
    totalwidth = count = 0;
    nextcommand = CurCommand;
    do {
      count++;
      totalwidth += CharWidth((long) nextcommand);
      nextcommand = ReadByte();
    } while (nextcommand <= SETC_127);
    RereadLastByte();
    MoveForward(totalwidth);
    if (count == 1) {
      WriteByte(PUT1);
      WriteByte(CurCommand);
    } else {
      PushWrite();
      p = ReadFilePosition();
      while (count--) WriteByte(*--p);
      PopWrite();
    }
  }
}



/*
 *  Record the current font number, which must be done no matter what
 *  state we're in.  The work is done by /FindFont/;  we just put the
 *  result into /CurFont/ and complain if the font is unknown.
 */
void
SetFont(fontnumber)
long fontnumber;
{
  CurFont = FindFont(fontnumber);
  if (CurFont == NULL) BadDVIAbort("font used but not defined");
}



/*
 *  Pages should end only in normal left-to-right mode.  We write the
 *  BOP, copy the ten \count registers printing the first if in verbose
 *  mode, and write out the pointer to the previous page.  (We can't copy
 *  the old one since page lengths may change.)  We also update this
 *  pointer; it must point to the newly written BOP.  Finally, we start
 *  out the font, W, and X registers correctly.
 */
void
BeginPage()
{
  long pagenumber;

  if (State != LTYPESETTING) BadDVIAbort("unexpected BOP");
  WriteByte(BOP);
  pagenumber = CopyWord();
  if (VerboseOutput) fprintf(stderr, "[%ld", pagenumber);
  CopyNBytes(36L);
  SkipNBytes(4L);
  WriteWord(PrevPagePointer);
  PrevPagePointer = BytesOutput - 45;
  WInput = WOutput = XInput = XOutput = 0;
  CurFont = NULL;
}



/*
 *  End a page.  Must happen in "normal" LTYPESETTING mode, but otherwise
 *  there's nothing to do but write the EOP and chatter if requested.
 */
void
EndPage()
{
  static int pages = 0;

  if (State != LTYPESETTING) BadDVIAbort("unexpected EOP");
  WriteByte(EOP);
  if (VerboseOutput) fprintf(stderr, "]%c", (++pages % 10) ? ' ' : '\n');
}



/*
 *  Start of a reflected segment, which may happen in any state.  If
 *  we're already simulating just increment /State/ to indicate nested
 *  reflections (see the overview above).  Otherwise start simulating:
 *  save the horizontal motion parameters, the current file location, the
 *  current font, and the current state.  Start the running total of the
 *  size of this segment at zero.  Change state to SIMULATING to start
 *  the first pass over the segment; the rest of the work is done at
 *  /EndReflect/.  One subtlety: it's OK to save the font, the state, and
 *  the file position in variables since there's never more than one
 *  simulation at a time.  We could do the same with the horizontal
 *  motion parameters, but we might as well use the extant stack.
 */
void
BegReflect()
{
  if (State >= SIMULATING) ++State;
  else {
    PushWX();
    SavedPosition = ReadFilePosition();
    SavedFont = CurFont;
    SavedState = State;
    State = SIMULATING;
    DeltaH = 0;
  }
}




/*
 *  Come here at the end of a reflected segment.  We first comment the
 *  easy case, which happens to be coded last:  If /State/ is greater
 *  than SIMULATING we're inside nested reflections that don't concern
 *  us yet, so just decrement /State/ to show that we're up a level.
 *
 *  If /State/ says we're at the outermost simulation, we must wrap up
 *  the simulation and start the second pass.  The direction will be the
 *  inverse of the direction just before the simulation started.  We
 *  also reset the font and the horizontal parameters to their saved
 *  values, and set things up so we're rescanning the input just *after*
 *  the BEG_REFLECT that started the simulation.  Finally, we move
 *  "forward" by the total width of the reflected segment;  we'll now
 *  typeset everything "backward" from that point.  (Note that we move
 *  "forward" by the *negative* of /DeltaH/;  this is because we've
 *  already turned the state around.  Exercise:  why must the /PopXW/
 *  precede the /MoveForward/?)  But we're not quite done with /DeltaH/;
 *  we need it at the *end* of the second pass to move forward over
 *  the reflected segment again.  Since the variable /DeltaH/ is used
 *  by any nested simulations, we must save its value on the stack.
 *
 *  If /State/ says we're typesetting (either direction) then we've
 *  just finished the second pass over a reflected segment:  grab the
 *  saved value of /DeltaH/, use it to move back over the reflected
 *  segment, then revert to the previous direction of typesetting
 *  (which is the opposite of the direction just finished).  Be sure
 *  to see the overview if you don't understand this move by /DeltaH/.
 *
 *  Harder exercise:  When we've finished typesetting a simulation as
 *  just described, we repeat the move that we started with to get back
 *  to the right place to continue.  Why don't we use PUSH and POP *in
 *  the output file* to remember the place?  That is, why not add a call
 *  to /PushWrite/ just after /MoveForward(-DeltaH)/ in the SIMULATING
 *  case, and *replace* the /MoveForward/ in the typesetting cases with
 *  a call to /PopWrite/?  We'd save a couple of bytes in the output,
 *  and wouldn't have to worry about the value of /DeltaH/ during the
 *  second pass (thus we could skip pushing and popping /DeltaH/).
 *  What's the disadvantage that vitiates this clever idea?
 */
void
EndReflect()
{
  switch(State) {
    case SIMULATING:
      CurFont = SavedFont;
      ResetFilePosition(SavedPosition);
      State = REVERSE(SavedState);	/* must precede MoveForward */
      PopXW();				/* must precede MoveForward */
      MoveForward(-DeltaH);
      PushDeltaH();
      break;
    case LTYPESETTING: case RTYPESETTING:
      PopDeltaH();
      MoveForward(-DeltaH);
      State = REVERSE(State);
      break;
    default:	/* State >SIMULATING */
      State--;
  }
}



/*
 *  Move "forward" by /distance/.  If we're simulating it's enough to
 *  accumulate /distance/ into the running total, otherwise we need a
 *  motion command in the output file.  Now "forward" means "right" if
 *  typesetting normally, but "left" if typesetting right-to-left, so
 *  first we negate distance if we're in the latter mode.  If we can
 *  accomplish this move with a simple W0 or X0 we do so.  Otherwise,
 *  we use a RIGHTn with the smallest possible n.  Notice that we
 *  test /distance/ against the *output* file's idea of W and X when
 *  trying for the short versions.  These may not agree with W and X
 *  in the input file;  see the two routines immediately following.
 */
void
MoveForward(distance)
long distance;
{
  unsigned bytes;

  if (State >= SIMULATING) { DeltaH += distance; return; }
  if (State == RTYPESETTING) distance = -distance;
  if (distance == WOutput) WriteByte(W0);
    else if (distance == XOutput) WriteByte(X0);
    else {
      bytes = SignedBytes(distance);
      WriteByte(RIGHT1 - 1 + bytes);
      WriteNumber(distance, bytes);
  }
}



/*
 *  Process a command in the range W1..W4.  The parameter has been
 *  already been read.  We always need to set this distance into WInput,
 *  the input file's idea of W.  If we're just simulating, just
 *  accumulate /distance/ into the current total and quit (the value of
 *  WOutput is irrelevant during simulation).  If typesetting left-to-
 *  right, output a Wn command to move and set W in the output file.  If
 *  typesetting right-to-left, the same, except we first negate
 *  /distance/.  This means two things: first of all, we'll move the
 *  opposite direction in the output file as desired.  Secondly, the
 *  value of W in the output file (recorded in /WOutput/) is the negative
 *  of its value in the input file.  That way, upcoming W0 commands will
 *  be directly usable as long as we're typesetting backwards.
 */
void
SetWandMoveForward(distance)
long distance;
{
  unsigned bytes;

  WInput = WOutput = distance;
  if (State >= SIMULATING) { DeltaH += distance; return; }
  if (State == RTYPESETTING) WOutput = distance = -distance;
  bytes = SignedBytes(distance);
  WriteByte(W1 - 1 + bytes);
  WriteNumber(distance, bytes);
}



/*
 *  Don't even think of finding comments for this routine.  Just look up
 *  at /SetWandMoveForward/, OK?
 */
void
SetXandMoveForward(distance)
long distance;
{
  unsigned bytes;

  XInput = XOutput = distance;
  if (State >= SIMULATING) { DeltaH += distance; return; }
  if (State == RTYPESETTING) XOutput = distance = -distance;
  bytes = SignedBytes(distance);
  WriteByte(X1 - 1 + bytes);
  WriteNumber(distance, bytes);
}



/*
 * Process the postamble, having just read a POST command from the input
 *  file.  Mostly we just copy the postamble into the output file, with a
 *  few exceptions: The previous page pointers and maximum stack depth may
 *  be different, and NOP commands are suppressed unless the -X flag was
 *  used.  When we hit POST_POST, we finish off the file with four 223's
 *  and enough more so that the total file length is divisible by four.
 *  Note that ivd2dvi stops reading its input file after seeing the dvi
 *  version character here.  As a consequence, it's always an error for
 *  the input file to come to EOF.
 */
void
Postliminaries()
{
  long i;

  if (VerboseOutput) fprintf(stderr, "[post]\n");
  WriteByte(POST);
  SkipNBytes(4L);
  WriteWord(PrevPagePointer);
  PrevPagePointer = BytesOutput - 5;
  CopyNBytes(20L);
  MaxPushLevelOutput();
  CopyNBytes(2L);
  while (CurCommand != POST_POST) {
    CurCommand = ReadByte();
    switch (CurCommand) {
      case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
	CopyFontDefinition(ReadSigned(CurCommand - FNT_DEF1 + 1),
			   CurCommand - FNT_DEF1 + 1);
	break;
      case NOP:
	if (ExactOutput) WriteByte(CurCommand);
	break;
      case POST_POST:
	break;
      default:
        BadDVIAbort("unrecognized command in postamble");
    }
  }
  WriteByte(POST_POST);
  SkipNBytes(4L);
  WriteWord(PrevPagePointer);
  if (CopyByte() != DVIVERSION)
    BadDVIAbort("wrong DVI version in postamble");
  for (i = 7 - ((BytesOutput-1) % 4); i > 0; i--) WriteByte(DVIPADCHAR);
}



/*
 *  Output the current command, then copy /bytes/ more bytes from the
 *  input file to the output file.  Surprisingly useful.
 */
void
CopyParametrizedCommand(bytes)
unsigned bytes;
{
  WriteByte(CurCommand);
  CopyNBytes((long) bytes);
}