summaryrefslogtreecommitdiff
path: root/dviware/ivd2dvi
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
Initial commit
Diffstat (limited to 'dviware/ivd2dvi')
-rw-r--r--dviware/ivd2dvi/Makefile22
-rw-r--r--dviware/ivd2dvi/auxiliary.c591
-rw-r--r--dviware/ivd2dvi/commands.h79
-rw-r--r--dviware/ivd2dvi/doc60
-rw-r--r--dviware/ivd2dvi/global.h65
-rw-r--r--dviware/ivd2dvi/io.c520
-rw-r--r--dviware/ivd2dvi/ivd2dvi.189
-rw-r--r--dviware/ivd2dvi/ivd2dvi.c957
-rw-r--r--dviware/ivd2dvi/ivd2dvi.readme9
-rw-r--r--dviware/ivd2dvi/makefile22
10 files changed, 2414 insertions, 0 deletions
diff --git a/dviware/ivd2dvi/Makefile b/dviware/ivd2dvi/Makefile
new file mode 100644
index 0000000000..6d64761254
--- /dev/null
+++ b/dviware/ivd2dvi/Makefile
@@ -0,0 +1,22 @@
+BINAREA=/usr/local/bin
+
+CC = gcc
+CFLAGS = -O
+
+OBJS = ivd2dvi.o auxiliary.o io.o
+SRCS = ivd2dvi.c auxiliary.c io.c
+INCLUDES = global.h commands.h
+
+all: ivd2dvi
+
+ivd2dvi: ${OBJS}
+ ${CC} ${CFLAGS} -o ivd2dvi ${OBJS}
+
+${OBJS}: ${INCLUDES}
+
+install: all
+ install -s -m 755 ivd2dvi ${BINAREA}
+
+bugs: ${SRCS} ${INCLUDES}
+ rm -f bugs
+ lint -hbxac ${SRCS} > bugs
diff --git a/dviware/ivd2dvi/auxiliary.c b/dviware/ivd2dvi/auxiliary.c
new file mode 100644
index 0000000000..c6fa51a3e1
--- /dev/null
+++ b/dviware/ivd2dvi/auxiliary.c
@@ -0,0 +1,591 @@
+/*
+ * Auxiliary routines for ivd2dvi. Copyright 1988 by Larry Denenberg.
+ * May be freely distributed as long as this notice is retained.
+ */
+
+
+#include <stdio.h>
+#include "commands.h"
+#include "global.h"
+
+/* Procedures and global variables defined in io.c */
+extern unsigned BufSize;
+extern unsigned_byte CopyByte(), ReadByte(), FReadByte();
+extern long FReadUnsigned(), FReadSigned(), ReadUnsigned(), ReadSigned();
+extern long CopyWord();
+extern void WriteByte(), CopyNBytes(), SkipNBytes(), FSkipNBytes();
+extern void InitIOBuffers(), BufOverflow(), WriteNumber();
+
+/* Global variables defined in ivd2dvi.c */
+extern char *ProgramName;
+extern long XInput, XOutput, WInput, WOutput, DeltaH;
+extern int State;
+extern boolean ExactOutput;
+
+
+/* Global variables defined here */
+int FilePushLevel; /* current excess of PUSH over POP */
+int MaxFilePushLevel; /* maximum FilePushLevel seen so far */
+char *TeXFontDirs; /* directories to search for TFM files */
+int NTeXFontDirs; /* number of directories in TeXFontDirs */
+FILE *tfmfp; /* fp for currently open TFM file */
+font *CurFont; /* font from which we're typesetting */
+font *FirstFont; /* head of linked list of all fonts */
+long *AuxStackPointer; /* first unused spot on the aux stack */
+long *AuxBeginStack; /* first usable spot on the aux stack */
+long *AuxEndStack; /* first location beyond the aux stack */
+
+
+/* Procedures defined in this file, in order of definition */
+void Initializations();
+void PushWX(), PopXW(), PushDeltaH(), PopDeltaH(), AuxPush();
+long AuxPop();
+void PushWrite(), PopWrite(), MaxPushLevelOutput();
+font *FindFont();
+void FontDefine(), NewFont();
+long WordMaybeRead();
+unsigned_byte ByteMaybeRead();
+void CopyFontDefinition(), SkipFontDefinition();
+long CharWidth();
+void ReadTFMFile();
+boolean TFMOpen(), TFMTrialOpen();
+void TFMHeaderLoad(), TFMWidthsLoad(), FontMemoryOverflow();
+void BadDVIAbort();
+
+
+
+/*
+ * Global initializations. At this point, the command line has been
+ * processed, so we know /BufSize/ and /ProgramName/. After trivial
+ * initializations, we change /TeXFontDirs/ from a colon-separated list
+ * to a sequence of null-terminated strings by simply changing each
+ * colon to a null and keeping a count in /NTeXFontDirs/. Finally, we
+ * set up the /AuxStack/ along with its pointer and end; giving it
+ * four times the size of a small stack since /PushWX/ puts four things
+ * on it at a time.
+ */
+void
+Initializations()
+{
+ char *pchar;
+
+ State = LTYPESETTING;
+ CurFont = FirstFont = NULL;
+ MaxFilePushLevel = FilePushLevel = 0;
+ InitIOBuffers();
+
+ TeXFontDirs = getenv("TEXFONTS");
+ if (TeXFontDirs == NULL) TeXFontDirs = TFMAREADEFAULT;
+ if (*TeXFontDirs == ':') TeXFontDirs++;
+ for (NTeXFontDirs = 0, pchar = TeXFontDirs; *pchar; NTeXFontDirs++) {
+ while (*pchar && (*pchar != ':')) pchar++;
+ if (*pchar == ':') *pchar++ = '\0';
+ }
+
+ AuxBeginStack = SEQALLOC(4*(BufSize/SMALLBUFDIVISOR), long);
+ AuxEndStack = AuxBeginStack + 4*(BufSize/SMALLBUFDIVISOR);
+ AuxStackPointer = AuxBeginStack;
+ if (AuxBeginStack == NULL) {
+ fprintf(stderr, "\n%s fatal error: can't allocate buffers\n",
+ ProgramName);
+ exit(3);
+ }
+}
+
+
+
+/*
+ * Save the horizontal motion parameters on the aux stack.
+ */
+void
+PushWX()
+{
+ AuxPush(WInput);
+ AuxPush(WOutput);
+ AuxPush(XInput);
+ AuxPush(XOutput);
+}
+
+
+
+/*
+ * Restore the horizontal motion parameters from the aux stack.
+ */
+void
+PopXW()
+{
+ XOutput = AuxPop();
+ XInput = AuxPop();
+ WOutput = AuxPop();
+ WInput = AuxPop();
+}
+
+
+
+/*
+ * Save /DeltaH/. We use the same stack as /PushWX/; only /EndReflect/
+ * has to worry about the necessary stack discipline.
+ */
+void
+PushDeltaH()
+{
+ AuxPush(DeltaH);
+}
+
+
+
+/*
+ * Restore /DeltaH/.
+ */
+void
+PopDeltaH()
+{
+ DeltaH = AuxPop();
+}
+
+
+
+/*
+ * Push /value/ onto the auxiliary stack.
+ */
+void
+AuxPush(value)
+long value;
+{
+ *AuxStackPointer++ = value;
+ if (AuxStackPointer >= AuxEndStack) BufOverflow();
+}
+
+
+
+/*
+ * Pop a value from the auxiliary stack. Underflow means that
+ * something is seriously wrong; even bad input shouldn't do it.
+ */
+long
+AuxPop()
+{
+ if (--AuxStackPointer < AuxBeginStack) {
+ fprintf(stderr, "\n%s internal error: simulation stack underflow\n",
+ ProgramName);
+ exit(4);
+ }
+ return *AuxStackPointer;
+}
+
+
+
+/*
+ * Write a PUSH command to the output DVI file. All such commands must
+ * be written by this routine so we can keep an accurate count of how
+ * deep we are. /FilePushLevel/ keeps this count and /MaxFilePushLevel/
+ * keeps track of the deepest we've ever been. (This routine should be
+ * called WritePush, but then its name would conflict with WritePop.)
+ */
+void
+PushWrite()
+{
+ WriteByte(PUSH);
+ if (++FilePushLevel > MaxFilePushLevel)
+ MaxFilePushLevel = FilePushLevel;
+}
+
+
+
+/*
+ * Write a POP command to the output DVI file. All such commands must
+ * be written by this routine, so we can keep an accurate count of how
+ * deep we are. The error here shouldn't occur no matter what garbage
+ * is in the DVI file since this stuff is checked at an even lower
+ * level; see /NestingValidate/ in io.c.
+ */
+void
+PopWrite() {
+ WriteByte(POP);
+ if (--FilePushLevel < 0) {
+ fprintf(stderr, "\n%s internal error: more pops than pushes!\n",
+ ProgramName);
+ exit(4);
+ }
+}
+
+
+
+/*
+ * Write out the maximum stack depth necessary to process this file.
+ * That number is exactly /MaxFilePushLevel/, as computed by the two
+ * routines immediately preceding. However, we start by reading the
+ * old value, and use it instead if it's big enough and the -X flag
+ * was used. Here's the reason: TeX doesn't always put the correct
+ * value in this field! If TeX is writing out a POP that immediately
+ * follows a PUSH, they are both dropped and nothing goes to the DVI
+ * file. But the variable that's worrying about the maximum stack
+ * depth is not informed of this optimization. So if TeX tries to
+ * write "PUSH PUSH PUSH POP POP POP" it thinks that the max stack
+ * depth is 3, but in fact nothing is written. Thus TeX's behavior
+ * is always conservative. See paragraphs 601, 619, and 629 of
+ * Volume B of {\it Computers and Typesetting}.
+ */
+void
+MaxPushLevelOutput()
+{
+ long inputlevel;
+
+ inputlevel = ReadUnsigned(2);
+ if (ExactOutput && (inputlevel > MaxFilePushLevel))
+ WriteNumber(inputlevel, 2);
+ else
+ WriteNumber((long) MaxFilePushLevel, 2);
+}
+
+
+
+
+/*
+ * Find the font associated with /fontnumber/ by searching the list.
+ * If it's not there, return NULL and let the caller worry.
+ */
+font *
+FindFont(fontnumber)
+long fontnumber;
+{
+ font *f;
+
+ f = FirstFont;
+ while ((f != NULL) && (f->number != fontnumber)) f = f->nextfont;
+ return f;
+}
+
+
+
+
+/*
+ * Define a new font as required by a FNT_DEFn command. If the font is
+ * already known, we must have seen this FNT_DEFn before---skip it if
+ * simulating, otherwise copy it over. (A given FNT_DEFn command may be
+ * seen any number of times because we must process them whether we're
+ * simulating or typesetting.) Call /NewFont/ on never-yet-seen fonts.
+ */
+void
+FontDefine(bytes)
+unsigned bytes;
+{
+ long fontnumber;
+
+ fontnumber = ReadUnsigned(bytes);
+ if (FindFont(fontnumber) == NULL)
+ NewFont(fontnumber, bytes);
+ else if (State >= SIMULATING)
+ SkipFontDefinition();
+ else
+ CopyFontDefinition(fontnumber, bytes);
+}
+
+
+
+/*
+ * Process a font for the first time. If we're not simulating, we must
+ * copy the FNT_DEFn command and parameters into the output file as we
+ * read them. Allocate the new font object and link it into the list.
+ * Fill in the /number/, /checksum/, /scalefactor/, /area/, and /name/
+ * fields from the arguments (skip the design size; we don't need it).
+ * Set /loaded/ to FALSE to indicate that we haven't read the TFM file.
+ * Only when we need the width of a character in the font---when
+ * /CharWidth/ is called---do we load the TFM file and fill in the rest
+ * of the fields. That is, we never read the TFM files of fonts that
+ * aren't used in reflected text.
+ */
+void
+NewFont(fontnumber, bytes)
+long fontnumber;
+unsigned bytes;
+{
+ font *newfont;
+ unsigned areasize, namesize;
+ char *p;
+
+ if (State < SIMULATING) {
+ WriteByte(FNT_DEF1 + bytes - 1);
+ WriteNumber(fontnumber, bytes);
+ }
+ newfont = OBJALLOC(font);
+ if (newfont == NULL) FontMemoryOverflow();
+ newfont->nextfont = FirstFont;
+ FirstFont = newfont;
+ newfont->number = fontnumber;
+ newfont->checksum = WordMaybeRead();
+ newfont->scalefactor = WordMaybeRead();
+ WordMaybeRead();
+ areasize = ByteMaybeRead();
+ namesize = ByteMaybeRead();
+ newfont->area = (char *) calloc(areasize+1, sizeof(char));
+ if (newfont->area == NULL) FontMemoryOverflow();
+ for (p = newfont->area; areasize != 0; areasize--) *p++ = ByteMaybeRead();
+ *p = '\0';
+ newfont->name = (char *) calloc(namesize+1, sizeof(char));
+ if (newfont->name == NULL) FontMemoryOverflow();
+ for (p = newfont->name; namesize != 0; namesize--) *p++ = ByteMaybeRead();
+ *p = '\0';
+ newfont->loaded = FALSE;
+}
+
+
+
+/*
+ * Either read or copy a word, depending on whether we're simulating.
+ * This routine and the next are useful because the definition of a
+ * font might be seen for the first time during a simulation or
+ * during normal left-to-right typesetting. Thus /NewFont/ might or
+ * might not want to copy out parameters as they're read.
+ */
+long
+WordMaybeRead()
+{
+ if (State >= SIMULATING) return ReadSigned(4); else return CopyWord();
+}
+
+
+
+/*
+ * Either read or copy a byte, depending on whether we're simulating.
+ */
+unsigned_byte
+ByteMaybeRead()
+{
+ if (State >= SIMULATING) return ReadUnsigned(1); else return CopyByte();
+}
+
+
+
+/*
+ * Copy a font definition from the input DVI file to the output file.
+ */
+void
+CopyFontDefinition(fontnumber, bytes)
+long fontnumber;
+unsigned bytes;
+{
+ WriteByte(FNT_DEF1 + bytes - 1);
+ WriteNumber(fontnumber, bytes);
+ CopyNBytes(12L);
+ CopyNBytes((long) (CopyByte() + CopyByte()));
+}
+
+
+
+/*
+ * Skip by a font definition. The FNT_DEFn command and the first
+ * parameter have already been skipped.
+ */
+void
+SkipFontDefinition()
+{
+ SkipNBytes(12L);
+ SkipNBytes((long) (ReadUnsigned(1) + ReadUnsigned(1)));
+}
+
+
+
+
+/*
+ * Find the width of character /c/ in /CurFont/. If the TFM file hasn't
+ * been read, read it. The /widths/ field is NULL in fonts whose TFM
+ * files we couldn't find; we just return 0 as the width of any character
+ * in such a font. Carefully reduce /c/ modulo 256 if necessary. If /c/
+ * is now between /bc/ and /ec/, it's a character in the font. Get its
+ * value from the /charwidths/ table (whose indices run from 0 to
+ * /ec/-/bc/) and use that number to index the /widths/ table. If /c/
+ * isn't in the font, issue a warning and return 0.
+ */
+long
+CharWidth(c)
+long c;
+{
+ if (CurFont->loaded == FALSE) ReadTFMFile(CurFont);
+ if (CurFont->widths == NULL) return 0L;
+ if (c < 0) c = 255 - ((-1 - c) % 256);
+ else if (c >= 256) c = c % 256;
+ if ((c >= CurFont->bc) && (c <= CurFont->ec) &&
+ (CurFont->charwidths[c - CurFont->bc] != 0))
+ return CurFont->widths[CurFont->charwidths[c - CurFont->bc]];
+ fprintf(stderr, "\n%s warning: character %d undefined in font %s\n",
+ ProgramName, c, CurFont->name);
+ return 0L;
+}
+
+
+
+
+/*
+ * Read in the width information for a font. This data isn't read until
+ * we actually need it, viz., the first time /CharWidth/ is called with
+ * /f/ in /CurFont/. If we can't find the TFM file, we set /f->widths/
+ * to NULL as a flag meaning "assume all widths are zero." In any case,
+ * we mark /f/ as "loaded" so we don't do this again.
+ */
+void
+ReadTFMFile(f)
+font *f;
+{
+ if (TFMOpen(f)) {
+ TFMHeaderLoad(f);
+ TFMWidthsLoad(f);
+ (void) fclose(tfmfp);
+ } else
+ f->widths = NULL;
+ f->loaded = TRUE;
+}
+
+
+
+/*
+ * Open the TFM file for a font, returning TRUE if we did so and FALSE
+ * otherwise. If the name of the font starts with a slash, we only
+ * try to open the exact file. Otherwise we try prepending each of
+ * the possible TFM directories until we get a hit or run out. Recall
+ * that at this point /TeXFontDirs/ is a null-separated list of names.
+ */
+boolean
+TFMOpen(f)
+font *f;
+{
+ char *pchar;
+ int ndir;
+
+ if ((*f->area == '/') || ((!*f->area) && (*f->name == '/'))) {
+ if (TFMTrialOpen("", f->area, f->name)) return TRUE;
+ } else {
+ for (pchar = TeXFontDirs, ndir = NTeXFontDirs; ndir > 0; ndir--) {
+ if (TFMTrialOpen(pchar, f->area, f->name)) return TRUE;
+ while (*pchar++);
+ }
+ }
+ fprintf(stderr, "\n%s warning: Can't find TFM file for font %s; ",
+ ProgramName, f->name);
+ fprintf(stderr, "assuming zero width\n");
+ return FALSE;
+}
+
+
+
+/*
+ * Try to open a font file. The "area" is mostly empty for TeX, but
+ * you never can tell. On success, leave the file descriptor in the
+ * global variable /tfmfp/ and return TRUE, otherwise return FALSE.
+ */
+boolean
+TFMTrialOpen(path,area,name)
+char *path, *area, *name;
+{
+ static char buffer[MAXFILENAMESIZE];
+
+ (void) sprintf(buffer, "%s/%s/%s.tfm", path, area, name);
+ tfmfp = fopen(buffer, "r");
+ if (tfmfp == NULL) return FALSE; else return TRUE;
+}
+
+
+
+/*
+ * Set up the remaining fields of the font structure. Get and store
+ * the first and last character codes and the number of distinct widths
+ * in the font. Allocate the tables /charwidths/ and /widths/. Check
+ * the checksum, but skip by the rest of the header. Finally, read the
+ * values into the /charwidths/ table.
+ */
+void
+TFMHeaderLoad(f)
+font *f;
+{
+ int nchars;
+ long lh;
+ unsigned_byte *pbyte;
+
+ FSkipNBytes(tfmfp,2L);
+ lh = FReadUnsigned(tfmfp,2);
+ f->bc = FReadUnsigned(tfmfp,2);
+ f->ec = FReadUnsigned(tfmfp,2);
+ nchars = f->ec - f->bc + 1;
+ if (nchars < 0) nchars = 0;
+ if (nchars > 0) {
+ f->charwidths = SEQALLOC(nchars, unsigned_byte);
+ if (f->charwidths == NULL) FontMemoryOverflow();
+ }
+ f->nw = FReadUnsigned(tfmfp,2);
+ if (f->nw > 0) {
+ f->widths = SEQALLOC(nchars, long);
+ if (f->widths == NULL) FontMemoryOverflow();
+ }
+ FSkipNBytes(tfmfp,14L);
+ if (f->checksum != FReadSigned(tfmfp,4))
+ fprintf(stderr, "\n%s warning: checksum mismatch on font %s\n",
+ ProgramName, f->name);
+ FSkipNBytes(tfmfp, 4*lh - 4);
+ for (pbyte = f->charwidths; nchars > 0; nchars--) {
+ *pbyte++ = FReadByte(tfmfp);
+ FSkipNBytes(tfmfp,3L);
+ }
+}
+
+
+
+/*
+ * Read the character widths of a font from the TFM file and convert
+ * to DVI units, a calculation that must be done with extreme care.
+ * Further explanation is punted to section 571 of volume B of the
+ * Encyclop\ae dia TeXnica. The results go into the /widths/ table.
+ */
+void
+TFMWidthsLoad(f)
+font *f;
+{
+ int nwidths;
+ unsigned_byte a,b,c,d;
+ long *pwidth,z,alpha,beta;
+
+ z = f->scalefactor;
+ alpha = 16;
+ while (z >= 040000000L) { z = z/2; alpha = alpha+alpha; }
+ beta = 256/alpha;
+ alpha *= z;
+ nwidths = f->nw;
+ for (pwidth = f->widths; nwidths > 0; nwidths--) {
+ a = FReadByte(tfmfp); b = FReadByte(tfmfp);
+ c = FReadByte(tfmfp); d = FReadByte(tfmfp);
+ *pwidth = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
+ if (a != 0) {
+ if (a != 255) {
+ fprintf(stderr, "\n%s fatal error: Bad TFM file for font %s\n",
+ ProgramName, f->name);
+ exit(2);
+ } else *pwidth -= alpha;
+ }
+ pwidth++;
+ }
+}
+
+
+
+/*
+ * Do you need comments for this one?
+ */
+void
+FontMemoryOverflow()
+{
+ fprintf(stderr, "\n%s: Insufficient memory for font definitions\n",
+ ProgramName);
+ exit(3);
+}
+
+
+
+/*
+ * Abort the run due to junk in the input.
+ */
+void
+BadDVIAbort(message)
+char *message;
+{
+ fprintf(stderr, "\n%s: Malformed DVI file (%s)\n", ProgramName, message);
+ exit(2);
+}
diff --git a/dviware/ivd2dvi/commands.h b/dviware/ivd2dvi/commands.h
new file mode 100644
index 0000000000..f7a30ffab2
--- /dev/null
+++ b/dviware/ivd2dvi/commands.h
@@ -0,0 +1,79 @@
+/*
+ * Commands and values found in DVI files.
+ */
+
+#define SETC_000 0
+/* . . .
+ * . . .
+ * . . .
+ */
+#define SETC_127 127
+#define SET1 128
+#define SET2 129
+#define SET3 130
+#define SET4 131
+#define SET_RULE 132
+#define PUT1 133
+#define PUT2 134
+#define PUT3 135
+#define PUT4 136
+#define PUT_RULE 137
+#define NOP 138
+#define BOP 139
+#define EOP 140
+#define PUSH 141
+#define POP 142
+#define RIGHT1 143
+#define RIGHT2 144
+#define RIGHT3 145
+#define RIGHT4 146
+#define W0 147
+#define W1 148
+#define W2 149
+#define W3 150
+#define W4 151
+#define X0 152
+#define X1 153
+#define X2 154
+#define X3 155
+#define X4 156
+#define DOWN1 157
+#define DOWN2 158
+#define DOWN3 159
+#define DOWN4 160
+#define Y0 161
+#define Y1 162
+#define Y2 163
+#define Y3 164
+#define Y4 165
+#define Z0 166
+#define Z1 167
+#define Z2 168
+#define Z3 169
+#define Z4 170
+#define FONT_00 171
+/* . . .
+ * . . .
+ * . . .
+ */
+#define FONT_63 234
+#define FNT1 235
+#define FNT2 236
+#define FNT3 237
+#define FNT4 238
+#define XXX1 239
+#define XXX2 240
+#define XXX3 241
+#define XXX4 242
+#define FNT_DEF1 243
+#define FNT_DEF2 244
+#define FNT_DEF3 245
+#define FNT_DEF4 246
+#define PRE 247
+#define POST 248
+#define POST_POST 249
+#define BEG_REFLECT 250
+#define END_REFLECT 251
+
+#define DVIVERSION 2
+#define DVIPADCHAR 223
diff --git a/dviware/ivd2dvi/doc b/dviware/ivd2dvi/doc
new file mode 100644
index 0000000000..dd23d74471
--- /dev/null
+++ b/dviware/ivd2dvi/doc
@@ -0,0 +1,60 @@
+NAME
+ ivd2dvi - convert a dvi-ivd file to a normal dvi file
+
+SYNOPSIS
+ ivd2dvi [-Xv] [-b buffersize] [filename]
+
+DESCRIPTION
+ An extension to TeX called TeX-XeT produces "dvi-ivd" files, which
+ are similar to dvi files but include nonstandard commands calling
+ for the "reflection" (horizontal reversal) of text. In general,
+ dvi-ivd files cannot be processed by standard dvi drivers because
+ the reflection commands are not recognized. ivd2dvi converts a
+ dvi-ivd file to an equivalent dvi file, that is, to a file in which
+ the necessary reflections are carried out using only standard dvi
+ commands. The resulting file can be processed by any dvi driver.
+
+ The dvi-ivd file to be processed is specified on the command line;
+ if the named file cannot be found and contains no period following
+ its rightmost slash, ".dvi" is appended and ivd2dvi tries again.
+ Unlike most dvi processors, ivd2dvi is a true filter and reads its
+ standard input if no file is specified. The dvi file produced by
+ ivd2dvi is written to standard output.
+
+ Since dvitype says only "unrecognized command!" about the reflection
+ commands found in dvi-ivd files, ivd2dvi also performs careful error
+ checking for proper use of these commands.
+
+OPTIONS
+ -v Verbose mode. The number of each page is printed as it is
+ processed.
+
+ -b newbuffersize
+ Change the size of ivd2dvi's buffers. ivd2dvi uses several
+ internal buffers and cannot proceed if any overflows. When this
+ happens, you should try again using -b to increase the buffer
+ size. The default buffer size is 1024.
+
+ -X Exact mode. In this mode, ivd2dvi attempts to process the input
+ file without modification. The comment string is not updated,
+ NOP commands are retained, and the maximum stack depth is copied
+ from the input file if possible (this number is conservatively
+ but imprecisely computed by TeX; normally ivd2dvi will substitute
+ the exact value). As a result, dvi files with no reflection
+ commands are more likely to emerge totally unchanged. Use of
+ this flag is discouraged except for debugging.
+
+ENVIRONMENT
+ TEXFONTS
+ Colon-separated list of directories to be searched for font
+ metric files. The default is /usr/lib/tex/fonts.
+
+FILES
+ /usr/lib/tex/fonts/*.tfm Font metric files.
+
+SEE ALSO
+ "Mixing right-to-left texts with left-to-right texts," Donald Knuth
+ and Pierre MacKay, TUGboat volume 8 (1987), number 1, pp. 14--25.
+
+AUTHOR
+ Larry Denenberg, larry@bbn.com or larry@harvard.edu.
diff --git a/dviware/ivd2dvi/global.h b/dviware/ivd2dvi/global.h
new file mode 100644
index 0000000000..27bcb67367
--- /dev/null
+++ b/dviware/ivd2dvi/global.h
@@ -0,0 +1,65 @@
+/*
+ * Global definitions for ivd2dvi. Copyright 1988 by Larry Denenberg.
+ * May be freely distributed as long as this notice is retained.
+ */
+
+#define TRUE 1
+#define FALSE 0
+
+
+/*
+ * Buffer sizing and allocation is explained in io.c.
+ */
+#define BUFDEFAULTSIZE 1024
+#define SMALLBUFDIVISOR 16
+#define MAXFILENAMESIZE 256
+#define TFMAREADEFAULT "/usr/local/lib/tex/fonts"
+
+
+/*
+ * Values of /State/; see the overview in ivd2dvi.c.
+ */
+#define LTYPESETTING 0
+#define RTYPESETTING 1
+#define SIMULATING 2
+
+
+/*
+ * Allocate an object of type T or a sequence of N such objects.
+ */
+#define OBJALLOC(T) ((T *) malloc(sizeof(T)))
+#define SEQALLOC(N,T) ((T *) calloc((unsigned) N, sizeof(T)))
+
+
+/*
+ * Library routines declared to avoid lint errors.
+ */
+extern char *calloc(), *malloc(), *rindex(), *index(), *getenv();
+extern void exit();
+
+
+/*
+ * Useful types. If your compiler doesn't recognize unsigned char,
+ * you'll have to find another one-byte unsigned integer.
+ */
+typedef unsigned char unsigned_byte;
+typedef short boolean;
+
+
+/*
+ * Structure for font information. Note that /charwidths/ is a table
+ * of length ec-bc+1 giving the width of each character; each entry
+ * in this table is an index into the /widths/ table where the widths
+ * really reside. Further information is in the file auxiliary.c.
+ */
+typedef struct font {
+ long number; /* font number in the DVI file */
+ char *area, *name; /* strings naming the associated file */
+ long checksum, scalefactor; /* you figure this one out */
+ boolean loaded; /* TRUE iff we've read the TFM file */
+ int nw; /* number of distinct widths */
+ long *widths; /* the distinct widths */
+ int bc, ec; /* beginning and ending char numbers */
+ unsigned_byte *charwidths; /* widths of the individual characters */
+ struct font *nextfont; /* pointer to the next font */
+} font;
diff --git a/dviware/ivd2dvi/io.c b/dviware/ivd2dvi/io.c
new file mode 100644
index 0000000000..c461920d4a
--- /dev/null
+++ b/dviware/ivd2dvi/io.c
@@ -0,0 +1,520 @@
+/*
+ * Input/output routines for ivd2dvi. Copyright 1988 by Larry Denenberg.
+ * May be freely distributed as long as this notice is retained.
+ *
+ * Here are the naming conventions for input routines: A routine is named
+ * "Read..." if it merely reads something from standard input, e.g.
+ * /ReadByte/, /ReadSigned/. Recall that the input DVI file always
+ * comes via standard input. As in C, there are routines "FRead..."
+ * whose first argument is of type FILE*; these read from an arbitrary
+ * file and are used to read TFM files. We also have the "Copy..."
+ * routines; these are just like their "Read..." counterparts but also
+ * copy every byte read to the output DVI file. There are no "FCopy..."
+ * routines for obvious reasons. The routine /CopyNBytes/ has no "Read"
+ * analogue because it returns no value.
+ */
+
+
+#include <stdio.h>
+#include "global.h"
+#include "commands.h"
+
+/* Procedure and global variable defined in auxiliary.c */
+extern void BadDVIAbort();
+extern font *CurFont;
+
+/* Global variables defined in ivd2dvi.c */
+extern int State;
+extern char *ProgramName;
+
+/* Global variables defined here */
+unsigned BufSize = BUFDEFAULTSIZE; /* buffer size, mutable with -b */
+unsigned_byte *Buffer; /* first char of input buffer */
+unsigned_byte *BufEnd; /* pointer just beyond buffer area */
+unsigned_byte *BufFirstNonchar; /* first unused spot; save input here */
+unsigned_byte *BufPointer; /* pointer into buffer; read from here */
+boolean ReadingCommand = FALSE; /* true iff reading a DVI main command */
+unsigned_byte *VStack; /* start of nesting validation stack */
+unsigned_byte *VEnd; /* pointer just beyond vstack area */
+unsigned_byte *VPointer; /* vstack pointer to first unused slot */
+long BytesOutput = 0L; /* number of bytes written to output */
+
+
+/* Procedures defined in this file, in order of definition */
+void InitIOBuffers();
+unsigned_byte ReadByte();
+void NestingValidate(), BufOverflow();
+unsigned_byte ReadCommand(), CopyByte(), FReadByte();
+void SkipNBytes(), FSkipNBytes(), CopyNBytes();
+long ReadUnsigned(), FReadUnsigned(), ReadSigned(), FReadSigned();
+long CopyWord(), CopyUnsigned();
+unsigned_byte *ReadFilePosition();
+void ResetFilePosition(), RereadLastByte();
+void WriteByte(), WriteWord(), WriteNumber(), WriteString();
+unsigned SignedBytes();
+
+
+
+/*
+ * Initialize two of the three main memory areas in ivd2dvi. The
+ * size of each is a function of the value /BufSize/ which can be
+ * changed by the user with the -b flag. So the user doesn't need to
+ * know that there are lots of buffers with different sizes; if there's
+ * not enough room, using the -b flag will make them all bigger.
+ */
+void
+InitIOBuffers()
+{
+ Buffer = SEQALLOC(BufSize, unsigned_byte);
+ BufPointer = NULL;
+ BufFirstNonchar = Buffer;
+ BufEnd = Buffer + BufSize;
+
+ VStack = SEQALLOC(BufSize/SMALLBUFDIVISOR, unsigned_byte);
+ VPointer = VStack;
+ VEnd = VStack + BufSize/SMALLBUFDIVISOR;
+
+ if ((Buffer == NULL) || (VStack = NULL)) {
+ fprintf(stderr, "\n%s fatal error: can't allocate buffers\n",
+ ProgramName);
+ exit(3);
+ }
+}
+
+
+
+
+/*
+ * Read a byte from the input DVI file. All input routines that read
+ * standard input must come through here.
+ *
+ * The problem to worry about is that ivd2dvi frequently needs to reread
+ * input. Rather than doing seeks, we've (perhaps foolishly) decided to
+ * buffer input so we can see it again later. This means that ivd2dvi is
+ * a true filter, which is a useless advantage since TeX doesn't write
+ * its standard output and since most dvi drivers (dvi2ps in particular)
+ * need real files and can't read standard input.
+ *
+ * Now, we don't buffer *all* input, since we know that only if we're
+ * simulating will we need to reread the input. So anytime we're
+ * reading from the real input file and we're simulating, we save the
+ * character in the buffer.
+ *
+ * /BufPointer/ is the place in the buffer from which we're reading
+ * saved input. It also serves as a flag; if it's NULL, then we're not
+ * reading saved input at all. So the basic idea is: if /BufPointer/
+ * is pointing, return the character it points to and advance it.
+ * Otherwise, do a real read, and save if simulating.
+ *
+ * What if /BufPointer/ points, but there's no more input? Then we want
+ * to go back to reading real input: set /BufPointer/ to NULL and call
+ * ourselves recursively to force real input. We also take this
+ * opportunity to clear out the buffer and start over, a step which is
+ * justified only because we start rereading input only when we stop
+ * simulating (thus nobody can need the saved input anymore).
+ *
+ * Note that it's an error if standard input ever comes to EOF; we
+ * should have seen the postamble and quit. Finally, /ReadingCommand/
+ * is TRUE iff the byte is a command and not a parameter; in this case
+ * we check it with /NestingValidate/.
+ */
+unsigned_byte
+ReadByte()
+{
+ int nextchar;
+
+ if (BufPointer) {
+ if (BufPointer < BufFirstNonchar) return *BufPointer++;
+ else {
+ BufPointer = NULL;
+ BufFirstNonchar = Buffer;
+ return ReadByte();
+ }
+ } else {
+ nextchar = getchar();
+ if (nextchar == EOF) BadDVIAbort("unexpected EOF");
+ if (ReadingCommand) NestingValidate((unsigned_byte) nextchar);
+ if (State >= SIMULATING) {
+ *BufFirstNonchar++ = (unsigned_byte) nextchar;
+ if (BufFirstNonchar > BufEnd) BufOverflow();
+ }
+ return nextchar;
+ }
+}
+
+
+
+/*
+ * Test a character for nesting. Since dvitype doesn't check DVI-IVD
+ * files for validity, it's important to do careful checking here---
+ * ivd2dvi will get horribly confused if reflections and pushes don't
+ * nest properly. This routine gets called to validate every single
+ * command read from the input file. The method is simple: if the
+ * command is PUSH, BEG_REFLECT, or BOP, just push it on the stack (the
+ * stack is the tiny /VStack/, not used for anything else). If the
+ * command is POP, END_REFLECT, or EOP, pop a command from the stack and
+ * be sure it matches. Ignore all other commands.
+ */
+void
+NestingValidate(nextchar)
+unsigned_byte nextchar;
+{
+ switch (nextchar) {
+ case PUSH: case BEG_REFLECT: case BOP:
+ if (VPointer >= VEnd) BufOverflow(); else *VPointer++ = nextchar;
+ break;
+ case POP:
+ if ((VPointer <= VStack) || (*--VPointer != PUSH))
+ BadDVIAbort("reflection commands incorrectly nested");
+ break;
+ case EOP:
+ if ((VPointer <= VStack) || (*--VPointer != BOP))
+ BadDVIAbort("reflection commands incorrectly nested");
+ break;
+ case END_REFLECT:
+ if ((VPointer <= VStack) || (*--VPointer != BEG_REFLECT))
+ BadDVIAbort("reflection commands incorrectly nested");
+ break;
+ }
+}
+
+
+
+/*
+ * As Knuth would say, ``Exit due to finiteness.'' We can become less
+ * finite, but we have to know in advance!
+ */
+void
+BufOverflow()
+{
+ fprintf(stderr, "\n%s: Buffer size %d was inadequate\n",
+ ProgramName, BufSize);
+ fprintf(stderr, "(Use the -b flag to increase the buffer size)\n");
+ exit(3);
+}
+
+
+
+
+/*
+ * Read a command. Just read a byte with /ReadingCommand/ turned on
+ * (and then turn it off!). See the discussion of /VStack/ above.
+ */
+unsigned_byte
+ReadCommand()
+{
+ unsigned_byte result;
+
+ ReadingCommand = TRUE;
+ result = ReadByte();
+ ReadingCommand = FALSE;
+ return result;
+}
+
+
+
+
+/*
+ * Copy a single byte from the input DVI file to the output file and
+ * return it.
+ */
+unsigned_byte
+CopyByte()
+{
+ unsigned result = ReadByte();
+
+ WriteByte(result);
+ return result;
+}
+
+
+
+/*
+ * Read a byte from the input file /fp/. We don't have to worry about
+ * buffering or special checks, just about EOF. The error message is
+ * justified because we do input only from the TFM file of the current
+ * font (except for the main input DVI file, of course).
+ */
+unsigned_byte
+FReadByte(fp)
+FILE *fp;
+{
+ int nextchar;
+
+ nextchar = getc(fp);
+ if (nextchar == EOF) {
+ fprintf(stderr, "\n%s: unexpected EOF in TFM file for font %s\n",
+ ProgramName, CurFont->name);
+ exit(2);
+ }
+ return nextchar;
+}
+
+
+
+
+/*
+ * Discard /n/ bytes from the input DVI file.
+ */
+void
+SkipNBytes(n)
+long n;
+{
+ while (n--) (void) ReadByte();
+}
+
+
+
+/*
+ * Discard /n/ bytes from the input file /fp/.
+ */
+void
+FSkipNBytes(fp,n)
+FILE *fp;
+long n;
+{
+ while (n--) (void) FReadByte(fp);
+}
+
+
+
+/*
+ * Copy /n/ bytes from the input DVI file to the output file.
+ */
+void
+CopyNBytes(n)
+long n;
+{
+ while (n--) WriteByte(ReadByte());
+}
+
+
+
+
+/*
+ * Read an unsigned integer of length /bytes/ from the input DVI file.
+ */
+long
+ReadUnsigned(bytes)
+unsigned bytes;
+{
+ long result = 0;
+
+ while (bytes-- != 0) {
+ result <<= 8;
+ result |= ReadByte();
+ }
+ return result;
+}
+
+
+
+/*
+ * Read an unsigned integer of length /bytes/ from the input file /fp/.
+ */
+long
+FReadUnsigned(fp,bytes)
+FILE *fp;
+unsigned bytes;
+{
+ long result = 0;
+
+ while (bytes--) {
+ result <<= 8;
+ result |= FReadByte(fp);
+ }
+ return result;
+}
+
+
+
+/*
+ * Read a signed integer of length /bytes/ from the input DVI file.
+ * This must be done with no assumptions about the length of long ints
+ * on the machine and without using sign-extending right shifts.
+ */
+long
+ReadSigned(bytes)
+unsigned bytes;
+{
+ long result;
+
+ result = ReadByte();
+ if (result >= 128) result -= 256;
+ while (--bytes) {
+ result <<= 8;
+ result |= ReadByte();
+ }
+ return result;
+}
+
+
+
+/*
+ * Read a signed integer of length /bytes/ from the input file /fp/.
+ */
+long
+FReadSigned(fp,bytes)
+FILE *fp;
+int bytes;
+{
+ long result;
+
+ result = FReadByte(fp);
+ if (result >= 128) result -= 256;
+ while (--bytes) {
+ result <<= 8;
+ result |= FReadByte(fp);
+ }
+ return result;
+}
+
+
+
+
+/*
+ * Copy a 32-bit word from the input DVI file to the output file, and
+ * return it.
+ */
+long
+CopyWord()
+{
+ long result = ReadSigned(4);
+
+ WriteWord(result);
+ return result;
+}
+
+
+
+/*
+ * Copy an unsigned integer of length /bytes/ from the input DVI file
+ * to the output file, and return it.
+ */
+long
+CopyUnsigned(bytes)
+unsigned bytes;
+{
+ long result = ReadUnsigned(bytes);
+
+ WriteNumber(result,bytes);
+ return result;
+}
+
+
+
+
+/*
+ * Get a file position from which we can take input later. If we're
+ * currently taking input from the buffer, the result is just the buffer
+ * pointer. If not, the saved position is the place inside the buffer
+ * where we're storing the input as it comes in. Details at /ReadByte/.
+ */
+unsigned_byte *
+ReadFilePosition()
+{
+ if (BufPointer) return BufPointer; else return BufFirstNonchar;
+}
+
+
+
+/*
+ * Reset to take input starting from a saved file position. Easy.
+ */
+void
+ResetFilePosition(position)
+unsigned_byte *position;
+{
+ BufPointer = position;
+}
+
+
+
+/*
+ * Arrange to see the last character of the input again. Possible only
+ * when we're reading buffered input, in which case it's simple. In
+ * fact, this routine is used only by /SetString/ while RTYPESETTING.
+ */
+void RereadLastByte() {
+ if (!BufPointer || (BufPointer == Buffer)) {
+ fprintf(stderr, "\n%s internal error: illegal attempt to backup input\n",
+ ProgramName);
+ exit(4);
+ }
+ BufPointer--;
+}
+
+
+
+
+/*
+ * Write a single byte to the output DVI file. All non-diagnostic output
+ * ***MUST*** go through this routine so that the number of bytes output
+ * is counted accurately.
+ */
+void
+WriteByte(value)
+unsigned value;
+{
+ putchar(value);
+ BytesOutput++;
+}
+
+
+
+/*
+ * Write a 32-bit word to the output file.
+ */
+void
+WriteWord(value)
+long value;
+{
+ WriteNumber(value, 4);
+}
+
+
+
+/*
+ * Write /value/ to the output file as an integer of length /bytes/.
+ */
+void
+WriteNumber(value,bytes)
+long value;
+unsigned bytes;
+{
+ if (bytes > 1) WriteNumber(value >> 8, bytes - 1);
+ WriteByte((unsigned_byte) value & 0377);
+}
+
+
+
+/*
+ * Write a string to the output DVI file.
+ */
+void
+WriteString(string)
+char *string;
+{
+ char *pchar;
+
+ for (pchar = string; *pchar; pchar++)
+ WriteByte((unsigned_byte) *pchar);
+}
+
+
+
+
+/*
+ * Calculate how many bytes are required to represent /number/. Don't
+ * say /number = -(number+1)/ because of the possibility of overflow.
+ */
+unsigned
+SignedBytes(number)
+long number;
+{
+ if (number > 0) { number = -number; number -= 1; }
+ if (number >= -128) return 1;
+ else if (number >= -(128*256)) return 2;
+ else if (number >= -(128*256*256)) return 3;
+ else return 4;
+}
diff --git a/dviware/ivd2dvi/ivd2dvi.1 b/dviware/ivd2dvi/ivd2dvi.1
new file mode 100644
index 0000000000..d8231be19c
--- /dev/null
+++ b/dviware/ivd2dvi/ivd2dvi.1
@@ -0,0 +1,89 @@
+.TH IVD2DVI 1 "31 August 1988"
+.SH NAME
+ivd2dvi \- convert a dvi-ivd file to a standard dvi file
+
+.SH SYNOPSIS
+.B ivd2dvi
+[\-Xvcb]
+.RI [ filename ]
+
+.SH DESCRIPTION
+An extension to TeX called TeX-XeT produces ``dvi-ivd'' files, which
+are similar to dvi files but include nonstandard commands calling
+for the ``reflection'' (horizontal reversal) of text. In general,
+dvi-ivd files cannot be processed by standard dvi drivers because
+the reflection commands are not recognized.
+.I ivd2dvi
+converts a
+dvi-ivd file to an equivalent dvi file, that is, to a file in which
+the necessary reflections are carried out using only standard dvi
+commands. The resulting file can be processed by any dvi driver.
+
+The dvi-ivd file to be translated is specified on the command line;
+if the file cannot be found and its name contains no period following
+the rightmost slash, ``.dvi'' is appended and
+.I ivd2dvi
+tries again.
+Unlike most dvi processors,
+.I ivd2dvi
+is a true filter and reads its
+standard input if no file is specified. The dvi file produced by
+.I ivd2dvi
+is written to standard output.
+
+.I ivd2dvi
+also performs careful error checking for proper placement
+of the reflection commands since
+dvitype(1), the dvi-file validation program,
+does not understand them.
+
+.SH OPTIONS
+.IP \-v
+Verbose mode.
+.IP \-c
+Error checking only. Report if reflection commands are misused
+in the input file (or if the input file is malformed in some other
+way detectable by
+.I ivd2dvi)
+but produce no other output.
+.IP \-b
+Double the size of
+.IR ivd2dvi 's
+buffers.
+.I ivd2dvi
+uses several
+internal buffers and cannot proceed if any overflows. When this
+happens, you should try again using \-b to increase the buffer
+size. This flag can be used more than once, but probably
+will never be needed
+(since the default buffers are plenty big).
+.IP \-X
+Exact mode. In this mode,
+.I ivd2dvi
+attempts to process the input
+file without modification. The comment string is not updated,
+NOP commands are retained, and the maximum stack depth is copied
+from the input file if possible (this number is conservatively
+but imprecisely computed by TeX; normally
+.I ivd2dvi
+will substitute
+the exact value). As a result, dvi files with no reflection
+commands will typically emerge unchanged. Unnecessary except when debugging.
+
+.SH ENVIRONMENT
+.IP TEXFONTS
+Colon-separated list of directories to be searched for font
+metric files. The default is /usr/lib/tex/fonts.
+
+.SH FILES
+.ta 32
+/usr/lib/tex/fonts/*.tfm Font metric files.
+
+.SH SEE ALSO
+``Mixing right-to-left texts with left-to-right texts,'' Donald Knuth
+and Pierre MacKay, TUGboat volume 8 (1987), number 1, pp. 14\-\-25.
+
+dvitype(1)
+
+.SH AUTHOR
+Larry Denenberg, larry@bbn.com or larry@harvard.edu.
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);
+}
diff --git a/dviware/ivd2dvi/ivd2dvi.readme b/dviware/ivd2dvi/ivd2dvi.readme
new file mode 100644
index 0000000000..82d286aa59
--- /dev/null
+++ b/dviware/ivd2dvi/ivd2dvi.readme
@@ -0,0 +1,9 @@
+Nothing very complicated here. Edit the Makefile to pick your C compiler
+and flags, and fix the installation directory if you use "make install".
+If your TFMs are not in /usr/lib/tex/fonts edit global.h to say where.
+Thanks to Justin Bur of U de Montreal for converting the man page to the
+right format. Send bugs and improvements. Good luck!
+
+/Larry Denenberg
+larry@bbn.com, larry@harvard.edu
+
diff --git a/dviware/ivd2dvi/makefile b/dviware/ivd2dvi/makefile
new file mode 100644
index 0000000000..cdaec45f31
--- /dev/null
+++ b/dviware/ivd2dvi/makefile
@@ -0,0 +1,22 @@
+# Set this variable to the ultimate location of the software.
+BINAREA=/usr/local/bin
+
+CFLAGS = -O
+
+OBJS = ivd2dvi.o auxiliary.o io.o
+SRCS = ivd2dvi.c auxiliary.c io.c
+INCLUDES = global.h commands.h
+
+all: ivd2dvi
+
+ivd2dvi: ${OBJS}
+ cc ${CFLAGS} -o ivd2dvi ${OBJS}
+
+${OBJS}: ${INCLUDES}
+
+install: all
+ install -s -m 755 ivd2dvi ${BINAREA}
+
+bugs: ${SRCS} ${INCLUDES}
+ rm -f bugs
+ lint -hbxac ${SRCS} > bugs