summaryrefslogtreecommitdiff
path: root/dviware/ivd2dvi/ivd2dvi.c
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/ivd2dvi/ivd2dvi.c
Initial commit
Diffstat (limited to 'dviware/ivd2dvi/ivd2dvi.c')
-rw-r--r--dviware/ivd2dvi/ivd2dvi.c957
1 files changed, 957 insertions, 0 deletions
diff --git a/dviware/ivd2dvi/ivd2dvi.c b/dviware/ivd2dvi/ivd2dvi.c
new file mode 100644
index 0000000000..d2f68397b0
--- /dev/null
+++ b/dviware/ivd2dvi/ivd2dvi.c
@@ -0,0 +1,957 @@
+/*
+ * 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);
+}