DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

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

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T U

⟦f9ec9455f⟧ TextFile

    Length: 231282 (0x38772)
    Types: TextFile
    Notes: Uncompressed file

Derivation

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

TextFile

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	symbolics
# This archive created: Fri Mar 11 16:07:32 1988
export PATH; PATH=/bin:$PATH
if test ! -d 'symbolics'
then
	mkdir 'symbolics'
fi
cd 'symbolics'
if test -f 'NOTICE'
then
	echo shar: will not over-write existing file "'NOTICE'"
else
cat << \SHAR_EOF > 'NOTICE'
The symbolics driver is obsolete and not maintained. Source code
can be found in the symbolics.sh file in ../   If you need it
uncompress symbolics.sh and run it through sh.  These empty
directories are here for your convenience.
SHAR_EOF
fi # end of overwriting check
if test -f '4.2-setup'
then
	echo shar: will not over-write existing file "'4.2-setup'"
else
cat << \SHAR_EOF > '4.2-setup'
# /bin/sh
# set the symbolics area up for 4.2bsd
cp 4.2Makefile Makefile
SHAR_EOF
fi # end of overwriting check
if test -f 'dvi2lgp.web'
then
	echo shar: will not over-write existing file "'dvi2lgp.web'"
else
cat << \SHAR_EOF > 'dvi2lgp.web'
% Copyright 1983 by Richard Furuta and Pierre MacKay. Copying without
% fee is permitted provided that the copies are not made or distributed
% for direct commercial advantage and credit to the source is given.
% Altered versions of this program are not to be copied 
% or distributed without permission from the authors.
% There are no restrictions on the use of this program.
%
% This program is based on the DVItype program by D. E. Knuth.
% CHECKME flags an uncertain part of the code
% --fixme-- flags things that need attending to
%
% Remember to change the Version number both on the TeX title page and also
%   within the body of the program (see the definition for ``banner'')
%   There's a date on the banner, too.

% History of modifications.  
% First working version (6 April, 1983), modestly called version 0.8.
% Version 1 (11 April 1983) includes provision for LGP vectors.
% Version 1.1 (11 May, 1983) changes ``write'' to ``tprint'' to
%   make the creation of a UNIX change file easier. Also made
%   the ``errors only'' production run less informative, changed the
%   old ``errors only'' run so that it is now identified as ``terse''
%   and changed the name of the old ``terse'' run to ``some_debug.''
%   Set a threshold to keep the right margin overrun warnings
%   from coming through on trivial numbers of scaled points.  On
%   a production run, the threshold is half a pixel-width.
%   And here is a special treat.  On production runs (and also
%   in ``terse'' mode), the default order of reading is from back
%   to front, so that the output pages get stacked in the right
%   order on the printer tray.  How's that!  This default can be
%   changed by the use of an option switch.
% Version 1.2 (August 21, 1983) [rkf] explicitely specify what factor
%   to use when multiplying by certain well known magnifications.  This
%   change required due to round-off errors and the new magstep
%   magnifications.
% Version 1.9 (October 3, 1983) [rkf] updates things so that they work with
%   the latest versions of Tangle (2.0), Weave (2.2), and TeX (0.9999).
%   This version also supports a number of bells and whistles---parsing
%   the command line, suggesting a following command, \special{s}, and
%   \special{v}. (Changed below to more specific forms, e.g. \special{vector}) 
%   Concurrently, [pam] fixes a bug in the way the default
%   mode handles a file of pages which all return |start_match| true.
%   and also a much more serious bug which causes some rules to be
%   badly positioned. Additional feature, big_char_threshold added
%   to provide for ephemeral loading of oversize characters (so
%   that LGP bit-map doesn't run out of space.
% Version 1.91 (October 24) magsteps provided for magnification as an
%   alternative to explicit numeric values.  Mysterious bug
%   in routine for -n:m switch cured.  This selective
%   partial printout option works only in so-called |backwards|
%   mode, which should be satisfactory.  [pam]
% Version 1.92 (November 14, 1983) Several bug fixes, in |print_octal|
%   and in the simple sequential count routine.  New switch added
%   to allow for rotation of the output into ``landscape'' mode, a
%   feature which will be most useful for transparencies.  A
%   small amount of cleanup in the \.{WEAVE} formatting, and the
%   creation of a new starred module, to make room for a full
%   description of the program's operation in the introduction. [pam]
%   Updated code to correspond with dvitype, version 2. [rkf]
% Version 2.0 (December 7, 1983).  Landscape mode for huge characters
%   corrected.  LGP \specials given more specific names.  Short
%   manual for use added to starred Introduction module.  Much
%   cleanup of WEAVE formatting.  [pam] Adjustments to manual [rkf]
% Version 2.1 (December 31, 1983).  The zero-width circle font in
%   Lamport's LaTeX uncovered a bug which needed fixing. [pam]
%   Changed initial message about entering CR for default values
%   to make it clearer (from "no magnification" to
%   "postamble-specified magnification").  Redid the help messages
%   for the maximum page value.  Modified prompts to begin with ">" [rkf]
%   Slight change once again in the maximum page value messages.
%   Made page_default_maximum into a WEB definition.  Better
%   error trapping in the use of the |sw_c|n:m switch (a negative
%   page count will be converted to the default). [pam]
% Version 2.2 (March 9, 1984).  Print the name of the problem font
%   in the error message referring to that font.  A null option switch
%   causes the question to be restated.  Switches are forced to upper
%   case without regard to which operating system is being used. [rkf]
%
% Here is TeX material that gets inserted after \input webmac
\def\hang{\hangindent 3em\indent\ignorespaces}
\font\ninerm=amr9
\let\mc=\ninerm % medium caps for names like PASCAL
\def\PASCAL{{\mc PASCAL}}

\def\(#1){} % this is used to make module names sort themselves better
\def\9#1{} % this is used for sort keys in the index

\def\title{DVI2LGP}
\def\contentspagenumber{1}
\def\topofcontents{\null
  \titletrue % This really is a title page
  \vfill
  \centerline{\titlefont The {\ttitlefont DVI2LGP} processor}
  \centerline{(Version 2.2)}
  \vskip 15pt
  \centerline{R. Furuta and P. MacKay}
  \centerline{Department of Computer Science}
  \centerline{University of Washington}
  \centerline{Seattle, Washington 98195}
  \vskip 15pt
  \centerline{(March 9, 1984)}
  \vfill}
\def\botofcontents{\vfill
  \centerline{\hsize 5.5in\baselineskip9pt
    \vbox{\ninerm\noindent
    The preparation of this report
  was supported in part by grants from Bell-Northern Research 
  and Northern Telecom, Inc.
  to the University of Washington. `\TeX' is a
  trademark of the American Mathematical Society.}}}
\pageno=\contentspagenumber \advance\pageno by 1

@* Introduction.
The \.{DVI2LGP} utility program reads binary device-independent (``\.{DVI}'')
files that are produced by document compilers such as \TeX, and converts 
them into a preprocessed \.{LGP} file in ``native-mode'' coding.
This program has two chief purposes:
(1)~It assembles and outputs a sequential file of \.{LGP} codes, and
(2)~It can be used to
determine whether a \.{DVI} file is valid or invalid, since it retains
the symbolic output form of its parent, \.{DVItype}.

Programs for
typesetting need to be especially careful about how they do arithmetic; if
rounding errors accumulate, margins won't be straight, vertical rules
won't line up, and so on. But if rounding is done everywhere, even in the
midst of words, there will be uneven spacing between the letters, and that
looks bad. Human eyes notice differences of a thousandth of an inch in the
positioning of lines that are close together; on low resolution devices,
where rounding produces effects four times as great as this, the problem
is especially critical. Experience has shown that unusual care is needed
even on high-resolution equipment; for example, a mistake in the sixth
significant hexadecimal place of a constant once led to a difficult-to-find
bug in some software for the Alphatype CRS, which has a resolution of 5333
pixels per inch (make that 5333.33333333 pixels per inch).  The document
compilers that generate \.{DVI} files make certain assumptions about the
arithmetic that will be used by \.{DVI}-reading software, and if these
assumptions are violated the results will be of inferior quality.
Therefore the present program follows its parent model very closely
in the critical places where a bit of subtlety is involved.

The parent program is the \.{DVItype} program designed by Donald Knuth
and this descendant shamelessly borrows all possible modules from
the \.{DVItype} \.{WEB} file.  

The |banner| string defined here should be changed whenever \.{DVI2LGP}
gets modified.

@d banner=='This is DVI2LGP, Version 2.2, last modified March 9, 1984'
{printed when the program starts}
@d copyright=='Copyright (C) 1983 by Richard Furuta and Pierre MacKay.'

@ This \.{DVI2LGP} processor attempts first of all to provide a simple and 
direct method for interpreting \.{DVI} files for the Symbolics Inc.
\.{LGP} printer. In the default mode, we assume that the user wants
an entire file printed, at true size, and delivered to the output
tray in correct order for reading.  If the user finds the default
mode insufficent, there is a wide range of options to produce various
forms of standard \TeX\ output, as well as several forms of diagnostic
output which can be used to check on the correctness of the \.{DVI}
file.  Finally, it is possible to call on certain special features of
the \.{LGP} which are outside the normal functions of \TeX.  These
are not part of the \TeX\ typesetting system, but they may be added
to an otherwise normal \.{DVI} file through the use of the \TeX\
\.{\\special} escape primitive.  Such usage will, of course, be
absolutely specific to the \.{LGP} printer, but a \.{DVI} file
containing ``\.{\\special}'' sequences can safely be run through
any properly designed \.{DVI} interpreter.  The presence of the
sequence will be noted in the diagnostic log file, but no action
will be taken (unless the interpreter uses exactly the same \.{\\special}
for some other purpose).

@ In default mode, we assume that the \\{input filename} argument appears
on the \\{command line} with the \\{program call name}.  If the program
senses the presence of the \\{input filename} on the 
\\{command line},
and finds no other arguments, flags or switch characters, the program
begins at once and uses preset default values for all options. 
If the \\{input filename} already has
the extension \.{.dvi}, this extension need not be supplied in the
\\{command line}, since it is assumed as the default.  Whatever the
input filename may be, the program in all cases generates an \\{output filename}
with the extension \.{.lgp} and creates this file on the
user's \\{current working directory}, (even if the input file has been
taken from some other directory or structure).  Thus, the first stages
of a {\mc UNIX} transaction will look like this:

\smallskip\noindent
\line{\.{This is DVI2LGP, Version $n$, last modified May 29, 1453}\hfil}
\line{\.{[sample.dvi->sample.lgp]}\hfil}
\medskip
\noindent
and the first stages of most other transactions will be similar, except
that the file names may be folded into uppercase.

After the program has started running in default mode, the \.{DVI}
preamble banner line is displayed on the terminal from which the
job was started, followed by a progress
report which displays the byte-count in from the start of the file
to mark the occurrence of each \.{DVI bop} byte.
A completed job creating an \.{LGP} file for the printer looks 
like this:

\smallskip\noindent
\line{\.{\% dvi2lgp sample}\hfil}
\line{\.{This is DVI2LGP, Version $n$, last modified May 29, 1453}\hfil}
\line{\.{[sample.dvi->sample.lgp]}\hfil}
\line{\.{' TeX output 1471.7.12:1200'}\hfil}
\line{\.{9296: beginning of page 3}\hfil}
\line{\.{5066: beginning of page 2}\hfil}
\line{\.{42: beginning of page 1}\hfil}
\line{\.{\%}\hfil}
\medskip
\noindent
(For some systems we add a final prompt---generated by the program---so
that all that is needed is to respond to it with a carriage
return and the \.{LGP} file will be queued to the printer, assuming
that your system has provided a protocol for this queuing operation).

@ If the \\{command line} does not include the \\{input filename},
then the full range of options becomes accessible.
The user is first given the opportunity to redirect the diagnostic
output (including the font identifications and the beginning-of-page
notifications) to a file. Then the program asks for an \\{input filename},
which may be supplied with or without the \.{.dvi} extension.  As in
the default mode, the \\{output filename} is automatically generated from
the \\{input filename}, and the file is created on the user's \\{current
working directory}.

At this point the user has three choices:
\medskip
\item{1.} The remaining default options can again be selected by
typing a carriage return in response to the prompt line, which reads:

\smallskip\noindent
\line
 {\.{> <CR> to accept default of all pages, postamble-specified magnification.}\hfil}
\line{\.{  ? for help}\hfil}
\smallskip
\item{2.} A display of help messages can be called up by typing
a question mark.
\smallskip
\item{3.} The full sequence of options can be started by typing any
visible ASCII character other than a question mark.

The first cluster of options must be specified all on the one line
and, in the present version of the program, may not have any
spaces between options. Each distinct option is introduced
by a ``switch character'' which is established by the use of a 
\.{WEB} definition in this program (see the index, under |sw_c|).
The following single-letter options exist:

\medskip\noindent
For position on the output sheet:
\smallskip\hang
\.{C}---\.{Center}.  Equal margins right and left, as nearly as possible.
This is the default.
\smallskip\hang
\.{L}---\.{Left}. Text pushed over to the left margin as close as the \.{LGP}
hardware allows.
\smallskip\hang
\.{R}---\.{Right}. Text pushed over to the right margin, leaving as wide
a left margin as the \.{LGP} hardware allows.

\medskip\noindent
For orientation on the output sheet:
\smallskip\hang
\.{V}---\.{Vertical}. Normal, portrait mode output. This is the default.
\smallskip\hang
\.{H}---\.{Horizontal}. Landscape mode output rotated ninety degrees.

\medskip\noindent
For sequential output in \.{DVI} order (which will stack up on the
tray in inverted order):
\smallskip\hang
\.{I}---\.{Inverted}. The default has no name.

\medskip\noindent
For different amounts of diagnostic output  :
\smallskip\hang
\.{P}---\.{Production}. The minimum described above as the default.
\smallskip\hang
\.{T}---\.{Terse}. Rather more information about fonts.
\smallskip\hang
\.{S}---\.{Some-debug}. Line by line reports of progress.
\smallskip\hang
\.{F}---\.{Full-debug}. Exhaustive, byte-by-byte reports.

@ In addition to these, it is also possible to specify, on the same line,
and with no intervening spaces, a simple selection of pages for output.
This option (assuming that \.{`-'} is the switch character) takes the forms
\.{-$n$} or \.{-$n$:$m$} or \.{-$n$:end}. Thus, it is possible to select a
single page only, or a range of pages, or a range of pages through to
the end of the file.  Note that this option does not use the values
in the \TeX\ counters (which often contain page numbers).  When you use
this option, you are simply telling the program to count in sequentially
from the first to
the {$n$}th~page of the file, to start there and to continue processing
until the {$m$}th~page of the file or the end of the file is reached.
The use of this option automatically sets the I(nverted) mode
switch.

@ The remaining options have been taken over from \.{DVItype}
and are treated rather differently.  For one thing, there is
a specific prompt for each option.
When you see the prompt reading:

\smallskip\noindent
\line{\.{> Starting page (default = *)}\hfil}
\smallskip\noindent
you can make use of the specific values in \TeX's count registers
(which will override any values set by use of the  \.{-$n$} option).
The way to do this is described in the chapter \&{Optional modes of output},
and that description need not be repeated here.  After you have decided
what you wish to do with that option, you have a second chance to
tell the program how many pages you really want to generate on the
printer.  The default is conservatively set at a million, unless you
have already used the \.{-$n$:$m$} option described above.

The last, but perhaps the most interesting option allows you to
magnify the entire output page by whatever magnification you think you
can get away with.  The limitation here is fonts.  If you have
fonts corresponding to really wierd percentages of magnification
then you can use such values as a response to this option prompt.
Normally you will want to use \.{magsteps} instead.  The principle
behind \.{magsteps} is part of \TeX\ itself, and you can look it up
in the \TeX book.

@ The use of \.{\\special} functions is really part of \TeX\ rather
than part of this driver.  We have not completed the work on these 
yet, but the \.{LGP} vector-drawing function is available.
For this, you should include the \TeX\ commands \.{\\special\{begin-vector\}}
at the point where you want the vector to begin, and later on give the
command \.{\\special\{vector\}}.  For obvious reasons, these commands
have to be paired on the same page.  But don't worry if the paging
operation of \TeX\ happens to split them up unexpectedly; the operations
will simply be cancelled, and you will be told about it.  It would
be nice to get \.{\\special\{overlay=}\\{filename}\.{\}}
working, but at present we
cannot test it, so it remains in skeletal form for now, as does
\.{\\special\{raster-data=}\\{filename}\.{\}} and 
\.{\\special\{screen-image=}\\{filename}\.{\}}.

@* General programming environment.
This program is written in standard \PASCAL, except where it is necessary
to use extensions; for example, \.{DVI2LGP} must read and write files whose
names are dynamically specified, and that would be impossible in pure \PASCAL.
All places where nonstandard constructions are used have been listed in
the index under ``system dependencies.''
@!@^system dependencies@>

One of the extensions to standard \PASCAL\ that we shall deal with is the
ability to move to a random place in a binary file; another is to
determine the length of a binary file. These features are needed
for efficiency in reading the \.{DVI} file and are virtually
essential for reading the \.{PXL} file.  The only alternative would be
to have a huge temporary storage buffer into which the entire \.{PXL}file
could be read for processing.  

Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
etc.

@d othercases == others: {default for cases not listed explicitly}
@d endcases == @+end {follows the default case in an extended |case| statement}
@f othercases == else
@f endcases == end

@ One of the features that \.{DVI2LGP} inherits from \.{DVItype} is a
wide range of symbolic output formats, some of which will be of little
use except for debugging.  For more information about these formats,
or |out_mode|s as they will be termed, you must wait until the chapter
on ``{\bf Optional modes of output.}''  We put the definitions of the
modes here, however, so that we can have free use of them throughout the
entire \.{WEB} file.

@d errors_only=0 {minimal printing}
@d terse=1 {abbreviated symbolic output}
@d some_debug=2 {quite a bit of information}
@d verbose=3 {lots and lots of detailed tracing}

@ The binary input comes from |dvi_file|, and symbolic output can be written
on \PASCAL's standard |output| file. The term |print| is used instead of
|write| when this program writes on |output|, so that all such output
can easily be redirected if desired.
We use the terms |tprint| and |tprint_ln|
for terminal output, in order to make any necessary
change easier.  The |term_in| and |term_out| files are defined later.

@d print(#)==write(#)
@d print_ln(#)==write_ln(#)
@d tprint(#)==write(term_out,#) {|write| will work in most environments}
@d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise}

@p @<Compiler directives@>
program DVI2LGP(@!output);
label @<Labels in the outer block@>@/
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var@?@<Globals in the outer block@>@/
@<Procedures for \.{LGP} specials@>
procedure initialize; {this procedure gets things started properly}
  var i,j,k,l,m,n:integer; {local variables for initializations}
  begin tprint_ln(banner);@/
  tprint_ln(copyright);@/
  @<Set initial values@>@/
  end;@/

@ Some of this code is optional for use when debugging only;
such material is enclosed between the delimiters |debug| and $|gubed|$.
Other parts, delimited by |stat| and $|tats|$, are optionally included
if statistics about \.{DVI2LGP}'s memory usage are desired.

@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
@f debug==begin
@f gubed==end
@#
@d stat==@{ {change this to `$\\{stat}\equiv\null$'
  when gathering usage statistics}
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
  when gathering usage statistics}
@f stat==begin
@f tats==end
 
@ The \PASCAL\ compiler used to develop this system has ``compiler
directives'' that can appear in comments whose first character is a dollar sign.
In production versions of \.{DVI2LGP} these directives tell the compiler that
@^system dependencies@>
it is safe to avoid range checks and to leave out the extra code it inserts
for the \PASCAL\ debugger's benefit, although interrupts will occur if
there is arithmetic overflow. There is also an directive to ensure that
the ``\&{extended addressing}'' feature in TOPS-20 \PASCAL\ is included.
 
@<Compiler directives@>=
@{@&$X+,C-,A+,D-@} {extended adr., no range check, catch overflow, no debug}
debug @{@&$X+,C+,D+@}@+ gubed {but turn everything on when debugging}

@ If the program has to stop prematurely, it goes to the
`|final_end|'. Another label, |done|, is used when stopping normally.

@d final_end=9999 {label for the end of it all}
@d done=30 {go here when finished with a subtask}

@<Labels...@>=final_end,done;

@ The following parameters can be changed at compile time to extend or
reduce \.{DVI2LGP}'s capacity.

@<Constants...@>=
@!max_fonts=252; {maximum number of fonts allowed in current \.{LGP} format}
@!line_length=79; {bracketed lines of output will be at most this long}
@!terminal_line_length=150; {maximum number of characters input in a single
  line of input from the terminal}
@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth}
@!name_size=12000; {total length of all font file names}
@!name_length=50; {a file name shouldn't be longer than this}
@!max_cached_fonts=10; {all that can be active at one time}

@ In addition to these system-related constants, there
are some which apply to the printer itself. The value
of |big_char_threshold| is chosen to protect against any
possible overflow in the byte-length arguments of the
\.{LGP} ``Define a Small-Size Character'' command.
The value of |huge_char_threshold| was chosen after inspection
of a very much magnified \.{MATHEX} font (the actual example 
was \.{AMEX10.2986PXL}). When |huge_char_threshold| is set at 512,
about half of this font will be passed into the font storage
memory of the \.{LGP}, but the really oversize characters will
be painted on the page bit-map directly, using the ``Define a
Block of Raster Data'' command so that they do
not crowd out other more useful font characters.
With |huge_char_threshold| set at 512, the minimum size at which
``Define a Block of Raster Data'' takes over is .25~in.~by 1~in.
@^Define a Small-Size Character@>
@^Define a Block of Raster Data@>

@<Const...@>=
@!physical_margin=75; {$75$ bits reserved because \.{LGP} will clip them anyway}
@!right_margin=75; {the same for the right side}
@!full_width = 2040; {$8.5$ inches at 240 pixels/inch}
@!normal_top=120; {half an inch seems to be a good working value}
@!page_pixel_height=2640; {11 inches at 240 pixels/inch}
@!threshold=5.0; {threshold before horizontal overrun reported is 5 pixels}
@!big_char_threshold=128; {this protects against overflow of an 8-bit field}
@!huge_char_threshold=512; {64 pixels wide and 256 pixels deep, for example}

@ Certain features of this program require a ``clever'' operating system
which will allow for such things as a rescan of the command line
and a prompt (complete with argument) for the next process in sequence.
We note the presence or absence of such features with the following
flag, whose initialization can be changed in other versions of this
program. We also set up for an eventual consideration of batch operation.

@d batch_mode=0

@<Glob...@>=
@!clever:boolean; {initialized to true if the command line can be rescanned}

@ @<Set initial...@>=
clever:=false;	{assume that most implementations aren't that clever}

@ Here are some macros for common programming idioms.

@d incr(#) == #:=#+1 {increase a variable by unity}
@d decr(#) == #:=#-1 {decrease a variable by unity}
@d do_nothing == {empty statement}

@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
\.{DVI2LGP} will give up, after issuing an error message about the symptoms
that were noticed.

Such errors might be discovered inside of subroutines inside of subroutines,
so a procedure called |jump_out| has been introduced. This procedure, which
simply transfers control to the label |final_end| at the end of the program,
contains the only non-local |goto| statement in \.{DVI2LGP}.
@^system dependencies@>

@d abort(#)==begin print(' ',#); jump_out;
    end
@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
@.Bad DVI file@>

@p procedure jump_out;
begin goto final_end;
end;

@* The character set.
Like all programs written with the  \.{WEB} system, \.{DVI2LGP} can be
used with any character set. But it uses ascii code internally, because
the programming for portable input-output is easier when a fixed internal
code is used, and because both \.{DVI} and \.{LGP} files use ascii code
for file names, reference to characters in \.{PXL} fonts 
and certain other strings.

The next few modules of \.{DVI2LGP} have therefore been copied from the
analogous ones in the \.{WEB} system routines. They have been considerably
simplified, since \.{DVI2LGP} need not print the ascii control
codes with values less than @'40. If such codes appear in the \.{DVI} file,
they will be printed as question marks. For the \.{LGP} file, however
we must have access to the full range of ascii code, and not merely
the graphic characters.

@<Types...@>=
@!ascii_code=0.."~"; {a subrange of the integers}

@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
character sets were common, so it did not make provision for lower case
letters. Nowadays, of course, we need to deal with both upper and lower case
alphabets in a convenient way, especially in a program like \.{DVI2LGP}.
So we shall assume that the \PASCAL\ system being used for \.{DVI2LGP}
has a character set containing at least the standard visible characters
of ascii code (|"!"| through |"~"|). This is a very safe assumption in
at least this version of the program.

Some \PASCAL\ compilers use the original name |char| for the data type
associated with the characters in text files, while other \PASCAL s
consider |char| to be a 64-element subrange of a larger data type that has
some other name.  In order to accommodate this difference, we shall use
the name |text_char| to stand for the data type of the characters in the
output file.  We shall also assume that |text_char| consists of
the elements |chr(first_text_char)| through |chr(last_text_char)|,
inclusive. The following definitions should be adjusted if necessary.
We include here a \.{WEB} constant for case changes, which ought to
work on any system in which lower-case letters can be folded into upper-case,
(which is certainly true for both {\mc ASCII} and {\mc EBCDIC}). We also
make life easier for {\mc UNIX} programmers, by using a macro to provide
the characters for file-name extensions.  If no folding is desired, just
redefine |system_case_of| to leave out the case adjustment provided by
|fold_upper|
(i.e., make it |xchr[#]).
@^ASCII@>@^EBCDIC@>
@^system dependencies@>

@d text_char == char {the data type of characters in text files}
@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
@d last_text_char=127 {ordinal number of the largest element of |text_char|}
@d case_adjust="a" - "A" {very useful for case changes in either direction}
@d fold_upper(#)==xchr[#-case_adjust]  {translate letter}
@d system_case_of(#)==fold_upper(#) {leave out the adjustment for {\mc UNIX}}

@<Types...@>=
@!text_file=packed file of text_char;

@ The \.{DVI2LGP} processor converts between ascii code and
the user's external character set by means of arrays |xord| and |xchr|
that are analogous to \PASCAL's |ord| and |chr| functions.

@<Glob...@>=
@!xord: array [text_char] of ascii_code;
  {specifies conversion of input characters}
@!xchr: array [0..255] of text_char;
  {specifies conversion of output characters}

@ Under our assumption that the visible characters of standard ascii are
all present, the following assignment statements initialize the
|xchr| array properly, without needing any system-dependent changes.

@<Set init...@>=
for i:=0 to @'37 do xchr[i]:='?';
xchr[@'40]:=' ';
xchr[@'41]:='!';
xchr[@'42]:='"';
xchr[@'43]:='#';
xchr[@'44]:='$';
xchr[@'45]:='%';
xchr[@'46]:='&';
xchr[@'47]:='''';@/
xchr[@'50]:='(';
xchr[@'51]:=')';
xchr[@'52]:='*';
xchr[@'53]:='+';
xchr[@'54]:=',';
xchr[@'55]:='-';
xchr[@'56]:='.';
xchr[@'57]:='/';@/
xchr[@'60]:='0';
xchr[@'61]:='1';
xchr[@'62]:='2';
xchr[@'63]:='3';
xchr[@'64]:='4';
xchr[@'65]:='5';
xchr[@'66]:='6';
xchr[@'67]:='7';@/
xchr[@'70]:='8';
xchr[@'71]:='9';
xchr[@'72]:=':';
xchr[@'73]:=';';
xchr[@'74]:='<';
xchr[@'75]:='=';
xchr[@'76]:='>';
xchr[@'77]:='?';@/
xchr[@'100]:='@@';
xchr[@'101]:='A';
xchr[@'102]:='B';
xchr[@'103]:='C';
xchr[@'104]:='D';
xchr[@'105]:='E';
xchr[@'106]:='F';
xchr[@'107]:='G';@/
xchr[@'110]:='H';
xchr[@'111]:='I';
xchr[@'112]:='J';
xchr[@'113]:='K';
xchr[@'114]:='L';
xchr[@'115]:='M';
xchr[@'116]:='N';
xchr[@'117]:='O';@/
xchr[@'120]:='P';
xchr[@'121]:='Q';
xchr[@'122]:='R';
xchr[@'123]:='S';
xchr[@'124]:='T';
xchr[@'125]:='U';
xchr[@'126]:='V';
xchr[@'127]:='W';@/
xchr[@'130]:='X';
xchr[@'131]:='Y';
xchr[@'132]:='Z';
xchr[@'133]:='[';
xchr[@'134]:='\';
xchr[@'135]:=']';
xchr[@'136]:='^';
xchr[@'137]:='_';@/
xchr[@'140]:='`';
xchr[@'141]:='a';
xchr[@'142]:='b';
xchr[@'143]:='c';
xchr[@'144]:='d';
xchr[@'145]:='e';
xchr[@'146]:='f';
xchr[@'147]:='g';@/
xchr[@'150]:='h';
xchr[@'151]:='i';
xchr[@'152]:='j';
xchr[@'153]:='k';
xchr[@'154]:='l';
xchr[@'155]:='m';
xchr[@'156]:='n';
xchr[@'157]:='o';@/
xchr[@'160]:='p';
xchr[@'161]:='q';
xchr[@'162]:='r';
xchr[@'163]:='s';
xchr[@'164]:='t';
xchr[@'165]:='u';
xchr[@'166]:='v';
xchr[@'167]:='w';@/
xchr[@'170]:='x';
xchr[@'171]:='y';
xchr[@'172]:='z';
xchr[@'173]:='{';
xchr[@'174]:='|';
xchr[@'175]:='}';
xchr[@'176]:='~';
for i:=@'177 to 255 do xchr[i]:='?';

@ The following system-independent code makes the |xord| array contain a
suitable inverse to the information in |xchr|.

@<Set init...@>=
for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
for i:=" " to "~" do xord[xchr[i]]:=i;

@* Device-independent file format.
Before we get into the details of \.{DVI2LGP}, we need to know exactly
what \.{DVI} files are. The form of such files was designed by David R.
Fuchs in 1979. Almost any reasonable device can be driven by a program
that takes \.{DVI} files as input, and dozens of such \.{DVI}-to-whatever
programs have been written. Thus, it is possible to print the output of
document compilers like \TeX\ on many different kinds of equipment.

A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
series of commands in a machine-like language. The first byte of each command
is the operation code, and this code is followed by zero or more bytes
that provide parameters to the command. The parameters themselves may consist
of several consecutive bytes; for example, the `|set_rule|' command has two
parameters, each of which is four bytes long. Parameters are usually
regarded as nonnegative integers; but four-byte-long parameters,
and shorter parameters that denote distances, can be
either positive or negative. Such parameters are given in two's complement
notation. For example, a two-byte-long distance parameter has a value between
$-2^{15}$ and $2^{15}-1$.
@.DVI {\rm files}@>

A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
|pre| command, with its parameters that define the dimensions used in the
file; this must come first.  Each ``page'' consists of a |bop| command,
followed by any number of other commands that tell where characters are to
be placed on a physical page, followed by an |eop| command. The pages
appear in the order that they were generated, not in any particular
numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
(which are allowed between any two commands in the file), each |eop|
command is immediately followed by a |bop| command, or by a |post|
command; in the latter case, there are no more pages in the file, and the
remaining bytes form the postamble.  Further details about the postamble
will be explained later.

Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
quantities that give the location number of some other byte in the file;
the first byte is number~0, then comes number~1, and so on. For example,
one of the parameters of a |bop| command points to the previous |bop|;
this makes it feasible to read the pages in backwards order, in case the
results are being directed to a device that stacks its output face up.
Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
first page occupies bytes 100 to 999, say, and if the second
page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)

@ The \.{DVI} format is intended to be both compact and easily interpreted
by a machine. Compactness is achieved by making most of the information
implicit instead of explicit; when a \.{DVI}-reading program reads the
commands for a page, it keeps track of several quantities: (a)~The current
font |f| is an integer; this value is changed only
by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
is given by two numbers called the horizontal and vertical coordinates,
|h| and |v|. Both coordinates are zero at the upper left corner of the page;
moving to the right corresponds to increasing the horizontal coordinate, and
moving down corresponds to increasing the vertical coordinate. Thus, the
coordinates are essentially Cartesian, except that vertical directions are
flipped; the Cartesian version of |(h,v)| would be |(h,-v)|.  (c)~The
current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
are used for vertical spacing. (d)~There is a stack containing
|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
change the current level of operation. Note that the current font~|f| is
not pushed and popped; the stack contains only information about
positioning.

The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
to 32 bits, including the sign. Since they represent physical distances,
there is a small unit of measurement such that increasing |h| by~1 means
moving a certain tiny distance to the right. The actual unit of
measurement is variable, as explained below.

@ Here is list of all the commands that may appear in a \.{DVI} file. With
each command we give its symbolic name (e.g., |bop|), its opcode byte
(e.g., 129), and its parameters (if any). The parameters are followed
by a bracketed number telling how many bytes they occupy; for example,
`|p[4]|' means that parameter |p| is four bytes long.

\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
such that the reference point of the character is at |(h,v)|. Then
increase |h| by the width of that character. Note that a character may
have zero or negative width, so one cannot be sure that |h| will advance
after this command; but |h| usually does increase.

\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
Do the operations of |set_char_0|, but use the appropriate character number
instead of character~0.

\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
number~|c| is typeset. \TeX82 uses this command for characters in the
range |128<=c<256|.

\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that~|c| is two
bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
command, which is intended for processors that deal with oriental languages;
but \.{DVI2LGP} may eventually allow character codes greater than 255,
assuming that they all have the same width as character 256.
@^oriental characters@>@^Chinese characters@>@^Japanese characters@>

\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that~|c| is three
bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
language has this many characters, but this command might prove useful
in some yet unforeseen way.

\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that~|c| is four
bytes long, possibly even negative. Imagine that.

\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
that if |b<0|, the value of |h| will decrease even though nothing else happens.
Programs that typeset from \.{DVI} files should be careful to make the rules
line up carefully with digitized characters, as explained in connection with
the |rule_pixels| subroutine below.

\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
such that the reference point of the character is at |(h,v)|. (The `put'
commands are exactly like the `set' commands, except that they simply put out a
character or a rule without moving the reference point afterwards.)

\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.

\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.

\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.

\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
|h| is not changed.

\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
may occur between \.{DVI} commands, but a |nop| cannot be inserted between
a command and its parameters or between two parameters.

\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ \dots $c_9[4]$ $p[4]$. Beginning
of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
the current font |f| to an undefined value.  The ten $c_i$ parameters can
be used to identify pages, if a user wants to print only part of a \.{DVI}
file; \TeX82 gives them the values of \.{\\count0} \dots \.{\\count9}
at the time \.{\\shipout} was invoked for this page.  The parameter |p|
points to the previous |bop| command in the file, where the first |bop|
has $p=-1$.

\yskip\hang|eop| 140.  End of page: Print what you have read since the
previous |bop|. At this point the stack should be empty. The \.{DVI}-reading
programs that drive most output devices will have kept a buffer of the
material that appears on the page that has just ended. This material is
largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
|h|~coordinate; so it usually needs to be sorted into some order that is
appropriate for the device in question. \.{DVItype} does not do such sorting,
nor does \.{DVI2LGP}.  Since the \.{LGP} device paints an entire page at
once, it can skip this tedious step.

\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
top of the stack; do not change any of these values. Note that |f| is
not pushed.

\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
of pushes, since it would be highly embarrassing if the stack were empty
at the time of a |pop| command.

\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
The parameter is a signed number in two's complement notation, |-128<=b<128|;
if |b<0|, the reference point actually moves left.

\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
two-byte quantity in the range |-32768<=b<32768|.

\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
this parameterless command will usually suffice, because the same kind of motion
will occur several times in succession; the following commands explain how
|w| gets particular values.

\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
signed quantity in two's complement notation, |-128<=b<128|. This command
changes the current |w|~spacing and moves right by |b|.

\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
parameter, |-32768<=b<32768|.

\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
commands are like the `|w|' commands except that they involve |x| instead
of |w|.

\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
signed quantity in two's complement notation, |-128<=b<128|. This command
changes the current |x|~spacing and moves right by |b|.

\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
parameter, |-32768<=b<32768|.

\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.

\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.

\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
The parameter is a signed number in two's complement notation, |-128<=a<128|;
if |a<0|, the reference point actually moves up.

\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
two-byte quantity in the range |-32768<=a<32768|.

\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
this parameterless command will usually suffice, because the same kind of motion
will occur several times in succession; the following commands explain how
|y| gets particular values.

\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
signed quantity in two's complement notation, |-128<=a<128|. This command
changes the current |y|~spacing and moves down by |a|.

\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
parameter, |-32768<=a<32768|.

\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
are like the `|y|' commands except that they involve |z| instead of |y|.

\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
signed quantity in two's complement notation, |-128<=a<128|. This command
changes the current |z|~spacing and moves down by |a|.

\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
parameter, |-32768<=a<32768|.

\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.

\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.

\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
defined by a \\{fnt\_def} instruction, as explained below.

\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
|f:=1|, \dots, |f:=63|, respectively.

\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
numbers in the range |64<=k<256|.

\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that~|k| is two
bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
command, but large font numbers may prove useful for specifications of
color or texture, or they may be used for special fonts that have fixed
numbers in some external coding scheme.

\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that~|k| is three
bytes long, so it can be as large as $2^{24}-1$.

\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that~|k| is four
bytes long; this is for the really big font numbers (and for the negative ones).

\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
programs are being used. \TeX82 generates |xxx1| when a normal \.{\\special}
appears, setting |k| to the number of bytes being sent. It is recommended that
|x| be a string having the form of a keyword followed by possible parameters
relevant to that keyword.

\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.

\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.

\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.

\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<63|; font definitions will be explained shortly.

\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<65536|.

\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |0<=k<@t$2^{24}$@>|.

\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{30}$@>|.

\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
Beginning of the preamble; this must come at the very beginning of the
file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.

\yskip\hang|post| 248. Beginning of the postamble, see below.

\yskip\hang|post_post| 249. Ending of the postamble, see below.

\yskip\noindent Commands 250--255 are undefined at the present time.

@ @d set_char_0=0 {typeset character 0 and move right}
@d set1=128 {typeset a character and move right}
@d set_rule=132 {typeset a rule and move right}
@d put1=133 {typeset a character}
@d put_rule=137 {typeset a rule}
@d nop=138 {no operation}
@d bop=139 {beginning of page}
@d eop=140 {ending of page}
@d push=141 {save the current positions}
@d pop=142 {restore previous positions}
@d right1=143 {move right}
@d w0=147 {move right by |w|}
@d w1=148 {move right and set |w|}
@d x0=152 {move right by |x|}
@d x1=153 {move right and set |x|}
@d down1=157 {move down}
@d y0=161 {move down by |y|}
@d y1=162 {move down and set |y|}
@d z0=166 {move down by |z|}
@d z1=167 {move down and set |z|}
@d fnt_num_0=171 {set current font to 0}
@d fnt1=235 {set current font}
@d xxx1=239 {extension to \.{DVI} primitives}
@d xxx4=242 {potentially long extension to \.{DVI} primitives}
@d fnt_def1=243 {define the meaning of a font number}
@d pre=247 {preamble}
@d post=248 {postamble beginning}
@d post_post=249 {postamble ending}
@d undefined_commands==250,251,252,253,254,255

@ The preamble contains basic information about the file as a whole. As
stated above, there are six parameters:
$$\hbox{|i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.}$$
The |i| byte identifies \.{DVI} format; currently this byte is always set
to~2. (Some day we will set |i=3|, when \.{DVI} format makes another
incompatible change---perhaps in 1992.)

The next two parameters, |num| and |den|, are positive integers that define
the units of measurement; they are the numerator and denominator of a
fraction by which all dimensions in the \.{DVI} file could be multiplied
in order to get lengths in units of $10^{-7}$ meters. Since there are
72.27 points per inch and 2.54 centimeters per inch, and since \TeX82
works with scaled points where there are $2^{16}$ sp in a point, \TeX82
sets |num=25400000| and $|den|=7227\cdot2^{16}=473628672$.
@^sp@>

The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
desired magnification. The actual fraction by which dimensions are
multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
does not call for any `\.{true}' dimensions, and if you change it only by
specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
creates will be completely unchanged except for the value of |mag| in the
preamble and postamble. (Fancy \.{DVI}-reading programs allow users to
override the |mag|~setting when a \.{DVI} file is being printed.)

Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
interpreted further. The length of comment |x| is |k|, where |0<=k<256|.

@d id_byte=2 {identifies the kind of \.{DVI} files described here}

@ Font definitions for a given font number |k| contain further parameters
$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
The four-byte value |c| is the check sum that \TeX\ (or whatever program
generated the \.{DVI} file) found in the \.{TFM} file for this font;
|c| should match the check sum of the font found by programs that read
this \.{DVI} file.
@^check sum@>

Parameter |s| contains a fixed-point scale factor that is applied to the
character widths in font |k|; font dimensions in \.{TFM} files and other
font files are relative to this quantity, which is always positive and
less than $2^{27}$. It is given in the same units as the other dimensions
of the \.{DVI} file.  Parameter |d| is similar to |s|; it is the ``design
size,'' and it is given in \.{DVI} units that have not been corrected for
the magnification~|mag| found in the preamble.  Thus, font |k| is to be
used at $|mag|\cdot s/1000d$ times its normal size.

The remaining part of a font definition gives the external name of the font,
which is an ascii string of length |a+l|. The number |a| is the length
of the ``area'' or directory, and |l| is the length of the font name itself;
the standard local system font area is supposed to be used when |a=0|.
The |n| field contains the area in its first |a| bytes.

Font definitions must appear before the first use of a particular font number.
Once font |k| is defined, it must not be defined again; however, we
shall see below that font definitions appear in the postamble as well as
in the pages, so in this sense each font number is defined exactly twice,
if at all. Like |nop| commands, font definitions can appear before the first
|bop|, or between an |eop| and a |bop|.

@ The last page in a \.{DVI} file is followed by `|post|'; this command
introduces the postamble, which summarizes important facts that \TeX\ has
accumulated about the file, making it possible to print subsets of the data
with reasonable efficiency. The postamble has the form
$$\vbox{\halign{\hbox{#\hfil}\cr
  |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
  $\langle\,$font definitions$\,\rangle$\cr
  |post_post| |q[4]| |i[1]| 223's|[>=4]|\cr}}$$
Here |p| is a pointer to the final |bop| in the file. The next three
parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
appeared in the preamble.

Parameters |l| and |u| give respectively the height-plus-depth of the tallest
page and the width of the widest page, in the same units as other dimensions
of the file. These numbers might be used by a \.{DVI}-reading program to
position individual ``pages'' on large sheets of film or paper.

Parameter |s| is the maximum stack depth (i.e., the largest excess of
|push| commands over |pop| commands) needed to process this file. Then
comes |t|, the total number of pages (|bop| commands) present.

The postamble continues with font definitions, which are any number of
\\{fnt\_def} commands as described above, possibly interspersed with |nop|
commands. Each font number that is used in the \.{DVI} file must be defined
exactly twice: Once before it is first selected by a \\{fnt} command, and once
in the postamble.

@ The last part of the postamble, following the |post_post| byte that
signifies the end of the font definitions, contains |q|, a pointer to the
|post| command that started the postamble.  An identification byte, |i|,
comes nest; this currently equals~2, as in the preamble.

The |i| byte is followed by four or more bytes that are all equal to
the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
these trailing bytes, until the total length of the file is a multiple of
four bytes, since this works out best on machines that pack four bytes per
word; but any number of 223's is allowed, as long as there are at least four
of them. In effect, 223 is a sort of signature that is added at the very end.
@^Fuchs, David Raymond@>

This curious way to finish off a \.{DVI} file makes it feasible for
\.{DVI}-reading programs to find the postamble first, on most computers,
even though \TeX\ wants to write the postamble last. Most operating
systems permit random access to individual words or bytes of a file, so
the \.{DVI} reader can start at the end and skip backwards over the 223's
until finding the identification byte. Then it can back up four bytes, read
|q|, and move to byte |q| of the file. This byte should, of course,
contain the value 248 (|post|); now the postamble can be read, so the
\.{DVI} reader discovers all the information needed for typesetting the
pages. Note that it is also possible to skip through the \.{DVI} file at
reasonably high speed to locate a particular page, if that proves
desirable. This saves a lot of time, since \.{DVI} files used in production
jobs tend to be large.


@* Input from and output to binary files.
We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes
appear physically in what is called a `|packed file of 0..255|'
in \PASCAL\ lingo.

Packing is system dependent, and many \PASCAL\ systems fail to implement
such files in a sensible way (at least, from the viewpoint of producing
good production software).  For example, some systems treat all
byte-oriented files as text, looking for end-of-line marks and such
things. Therefore some system-dependent code is often needed to deal with
binary files, even though most of the program in this section of
\.{DVI2LGP} is written in standard \PASCAL.
@^system dependencies@>

One common way to solve the problem is to consider files of |integer|
numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
a sequence of four bytes $(a,b,c,d)$ using the following code, which
avoids the controversial integer division of negative numbers:
$$\vbox{\halign{#\hfil\cr
|if x>=0 then a:=x div @'100000000|\cr
|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
\quad|end|\cr
|x:=x mod @'100000000;|\cr
|b:=x div @'200000; x:=x mod @'200000;|\cr
|c:=x div @'400; d:=x mod @'400;|\cr}}$$
The four bytes are then kept in a buffer and output one by one. (On 36-bit
computers, an additional division by 16 is necessary at the beginning.
Another way to separate an integer into four bytes is to use/abuse
\PASCAL's variant records, storing an integer and retrieving bytes that are
packed in the same place; {\sl caveat implementor!\/}) It is also desirable
in some cases to read a hundred or so integers at a time, maintaining a
larger buffer.

We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
even if such simplicity is sometimes unrealistic.

@<Types...@>=
@!eight_bits=0..255; {unsigned one-byte quantity}
@!byte_file=packed file of eight_bits; {files that contain binary data}

@ The program deals with three binary file variables: |dvi_file| is the main
input file that we are translating and |lgp_file|
is the output file in \.{LGP} preprocessed ``native-mode''
format.  The font files, which we will describe later, are elements in
an array |pxl_files|, which allows us access to several of the most active
fonts at the same time.  

@<Glob...@>=
@!dvi_file:byte_file; {the stuff we are printing}
@!lgp_file:byte_file; {a file of \.{LGP} ``native-mode'' commands}

@ To prepare these files for use, we |reset| or |rewrite| them.
On any system which allows a rescan of the command line, the normal
practice will be to give the |dvi_file| argument on that line, and
even the alternative will involve a prompt for the user to supply
a |dvi_file| name.  We use the very mildly non-standard form of
the |reset| statement here to open both the |dvi_file| and the
|lgp_file|. The name of the |lgp_file| is generated from the
essential part (before the extension) of the |dvi_file| name.  The
assumption here, therefore, is that |dvi_name| and |lgp_name| are
set before |open_dvi_file| and |open_lgp_file| are called.

@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file,dvi_name);
cur_loc:=0;
end;
@#
procedure open_lgp_file; {prepares \.{LGP} file for output}
begin rewrite(lgp_file,lgp_name);
end;

@ The global
variable |cur_loc| is the number of the byte about to be read next from
|dvi_file|.

@<Glob...@>=
@!cur_loc:integer; {where we are about to look, in |dvi_file|}

@ We shall use another set of simple functions to read the next byte or
bytes from |dvi_file|. There are seven possibilities, each of which is
treated as a separate function in order to minimize the overhead for
subroutine calls.
@^system dependencies@>

@p function get_byte:integer; {returns the next byte, unsigned}
var b:eight_bits;
begin if eof(dvi_file) then get_byte:=0
else  begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
  end;
end;
@#
function signed_byte:integer; {returns the next byte, signed}
var b:eight_bits;
begin read(dvi_file,b); incr(cur_loc);
if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
end;
@#
function get_two_bytes:integer; {returns the next two bytes, unsigned}
var a,@!b:eight_bits;
begin read(dvi_file,a); read(dvi_file,b);
cur_loc:=cur_loc+2;
get_two_bytes:=a*256+b;
end;
@#
function signed_pair:integer; {returns the next two bytes, signed}
var a,@!b:eight_bits;
begin read(dvi_file,a); read(dvi_file,b);
cur_loc:=cur_loc+2;
if a<128 then signed_pair:=a*256+b
else signed_pair:=(a-256)*256+b;
end;
@#
function get_three_bytes:integer; {returns the next three bytes, unsigned}
var a,@!b,@!c:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
cur_loc:=cur_loc+3;
get_three_bytes:=(a*256+b)*256+c;
end;
@#
function signed_trio:integer; {returns the next three bytes, signed}
var a,@!b,@!c:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
cur_loc:=cur_loc+3;
if a<128 then signed_trio:=(a*256+b)*256+c
else signed_trio:=((a-256)*256+b)*256+c;
end;
@#
function signed_quad:integer; {returns the next four bytes, signed}
var a,@!b,@!c,@!d:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
cur_loc:=cur_loc+4;
if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
end;

@ Finally we come to the routines that assume the \PASCAL\ used for this
program has the non-standard extension for random reading of files.
The driver program below needs two such routines: |dvi_length| should
compute the total number of bytes in |dvi_file|, possibly also
causing |eof(dvi_file)| to be true; and |move_to_byte(n)|
should position |dvi_file| so that the next |get_byte| will read byte |n|,
starting with |n=0| for the first byte in the file.
@^system dependencies@>

Such routines are, of course, highly system dependent. They are implemented
here in terms of two assumed system routines called |set_pos| and |cur_pos|.
The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
negative or larger than the total number of items in |f|; in the latter
case, |set_pos(f,n)| moves to the end of file |f|.
The call |cur_pos(f)| gives the total number of items in |f|, if
|eof(f)| is true; we use |cur_pos| only in such a situation.

@p function dvi_length:integer;
begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
end;
@#
procedure move_to_byte(n:integer);
begin set_pos(dvi_file,n); cur_loc:=n;
end;

@ The attempt to output |eight_bits| bytes is virtually certain to
require some sort of system-dependent code.  What we show here is far too
simple-minded to work on any computer system we have access to, and
it is intended primarily to provide a place for the appropriate
routine in a \.{WEB} change file.

@p procedure output_byte(b:integer);

begin
 if b<0 then b:=b+@'400; {for an |eight_bits| value, remove sign}
 write(lgp_file,b);
end;
@^system dependencies@>

\f

@* Reading the font information.
\.{DVI} file format does not include information about character widths, since
that would tend to make the files a lot longer. But this program must know
everything about the character, even the pattern of the bit-map used
to paint it on the paper. \.{DVI2LGP} gets this information from
\.{PXL} files.

Full details about the organization of \.{PXL}
files can be found in \\{TUGboat}, Volume 2 No. 3, pages 8-12.
The elements of the Font Directory that most concern us here are
the |tfm_width|s, which are recorded as the
fourth word in a four word item associated with each character in 
the font.

@ The following two routines are exactly analogous in their operation
with the |dvi_file| routines |dvi_length| and |move_to_byte|, but
they work on an element in an array of files, and it is simpler
in every way to declare them as distinct functions.

@p function get_pxl_length:integer;
begin set_pos(pxl_files[active_font],-1); 
get_pxl_length:=cur_pos(pxl_files[active_font]);
end;
@#
procedure pxl_move_to_byte(n:integer);
begin set_pos(pxl_files[active_font],n);
end;
@^system dependencies@>

@ Here is the array and its index.

@<Glob...@>=
@!pxl_files:array [1..max_cached_fonts] of byte_file;
@!active_font:integer; {index into this array}

@ When we are inputting from \.{PXL} files,
it is usually preferable to read four bytes at a time. 
The input goes into global variables
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
the fourth. We also need |cur_name|, a string variable that will be set
to the current \.{PXL} font file name before each of the elements in the 
|pxl_files| array is opened or |reset|.


@<Glob...@>=
@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
@!cur_name:packed array[1..name_length] of char; {external name,
  with no lower case letters}

@ The |read_pxl_bytes| procedure sets |b0| through |b3| to the next
four bytes in the current \.{PXL} file.
@^system dependencies@>

@p procedure read_pxl_bytes;
begin read(pxl_files[active_font],b0); read(pxl_files[active_font],b1);
read(pxl_files[active_font],b2); read(pxl_files[active_font],b3);
end;

@ Here is a routine to read a full word of data from |pxl_files|
without any system dependent problems.

@p procedure get_pxl_word(var a:integer);
begin
  read_pxl_bytes;
  if b0 < 128 then a:=((b0*256+b1)*256+b2)*256+b3
  else a:=(((b0-256)*256+b1)*256+b2)*256+b3;
end;

@ For purposes of this program, we need to know several things about a
given character |c| in a given font |f|: (1)~Is |c| a legal character
in~|f|? (2)~What are the dimensions of the rectangular character
envelope of |c|? (3)~What offsets are needed to get to the top
left (or, in rotated fonts, the top right) pixel position in the
character envelope? (4)~What is the relative |tfm_width| of |c|---that
is, how wide is this character relative to the EM-square design width
of the font? We also need to know the
symbolic name of each font, so it can be printed out, and we need to
compute a value which we will call |font_space| (for which, see the
index).  At the time when each character is first encountered, we
will also need to have access to its bit-map.

The answers to these questions appear implicitly in the following data
structures. The current number of known fonts is |nf|. Each known font has
an internal number |f|, where |0<=f<nf|; the external number of this font,
i.e., its font identification number in the \.{DVI} file, is
|font_num[f]|, and the external name of this font is the string that
occupies positions |font_name[f]| through |font_name[f+1]-1| of the array
|names|. The latter array consists of |ascii_code| characters, and
|font_name[nf]| is its first unoccupied position.  A horizontal motion
less than |font_space[f]| will be treated as a `kern' that is not
indicated in the printouts that \.{DVI2LGP} produces between brackets
when it is printing symbols for a diagnostic run.

There are always 128 character positions in
the current form of a \.{PXL} font, and even
a null character could have a width (though we do not use this feature
in normal practice).  In practice, most fonts are quite full, so there
is no point in trying to save space by closing up the zero-width
entries for null characters.


@d invalid_width==@'17777777777
@d pxl_mark = 1001

@<Glob...@>=
@!font_num:array [0..max_fonts] of integer; {external font numbers}
@!font_name:array [0..max_fonts] of 0..name_size; {starting positions
  of external font names}
@!names:array [0..name_size] of ascii_code; {characters of names}
@!font_check_sum:array [0..max_fonts] of integer; {check sums}
@!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
@!font_design_size:array [0..max_fonts] of integer; {design sizes}
@!font_mag:array [0..max_fonts] of integer; {product of overall and
``at size'' values}
@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
  and ``large'' spaces}
@!nf:0..max_fonts; {the number of known fonts}

@ @<Set init...@>=
nf:=0; font_name[0]:=0; font_space[0]:=0;

@ It is, of course, a simple matter to print the name of a given font.
|print_font| is the normal routine.  |print_budding_font| is used to print
a font that is in the process of being defined.

@p procedure print_font(@!f:integer); {|f| is an internal font number}
var k:0..name_size; {index into |names|}
begin if f>=nf then print('UNDEFINED!')
@.UNDEFINED@>
else  begin for k:=font_name[f]+2 to font_name[f+1]-1 do
    print(xchr[names[k]]);
  end;
end;

procedure print_budding_font(f:integer); {|f| is an internal font number}
var k:0..name_size; {index into |names|}
begin if f>nf then print('UNDEFINED!')
else  begin for k:=font_name[f]+2 to font_name[f+1]-1 do
    print(xchr[names[k]]);
  end;
end;

@ In the \.{PXL} directory there are four words of data associated
with each valid character:                            
\item{1)}the |envelope|, which gives the exact width 
and height in pixels of each character of the font,   
\item{2)}the |offsets|,
which give the X-offset and Y-offset (in pixels) from the top left
corner of the |envelope| needed to reach the starting reference pixel
of the character (which should be close to the point that \TeX\ would
consider the base-line of that character),
\item{3)}the |raster_ptr| pointer to the first line in an array of
bit-maps provided for each character.  (By the conventions of the current 
style in \.{PXL} files, these are loosely packed, with an integer 
number of words allowed for each line. But only the BigEndian $32$
bits are used.)
\item{4)}the width of this character relative to the width of the EM-square
design size of this font. 
(This value must correspond with the width value in the \.{TFM} file
which \TeX\ associated with this font.)

To save storage, we leave the two values which are represented in
16-bit fields in the 32-bit |envelope| alone.  Since neither of
them can be negative in a well-formed \.{PXL} file, it is easy
to unpack them later, using \&{mod} and \&{div} and we will hope
that our compiler has the \\{nous} to use a \\{shift} operation
when it spots division by a power of 2.

@<Type...@>=

@!font_element = record
 @!envelope:integer; {this is actually two values, but neither can be $<0$}
 @!x_offset,@!y_offset:integer;
 @!raster_ptr:integer; {pointer to top raster ($-1$ if already processed)}
 @!tfm_width:integer;
  {slightly misleading; what goes here is computed from the genuine |tfm_width|}
 @!lgp_width:integer; {computed pixel width}
end;

@ @<Glob...@>= 
@!font_mem: array[0..max_fonts, 0..127] of font_element;

@ An error message array is used to hold specific messages about errors
in PXL input. The global variable |pxl_check_sum| is set to the check
sum that appears in the current \.{PXL} file.

@d error_size = 20
 
@<Glob...@>=
@!error_in_pxl:packed array[1..error_size] of char; {\.{PXL} error messages}
@!pxl_check_sum:integer; {check sum found in |pxl_files|}

@ Here is a procedure that absorbs the necessary information from a
\.{PXL} file, assuming that the file has just been successfully \&{reset}
so that we are ready to read it.  The procedure gives a few simple warnings
if it detects anything amiss in the \.{PXL} data.

@p function in_PXL(z: integer):boolean; {input \.{PXL} data or return |false|}
label 9997, {go here when the format is bad}
  9998,  {go here when the information cannot be loaded}
  9999;  {go here to exit}
var k:integer; {index for loops}
@!alpha, @!beta: integer; {quantities used in the scaling computation}
begin @<Read the \.{PXL ID} in the first word, then jump to the end
of file and check the last five 32-bit values; |goto 9997| if there is
a problem@>;
@<Read the |offsets| and |tfm_width| values@>;
in_PXL:=true; goto 9999;                                   
9997: print_ln('---not loaded, PXL file is bad');print_ln(error_in_pxl);
@.PXL file is bad@>
9998: in_PXL:=false;
9999: end;

@ Here we look at several general parameters for the font, 
so that we can check them against the \.{TFM} values recorded
in the |dvi_file|.

@<Read the \.{PXL ID}...@>=
error_in_pxl:=('PXL id word <> 1001!');
@.PXL id word <> 1001!@>
get_pxl_word(pxl_id);
if pxl_id <> pxl_mark then goto 9997;
pxl_length:=get_pxl_length;
pxl_pos:=pxl_length-20; {five words of four bytes each}
pxl_move_to_byte(pxl_pos); {we count on the implied \&{get} here}
get_pxl_word(pxl_check_sum);
if pxl_check_sum = 0 then print_ln('PXL has 0 check-sum!');
@.PXL has 0 check-sum!@>
error_in_pxl:=('Mag. DVI units <= 0!');
@.Mag. DVI units <= 0!@>
get_pxl_word(pxl_mag_val);
if pxl_mag_val <= 0 then goto 9997
else begin error_in_pxl:=('Design DVI units<=0!');
@.Design DVI units<=0!@>
  get_pxl_word(pxl_design_size); end;
if pxl_design_size <= 0 then goto 9997
else begin error_in_pxl:=('No directory pointer');
@.No directory pointer@>
get_pxl_word(pxl_directory_ptr); end;
if pxl_directory_ptr <> (pxl_length div 4)-517 then begin
print('Directory pointer should be ', (pxl_length div 4)-517:1);
@.Directory pointer should be@>
print_ln(', but file gives ', pxl_directory_ptr:1);
goto 9997; end;
pxl_directory_ptr:=pxl_directory_ptr * 4; {adjust for |byte_file|}
error_in_pxl:=('No PXL EOF!  Chaos! ');
@.No PXL EOF!  Chaos! @>
get_pxl_word(pxl_id); if not eof(pxl_files[active_font]) then goto 9997;
if pxl_id <> pxl_mark then                     
print_ln('Bad final PXL ID word.  Odd but trivial.')
@.Bad final PXL ID word@>
@^system dependencies@>

@ There are a number of identifiers in that
last module which haven't yet been provided for, so\dots

@<Glob...@>=
@!pxl_id:integer;
@!pxl_length:integer;
@!pxl_pos:integer; {for direct access reading of |pxl_files|}
@!pxl_mag_val,@!pxl_design_size:integer; {in DVI units}
@!pxl_directory_ptr:integer;

@ The first really useful part of |in_PXL| is setting up the
directory table of |offsets|, |raster_ptr|s and |tfm_width|s.
We also calculate the |lgp_width| (in pixels) of each character,
by applying the |conv| value to the |tfm_width|.

@d half_shift==@'200000 {to place a value in upper half-word of 32-bit word}

@<Read the |offsets|...@>=
pxl_pos:=pxl_directory_ptr;
pxl_move_to_byte(pxl_pos); {remember the implied \&{get} here}
for k:=0 to 127 do begin
  with font_mem[nf,k] do begin
    get_pxl_word(envelope);
    read_pxl_bytes;
    x_offset:=(b0 * 256) + b1;
    y_offset:=(b2 * 256) + b3;
    if x_offset>@'77777 then x_offset:=x_offset-@'200000;  {extend sign}
    if y_offset>@'77777 then y_offset:=y_offset-@'200000;  {extend sign}
    get_pxl_word(raster_ptr); {the top line pointer value}
    raster_ptr:=raster_ptr*4; {adjust for |byte_file|}
    @<Read and convert the |tfm_width| values@>;
    lgp_width:=trunc((conv*tfm_width) + 0.5)
    end;
  end
@^system dependencies@>

@ The most critical part of |in_PXL| is the width computation, which
involves multiplying the relative |tfm_width|s in the \.{PXL} file by the
scaling factor in the \.{DVI} file. This fixed-point multiplication
must be done with precisely the same accuracy by all \.{DVI}-reading programs,
in order to validate the assumptions made by \.{DVI}-writing programs
like \TeX82.

Let us therefore summarize what needs to be done. Each width in a \.{TFM}
file represents a real quantity, indicating what fraction each valid
character's width is of the |font_scaled_size| (which        
may $=$ |font_design_size|) and which, in its turn, gives the \.{EM-square}
width, in \.{DVI} units, of the font in the current font
definition. To avoid the inaccuracies caused by different system
implementations of the \&{real} type, each of these real values
appears as a four-byte quantity called a |fix_word|.  A |fix_word|
whose respective bytes are $(a,b,c,d)$ represents the number
$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
dimension must be less than 16.)  We want to multiply this quantity by the
integer~|z|, which is known to be less than $2^{27}$. Let $\alpha=16z$.
If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
16, to obtain a multiplier less than $2^{23}$, and we can compensate for
this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
$\beta=2^{4-e}$; we shall compute
$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
or the same quantity minus $\alpha$ if $a=255$.  This calculation must be
done exactly, for the reasons stated above; the following program does the
job in a system-independent way, assuming that arithmetic is exact on
numbers less than $2^{31}$ in magnitude.

We have adapted this module from the code provided in
an early release of \.{DVItype}, taking care to maintain precisely 
the same level of precision. Note particularly how the multiplications 
proceed from the low-order to the high-order bytes of the value, moving 
byte by byte, which ensures against overflow, and how the least significant
bits are removed by integer division before they can affect the value in
the more significant bytes.
 
@<Read and convert...@>=
@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
  begin error_in_pxl:='Bad width--byte0 bad';
  read_pxl_bytes;
  tfm_width:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
  if b0>0 then if b0<255 then goto 9997
    else tfm_width:=tfm_width-alpha;
  end

@ @<Replace |z|...@>=
begin alpha:=16*z; beta:=16;
while z>=@'40000000 do
  begin z:=z div 2; beta:=beta div 2;
  end;
end

\f

@* Font caching by history of use.
Truly ``optimal'' systems of caching require a system of reference to 
future needs, as described in the Stanford University Computer Science
Report {\mc STAN-CS-82-901}. \\{Optimal Font Caching} by David Fuchs and
Donald Knuth. (March, 1982), but a reference to past history 
is a pretty good second choice, particularly when the output device
has a very large internal memory for the storage of font characters. 
This system maintains a linked queue,  
to which each font is added until the queue is full.  Each time a font 
that is already in the queue is |referenced| again, it is put down at 
the end of the queue. If the queue is full, and a new font must be   
added, the font at the head of the queue is removed to make room for it.
Any font which does not get |referenced| frequently is likely to 
float to the head of the queue, and any font which is often |referenced| 
is likely to spend most of its time at or near the end of the queue, where
it will not risk being bumped off. Alternatively, if a font has been very
extensively used, it will eventually have contributed every needed character
to the internal font memory of the \.{LGP}, and there will be no further
reason for it to be |referenced|.

@ @<Type...@>=
@!cache_pointer=^cached_font;
@!cached_font=record
 @!cache_number:1..max_cached_fonts;
  @!font_ptr:0..max_fonts;
  @!c_link:cache_pointer;
 end;


@ @<Glob...@>= 
@!font_cache_list:cache_pointer; {list of open fonts}

@ And now for the procedure that does all this.

@d append_it=99
 
@p function open_font(this_font:integer):integer;
 
label append_it;
var @!cfn:integer; {temporary for cached font index number}
   @!fm:integer; {gets |font_mag| value for |this_font|}
   @!a:integer; {where |ascii_code| created from |font_mag| value}
   @!r:integer; {index into |cur_name|}
   @!k:integer; {index into |names|}
   @!p_f,@!q_f,@!referenced:cache_pointer;
begin
  p_f:=font_cache_list; q_f:=p_f; referenced:=nil; cfn:=0;
  @<Set |q_f| to be the last element of the queue. If we find the
  entry for |this_font| along the way, put it in |referenced|.@>;

  if referenced<>nil then begin p_f:=referenced; goto append_it
  end;

  @<If there is still room, add to the queue, otherwise, bump an old
 entry off the head, so that its |cache_number| can be reused.@>;

  @<Move font name for |p_f| into the |cur_name| string and open the font file
 as one of the elements of the |pxl_files| array.@>;
  @<Append |p_f| to the end of the queue.@>;
end;

@ We begin by searching along the queue to find the end. If we find that 
our new font has already been |referenced|, we remove it from its 
current position in the queue for replacement at the end of the queue.
Thus, the more frequently a font has been used, the more likely it
is to be near the end of the queue, and the less likely it is to
be bumped off the head of the queue by a newcomer.
 
@<Set |q_f| to be...@>=
 while p_f<>nil do
  if p_f^.font_ptr=this_font then begin {dequeue |p|}
    referenced:=p_f;
    if p_f=font_cache_list then begin {was it at head of queue?}
      font_cache_list:=p_f^.c_link; {if so, bump it out}
      q_f:=p_f^.c_link {set |q_f| to head of queue too}
    end
    else q_f^.c_link:=p_f^.c_link;

    p_f:=p_f^.c_link
  end
  else begin q_f:=p_f; p_f:=p_f^.c_link; cfn:=cfn+1 {through the queue}
  end

 
@ @<If there is still room...@>=
 if cfn<max_cached_fonts then begin
  new(p_f);
  p_f^.cache_number:=cfn+1 {set up a new |cache_number|}
 end
 else begin
  p_f:=font_cache_list; {we want to reuse this |cache_number|}
  font_cache_list:=font_cache_list^.c_link {bump out font at head of list}
 end

@ An extension to standard 
\PASCAL\ is needed for file management since we want to associate
each element in an array of files, |pxl_files|,
with external files whose names are specified dynamically (i.e., not
known at compile time). The following code assumes that `|reset(f,s)|'
does this, when |f| is a file variable and |s| is a string variable that
specifies the file name. If |eof(f)| is true immediately after
|reset(f,s)| has acted, we assume that no file named |s| is accessible.

The string |cur_name| is supposed to be set to the external name of the
\.{PXL} or \.{VRL} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the four digits which indicate the magnification applied to
this font when it was created, and to add the suffix `\.{PXL}' 
or \.{VRL} after these.
In most environments, we should also change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.

If |p=0|, i.e., if no font directory has been specified, \.{DVI2LGP}
is supposed to use the default font directory, which is a
system-dependent place where the standard fonts are kept.
The string variable |default_directory| contains the name of this area.
@^system dependencies@>

@d default_directory_name=='TEXFONTS:' {change this to the correct name}
@d default_directory_name_length=9 {change this to the correct length}
@d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''}

@<Glob...@>=
@!default_directory:packed array[1..default_directory_name_length] of char;

@ @<Set init...@>=
default_directory:=default_directory_name;

@^system dependencies@>

@ Now for the real work of opening the font file.

@<Move font name...@>=
for k:=1 to name_length do cur_name[k]:=' ';
p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1];
if p=0 then
  begin for k:=1 to default_directory_name_length do
    cur_name[k]:=default_directory[k];
  r:=default_directory_name_length;
  end
else r:=0;
for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do
  begin incr(r);
  if r+4>name_length then
    abort('DVI2LGP capacity exceeded (max font name length=',
      name_length:1,')!');
@.DVI2LGP capacity exceeded...@>
  if (names[k]>="a")and(names[k]<="z") then
    cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}}
  else cur_name[r]:=xchr[names[k]]; 
  end;
fm:=font_mag[this_font];
cur_name[r+1]:='.';
if fm > 999 then begin
  a:=(fm div 1000)+ord('0');
  cur_name[r+2]:=xchr[a];
  fm:=fm mod 1000; 
end else decr(r); {for diminished fonts such as \.{999PXL}}
a:=(fm div 100)+ord('0');
cur_name[r+3]:=xchr[a];
fm:=fm mod 100; 
a:=(fm div 10)+ord('0');
cur_name[r+4]:=xchr[a];
fm:=fm mod 10;
a:=fm+ord('0');
cur_name[r+5]:=xchr[a];
cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x");
cur_name[r+8]:=system_case_of("l");
if not take_defaults then
begin
  if in_postamble then print(' == ') else
   if out_mode>errors_only then print(' requeue ');
@.requeue (a previously queued font)@>
  if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin
     for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' ');
     end;
end;
reset(pxl_files[p_f^.cache_number],cur_name);
p_f^.font_ptr:=this_font
@^system dependencies@>

@ When we reach this point, we know that |p_f| is either a new addition
to the font cache, or it is a previously |referenced| font, which has
been removed from its old position in the list.
 
@<Append |p_f| to the...@>= 
append_it:
 if q_f=nil then {This does the first font of all. It only happens once.}
   font_cache_list:=p_f
 else
   q_f^.c_link:=p_f; {|q_f| has been set to the end of the queue.}
 p_f^.c_link:=nil; {Show that |this_font| is the new end-of-queue} 
 open_font:=p_f^.cache_number {return index into |pxl_files| array}

@* Defining fonts.
\.{DVI2LGP} reads the postamble first and loads
all of the fonts defined there; then it processes the pages. In this
case, a \\{fnt\_def} command should match a previous definition if and only
if the \\{fnt\_def} being processed is not in the postamble.

A global variable |in_postamble| is provided to tell whether we are
processing the postamble or not.

@<Glob...@>=
@!in_postamble:boolean; {are we reading the postamble?}

@ @<Set init...@>=
in_postamble:=false;

@ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property
list in octal notation, and it is helpful to be able to check
errors against the information provided by \.{TFtoPL}, so if
any |check_sum| discrepancies appear, we print the information 
in octal notation. This is not the same routine as the one in
\.{PLtoTF} because, in this program, the value to be printed
may already have been altered by sign-extension. The value of
|kill_sign| is legitimate in \.{WEB} but may cause trouble in some
environments.
@^system dependencies@>

@d kill_sign==(-@'17777777777-1) {so that we can have an unsigned value}

@p procedure print_octal(@!a:integer); {outputs an integer in octal}
var
@!s:integer; {surrogate sign-bit}
@!i:integer; {what we are working on}
@!j:0..11; {one more than the number of digits of output}
@!dig:array[0..10] of "0".."7"; {eleven digit maximum}
begin print(' O '); {specify octal format}
s:=0;
if a < 0 then begin i:=a + kill_sign; s:= 2; end else i:=a;
for j:=0 to 10 do dig[j]:="0"; j:=0;
while (i>0)or(j=0) do
  begin dig[j]:=(i mod 8) + "0"; i:=i div 8; incr(j);
  end;
if s>0 then begin
  dig[10]:=dig[10] + s; {restore lost ``sign-bit'' if any}
  j:=11; {picks up effects such as \.{O} 20000012345}
end;
repeat decr(j); print(xchr[dig[j]]); until j=0;
end;


@ In \TeX82, Versions 0.9999 and later, the concept of a ``magnification
factor,'' based on factors of $\sqrt2$, was included.
This meant that the \.{.PXL} files started getting odd numbered magnifications
like 2074.  Since these magnifications are really rounded values, we started
failing to find fonts when the 1.2 device magnification factor was applied.
For example, multiplying 2074 by 1.2 gave us 2489 rather than the
proper 2488---a real drag.
This function guesses what the proper magnification is, based on some knowledge
of what the factors that might be generated are.

@p function get_actual_mag(fm:integer): real;
var
   rfm: real;
begin
if fm=1095 then rfm:=1.095445  {stephalf}
else if fm=1315 then rfm:=1.314534  
  {stephalf above step \\{i}, 
    needed for the \.{LGP} with its hidden step \\{i} mag}
else if fm=2074 then rfm:=2.0736  {step \\{iv}}
else if fm=2488 then rfm:=2.48832  {step \\{v}}
else if fm=2986 then rfm:=2.985984  {step \\{vi}}
else rfm:=fm/1000.0;  {the remaining magnifications haven't been a problem}
get_actual_mag:=rfm;
end;

@ The following subroutine does the necessary things when a \\{fnt\_def}
command is being processed.

@p procedure define_font(@!e:integer); {|e| is an external font number}
var f:0..max_fonts;
@!p:integer; {length of the area/directory spec}
@!n:integer; {length of the font name proper}
@!c,@!q,@!d:integer; {check sum, scaled size, and design size}
@!rfm:real; {fudged font magnification as a real number}
@!j,@!k:0..name_size; {indices into |names|}
@!mismatch:boolean; {do names disagree?}
begin if nf=max_fonts then abort('DVI2LGP capacity exceeded (max fonts=',
    max_fonts:1,')!');
@.DVI2LGP capacity exceeded...@>
font_num[nf]:=e; f:=0;
while font_num[f]<>e do incr(f);
@<Read the font parameters into position for font |nf|, and
  print the font name@>;
if in_postamble then
  begin if f<nf then print_ln('---this font was already defined!');
@.this font was already defined@>
  end
else  begin if f=nf then print_ln('---this font wasn''t loaded before!');
@.this font wasn't loaded before@>
  end;
if f=nf then @<Load the \.{PXL} font, unless there are problems@>
else @<Check that the current font definition matches the old one@>;
end;

@ @<Check that the current...@>=
begin if font_check_sum[f]<>c then begin
  print_budding_font(f);
  print_ln('---check sum doesn''t match previous definition!');
@.check sum doesn't match@>
end;
if font_scaled_size[f]<>q then begin
  print_budding_font(f);
  print_ln('---scaled size doesn''t match previous definition!');
@.scaled size doesn't match@>
end;
if font_design_size[f]<>d then begin
  print_budding_font(f);
  print_ln('---design size doesn''t match previous definition!');
@.design size doesn't match@>
end;
j:=font_name[f]; k:=font_name[nf]; mismatch:=false;
while j<font_name[f+1] do
  begin if names[j]<>names[k] then mismatch:=true;
  incr(j); incr(k);
  end;
if k<>font_name[nf+1] then mismatch:=true;
if mismatch then begin
   print_budding_font(f);
   print_ln('---font name doesn''t match previous definition!');
@.font name doesn't match@>
end
end

@ We have to play a little trick here, owing to the fact that
any given font may be \&{reset} more than once in the program.
In order to regenerate the font file name correctly, we must
store the length of the ``area'' string as well as the length
of the basic ``name'' string for the font.  Fortunately, since the
normal contents of the |names| array is a subset of the integers
in any case, we have no difficulty in putting these small integers
in the same array.  

Another, very important trick appears in the evaluation of |font_mag|.
\.{PXL} fonts came into being with the assumption that they were
intended for 200-dot/inch devices, but the \.{LGP} we are preparing
this file for is a 240-dot/inch device.  Therefore, we want \.{1200PXL}
fonts for true size output, and we want a 1.2 magnification over and
above any font magnification specified in the font definition in the
\.{DVI} file.  However, as we discovered in the first run of this program,
we most emphatically do \\{not} want to magnify anything but the font
by this extra factor, so we plug it in quietly to the evaluation of |font_mag|.

Finally, since the \\{magstep} values in |font_scaled_size| are already
rounded as they appear in the \.{DVI} file,
we also have the difficulty that two successive stages of
rounding (one in the \TeX\ processor and one here) can produce undesirable
values and create references to nonexistent fonts.  The |get_actual_mag|
function ``regenerates'' the |real| values corresponding to the
various \\{magstep}s with just enough precision to obviate this twice-rounded
effect.

@<Read the font parameters into position for font |nf|...@>=
c:=signed_quad; font_check_sum[nf]:=c;@/
q:=signed_quad; font_scaled_size[nf]:=q;@/
d:=signed_quad; font_design_size[nf]:=d;@/
rfm:=get_actual_mag(round(((q*1.0)/(d*1.0))*1000));
font_mag[nf]:=round(rfm*get_actual_mag(mag)*1.2*1000);
 {``at size'' magnification * overall magnification--note the $1.2$ for \.{LGP}}
p:=get_byte; n:=get_byte;
if font_name[nf]+n+p+2>name_size then
  abort('DVI2LGP capacity exceeded (name size=',name_size:1,')!');
@.DVI2LGP capacity exceeded...@>
font_name[nf+1]:=font_name[nf]+n+p+2;
if showing then print(': ')
  {when |showing| is true, the font number has already been printed}
else if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then
  print('Font ',e:1,': ');
if n+p=0 then print('null font name!  Number ',e:1)
@.null font name@>
else begin
names[font_name[nf]]:=p; names[font_name[nf]+1]:=n;
for k:=font_name[nf]+2 to font_name[nf+1]-1 do names[k]:=get_byte;
end;
if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then
   print_budding_font(nf); 
  {|out_mode| is explained in the next chapter.}

@ @<Load the \.{PXL} font, unless there are problems@>=
begin 
active_font:=open_font(f);
if eof(pxl_files[active_font]) then begin
  print_budding_font(f);
  print('---not loaded, PXL file can''t be opened!')
@.PXL file can\'t be opened@>
end
else  begin if (q<=0)or(q>=@'1000000000) then begin
    print_budding_font(f);
    print('---not loaded, bad scale (',q:1,')!')
@.bad scale@>
end
  else if (d<=0)or(d>=@'1000000000) then begin
    print_budding_font(f);
    print('---not loaded, bad design size (',d:1,')!')
@.bad design size@>
 end
  else if in_PXL(q) then @<Finish loading the new font info@>;
  end;
if ((nf<>(f+1)) and (out_mode=errors_only)) then print_ln(' ');
  {provide a line-end for unterminated error messages in case of failure}
end

@ @<Finish loading...@>=
begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
if (c<>0)and(pxl_check_sum<>0)and(c<>pxl_check_sum) then
  begin
     print_budding_font(nf);
     print_ln('---beware: check sums do not agree!');
@.beware: check sums do not agree@>
  print('('); print_octal(c); print(' in DVI vs. ');
  print_octal(pxl_check_sum);print_ln(' in PXL)');
  end;
if out_mode>errors_only then
  print('---defined in postamble at size ',q:1,' DVI units');
  {|out_mode| is explained in the next chapter.}
@.defined in postamble at size@>
d:=trunc((100.0*conv*q)/(true_conv*d)+0.5);
if d<>100 then
  begin
   if font_mag[nf]=pxl_mag_val then begin
    if not take_defaults then begin
       if out_mode > errors_only then print_ln(' ');
       print(' (font as loaded is magnified ',d:1,'%)');
       if out_mode=errors_only then print_ln(' ');
    end
@.font as loaded is magnified@>
   end else begin print_ln(' ');
    print_ln('Warning!');
    print('Font name implies ', font_mag[nf]:5, ' magnification,');
    print(' but magnification field shows ',pxl_mag_val:5);
@.Warning! Font name implies@>
    if out_mode=errors_only then print_ln(' ');
    end;
  end;
incr(nf); {now the new font is officially present}
end



@* Optional modes of output.
\.{DVI2LGP} will print different quantities of information based on some
options that the user must specify: The |out_mode| level is set to one of
four values (|errors_only|, |terse|, |some_debug|, |verbose|)
which were defined back at the start of this \.{WEB} file, giving
different degrees of output; and the typeout can be confined to a
restricted subset of the pages by specifying the desired starting page and
the maximum number of pages. Furthermore there is an option to override the
magnification factor that is stated in the \.{DVI} file.
In the parent \.{DVItype} program there is an option to specify the
resolution of an assumed discrete output device, so that pixel-oriented
calculations will be shown, but this is commented out in the present
program, since we are thinking exclusively of the \.{LGP}-printer, at 
240~pixels/inch.

The starting page is specified by giving a sequence of 1 to 10 numbers or
asterisks separated by dots. For example, the specification `\.{1.*.-5}'
can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
are followed by ten `count' values.) An asterisk matches any number,
so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
specifying the first page. If several pages match the given specification,
\.{DVI2LGP} will begin with the earliest such page in the file
(the interpretation of ``earliest'' depends, of course, on whether
the file is being read from front to back or from back to front). The
default specification `\.*' (which matches all pages) therefore denotes
the page at the beginning of the file. If the file is being read in
reverse order so as to get it stacked in normal order on the output
tray, then `\.*' matches the last page before the postamble, but
if the program is running in what we call |backwards| mode, which
is, in fact, reading sequentially from the preamble on down to the
postamble, then `\.*' matches the page right after the preamble.

When \.{DVI2LGP} begins, it engages the user in a brief dialog so that the
options will be specified. This part of \.{DVI2LGP} requires nonstandard
\PASCAL\ constructions to handle the online interaction; so it may be
preferable in some cases to omit the dialog and simply to stick to the
default options (starting page `\.*', |new_mag=0|).  On other hand, the
system-dependent routines that are needed are not complicated, so it will
not be terribly difficult to introduce them.
@^system dependencies@>

@<Glob...@>=
@!out_mode:errors_only..verbose; {controls the amount of output}
@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
@!resolution:real; {pixels per inch}
@!new_mag:integer; {if positive, overrides the postamble's magnification}
@!nett_width:integer; {size of page in pixels, less margins set by printer}
@!left_margin:integer; {value in pixels, white space at left edge of paper}
@!top_margin:integer; {white space at top}

@ The starting page specification is recorded in two arrays called
|start_count| and |start_there|. Any one of the values in
|start_count| may be declared significant to select the first
page for processing, depending on whether the corresponding value
in |start_there[]=true| or not. For example, `\.{1.*.-5}' is represented
by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
|start_there[2]=true|, |start_count[2]=-5|.
We also set |start_vals=2|, to indicate that count 2 was the last one
mentioned. In this example, the remaining values of |start_count| 
and |start_there| are not important, 

@<Glob...@>=
@!start_count:array[0..9] of integer; {count values to select starting page}
@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
@!start_vals:0..9; {the last count considered significant}
@!count:array[0..9] of integer; {the count values on the current page}

@ Here are some values for a different and rather cruder set of
options. In this case, if |start_on_page>1| the program will
skip pages until it gets to the designated |bop|, without
considering the values recorded from \TeX's count registers.
Finally, there is the |backwards| flag, which determines which
way the program will read the pages in the \.{DVI} file.  The odd thing
about this flag is that sequential reading from $1\to n$ is ``backwards''
because it will deposit the finished pages on the output tray in
reverse order.  Since most users will prefer to have the pages already
arranged in the proper order, the default is to read through from the
|postamble| forward, using the backpointers.  The switch for changing
this default is \.{I} (for ``inverse'' order). 
Since the default, in this case, requires the
use of the |move_to_byte| procedure, we had better admit that it is
system dependent.
@^system dependencies@>

@d sw_c="/"  {change this to the switch character you like best}

@<Glob...@>=
@!start_on_page:integer; {a crude counting method, unlike |start_count|} 
@!margin_set:integer; 
    {value of |xchr[sw_c]|L, |xchr[sw_c]|C or |xchr[sw_c]|R switch}
@!margin_count:integer; {value in pixels}
@!right_reading:boolean; {normal vertical output if \.{TRUE}}
@!backwards:boolean; {if set to \.{TRUE}, then read from first to last page}

@ Here we preset most of the options to the defaults for a normal
production run.

@d page_default_maximum==1000000 {a million pages is all you are allowed}
@d new_mag_default==0 {by default, use the postamble value}

@<Set init...@>=
out_mode:=errors_only; start_vals:=0; start_there[0]:=false;
new_mag:=new_mag_default;
max_pages:=page_default_maximum;
margin_set:="C";
right_reading:=true;
backwards:=false;
@^system dependencies@>

@ Here is a simple subroutine that tests if the current page might be the
starting page.

@p function start_match:boolean; {does |count| match the starting spec?}
var k:0..9;  {loop index}
@!match:boolean; {does everything match so far?}
begin match:=true;
for k:=0 to start_vals do
  if start_there[k]and(start_count[k]<>count[k]) then match:=false;
start_match:=match;
end;

@ The |input_ln| routine waits for the user to type a line at his or her
terminal; then it puts ascii-code equivalents for the characters on that line
into the |buffer| array. Terminal I/O often involves problems of system
dependency, and some systems do not respond at all well fo the use of
|write| and |write_ln| for interactive communication with the terminal.

The |term_in| file is used for terminal input,
and |term_out| for terminal output.
@^system dependencies@>

@<Glob...@>=
@!buffer:array[0..terminal_line_length] of ascii_code;
@!term_in:text_file; {the terminal, considered as an input file}
@!term_out:text_file; {the terminal, considered as an output file}

@ Since the terminal is being used for both input and output, some systems
need a special routine to make sure that the user can see a prompt message
before waiting for input based on that message. (Otherwise the message
may just be sitting in a hidden buffer somewhere, and the user will have
no idea what the program is waiting for.) We shall call a system-dependent
subroutine |update_terminal| in order to avoid this problem.
@^system dependencies@>

@d update_terminal == break(term_out) {empty the terminal output buffer}

@ During the dialog, \.{DVI2LGP} will treat the first blank space in a
line as the end of that line. Therefore |input_ln| makes sure that there
is always at least one blank space in |buffer|.
@^system dependencies@>

@p procedure input_ln; {inputs a line from the terminal}
var k:0..terminal_line_length;
begin update_terminal; reset(term_in);
if eoln(term_in) then read_ln(term_in);
k:=0;
while (k<terminal_line_length)and not eoln(term_in) do
  begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
  end;
buffer[k]:=" ";
end;

@ The global variable |buf_ptr| is used while scanning each line of input;
it points to the first unread character in |buffer|.

@<Glob...@>=
@!buf_ptr:0..terminal_line_length; {the number of characters read}

@ Here is a routine that scans a (possibly signed) integer and computes
the decimal value. If no decimal integer starts at |buf_ptr|, the
value 0 is returned. The integer should be less than $2^{31}$ in
absolute value.

@p function get_integer:integer;
var x:integer; {accumulates the value}
@!negative:boolean; {should the value be negated?}
begin if buffer[buf_ptr]="-" then
  begin negative:=true; incr(buf_ptr);
  end
else negative:=false;
x:=0;
while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do
  begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr);
  end;
if negative then get_integer:=-x @+ else get_integer:=x;
end;

@ The selected options are put into global variables by the |dialog|
procedure, which is called just as \.{DVI2LGP} begins.
@^system dependencies@>

@p procedure dialog;
label 0,1,2,3,4,5;
var k:integer; {loop variable}
    ctr, tctr, lctr, dctr: integer;  {loop variables for getting file names}
begin rewrite(term_out); {prepare the terminal for output}
if argc < 2 then @t{\kern1em}}%--fixme--@>@/
 {presently assumes---if more than one argument, use default}
   print_ln(banner);@/
@<Get \.{DVI} and \.{LGP} file names@>;
@<Set option switch values@>;
@<Determine the desired |out_mode|@>; {from \.{DVItype}}
@<Determine the desired |start_count| values@>;
@<Determine the desired |max_pages|@>;
@<Determine the desired |new_mag|@>;
@<Determine the desired |resolution|@>; {from \.{DVItype}}
if not take_defaults then begin @<Print all the selected options@> end;
end;

@ Here are some global flags which are used to direct the processing.
|first_get| is defined because
it is necessary to flag the first |get|
done so that an initial |eoln| is accepted without
requiring another character be entered.  |take_defaults| is defined
to distinguish the case of file name entered on command line from
the case when the file name was queried for.

@<Glob...@>=
@!first_get:boolean; {true at start, then always false}
@!take_defaults:boolean; {true if defaults should be taken without prompting}
@!interaction:integer; {level of interaction, perhaps only batch and not batch}
@^system dependencies@>

@ @<Set init...@>=
first_get:=true; {needed in some cases, harmless in others}
interaction:=batch_mode+1; {that takes care of that for now}

@ It is necessary for both |open_dvi_file| and |open_lgp_file| to know what
the \.{DVI} file name is.  Hence, |dvi_name| is declared global.  |lgp_name| is
then generated from the essential part of the |dvi_name|.  This operation
assumes that we have access to the same extensions of the standard |reset|
statement as are used for |pxl_files| or |vrl_files|.  |argv_nm_type| is
defined here because the TOPS-20 Pascal compiler does not allow the definition
|packed array[1..name_length] of text_char| in the |argv| procedure
declaration which we will be supplying later.  The
{\mc UNIX} |argv| also returns a |packed array [1..name_length] of char|.

@<Types...@>=
@!argv_nm_type = packed array[1..name_length] of text_char;

@ @<Glob...@>=
@!dvi_name : argv_nm_type;
  {the name of the \.{DVI} file either on the command line or entered from
   the terminal--returned from the |argv| procedure}
@!lgp_name: packed array [1..name_length] of char; 
   {the name of the \.{LGP} file}

@^system dependencies@>

@ Here we want to generate the file names for the |dvi_file| and the
|lgp_file|.  If the |dvi_file| name is not present on the command
line, it is prompted for.  The |lgp_name| is generated from |dvi_name|.
This section makes reference to two non-standard procedures which
are part of the {\mc UNIX} \PASCAL\ environment, but which must be
provided in the system dependent code in other environments such
as TOPS-20. The |argc| function shows the number of arguments on the
command line, and must return a value |>=1| unless the system has gone
completely berserk.  The |argv| procedure extracts any argument from
the command line (with the value 0 in the first argument for the
first element of the command line).

@<Get \.{DVI} and \.{LGP} file names@>=
    if argc > 2 then begin
        write_ln(term_out, 'Usage: dvi2lgp dvifile[.dvi]');
	jump_out;@t}%--fixme--@>@/
    end
    else if argc <> 2 then begin
        @<Ask for the \.{DVI} file name@>@;
	take_defaults:=false;
    end
    else begin
        argv(1, dvi_name);
	take_defaults:=true;
    end;
    @<Make sure \.{DVI} file has extension@>@;
    @<Show the \.{DVI} file name@>@;
    @<Figure out what to name the \.{LGP} file@>@;
    @<Print the \.{LGP} file name@>@;

@^system dependencies@>

@ The \.{DVI} file name may be specified in one of two
ways.  In the first, it is included on the command line which
invokes \.{DVI2LGP}.  In the second, used when no names were specified on
the command line, \.{DVI2LGP} asks for the names.  Here is where we
define the code to ask for the name.

@<Ask for the \.{DVI} file name@>=
  tprint('> DVI file name? ');
  input_ln;
  while((buffer[0] = " ") or (buffer[0] = "?")) do
  begin
    if(buffer[0] = " ") then
       tprint('Whoops, please enter a non-null name? ')
    else begin
       tprint_ln('Enter the name of the DVI file produced by TeX.  You can');
       tprint_ln('leave the extension off if you wish.');
       tprint('> DVI file name? ');
    end;
    input_ln;
  end;
  ctr := 0;
  while (buffer[ctr] <> " ") do
  begin
    if (buffer[ctr]>="a") and (buffer[ctr]<="z") then
      dvi_name[ctr+1]:=system_case_of(buffer[ctr]) 
       {harmless do-nothing in {\mc UNIX}}
    else dvi_name[ctr+1] := xchr[buffer[ctr]];
    incr(ctr);
  end;
  incr(ctr);
    { set remainder of buffer to blanks }
  while(ctr <= name_length) do
  begin
    dvi_name[ctr] := ' '; incr(ctr);
  end;
@^system dependencies@>

@ Scan through the \.{DVI} file name which has been extracted
to guarantee that it has an extension.  If none, add a \.{.DVI} to
the end. (Always assuming that your system uses this sort of suffix.)
The |alt_marker| can be the same as the |area_marker| in a {\mc UNIX}
system, or it can be defined as the closing bracket for an alternate
directory.

@d area_marker==':' {or whatever your system uses} 
@d alt_marker=='>' {change this to things like the $\rangle$ directory bracket} 
@d ext_marker=='.' {the normal character for file-name extensions} 

@<Make sure \.{DVI} file has extension@>=
ctr:=1;
while(dvi_name[ctr]<>' ') do
  incr(ctr); { find the end of the name }
tctr:=ctr-1;
while((dvi_name[tctr]<>area_marker)
  and (dvi_name[tctr]<>alt_marker)
  and (dvi_name[tctr]<>ext_marker) and (tctr>1)) do
  decr(tctr);
if(dvi_name[tctr]<>ext_marker) then
begin  { no extension, add one }
  dvi_name[ctr] := ext_marker; incr(ctr);
  dvi_name[ctr] := system_case_of("d"); incr(ctr);
  dvi_name[ctr] := system_case_of("v"); incr(ctr);
  dvi_name[ctr] := system_case_of("i"); incr(ctr);
  dvi_name[ctr] := ' ';
end;

@^system dependencies@>

@ Having determined what the \.{DVI} file name is, we now
want to display it.

@<Show the \.{DVI} file name@>=

if interaction > batch_mode then begin 
    print('[');
    ctr := 1;
    while(dvi_name[ctr] <> ' ')  do
    begin
      print(dvi_name[ctr]);
      incr(ctr);
    end;
    print('->');
  end;
@^system dependencies@>

@ Given the |dvi_name|, we now need to figure out where to put the \.{LGP}
file.  We will put the \.{LGP} file into a |lgp_name| which is in the local
directory, with the same file name as |dvi_name|, but with the extension
\.{.LGP} rather than \.{.DVI}.

@<Figure out what to name the \.{LGP} file@>=
    dctr := 1;  {for traversing the \.{DVI} file name}
    {find the end of the \.{DVI} file name}
    while(dvi_name[dctr] <> ' ') do
      incr(dctr);
    {back up until either 
        |area_marker|, |alt_marker| or beginning of name encountered}
    while((dvi_name[dctr] <> area_marker) 
      and (dvi_name[dctr]<> alt_marker) and (dctr > 1))  do
      decr(dctr);
    {unles we are right at the beginning, skip this token}
    if((dvi_name[dctr]=area_marker) or (dvi_name[dctr]=alt_marker)) then
      incr(dctr);
    {copy the \.{DVI} name to the \.{LGP} file name array}
    lctr := 1;  {for traversing the \.{LGP} file name}
    while((dvi_name[dctr] <> ext_marker) and (dvi_name[dctr] <> ' ')) do
    begin
       lgp_name[lctr] := dvi_name[dctr];
       incr(lctr); incr(dctr);
    end;
    lgp_name[lctr] := ext_marker;
    incr(lctr); lgp_name[lctr] := system_case_of("l");
    incr(lctr); lgp_name[lctr] := system_case_of("g");
    incr(lctr); lgp_name[lctr] := system_case_of("p");
    incr(lctr); lgp_name[lctr] := ' ';
    {and blank out the rest of the \.{LGP} name array}
    for ctr:=lctr+1 to name_length do
       lgp_name[ctr] := ' ';

@^system dependencies@>

@ And now finish the line which was started back there with the \.{DVI}
filename print-out by printing the \.{LGP} file name.

@<Print the \.{LGP} file name@>=
if interaction > batch_mode then begin
    ctr := 1;
    while(lgp_name[ctr] <> ' ')  do
    begin
       print(lgp_name[ctr]);
       incr(ctr);
    end;
    print_ln(']');
  end
@^system dependencies@>
 
@ The option switches are an addition to the \.{DVItype} code.
 
@<Set option...@>=
  if take_defaults then goto 5;
0: tprint('> <CR> to accept default of all pages, ');
 tprint_ln('postamble-specified magnification.');
 tprint('  ? for help: ');
@.CR to accept default...@>
 input_ln; buf_ptr:=0; if buffer[buf_ptr]=" " then goto 5
 else if buffer[buf_ptr]="?" then begin
   tprint_ln('Type any character (which will be ignored), except for "?", if you');
   tprint_ln('want to specify values.  Type CR if you are willing to accept the');
   tprint_ln('default values.  Typing "?" prints this message.  Typing "?" in');
   tprint_ln('response to any of the following questions also produces help text.');
   goto 0;
 end;
1: tprint('> Switches, in form ',xchr[sw_c],'switch',xchr[sw_c],'etc. (No spaces): ');
@.Switches, in form...@>
 input_ln; buf_ptr:=0;
 start_on_page:=0;
 max_pages:=page_default_maximum; {preset to default value}
 if buffer[buf_ptr]=sw_c then begin
 while buffer[buf_ptr]<>" " do
  if buffer[buf_ptr]=sw_c then begin
   incr(buf_ptr);
   if (buffer[buf_ptr]>="0") and (buffer[buf_ptr]<="9") then begin
    start_on_page:=get_integer; max_pages:=1; backwards:=true;
    if buffer[buf_ptr]=":" then begin
     incr(buf_ptr);
     if (buffer[buf_ptr]="E") 
       or (buffer[buf_ptr]="e") then max_pages:=page_default_maximum
     else max_pages:=(get_integer-start_on_page)+1;
     if max_pages<1 then begin max_pages:=page_default_maximum;
       tprint_ln('Negative or zero page count reset to default of 1000000!');
@.Negative or zero page count@>
       end;
     end;
    end else begin
    if (buffer[buf_ptr]>="a") or (buffer[buf_ptr]<="z") then
     buffer[buf_ptr]:=xord[fold_upper(buffer[buf_ptr])];
	{fold into uppercase, not an optional fold}
     @<Set the switches@>
    end;
   while(buffer[buf_ptr]<>sw_c)and(buffer[buf_ptr]<>" ")do incr(buf_ptr); 
   end;
 end else if buffer[buf_ptr]<>" " then begin
  tprint_ln('The following switches are available: ');
@.The following switches@>
  tprint_ln(xchr[sw_c],'L(eft)',xchr[sw_c],'C(enter)',xchr[sw_c],
     'R(ight) for position on page,');
  tprint_ln(xchr[sw_c],'n or ',xchr[sw_c],'n:m or ',xchr[sw_c],
     'n:E(nd) for simple page count,');
  tprint(xchr[sw_c],'P(roduction)',xchr[sw_c],'T(erse)',xchr[sw_c],
     'S(ome-debug)',xchr[sw_c],'F(ull-debug)');
  tprint_ln(' for mode selection.');
  tprint_ln
   (xchr[sw_c],'I(nvert) for output in DVI-file order--inverse order on tray');
  tprint_ln
   (xchr[sw_c],'H(orizontal) or V(ertical)--Vertical is the default');
@.mode selection@>
@.order on tray@>
@.H(orizontal) or V(ertical)@>
  goto 1; 
  end; 
@^system dependencies@>

@ @<Set the switches@>=
    case buffer[buf_ptr] of
      "C": begin margin_set:="C";@+ end; {this is the default}
      "F": begin out_mode:=verbose; backwards:=true; end;
      "H": begin right_reading:=false; end;
      "I": begin backwards:=true; end;
      "L": begin margin_set:="L"; end;
      "R": begin margin_set:="R"; end;
      "P": begin out_mode:=errors_only;@+ end; {this is the default}
      "T": begin out_mode:=terse; end;
      "S": begin out_mode:=some_debug; backwards:=true; end;
      "V": begin right_reading:=true;@+ end; {this is the default}
     othercases begin
        print_ln('Undefined switch: ''',xchr[buffer[buf_ptr]],''', try again');
	goto 1;
@.Undefined switch@>
        end
    endcases

@ @<Determine the desired |out_mode|@>=
do_nothing {we already took care of this with \.{P, T, S,} or \.{F}}
	

@ @<Determine the desired |start...@>=
2: tprint('> Starting page (default=');
 if start_on_page>0 then begin
   tprint_ln(start_on_page:1,', simple count, set by ',xchr[sw_c],'n switch)');
   end else tprint_ln('*) ');
   tprint('  (for help, type ?): ');
start_vals:=0; start_there[0]:=false;
input_ln; buf_ptr:=0; k:=0;
if (buffer[0]<>" ") and (start_on_page<>0) then begin
  if buffer[0]<>"?" then
   tprint_ln('   (TeX''s \count values override the ',xchr[sw_c],'n value.)')
  end;
if buffer[0]<>" " then
  repeat if buffer[buf_ptr]="*" then
    begin start_there[k]:=false; incr(buf_ptr);
    start_on_page:=0; max_pages:=page_default_maximum;{cancel any /n values}
    end
  else  begin 
    if (buffer[0]="-")or((buffer[buf_ptr]>="0") and (buffer[buf_ptr]<="9"))
    then begin
     start_on_page:=0; max_pages:=page_default_maximum;{cancel any /n values}
     end;
    start_there[k]:=true; start_count[k]:=get_integer;
    backwards:=true;
    end;
  if (k<9)and(buffer[buf_ptr]=".") then
    begin incr(k); incr(buf_ptr);
    end
  else if buffer[buf_ptr]=" " then start_vals:=k
  else  begin 
    tprint('At this point you can make use of the values ');
    tprint_ln('in TeX''s \count registers ');
    tprint('Type, e.g., 1.*.-5 to specify the ');
    tprint_ln('first page with \count0=1, \count2=-5.');
    tprint_ln('When you specify a *, dvi2lgp will just print out the corresponding');
    tprint_ln('\count field but won''t pay attention to it.');
    tprint_ln('If you type *.*.*.*.*.*.*.*.*.*,  you will just see what was');
    tprint_ln('in all of the \count fields when TeX output the page.');
    if start_on_page<>0 then begin
      tprint_ln('(If you enter anything but ? here, you will use TeX''s \count');
@.If you use TeX's count@>
      tprint_ln('values which will override the ',xchr[sw_c],'n value.)');
      end;
    goto 2;
    end;
  until start_vals=k

@ @<Determine the desired |max_pages|@>=
3: tprint('> Maximum number of pages (default=',max_pages:1);
if max_pages <> page_default_maximum then 
  tprint(' as set by use of the ',xchr[sw_c],'n switch');
   {this situation results from use of the |sw_c|n or |sw_c|n:m switch}
tprint('): ');
input_ln; buf_ptr:=0;
if buffer[0]="?" then begin
   tprint_ln('Maximum number of pages to be printed, a positive number');
   goto 3;
end;
if buffer[0]<>" " then
begin max_pages:=get_integer; end;
if max_pages<=0 then
  begin tprint_ln('The maximum number of pages must be positive.');
  max_pages:=page_default_maximum;
  goto 3;
  end

@ @<Determine the desired |new_mag|@>=
4: tprint_ln('> New magnification (default is the magnification value');
tprint('  in the DVI postamble): ');
new_mag:=new_mag_default; input_ln; buf_ptr:=0;
if buffer[0]<>" " then begin
  if buffer[0]="m" then @<Check for magstep@>
  else begin
    if (buffer[0]>="0")and(buffer[0]<="9") then new_mag:=get_integer
    else  begin 
      tprint_ln('Type a 0 to retain the value specified in the DVI postamble.');
      tprint('Type a positive integer ');
      tprint_ln('or specify a magstep to override ');
      tprint_ln('the magnification in the DVI file.');
      tprint_ln('You may either specify a magstep (e.g. magstep1), or');
      tprint('type an integer equal to 1000 times desired magnification');
      tprint_ln(' (e.g. 1315)');
      goto 4;
      end
    end
  end

@ @<Determine the desired |resolution|@>=
5: resolution:=240.0 {This value is fixed for the \.{LGP}}

@ @<Check for magstep@>=
begin
 if ((buffer[1]="a") and (buffer[2]="g") and (buffer[3]="s") and
    (buffer[4]="t") and (buffer[5]="e") and (buffer[6]="p")) {whew!}
  then
    case buffer[7] of 
    "h": begin
      if ((buffer[8]="a") and(buffer[9]="l") and(buffer[10]="f"))
      then new_mag:=1095 else begin
        tprint_ln('Not a valid magstep specification');
        goto 4;
        end
      end;
    "0": begin new_mag:=1000; tprint_ln(' '); tprint_ln
      ('That''s no magnification at all!'); end;
    "1": begin new_mag:=1200; end;
    "2": begin new_mag:=1440; end;
    "3": begin new_mag:=1728; end;
    "4": begin new_mag:=2074; tprint_ln(' '); tprint_ln
       ('That''s ABSOLUTELY HUGE!  Will it fit, and do the fonts exist?'); 
       tprint_ln(' '); end;
    "5","6","7","8","9": begin 
      for k:=0 to 7 do tprint(xchr[buffer[k]]);
        tprint_ln(' is MUCH TOO LARGE!');
        tprint_ln(' '); 
      goto 4;
      end;
    othercases begin tprint_ln(' '); tprint_ln
        ('Not a valid magstep specification');
      goto 4;
      end;
    endcases
end

@ After the dialog is over, we print the options so that the user
can see what \.{DVI2LGP} thought was specified.

@<Print all the selected options@>=
print_ln('Options selected:');
@.Options selected@>
if backwards then print_ln('Read DVI from front to back, inverted output')
 else print_ln('Read DVI from back to front, output on tray in order');
if not right_reading then print_ln('Rotated (landscape mode) output');
print('  Starting page = ');
if start_on_page = 0 then begin
for k:=0 to start_vals do
  begin if start_there[k] then print(start_count[k]:1)
  else print('*');
  if k<start_vals then print('.')
  else print_ln(' ');
  end;
end else print_ln(start_on_page:1,' (sequential count, not TeX''s \count)');
print_ln('  Maximum number of pages = ',max_pages:1);
print('  Output level = ',out_mode:1);
case out_mode of
errors_only: print_ln(' (bops, error messages and LGP-code file)');
terse: print_ln(' (bops, fonts, error messages and LGP-code file)');
some_debug: print_ln(' (line-by-line debug, with LGP-code file)');
verbose: print_ln(' (byte-by-byte debug, with LGP-code file)');
end;@/
print_ln('  Resolution = ',resolution:12:8,' pixels per inch');
if new_mag>0 then
   print_ln('  New magnification factor = ',get_actual_mag(new_mag):8:3)
@^system dependencies@>

@* Low level output routines.
Simple text in the \.{DVI} file is saved in a buffer until |line_length-2|
characters have accumulated, or until some non-simple \.{DVI} operation
occurs. Then the accumulated text is printed on a line, surrounded by
brackets. The global variable |text_ptr| keeps track of the number of
characters currently in the buffer.

@<Glob...@>=
@!text_ptr:0..line_length; {the number of characters in |text_buf|}
@!text_buf:array[1..line_length] of ascii_code; {saved characters}

@ @<Set init...@>=
text_ptr:=0;

@ The |flush_text| procedure will empty the buffer if there is something in it.

@p procedure flush_text;
var k:0..line_length; {index into |text_buf|}
begin if text_ptr>0 then
  begin if out_mode>terse then
    begin print('[');
    for k:=1 to text_ptr do print(xchr[text_buf[k]]);
    print_ln(']');
    end;
  text_ptr:=0;
  end;
end;

@ And the |out_text| procedure puts something in it.

@p procedure out_text(c:ascii_code);
begin if text_ptr=line_length-2 then flush_text;
incr(text_ptr); text_buf[text_ptr]:=c;
end;


@* Native-mode commands for the {\tentex LGP}.  The \.{LGP} is driven by
escape code sequences, using the {\mc ASCII} escape (decimal 27) as
the initial character.  A full list of these is provided in the
\\{Symbolics, Inc.} technical manual for the \.{LGP}.  The ones we
make use of here will largely be understood by reference to the
code used. Following the escape, we may have the \\{character}, which is a code
in the range of the {\mc ASCII} table
(the same range as |ascii_code|), the \\{unsigned byte}, with a value
of |0..255|, the \\{signed byte}, with a value of |-128..127|, and the
signed 16-bit microprocessor \\{``word''} with a value 
of |-32768..32767|. The |ascii_code|
value following the escape specifies the desired action, and is reasonably
evocative of what we intend.
\yskip
\item{\.{"b"}} paints pixels directly on the page bit-map, and does not
  use up any of the \.{LGP}'s font storage area.
  See under ``Define a Block of Raster Data'' in the index.
\item{\.{"c"}} defines a character with fairly hefty dimensions.  
  See under ``Define a Large-Size Character'' in the index.
\item{\.{"d"}} defines a character.  See under ``Define a Small-Size Character''
  in the index.
\item{\.{"e"}} end the job---a simple two character sequence without parameters.
\item{\.{"f}"} select a font. See under ``Set Font'' in the index.
\item{\.{"m"}} move around on the bit-map. See the second entry under ``Define a
  a Small-Size Character,'' and also under ``Draw a Rectangle.''
\item{\.{"o"}} define a forms overlay.  See under ``Specials'' in the index.
\item{\.{"p"}} finish a page---no parameters.  Tells the \.{LGP} to go ahead
  and print this page.
\item{\.{"r"}} set a rule.  See the index under ``Draw a Rectangle.''
\item{\.{"v"}} draw a vector. See under ``Specials'' in the index.
 
@ We begin by setting up a macro for the initial escape-code string, 
another for the familiar ``low-high'' sequence of bytes so beloved
by microprocessors, and a third which is used to send a pair
of \\{escape} codes if we want to set a character with the same
|ascii_code| value as \\{escape}.
 
@d escape_string(#)==begin output_byte(27); output_byte(#) end
@d send_in_low_high_sequence(#)==
     begin output_byte(# mod 256); output_byte(# div 256) end
@d output_char(#)==begin if #=27 then output_byte(27); output_byte(#) end
 
 
@ The printer's microprocessor program has one further idiosyncracy, in
that it wants each byte of the bit-maps for characters with the bits in 
reversed order.  It would
be excessively tedious to do the calculations for this reversal each 
time it is needed, so we do it once for all in the initialization by 
setting up an array representing the values $0\to 255$ with the bits
in reverse order---high-order bit to the right. 
 
@<Glob...@>=
@!bits_reversed:array[0..255] of 0..255;

@ @<Set init...@>=
for m:=0 to 255 do
  begin
    l:=m; n:=0; i:=1; j:=128;
    for k:=1 to 8 do begin
        if (l div j)<>0 then begin
            n:=n+i;
            l:=l-j
        end;
        i:=2*i; j:=j div 2
    end;
    bits_reversed[m] := n;
  end;

\f

@* Translation to {\tentex LGP}-code or to symbolic form.
The main work of \.{DVI2LGP} is accomplished by the |do_page| procedure,
which produces the output for an entire page, assuming that the |bop|
command for that page has already been processed. This procedure is
essentially an interpretive routine that reads and acts on the \.{DVI}
commands.

@ The definition of \.{DVI} files refers to six registers,
$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.  In practice,
we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
since it is not always true that |hh=pixel_round(h)| or
|vv=pixel_round(v)|.

The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.

@<Glob...@>=
@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
  array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
@!hhstack,@!vvstack:
  array [0..stack_size] of integer; {pushed down values in pixels}
@!lgp_h,@!lgp_v:integer; {where we got to after the last |set_char|, \\{vel sim}}

@ Three characteristics of the pages (their |max_v|, |max_h|, and
|max_s|) are specified in the postamble, and a warning message
is printed if these limits are exceeded. Actually |max_v| is set to
the maximum height plus depth of a page, and |max_h| to the maximum width,
for purposes of page layout. Since characters can legally be set outside
of the page boundaries, it is not an error when |max_v| or |max_h| is
exceeded. But |max_s| should not be exceeded.

The postamble also specifies the total number of pages; \.{DVI2LGP}
checks to see if this total is accurate.

@d infinity==@'17777777777 {$\infty$ (approximately) for range checks etc.}

@<Glob...@>=
@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
@!max_s:integer; {the stack depth should not exceed this}
@!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
@!total_pages:integer; {the stated total number of pages}
@!page_count:integer; {the total number of pages seen so far}
@!hh_vector,@!vv_vector:integer; {used for the \.{LGP} vector drawing feature}

@ @<Set init...@>=
max_v:=infinity; max_h:=infinity; max_s:=stack_size+1;@/
max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
hh_vector:=infinity;vv_vector:=infinity;

@ Before we get into the details of |do_page|, it is convenient to
consider a simpler routine that computes the first parameter of each
opcode.

@d four_cases(#)==#,#+1,#+2,#+3
@d eight_cases(#)==four_cases(#),four_cases(#+4)
@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)

@p function first_par(o:eight_bits):integer;
begin case o of
sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
  first_par:=o-set_char_0;
set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
  fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
w0: first_par:=w;
x0: first_par:=x;
y0: first_par:=y;
z0: first_par:=z;
sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
end;
end;

@ Here is another subroutine that we need: It computes the number of
pixels in the height or width of a rule. Characters and rules will line up
properly if the sizes are computed precisely as specified here.  (Since
|conv| is computed with some floating-point roundoff error, in a
machine-dependent way, format designers who are tailoring something for a
particular resolution should not plan their measurements to come out to an
exact integer number of pixels; they should compute things so that the
rule dimensions are a little less than an integer number of pixels, e.g.,
4.99 instead of 5.00.)

@p function rule_pixels(x:integer):integer;
  {computes $\lceil|conv|\cdot x\rceil$}
var n:integer;
begin n:=trunc(conv*x);
if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
end;

@ Strictly speaking, the |do_page| procedure is really a function with
side effects, not a `\&{procedure}'; it returns the value |false| if
\.{DVI2LGP} should be aborted because of some unusual happening. The
subroutine is organized as a typical interpreter, with a multiway branch
on the command code followed by |goto| statements leading to routines that
finish up the activities common to different commands. We will use the
following labels:

@d fin_set=41 {label for commands that set or put a character}
@d fin_rule=42 {label for commands that set or put a rule}
@d move_right=43 {label for commands that change |h|}
@d move_down=44 {label for commands that change |v|}
@d show_state=45 {label for commands that change |s|}
@d change_font=46 {label for commands that change |cur_font|}

@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
so we shall split |do_page| into two parts, one of which is
called |special_cases|. The different parts communicate with each other
via the global variables mentioned above, together with the following ones:

@<Glob...@>=
@!s:integer; {current stack size}
@!ss:integer; {stack size to print}
@!cur_font:integer; {current internal font number}
@!showing:boolean; {is the current command being translated in full?}

@ Here is the overall setup.

@p @<Declare the function called |special_cases|@>@;
function do_page:boolean;
label fin_set,fin_rule,move_right,show_state,done,9998,9999;
var o:eight_bits; {operation code of the current command}
@!p,@!q:integer; {parameters of the current command}
@!a:integer; {byte number of the current command}
@!i,@!j:integer; {to hold temporaries for |escape_string("m")| command etc.}
@!hs,@!vs: integer; {used to define character for \.{LGP} memory}
@!hs_in_pixels,hs_in_bytes: integer; {to make the job more exact}
@!lgp_sixteen_bit_words,lgpsbw_count:integer; 
   {used to trim down wasted font storage in the \.{LGP}}
@!bit_columns,@!bit_rows:integer; {size of envelope in bits}
@!glyph_bytes: array [0..10000] of integer; {temporary storage for bit-map}
@!glyph_step,@!row_offset:integer; 
    {offset start of index and govern size of increment through array}
@!clump_columns,@!clump_limit,non_pixels:integer; 
    {control movement back through unrotated row}
@!rotated_byte:integer; {what we are trying to achieve}
begin cur_font:=nf; {set current font undefined}
s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=left_margin;
  if right_reading then
   vv:=page_pixel_height - top_margin
  else vv:=top_margin;
  {initialize the state variables}
escape_string("m"); 
send_in_low_high_sequence(0);
send_in_low_high_sequence(0);
while true do @<Translate the next command in the \.{DVI} file;
    |goto 9999| with |do_page=true| if it was |eop|;
    |goto 9998| if premature termination is needed@>;
9998: print_ln('!'); do_page:=false;
9999: end;

@ Commands are broken down into ``major'' and ``minor'' categories:
A major command is always shown in full, while a minor one is
put into the buffer in abbreviated form. Minor commands, which
account for the bulk of most \.{DVI} files, involve horizontal spacing
and the typesetting of characters in a line; these are shown in full
only if |out_mode>=verbose|.

@d show(#)==begin flush_text; showing:=true; print(a:1,': ',#);
  end
@d major(#)==if out_mode>terse then show(#)
@d minor(#)==if out_mode=verbose then
  begin showing:=true; print(a:1,': ',#);
  end
@d error(#)==if not showing then show(#) else print(' ',#)

@<Translate the next command...@>=
begin a:=cur_loc; showing:=false;
o:=get_byte; p:=first_par(o);
if eof(dvi_file) then bad_dvi('the file ended prematurely!');
@.the file ended prematurely@>
@<Start translation of command |o| and |goto| the appropriate label to
  finish the job@>;
fin_set: @<Finish a command that either sets or puts a character, then
    |goto move_right| or |done|@>;
fin_rule: @<Finish a command that either sets or puts a rule, then
    |goto move_right| or |done|@>;
move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
show_state: @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|,
  |hh|, and |vv|; then |goto done|@>;
done: if showing then print_ln(' ');
end

@ The multiway switch in |first_par|, above, was organized by the length
of each command; the one in |do_page| is organized by the semantics.

@<Start translation...@>=
if o<set_char_0+128 then @<Translate a |set_char| command@>
else case o of
  four_cases(set1): begin major('set',o-set1+1:1,' ',p:1); goto fin_set;
    end;
  set_rule: begin major('setrule'); goto fin_rule;
    end;
  put_rule: begin major('putrule'); goto fin_rule;
    end;
  @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
  @t\4@>@<Cases for horizontal motion@>@;
  othercases if special_cases(o,p,a) then goto done@+else goto 9998
  endcases

@ @<Declare the function called |special_cases|@>=
function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
label change_font,move_down,done,9998;
var q:integer; {parameter of the current command}
@!k,n:integer; {loop indices, the |n| is used for \.{\\special}s}
@!spec_string:array[1..name_length] of ascii_code;
@!bad_char:boolean; {has a non-ascii character code appeared in this |xxx|?}
@!pure:boolean; {is the command error-free?}
@!end_of_special:boolean; {end of overlay or big character found}
begin pure:=true;
case o of
four_cases(put1): begin major('put',o-put1+1:1,' ',p:1); goto done;
  end;
@t\4@>@<Cases for vertical motion@>@;
@t\4@>@<Cases for fonts@>@;
four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
pre: begin error('preamble command within a page!'); goto 9998;
  end;
@.preamble command within a page@>
post,post_post: begin error('postamble command within a page!'); goto 9998;
@.postamble command within a page@>
  end;
othercases begin error('undefined command ',o:1,'!');
  goto done;
@.undefined command@>
  end
endcases;
move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
change_font: @<Finish a command that changes the current font,
  then |goto done|@>;
9998: pure:=false;
done: special_cases:=pure;
end;

@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
nop: begin minor('nop'); goto done;
  end;
bop: begin error('bop occurred before eop'); goto 9998;
@.bop occurred before eop@>
  end;
eop: begin major('eop');
  escape_string("p");
  if s<>0 then error('stack not empty at end of page (level ',
    s:1,')!');
@.stack not empty...@>
  if (hh_vector<>infinity) or (vv_vector<>infinity) then
   error('page ended with an unused start-of-vector: hh= ',
   hh_vector:1, ', vv= ', vv_vector:1, '!');
  hh_vector:=infinity;vv_vector:=infinity;
  do_page:=true; if out_mode > terse then print_ln(' '); goto 9999;
  end;
push: begin major('push');
  if s=max_s_so_far then
    begin max_s_so_far:=s+1;
    if s=max_s then error('deeper than claimed in postamble!');
@.deeper than claimed...@>
@.push deeper than claimed...@>
    if s=stack_size then
      begin error('DVI2LGP capacity exceeded (stack size=',
        stack_size:1,')'); goto 9998;
      end;
    end;
  hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
  xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
  hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto show_state;
  end;
pop: begin major('pop');
  if s=0 then error('(illegal at level zero)!')
  else  begin decr(s); hh:=hhstack[s]; vv:=vvstack[s];
    h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
    x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
    end;
  ss:=s; goto show_state;
  end;

@ Rounding to the nearest pixel is best done in the manner shown here, so as
to be inoffensive to the eye: When the horizontal motion is small, like a
kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
changes by rounding the true position |h| so that accumulated rounding errors
disappear.


@d pixel_round(#)==trunc(conv * (#)+0.5)
@d out_space(#)==if abs(p)>=font_space[cur_font] then
    begin if out_mode>terse then out_text(" "); 
     hh:=pixel_round(h+p)+left_margin;
    end
  else hh:=hh+pixel_round(p);
  minor(#,' ',p:1); q:=p; goto move_right

@<Cases for horizontal motion@>=
four_cases(right1):begin out_space('right',o-right1+1:1);
  end;
w0,four_cases(w1):begin w:=p; out_space('w',o-w0:1);
  end;
x0,four_cases(x1):begin x:=p; out_space('x',o-x0:1);
  end;

@ Vertical motion is done similarly, but with the threshold between
``small'' and ``large'' increased by a factor of five. The idea is to make
fractions like ``$1\over2$'' round consistently, but to absorb accumulated
rounding errors in the baseline-skip moves.

@d out_vmove(#)==if abs(p)>=5*font_space[cur_font] then begin
  if right_reading then
     vv:=page_pixel_height-(top_margin+(pixel_round(v+p)))
  else vv:=top_margin+(pixel_round(v+p));
  end
 else begin
  if right_reading then
   vv:=vv-pixel_round(p) { in \.{DVItype} you add---here you subtract }
  else vv:=vv+pixel_round(p) {but not in rotated mode}
  end;
  major(#,' ',p:1); goto move_down

@<Cases for vertical motion@>=
four_cases(down1):begin out_vmove('down',o-down1+1:1);
  end;
y0,four_cases(y1):begin y:=p; out_vmove('y',o-y0:1);
  end;
z0,four_cases(z1):begin z:=p; out_vmove('z',o-z0:1);
  end;

@ @<Cases for fonts@>=
sixty_four_cases(fnt_num_0): begin major('fntnum',p:1);
  goto change_font;
  end;
four_cases(fnt1): begin major('fnt',o-fnt1+1:1,' ',p:1);
  goto change_font;
  end;
four_cases(fnt_def1): begin major('fntdef',o-fnt_def1+1:1,' ',p:1);
  define_font(p); goto done;
  end;

@ Here is where we add some special \.{LGP} features, which
will be called up by the use of the \.{\\special} primitive in \TeX.
See the chapter,
``\&{Special }{\tentex LGP}\&{ functions}'' for more information.

@<Translate an |xxx| command and |goto done|@>=
begin major('xxx'''); bad_char:=false;
for k:=1 to name_length do spec_string[k]:=0;
for k:=1 to p do
  begin q:=get_byte; spec_string[k]:=q;
  if (q>=" ")and(q<="~") then
    begin if showing then print(xchr[q]);
    end
  else bad_char:=true;
  end;
if showing then print('''');
if bad_char then error('non-ascii character in xxx command!');
if ((spec_string[1]="b") or
    (spec_string[1]="v") or
    (spec_string[1]="o") or
    (spec_string[1]="r") or
    (spec_string[1]="s"))
   then @<Check for valid \.{LGP} special@>;
@.non-ascii character...@>
goto done;
end
@^Specials@>

@ @<Translate a |set_char|...@>=
begin
@<Check for correct position on full-page bit-map, and update if needed@>;
 with font_mem[cur_font,p] do begin
  q:= tfm_width; {used later, for left to right movement} 
   if raster_ptr<>0 then begin
    if raster_ptr=-1 then output_char(p) {character already in \.{LGP} memory}
    else begin
     active_font:=open_font(cur_font);
     hs_in_pixels:=(envelope div half_shift);
     hs:=(hs_in_pixels+31) div 32; {number of 32-bit words per line}
     hs_in_bytes:=(hs_in_pixels+7) div 8;
     lgp_sixteen_bit_words:=(hs_in_pixels+15) div 16;
     vs:=(envelope mod half_shift);
     if (hs*vs) < big_char_threshold then @<Define a small-size character@>
     else @<Use 16-bit arguments for character dimensions@>;
     pxl_move_to_byte(raster_ptr);
     @<Send the rasters@>;
     if (hs*vs) < huge_char_threshold then begin raster_ptr:=-1;
      {if {\tt "d"} or {\tt "c"} string used, mark this character as stored}
      output_char(p); {and call it into the text stream for setting}
      end
    end;
@^Define a Small-Size Character@>
@^Define a Large-Size Character@>
@^Define a Block of Raster Data@>
   end else begin
      print_font(cur_font); print_ln(': character ',p:1,' is undefined')
      end;
 end;
 if (o>" ")and(o<="~") then
  begin if out_mode>terse then out_text(p); minor('setchar',p:1);
  end
else major('setchar',p:1);
goto fin_set;
end
 
@ For most characters we can use byte-length arguments for
all dimensions.

@<Define a small-size...@>=
begin 
  escape_string("d"); {use byte-length arguments for character dimensions}
  output_byte(p);
  output_byte(cur_font);
  if right_reading then begin
   output_byte(lgp_sixteen_bit_words);
   output_byte(vs);
   output_byte(-x_offset);
   output_byte(y_offset);
   output_byte(lgp_width); 
    {set \\{xinc} value to advance the print position in the \.{LGP}}
   output_byte(0); {zero for the \\{yinc} value}
   end
  else begin
   output_byte((vs+15)div 16); {16 bit micro-processor words/scan line}
   output_byte(hs_in_pixels);
   output_byte(-y_offset);
   output_byte(hs_in_pixels - x_offset);
   output_byte(0); {zero for the \\{xinc} value}
   output_byte(lgp_width); 
    {set \\{yinc} value to advance the print position in the \.{LGP}}
   end
end

@ For oversize characters we use either the ``Define a Large-Size
Character'' sequence (with 16-bit arguments), or the ``Define a
Block of Raster Data'' sequence that paints directly on
the bit map, and does not use up storage in the \.{LGP}.

@d sign_extension = @'377

@<Use 16-bit...@>=
begin 
  if (hs*vs) < huge_char_threshold then begin
    escape_string("c");
    output_byte(p);
    output_byte(cur_font);
    end else escape_string("b");
  if right_reading then begin
   send_in_low_high_sequence(lgp_sixteen_bit_words);  
     {16 bit micro-processor words per scan line}
   send_in_low_high_sequence(vs);
   output_byte(-(x_offset mod 256));
   if x_offset>255 then output_byte(-(x_offset div 256)) else
      begin if x_offset>0 then output_byte(sign_extension)
       else output_byte(-(x_offset) div 256);
       end; {These tiresome complications necessary for negative values}
   if y_offset>=0 then send_in_low_high_sequence(y_offset) 
   else begin i:=-y_offset;   output_byte(-(i mod 256));
     if i>255 then output_byte(-(i div 256)) else
      output_byte(sign_extension);
     end; {These tiresome complications necessary for negative values}
   send_in_low_high_sequence(lgp_width); 
     {set \\{xinc} value to advance the print position in the \.{LGP}}
   output_byte(0); output_byte(0); {zero for the \\{yinc} value}
   end
  else begin
   i:=((vs+15)div 16); {calculate first, then send}
   send_in_low_high_sequence(i);
     {16 bit micro-processor words per scan line}
   send_in_low_high_sequence(hs_in_pixels);
   output_byte(-(y_offset mod 256));
   if y_offset>255 then output_byte(-(y_offset div 256)) else
      begin if y_offset>0 then output_byte(sign_extension)
       else output_byte(-(y_offset) div 256);
       end; {These tiresome complications necessary for negative values}
   i:=(hs_in_pixels - x_offset); {calculate first, then send}
   if i>=0 then send_in_low_high_sequence(i)
   else begin i:=-i;   output_byte(-(i mod 256));
     if i>255 then output_byte(-(i div 256)) else
      output_byte(sign_extension);
     end; {These tiresome complications necessary for negative values}
   output_byte(0); output_byte(0); {zero for the \\{xinc} value}
   send_in_low_high_sequence(lgp_width); 
    {set \\{yinc} value to advance the print position in the \.{LGP}}
   end
end
@^Define a Block of Raster Data@>
@^Define a Large-Size Character@>

@ If the current \.{DVI} |set_char| command was preceded
by any of the various \.{DVI} commands which alter the |h| and |v|
coordinates, it is necessary to change coordinates in the \.{LGP}.  
This action is unnecessary after a preceding |set_char|, |fnt_num|
or |fnt_def|, if we discover that the baseline has not 
changed, since we can assume that the horizontal position has been updated
in the \.{LGP} by adding the \\{xinc} value (or in the case of rotated
characters, the \\{yinc} value) from the font character
definition of the last character processed
to the previous position.  (Right-reading font character definitions
in this program do not use the \\{yinc} value, which is always
set to 0. In rotated definitions, it is the \\{xinc} value which is set
to 0.)

@<Check for correct...@>= 
  begin
  if (lgp_h<>h) or (lgp_v<>v) then begin {see whether either has been changed}
      escape_string("m");
      if right_reading then begin
       send_in_low_high_sequence(hh);
       send_in_low_high_sequence(vv);
       end 
      else begin
       send_in_low_high_sequence(vv);
       send_in_low_high_sequence(hh);
       end
    end;
  end
@^Define a Small-Size Character@>

@ @<Send the rasters@>=
begin
 lgpsbw_count:=lgp_sixteen_bit_words;
 if right_reading then begin
  for k:=1 to hs*vs do begin
   read_pxl_bytes;
   output_byte(bits_reversed[b0]);
   output_byte(bits_reversed[b1]); decr(lgpsbw_count);
   if lgpsbw_count>0 then begin {this trims out unnecessary bytes of zero}
    output_byte(bits_reversed[b2]);
    output_byte(bits_reversed[b3]); decr(lgpsbw_count);
    end;
   if lgpsbw_count=0 then lgpsbw_count:=lgp_sixteen_bit_words;
   end
  end
 else begin
  @<Copy |glyph_bytes| into temporary array@>;
  bit_columns:=hs_in_pixels; {full bit count}
  glyph_step:=hs*4; 
   {controls increment of index; 4 bytes/32-bit loose-packed \.{PXL} word}
  row_offset:=hs_in_bytes-1; {controls starting point}   
  @<Read bit-columns from right to left, to get past zero-fill,
    shifting as you go@>;  
  clump_columns:=0; 
  repeat 
   @<Pick up column bits, top to bottom, starting with right-hand column@>  
   until bit_columns = 0; 
  end
end

@ This routine ticked along just fine until the arrival of the first
font with zero width picture drawing characters.  Then we discovered
that there has to be a special provision to force the \.{LGP}
update its coordinates when there is a sequence of zero-width characters.

@<Finish a command that either sets or puts a character...@>=
if q=invalid_width then
  begin error('character ',p:1,' invalid in font ');
@.character $c$ invalid...@>
  print_font(cur_font);
  if cur_font<>nf then print('!'); {font |nf| has `\.!' in its name}
  end;
lgp_v:=v; {make sure that |lgp_v| holds the current baseline coordinate}
if o>=put1 then goto done;
if q=invalid_width then q:=0
  else hh:=hh+pixel_round(q);
if q=0 then lgp_h:=0 {must force \.{LGP} repositioning after zero width}
  else lgp_h:=h+q; {set up value to match what |move_right| will produce}
goto move_right

@ There is an interesting point here, that caused a bug until it was
noticed.  The actual rectangle defined for a rule uses the function
|rule_pixels| to adjust for floating-point error, but this function
can not be used to update the spacing, or we will have trouble with
certain mixtures of rules and characters.  The spacing is updated
by means of the same |pixel_round| function that is used for everything
else except rules.

@<Finish a command that either sets or puts a rule...@>=
q:=signed_quad;
escape_string("m");
if right_reading then begin
 send_in_low_high_sequence(hh);
 send_in_low_high_sequence(vv);
 end
else begin
 send_in_low_high_sequence(vv);
 send_in_low_high_sequence(hh);
 end;
if showing then
  begin print(' height ',p:1,', width ',q:1);
  if (p<=0)or(q<=0) then print(' (invisible)')
  else print(' (',rule_pixels(p):1,'x',rule_pixels(q):1,' pixels)');
  end;
escape_string("r");
i:=hh+rule_pixels(q);
if right_reading then begin
 j:=vv+rule_pixels(p); {any change in y is explicit in the {\tt DVI} file}
 send_in_low_high_sequence(i); 
 send_in_low_high_sequence(j); 
 end
else begin
 j:=vv-rule_pixels(p); {height of rotated rule is to the left}
 send_in_low_high_sequence(j);
 send_in_low_high_sequence(i);
 end;
@^Draw a Rectangle@>
if o=put_rule then goto done;
if out_mode > terse then print_ln(' ');
p:=q; {that's what |out_space| expects}
out_space('rule spacing');
@.rule spacing@>

@ Since the parent program,
\.{DVItype}, is intended to diagnose strange errors, it checks
carefully to make sure that |h| and |v| do not get out of range.
Normal \.{DVI}-reading programs need not do this, but it comes in
handy at unexpected times, so we keep the code active.

@<Finish a command that sets |h:=h+q|, then |goto done|@>=
if (h>0)and(q>0) then if h>infinity-q then
  begin error('arithmetic overflow! parameter changed from ',
@.arithmetic overflow...@>
    q:1,' to ',infinity-h:1);
  q:=infinity-h;
  end;
if (h<0)and(q<0) then if -h>q+infinity then
  begin error('arithmetic overflow! parameter changed from ',
    q:1, ' to ',(-h)-infinity:1);
  q:=(-h)-infinity;
  end;
if showing then
  begin print(' h:=',h:1);
  if q>=0 then print('+');
  print(q:1,'=',h+q:1,', hh:=',hh:1);
  end;
h:=h+q;
if abs(h)>max_h_so_far then
  begin max_h_so_far:=abs(h); 
  if out_mode=errors_only then max_h_so_far:=max_h_so_far + warn_pixel;
  {reduce the incidence of these messages by using a |warn_pixel| threshold}
  if abs(h)>max_h then error('warning: |h|>',max_h_so_far:1,'!');
@.warning: |h|...@>
  end;
goto done

@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
if (v>0)and(p>0) then if v>infinity-p then
  begin error('arithmetic overflow! parameter changed from ',
@.arithmetic overflow...@>
    p:1,' to ',infinity-v:1);
  p:=infinity-v;
  end;
if (v<0)and(p<0) then if -v>p+infinity then
  begin error('arithmetic overflow! parameter changed from ',
    p:1, ' to ',(-v)-infinity:1);
  p:=(-v)-infinity;
  end;
if showing then
  begin print(' v:=',v:1);
  if p>=0 then print('+');
  print(p:1,'=',v+p:1,', vv:=',vv:1);
  end;
v:=v+p;
if abs(v)>max_v_so_far then
  begin max_v_so_far:=abs(v);
  if abs(v)>max_v then error('warning: |v|>',max_v_so_far:1,'!');
@.warning: |v|...@>
  end;
goto done

@ @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|...@>=
if showing then
  begin print_ln(' ');
  print('level ',ss:1,':(h=',h:1,',v=',v:1,
    ',w=',w:1,',x=',x:1,',y=',y:1,',z=',z:1,
    ',hh=',hh:1,',vv=',vv:1,')');
  end;
goto done

@ @<Finish a command that changes the current font...@>=
font_num[nf]:=p; cur_font:=0;
while font_num[cur_font]<>p do incr(cur_font);
escape_string("f");
output_byte(cur_font);
if showing then
  begin print(' current font is '); print_font(cur_font);
  end;
goto done
@^Set Font@>

\f

@* Rotating the bit-map.
For certain applications such as the making of transparencies, it
is useful to have the capacity to set in landscape mode.  For this
we must turn the bit-map around $90\deg$.


@ @<Pick up column bits...@>= 
begin
 bit_rows:=((vs+15) div 16) * 16;
    {vertical height of envelope, or number of rows, rounded up}
 n:=row_offset;
 repeat 
  @<Record each group of eight bits in reverse order and send to the 
    \.{LGP}@>; 
  until bit_rows <= 0; 
 incr(clump_columns); if clump_columns >= clump_limit then begin
  clump_columns:=0; {ready to start again} 
  clump_limit:=8; {always reset to 8 after first pass which might be smaller} 
  decr(row_offset); {begin from a different starting point}
  end;
 decr(bit_columns);
end

@ This needs a couple of definitions to make it look right.
The two opposing shifts take care of the bit reversal for the
\.{LGP}. 

@d shift_left(#)==#:=#+#; {shift left by addition}
@d shift_right(#)==#:=# div 2; {and right by integer division}
 
@<Record each group...@>=
begin
 rotated_byte:=0;
 for k:=1 to 8 do begin
  shift_left(rotated_byte);
  if odd(glyph_bytes[n]) then rotated_byte:=rotated_byte + 1;
  shift_right(glyph_bytes[n]);
  n:=n + glyph_step;
  decr(bit_rows);
  end;
 output_byte(bits_reversed[rotated_byte]);
end

@ We need not waste time and font storage space on the zero-fill
in the loose-packed \.{PXL} format.  We go to the last significant
byte in each row, starting from |row_offset| and incrementing the
index of our temporary array by |glyph_step|.  We pass over and over
through the designated bytes, shifting continually to the right,
until we have brought a non-zero bit into the low-order position
of at least one row. 

@<Read bit-columns from right...@>= 
begin
 non_pixels:=(hs_in_bytes*8)-hs_in_pixels;
 clump_limit:=8-non_pixels; 
  {same as |hs_in_pixels mod 8|--significant bits in final byte of each row}
 if non_pixels > 0 then begin
  n:=row_offset;
  repeat
   for m:=1 to non_pixels do shift_right(glyph_bytes[n]);
   n:=n+glyph_step;
   until n > (vs*hs*4); {count of bytes in loose-packed \.{PXL} format}
  end
end

@ @<Copy |glyph_bytes|...@>=
begin
  for k:=1 to 10000 do glyph_bytes[k]:=0;
  n:=0;
  for k:=1 to hs*vs do begin
    read_pxl_bytes;
    glyph_bytes[n]:=b0; incr(n);
    glyph_bytes[n]:=b1; incr(n);
    glyph_bytes[n]:=b2; incr(n);
    glyph_bytes[n]:=b3; incr(n);
    end
end

\f

@* Skipping pages.
A routine that's much simpler than |do_page| is used to pass over
pages that are not being translated. The |skip_pages| subroutine
is assumed to begin just after the preamble has been read, or just
after a |bop| has been processed. It continues until either finding a
|bop| that matches the desired starting page specifications, or until
running into the postamble. For the moment, this routine is unused,
so we isolate it in comment braces.  It would be wasteful to remove it
altogether, since it may come in handy for more sophisticated versions
of \.{DVI2LGP}.

@p @{
procedure skip_pages;
label 9999; {end of this subroutine}
var p:integer; {a parameter}
@!k:0..255; {command code}
@!down_the_drain:integer; {garbage}
begin showing:=false;
while true do
  begin if eof(dvi_file) then bad_dvi('the file ended prematurely!');
@.the file ended prematurely@>
  k:=get_byte;
  p:=first_par(k);
  case k of
  bop: begin @<Pass a |bop| command, setting up the |count| array@>;
    if not started and start_match then
      begin started:=true; goto 9999;
      end;
    end;
  set_rule,put_rule: down_the_drain:=signed_quad;
  fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: define_font(down_the_drain);
  xxx1,xxx1+1,xxx1+2,xxx1+3: while p>0 do
    begin down_the_drain:=get_byte; decr(p);
    end;
  post: begin in_postamble:=true; goto 9999;
    end;
  othercases do_nothing
  endcases;
  end;
9999:end;
@}

@ Global variables called |old_backpointer| and |new_backpointer|
are used to check whether the back pointers are properly set up.
Another one tells whether we have already found the starting page.

@<Glob...@>=
@!old_backpointer:integer; {the previous |bop| command location}
@!new_backpointer:integer; {the current |bop| command location}
@!started:boolean; {has the starting page been found?}

@ @<Set init...@>=
old_backpointer:=-1; started:=false;

@ When the \.{DVI} file is being read sequentially, in what
we paradoxically call |backwards| mode, it is possible
to make an extra check for valid file organization here,
but if we actually make use of the backpointers to read
the file last to first (so that it will come out sorted on the
output tray) this check becomes meaningless. The need for
full debugging information, incidentally, explains why
the two debugging modes have a default setting of |backwards|.
The convenient default for production mode is, unfortunately,
system dependent, since it calls on the |move_to_byte| procedure.
@^system dependencies@>

@<Pass a |bop|...@>=
if backwards then new_backpointer:=cur_loc-1; incr(page_count);
for k:=0 to 9 do count[k]:=signed_quad;
if backwards then begin
  if signed_quad<>old_backpointer
    then print_ln('backpointer in byte ',cur_loc-4:1,
      ' should be ',old_backpointer:1,'!');
@.backpointer...should be p@>
  old_backpointer:=new_backpointer;
  end else new_backpointer:=signed_quad

@* Using the backpointers.
First comes a routine that illustrates how to find the postamble quickly.

@<Find the postamble, working back from the end@>=
n:=dvi_length;
if n<53 then bad_dvi('only ',n:1,' bytes long');
@.only n bytes long@>
m:=n-4;
repeat if m=0 then bad_dvi('all 223s');
@.all 223s@>
move_to_byte(m); k:=get_byte; decr(m);
until k<>223;
if k<>id_byte then bad_dvi('ID byte is ',k:1);
@.ID byte is wrong@>
move_to_byte(m-3); q:=signed_quad;
if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
@.post pointer is wrong@>
move_to_byte(q); k:=get_byte;
if k<>post then bad_dvi('byte ',q:1,' is not post');
@.byte n is not post@>
post_loc:=q; first_backpointer:=signed_quad

@ Note that the last steps of the above code save the locations of the
the |post| byte and the final |bop|.  We had better declare these global
variables, together with another one that we will need shortly.

@<Glob...@>=
@!post_loc:integer; {byte location where the postamble begins}
@!first_backpointer:integer; {the pointer following |post|}
@!start_loc:integer; {byte location of the first page to process}
@!back_count:integer; {reverse count for simple page positioning}

@ The next little routine shows how the backpointers can be followed
to move through a \.{DVI} file in reverse order. It would of course be 
simpler and faster
just to read the whole file from the beginning but we would lose some
rather useful features in that case. 
The chief of these is the ability to read back-to-front and stack
the output pages on the tray in the expected order of reading.  This
is the default format for production runs of the program.  There is
one trick to that, however.  When we have a long file of pages
which all have the same page number in \TeX\ \.{count0}, we have to
keep the program from backing up to the preamble when it is
in default mode.  In |backwards| mode, we keep changing the |start_loc|
byte indicator every time |start_match| returns |true|, but in default
mode, we prevent any further change after the first |start_match|.
Note that the use of the
|move_to_byte| procedure makes this module system dependent, however.
@^system dependencies@>

@<Count the pages and move to the starting page@>=
if not backwards then if start_on_page>0 then decr(start_on_page);
  {a little adjustment that turned out to be necessary}
if backwards then back_count:=total_pages-start_on_page+1
else begin if ((start_on_page + max_pages)<total_pages) then 
  back_count:=total_pages-start_on_page-max_pages+1 else back_count:=1;
  end;
q:=post_loc; p:=first_backpointer; start_loc:=-1;
if p>=0 then
  repeat {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
  if p>q-46 then
    bad_dvi('page link ',p:1,' after byte ',q:1);
@.page link wrong...@>
  q:=p; move_to_byte(q); k:=get_byte;
  if k=bop then incr(page_count)
  else bad_dvi('byte ',q:1,' is not bop');
@.byte n is not bop@>
  for k:=0 to 9 do count[k]:=signed_quad;
  if page_count=back_count then begin
    start_there[0]:=true; start_count[0]:=count[0];
  end; {force a match}
  if start_match then begin 
    if backwards then start_loc:=q {keep on looking for first page}
    else begin
      if start_loc=-1 then start_loc:=q {in this instance, first match wins}
      end
    end;
  p:=signed_quad;
  until p<0;
if page_count<>total_pages then
  print_ln('there are really ',page_count:1,' pages, not ',total_pages:1,'!');
@.there are really n pages@>
if start_loc<0 then abort('starting page number could not be found!');
move_to_byte(start_loc+1); old_backpointer:=start_loc;
for k:=0 to 9 do count[k]:=signed_quad;
new_backpointer:=signed_quad; started:=true

@* Reading the postamble.
Now imagine that we are reading the \.{DVI} file and positioned just
four bytes after the |post| command. That, in fact, is the situation,
when the following part of \.{DVI2LGP} is called upon to read, translate,
and check the rest of the postamble.

@p procedure read_postamble;
var k:integer; {loop index}
@!p,@!q,@!m:integer; {general purpose registers}
begin showing:=false; post_loc:=cur_loc-5;
if not take_defaults then
   print_ln('Postamble starts at byte ',post_loc:1,'.');
@.Postamble starts at byte n@>
if signed_quad<>numerator then
  print_ln('numerator doesn''t match the preamble!');
@.numerator doesn't match@>
if signed_quad<>denominator then
  print_ln('denominator doesn''t match the preamble!');
@.denominator doesn't match@>
if signed_quad<>mag then if new_mag=0 then
  print_ln('magnification doesn''t match the preamble!');
@.magnification doesn't match@>
max_v:=signed_quad; max_h:=signed_quad;@/
if not take_defaults then
   print('maxv=',max_v:1,', maxh=',max_h:1);@/
max_s:=get_two_bytes; total_pages:=get_two_bytes;@/
if not take_defaults then
   print_ln(', maxstackdepth=',max_s:1,', totalpages=',total_pages:1);
@<Process the font definitions of the postamble@>;
@<Make sure that the end of the file is well-formed@>;
end;

@ When we get to the present code, the |post_post| command has
just been read.

@<Make sure that the end of the file is well-formed@>=
q:=signed_quad;
if q<>post_loc then
  print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!');
@.bad postamble pointer@>
m:=get_byte;
if m<>id_byte then print_ln('identification in byte ',cur_loc-1:1,
@.identification...should be n@>
    ' should be ',id_byte:1,'!');
k:=cur_loc; m:=223;
while (m=223)and not eof(dvi_file) do m:=get_byte;
if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
@.signature...should be...@>
    ' should be 223!')
else if cur_loc<k+4 then
  print_ln('not enough signature bytes at end of file (',
@.not enough signature bytes...@>
    cur_loc-k:1,')');

@ @<Process the font definitions...@>=
open_lgp_file; {it's going to be needed now}
repeat k:=get_byte;
if (k>=fnt_def1)and(k<fnt_def1+4) then
  begin p:=first_par(k); define_font(p); 
   if out_mode>errors_only then print_ln(' '); k:=nop;
  end;
until k<>nop;
if k<>post_post then print_ln('byte ',cur_loc-1:1,' is not postpost!')
@.byte n is not postpost@>

@* The main program.
Now we are ready to put it all together. This is where \.{DVI2LGP} starts,
and where it ends. The parent program contains two short tests 
to determine whether the |postamble| can be read first.  Since the
calculations for shifting output left, center or right
require some information from the postamble, we have eliminated those tests.
This program always reads the postamble before starting on the main text.

@p begin initialize; {get all variables initialized}
dialog; {set up all the options}
@<Process the preamble@>;
@<Find the postamble, working back from the end@>;
  in_postamble:=true; read_postamble; in_postamble:=false;
  @<Count the pages and move to the starting page@>;
@<Position output left, center or right on output page@>;
if not in_postamble then @<Translate up to |max_pages| pages@>;
escape_string("e"); {finish the \.{LGP} file}
if clever then @<Set up prompt for next step@>;
final_end:end.

@ The main program needs a few global variables in order to do its work.

@<Glob...@>=
@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}

@ A \.{DVI}-reading program that reads the postamble first need not look at the
preamble; but \.{DVI2LGP} looks at the preamble in order to do error
checking, and to display the introductory comment.

@<Process the preamble@>=
open_dvi_file;
p:=get_byte; {fetch the first byte}
if p<>pre then bad_dvi('First byte isn''t start of preamble!');
@.First byte isn't...@>
p:=get_byte; {fetch the identification byte}
if p<>id_byte then
  print_ln('identification in byte 1 should be ',id_byte:1,'!');
@.identification...should be n@>
@<Compute the conversion factor@>;
p:=get_byte; {fetch the length of the introductory comment}
if interaction>batch_mode then begin
 print('''');
 while p>0 do
   begin decr(p); print(xchr[get_byte]);
   end;
 print_ln('''');
 end

@ Here are some globals to store values from the postamble, along
with some other values derived from them.

@<Glob...@>=
@!conv: real; {converts \.{DVI} units to pixels}
@!true_conv: real; {converts unmagnified \.{DVI} units to pixels}
@!numerator,@!denominator: integer; {stated conversion ratio}
@!mag: integer; {magnification factor times $1000$}
@!warn_pixel: integer; {threshold for reporting right margin overrun}

@ The conversion factor |conv| is figured as follows: There are exactly
|n/d| \.{DVI} units per decimicron, and 254000 decimicrons per inch,
and |resolution| pixels per inch. Then we have to adjust this
by the stated amount of magnification.

@<Compute the conversion factor@>=
numerator:=signed_quad; denominator:=signed_quad;
if numerator<=0 then bad_dvi('numerator is ',numerator:1);
@.numerator is wrong@>
if denominator<=0 then bad_dvi('denominator is ',denominator:1);
@.denominator is wrong@>
if not take_defaults then
   print_ln('numerator/denominator=',numerator:1,'/',denominator:1);
conv:=(numerator/254000.0)*(resolution/denominator);
warn_pixel:=trunc(threshold/conv); 
      {threshold for reporting right-margin overrun}@t}% --CHECKME--RKF @>@/
if out_mode=errors_only then max_h_so_far:=max_h+warn_pixel;
  {give a right-margin overrun warning only for significant amounts}
mag:=signed_quad;
if new_mag>0 then mag:=new_mag
else if mag<=0 then bad_dvi('magnification is ',mag:1);
@.magnification is wrong@>
true_conv:=conv; conv:=true_conv*(mag/1000.0);
if not take_defaults then
   print_ln('magnification=',mag:1,'; ',conv:16:8,' pixels per DVI unit')

@ It is possible to shift the output frame to the left, center or right 
of the paper sheet, by specifying |xchr[sw_c]|L, 
|xchr[sw_c]|C or |xchr[sw_c]|R in the option switch line.  

@d both_margins==physical_margin + right_margin 
 
@<Position output left...@>=
begin 
 @<Position rotated output if necessary@>;
  if right_reading then 
    nett_width:=full_width-both_margins
   else 
    nett_width:=page_pixel_height; 
     {\.{LGP} will quite happily print on bottom line of page without clipping}
 left_margin:=physical_margin; {initialize for the minimum}
   {protect left overshoot with a minimum pixel count at left edge}
 if margin_set <> "L" then margin_count:=nett_width - pixel_round(max_h);
 if margin_count < 0 then begin
  margin_count := 0;    { Keep it safe! }
  print_ln('Page is already too wide.  I cannot add a margin ');
  print_ln('I will proceed as if you had specified the ',xchr[sw_c],'L margin');
  margin_set:="L"; {and will even reset the switch to prove it}
 end;
 if margin_set = "C" then begin
  left_margin := margin_count div 2;
  if (not right_reading) and (left_margin > both_margins)
    then left_margin:=left_margin+physical_margin;
 {Shove output to top of page by amount of hardware margin, but don't overrun}
    end;
 if margin_set = "R" then left_margin:=margin_count;
 if not take_defaults then
 begin
    print('Left margin is ');
    print_ln(left_margin:1,' pixels wide.');
 end;
 margin_count:=0;
end

@ @<Position rotated...@>=
begin
 if right_reading then top_margin:=normal_top 
 else begin
  top_margin:=(full_width-pixel_round(max_v)) div 2;
  if top_margin < physical_margin then top_margin:=physical_margin;
  end
end

@ The code shown here uses a convention that has proved to be useful:
If the starting page was specified as, e.g., `\.{1.*.-5}', then
all page numbers in the file are displayed by showing the values of
counts 0, 1, and~2, separated by dots. Such numbers can, for example,
be displayed on the console of a printer when it is working on that
page. Since we have chosen to make back to front reading the default
for normal production runs, we have to confess that this module is
system dependent.
@^system dependencies@>

@<Translate up to...@>=
begin while max_pages>0 do
  begin decr(max_pages);
  if interaction>batch_mode then begin
   if out_mode > errors_only then print_ln(' '); 
   print(cur_loc-45:1,': beginning of page ');
   end;
@.beginning of page@>
  for k:=0 to start_vals do
    begin print(count[k]:1);
    if k<start_vals then print('.')
    else print_ln(' ');
    end;
  if not do_page then bad_dvi('page ended unexpectedly!');
@.page ended unexpectedly@>
  if not backwards then if (new_backpointer<>-1) then 
    move_to_byte(new_backpointer) else goto done;
  repeat k:=get_byte;
  if (k>=fnt_def1)and(k<fnt_def1+4) then
    begin p:=first_par(k); define_font(p); k:=nop;
    end;
  until k<>nop;
  if k=post then
    begin
      in_postamble:=true; goto done;
    end;
  if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop');
@.byte n is not bop@>
  @<Pass a |bop|...@>;
  end;
done:end

@ Here we provide a prompt at the close of the program, which sets
up a command line for the next step and tricks the operating system 
into thinking the user typed it.  If the line looks right, it 
takes no more than a simple carriage return to initiate the 
next step of processing.  This sort of thing is, of course, 
not even remotely possible in standard \PASCAL, and 
all we can really do outside the limits of acutely system dependent
programming is set up the prompt array.  Then you are on your own. 
 
@d prompt_length=80 {or whatever terminal width you want to simulate} 
@d prompt_name=='TLGP' {change to whatever suits you}
@d prompt_name_length=4  {this is set for 'TLGP'; change as needed}
 
@<Glob...@>=
@!prompt_array:packed array[1..prompt_name_length] of char; 
@!prompt_string:packed array[1..prompt_length] of char; 

 
@ @<Set up prompt for next step@>= 
prompt_array:=prompt_name;
for p:=1 to prompt_length do prompt_string[p]:=' ';
for p:=1 to prompt_name_length do prompt_string[p]:=prompt_array[p];
q:=prompt_name_length+2; {leave a space after the command}
p:=1; while lgp_name[p]<>' ' do begin
  prompt_string[q]:=lgp_name[p]; incr(p); incr (q);
  end;@/
{a system-dependent module goes here}

@* Special {\tentex LGP} functions.
There are several rather useful features of the \.{LGP} that are not
part of Vanilla \TeX\ but must be reached through the \TeX\
\.{\\special} escape.  We have
reserved these for a new chapter, to avoid confusion in the
main program, which is devoted to the essentials of \TeX.

The vector drawing feature, which produces a line one pixel thick
from any arbitrary point on the bit-map to any other point, is
made available for the user of \TeX\ through the use of two 
calls on the \.{\\special} primitive.  The first call sets the starting
point for the vector, and reads \.{\\special\{begin-vector\}}.  This must
precede the \.{\\special\{vector\}} command, which actually draws the vector.
The two commands need not be contiguous but, for obvious reasons, they
must be on the same page.  If the paging operation of \TeX\ accidentally
separates a vector-start from its associated vector-draw command, 
the vector-draw command will be cancelled.

The program also recognizes \.{\\special\{overlay=}\\{filename}\.{\}}
for the ``forms overlay'' feature, as well as  
\.{\\special\{raster-data=}\\{filename}\.{\}} and
\.{\\special\{screen-image=}\\{filename}\.{\}} for reading predigested
graphics.  The ``forms overlay'' feature doesn't seem to have been
implemented in the \.{LGP} hardware, however,
so the code implementing
that feature is of no real use at present.  We haven't yet got around to
the other two, but the framework is in place.  

@ We will need some extra globals.

@<Glob...@>=
@!spec_file:packed file of eight_bits; {a previously prepared LGP file}
@!lgp_spec_file_name:packed array[1..name_length] of text_char;

@ @<Check for valid \.{LGP} special@>=
case spec_string[1] of 
            "b":@<Store starting point of vector@>;
            "v":@<Complete vector drawing command@>;
            "o":@<Bring in a forms overlay@>;
            "r":@<Get a block of raster data from a file@>;
            "s":@<Get a block of screen-image data from a file@>;
  endcases

@ The full form of this is \.{\\special\{begin-vector\}},
but we only check for the word \.{begin} and a correct string length.

@<Store starting point of vector@>=
begin
 if ((spec_string[2]="e") and (spec_string[3]="g") and (spec_string[4]="i") and
     (spec_string[5]="n") and (p=12)) then
   begin 
    hh_vector:=hh; vv_vector:=vv; { (already checked for range) }
    end;
 end

@ The command for this is \.{\\special\{vector\}}.

@<Complete vector drawing command@>=
begin
 if ((spec_string[2]="e") and (spec_string[3]="c") and (spec_string[4]="t") and
 (spec_string[5]="o") and (spec_string[6]="r")) then
  begin
   if (hh_vector=infinity) or (vv_vector=infinity) then begin
    print_ln('--you haven''t set a start for this vector!');
    print_ln
      ('Cancelled---use \special{begin-vector} before \special{vector}') 
   end else begin
    if showing then
      print(' vector from hh= ',hh_vector:1,
      ' and vv= ', vv_vector:1, ' to hh=', hh:1,
      ' and vv= ', vv:1);
    escape_string("m"); {reposition \.{LGP} to vector start}
    if right_reading then begin
     send_in_low_high_sequence(hh_vector);
     send_in_low_high_sequence(vv_vector);
     end 
    else begin
     send_in_low_high_sequence(vv_vector);
     send_in_low_high_sequence(hh_vector);
     end;
    escape_string("v"); 
    if right_reading then begin {\.{LGP} gets repositioned to location}
     send_in_low_high_sequence(hh); {at which \.{\\special\{vector\}} }
     send_in_low_high_sequence(vv); {was called in \TeX\ file}
     end 
    else begin
     send_in_low_high_sequence(vv);
     send_in_low_high_sequence(hh);
     end;
    hh_vector:=infinity; vv_vector:=infinity;
    end
   end;
 end

@ @<Set up |lgp_spec_file_name|@>=
begin
  if (spec_string[k]>="a") or (spec_string[k]<="z") then begin
   lgp_spec_file_name[n]:=system_case_of(spec_string[k]);
   end
  else lgp_spec_file_name[n]:=xchr[spec_string[k]];
  incr(k);
  end

@ The command for this is \.{\\special\{overlay=}\\{filename}\.{\}}.

@<Bring in a forms overlay@>=
begin
 if ((spec_string[2]="v") and (spec_string[3]="e") and (spec_string[4]="r") and
 (spec_string[5]="l") and (spec_string[6]="a") and (spec_string[7]="y") and
 (spec_string[8]="=")) then 
  begin
   for k:=1 to name_length do lgp_spec_file_name[k]:=' '; 
   k:=9; for n:=1 to p-8 do @<Set up |lgp_spec_file_name|@>;
   @<Get the overlay@>;
   end
 end

@ An overlay file should look more or less like a normal \.{LGP}
file, but it can only be one page long, and it must terminate with
an \.{<ESC> p} sequence.

@d escape=@'033  {{\mc ASCII} \.{ESC} character}

@<Get the overlay@>=
begin
  open_spec_file; 
  if not eof(spec_file) then
    repeat 
      if eof(spec_file) then begin error(' incomplete overlay file!'); 
        goto 9998; end;
      p:=get_spec_byte; 
      if (q=escape) then end_of_special:=(p="p");
      output_byte(p); q:=p;
      until end_of_special
    end;


@ The full form of this is \.{\\special\{raster-data=}\\{filename}\.{\}},
but we only check for the word \.{raster} and a correctly positioned
`$=$' character. The file will take the form of a fully pre-prepared
``Define a Block of Raster Data'' sequence.
@^Define a Block of Raster Data@>

@<Get a block of raster data from a file@>=
begin
 if ((spec_string[2]="a") and (spec_string[3]="s") and (spec_string[4]="t") and
 (spec_string[5]="e") and (spec_string[6]="r")
 and (spec_string[12]="=")) then {should check it all, but enough is enough!}
 begin
  do_nothing 
  end
 end

@ The full form of this is \.{\\special\{screen-image=}\\{filename}\.{\}},
but we only check for the word \.{screen} and a correctly positioned
`$=$' character.  The file will be of the form described in the
\\{Symbolics Inc.} manual under ``Print a Screen Image.''
@^Print a Screen Image@>

@<Get a block of screen-image data from a file@>=
begin 
 if ((spec_string[2]="c") and (spec_string[3]="r") and (spec_string[4]="e") and
 (spec_string[5]="e") and (spec_string[6]="n")
 and (spec_string[13]="=")) then
  begin
   do_nothing 
   end
 end

@ We use the simple |get_spec_byte| function to read the next byte or
bytes from the designated |spec_file|. This is exactly the same function
as the one used for |dvi_file| reading, but since the file name is
explicitly declared, we take the easy way out and copy it here.
@^system dependencies@>

@<Procedures for \.{LGP}...@>=

function get_spec_byte:integer; {returns the next byte, unsigned}
var b:eight_bits;
begin if eof(spec_file) then get_spec_byte:=0
else  begin read(spec_file,b); get_spec_byte:=b;
  end;
end;
@#
procedure open_spec_file;
begin 
  reset(spec_file,lgp_spec_file_name);
end;

@* System-dependent changes.
This module should be replaced, if necessary, by changes to the program
that are necessary to make \.{DVI2LGP} work at a particular installation.
It is usually best to design your change file so that all changes to
previous modules preserve the module numbering; then everybody's version
will be consistent with the printed program. More extensive changes,
which introduce new modules, can be inserted here; then only the index
itself will get a new module number.
@^system dependencies@>

@* Index.
Pointers to error messages appear here together with the section numbers
where each ident\-i\-fier is used.
SHAR_EOF
fi # end of overwriting check
if test -f 'lgpext.h'
then
	echo shar: will not over-write existing file "'lgpext.h'"
else
cat << \SHAR_EOF > 'lgpext.h'
(* borrowed from Symbolics' randomio.h *)
function curpos(var fileptr:bytefile):integer;
external;

procedure setpos(var fileptr:bytefile; n: integer);
external;

(* borrowed from Howard Trickey's dvip *)
procedure opendvifile;
	external;
function getbyte: integer;
	external;
function signedbyte: integer;
	external;
function gettwobytes: integer;
	external;
function signedpair: integer;
	external;
function getthreebytes: integer;
	external;
function signedtrio: integer;
	external;
function signedquad: integer;
	external;

(* modified from dvip *)
function curdvipos(var f:bytefile):integer;
	external;
procedure setdvipos(var f:bytefile;n:integer);
	external;

(* and more routines to speed things up *)
procedure openlgpfile;
	external;

procedure outputbyte(b: integer);
	external;

function readbyte(var f: bytefile): integer;
	external;

function testeof(var f: bytefile): boolean;
    external;

(* now, some routines to implement paths for file names.  These are borrowed
   from Howard Trickey's dvitype *)
procedure setpaths;
    external;

function testaccess(accessmode:integer; filepath:integer): boolean;
    external;
SHAR_EOF
fi # end of overwriting check
if test -f 'dvi2lgp.1'
then
	echo shar: will not over-write existing file "'dvi2lgp.1'"
else
cat << \SHAR_EOF > 'dvi2lgp.1'
.ds TX T\h'-.1667m'\v'.22m'E\h'-.125m'\v'-.22m'X
.TH DVI2LGP 1 12/5/83
.SH NAME
dvi2lgp \- translate from .dvi file to .lgp file
.SH SYNOPSIS
.B dvi2lgp
.PP
.B dvi2lgp
dvifile[.dvi]

.SH DESCRIPTION
The DVI2LGP utility program reads binary device-independent (``.dvi'')
files that are produced by document compilers such as \*(TX, and
converts them into a preprocessed .lgp file in ``native-mode'' coding.
This program has two chief purposes:
.RS .5i
.TP 5
(1)
It assembles and outputs a sequential file of LGP codes, and
.TP 5
(2)
It can be used to determine whether a .dvi file is valid or
invalid, since it retains the symbolic output form of its parent,
DVItype.
.RE
.PP
There are two ways to invoke the program.  When the program is invoked
with a file name on the command line, translation of the .dvi file to
make a .lgp file starts immediately.  This is called ``default mode''
because default values are assigned to various options.  If no
arguments are placed on the command line, then the program will
inquire about the desired settings for a number of options before
translation begins.
.PP
DVI2LGP locates its font bit maps by looking in the directories specified in
the search path found in the TEXFONTS environment variable.  This is the
same variable that TeX uses to locate its font metric files.  If TEXFONTS is
not defined, then a default value is used.  The entire path must be no
longer than 100 characters long.  You can change the value of this
environment with the 
.I csh
command (for example)
.br
.in +2
setenv TEXFONTS .:/usr/lib/tex/fonts
.in -2
or the 
.I sh
command sequence
.br
.in +2
TEXINPUTS=.:/usr/lib/tex/fonts
.br
export TEXINPUTS
.in -2
.br
both of which will cause DVI2LGP to look for its bit maps first in the
current directory and then in /usr/lib/tex/fonts.
.PP
The rest of this description explains DVI2LGP's processing modes in more
detail.
.PP
The DVI2LGP processor attempts first of all to provide a simple and
direct method for interpreting .dvi files for the Symbolics Inc.
LGP-1 printer. In the default mode, we assume that the user wants an
entire file printed, at true size, and delivered to the output tray in
correct order for reading (i.e., backwards order for printing).  If
the user finds the default mode insufficent, there is a wide range of
options to produce various forms of standard \*(TX output, as well as
several forms of diagnostic output which can be used to check on the
correctness of the .dvi file.  Finally, it is possible to call on
certain special features of the LGP which are outside the normal
functions of \*(TX.  These are not part of the \*(TX typesetting system,
but they may be added to an otherwise normal .dvi file through the use
of the \*(TX \\special escape primitive.  Such usage will, of course, be
absolutely specific to the LGP printer, but a .dvi file containing
``\\special'' sequences can safely be run through any properly designed
.dvi interpreter.  The presence of the sequence will be noted in the
diagnostic log file, but no action will be taken (unless the
interpreter uses exactly the same \\special for some other purpose).
.PP
In default mode, we assume that the input filename argument appears on
the command line with the program call name.  If the program senses
the presence of the input filename on the command line, and finds no
other flags or switch characters, the processing begins at once and
selects defaults values for all options. If the input filename already
has the extension .dvi, this extension need not be supplied in the
command line, since it is assumed as the default.  Whatever the input
filename may be, the program in all cases generates an output filename
with the extension .lgp and creates this file on the user's current
working directory, (even if the input file has been taken from some
other directory or structure).  Thus, the first stages of a Unix
transaction will look like this:
.RS .5i
.br
This is DVI2LGP, Version n, last modified May 29, 1453
.br
[sample.dvi->sample.lgp]
.br
.RE
and the first stages of the transaction on other computers will be
similar, except that the file names may be folded into uppercase
characters.
.PP
After the program has started running in default mode, the .dvi
preamble banner line is displayed on the terminal from which the job
was started, followed by a
progress report which marks the occurrence of each beginning of page
marker in the .dvi file.  A completed job creating an .lgp file for
the printer looks like this:
.RS .5i
.br
% dvi2lgp sample
.br
This is DVI2LGP, Version n, last modified May 29, 1453
.br
[sample.dvi->sample.lgp]
.br
\' TeX output 1471.7.12:1200\'
.br
9296: beginning of page 3
.br
5066: beginning of page 2
.br
42: beginning of page 1
.br
%
.br
.RE
.PP
On the Unix system, you would then spool the .lgp file to the printer
with the command
.RS .5i
.br
% lgp sample.lgp
.br
.RE
(On some systems, such as Tops-20, we also set up the next command---this is
generated by the program---so that all that is needed is to respond to it
with a carriage return and the LGP file will be queued to the printer,
assuming that your system has provided a protocol for this queuing
operation.  We may invoke the subsequent 
.B lgp
command directly in later
versions of this software on Unix.)
.PP
If the command line does not include the input filename, then the full
range of options becomes accessible.  The user is first given the
opportunity to redirect the diagnostic output (including the font
identifications and the beginning-of-page notifications) to a file.
Then the program asks for an input filename, which may be supplied
with or without the .dvi extension.  As in the default mode, the
output filename is automatically generated from the input filename,
and the file is created on the user's current working directory.
.PP
At this point the user has three choices:
.RS .5i
.TP 5
1.
The remaining default options can again be selected by typing a
carriage return in response to the prompt line, which reads:
.RS .5i
.br
.ce 1
CR to accept default of all pages, no magnification. ? for help.
.RE
.TP 5
2.
A display of help messages can be called up by typing a question
mark.
.TP 5
3.
The full sequence of option specification queries can be started by
typing any visible ASCII character other than a question mark.
.RE
.PP
The first cluster of options must be specified all on the one line
and, in the present version of the program, may not have any spaces
between options. Each distinct option is introduced by a ``switch
character'' which may differ from system to system.
.PP
The following single-letter options exist:
.PP
For position on the output sheet:
.RS .5i
.br
C---Center.  Equal margins right and left, as nearly as possible.
This is the default.
.br
L---Left. Text pushed over to the left margin as close as the LGP
hardware allows.
.br
R---Right. Text pushed over to the right margin, leaving as wide a
left margin as the LGP hardware allows.
.br
.RE
.PP
For orientation on the output sheet:
.RS .5i
.br
V---Vertical. Normal, portrait mode output. This is the default.
.br
H---Horizontal. Landscape mode output rotated ninety degrees.
.br
.RE
.PP
For sequential output in .dvi order (which will stack up on the tray
in inverted order):
.RS .5i
.br
I---Inverted. The default has no name.
.br
.RE
.PP
For different amounts of diagnostic output:
.RS .5i
.br
P---Production. The minimum described above as the default.
.br
T---Terse. Rather more information about fonts.
.br
S---Some-debug. Line by line reports of progress.
.br
F---Full-debug. Exhaustive, byte-by-byte reports.
.br
.RE
.PP
In addition to these, it is also possible to specify, on the same
line, and with no intervening spaces, a simple selection of pages for
output.  This option (assuming that `-' is the switch character) takes
the forms -<n> or -<n>:<m> or -<n>:end. Thus, it is possible to select
a single page only, or a range of pages, or a range of pages through
to the end of the file.  Note that this option does not use the values
in the \*(TX counters (which often contain page numbers).  When you use
this option, you are simply telling the program to count in
sequentially from the first to the <n>th page of the file, to start
there and to continue processing until the <m>th page of the file or
the end of the file is reached.  The use of this option automatically
sets the I(nverted) mode switch.
.PP
When you see the prompt reading:
.RS .5i
.br
.ce 1
Starting page (default = *)
.br
.RE
you can make use of the specific values in \*(TX's count registers.
After you have decided what you wish to do with that option, you have
a second chance to tell the program how many pages you really want to
generate on the printer.  The default is conservatively set at a
million, unless you have already used the -<n>:<m> option described
above.
.PP
The last, but perhaps the most interesting option allows you to
magnify the entire output page by whatever magnification you think you
can get away with.  The limitation here is fonts.  If you have fonts
corresponding to really wierd percentages of magnification then you
can use such values as a response to this option prompt.  Normally you
will want to use magsteps instead.  The principle behind magsteps is
part of \*(TX itself, and you can look it up in the \*(TX book.
.PP
The use of \\special functions is really part of \*(TX rather than part
of this driver.  We have not completed the work on these yet, but the
LGP vector-drawing function is available.  For this, you should
include the \*(TX commands \\special{begin-vector} at the point where you
want the vector to begin, and later on give the command
\\special{vector}.  For obvious reasons, these commands have to be
paired on the same page.  But don't worry if the paging operation of
\*(TX happens to split them up unexpectedly; the operations will simply
be cancelled, and you will be told about it.  It would be nice to get
\\special{overlay} working, but at present we cannot test it, so it
remains in skeletal form for now, as does \\special{raster-data} and
\\special{screen-image}.
.SH "ENVIRONMENT"
.IP TEXFONTS
Search path for font bit maps (PXL files, so called because they have an
.nnnnpxl extension).  The entire path must be no longer than 100 characters
long.  The default doesn't include the current area (".") to avoid confusing
the programs that convert the output for printing on the various output
devices (most of which don't know about the path stuff yet).
Default: /usr/lib/tex/fonts
.SH "SEE ALSO"
tex(1), lgp(1)
.SH "BUGS"
At present, the command line processing is primitive.  Switch processing
needs to be added.  Also, it is not possible to print a .dvi file which
does not have a filename extension.
.PP
The .dvi file format supported is Version 2.  Older versions
of the .dvi file (such as those produced by older versions of \*(TX)
are not supported and cannot be printed using this program.)
.PP
Bugs should be reported to Richard Furuta or Pierre MacKay at the University
of Washington.
.SH "AUTHORS"
Pierre MacKay and Richard Furuta.
SHAR_EOF
fi # end of overwriting check
if test -f 'lgpext.c'
then
	echo shar: will not over-write existing file "'lgpext.c'"
else
cat << \SHAR_EOF > 'lgpext.c'
/* ---routines which are required because pc won't do it--- */
/* borrowed from Symbolics' randomio.c */
#include "../../h00vars.h"		/* defines Pascal I/O structure */
#include <sys/types.h>
#include <sys/stat.h>

/* number of bytes in the file */
curpos(curfile)
	register struct iorec *curfile;
{
	struct stat st;

	if (fstat(fileno(curfile->fbuf), &st) == -1)
		PERROR("Could not stat ", curfile->pfname);
	else
		return (st.st_size);
}

/*
 * This is a variant of SEEK in libpc.
 */
setpos(curfile, loc)
	int loc;
	register struct iorec *curfile;
{
	int n;

	curfile->funit |= SYNC;
	curfile->funit &= ~(EOFF|EOLN|SPEOLN);
	if (loc < 0)
		n = fseek(curfile->fbuf, 0, 2);
	else
		n = fseek(curfile->fbuf, loc, 0);
	if (n == -1)
		PERROR("Could not seek ", curfile->pfname);
}

/* ---routines provided for efficiency reasons--- */
/* if you find yourself porting this code to another machine, you will
   probably want to replace all of these routines with Pascal routines,
   at least initially.  Don't forget that eofdvifile, a variable below,
   really represents eof(dvi_file). */
/* borrowed from Howard Trickey's dvip */
/*
	External procedures for use with dvip.
	Written by H. Trickey, 9/3/83.
	
	Implement the following procedures (as declared in Pascal)

	procedure opendvifile;
	function getbyte: integer;
	function signedbyte: integer;
	function gettwobytes: integer;
	function signedpair: integer;
	function getthreebytes: integer;
	function signedtrio: integer;
	function signedquad: integer;
*/

#include <stdio.h>

static FILE *dvif;
extern int eofdvifile; /* needs to be set true when this happens */
extern int curloc; /* needs to be incremented by getbyte, signedbyte */
extern char dviname[];

/* open the dvifile, using the name in dvifilename (modified from hwt by rkf) */
opendvifile()
{
	char lcldviname[BUFSIZ];	/* for a local copy of the dvi name */
	int ctr;

	strcpy(lcldviname,dviname);	/* a local copy to play with */

	/* the dvi name terminates on the first blank */
	for(ctr = 0;
		(ctr < BUFSIZ) && (lcldviname[ctr] != '\0') && (lcldviname[ctr] != ' ');
		ctr++)
			;
	lcldviname[ctr] = '\0';
	if ((dvif=fopen(lcldviname,"r"))==NULL)  {
		eofdvifile = 1;		/* meaningless but otherwise someone
					   would forget to do it when
					   modifying this code */
		fprintf(stderr,"Sorry, cannot open %s for reading\n",lcldviname);
		fflush(stderr);
		exit(1);
	}
	else
		eofdvifile = 0;
}

/* return unsigned version of next byte in dvifile */
int getbyte()
{
	register int c;
	if ((c = getc(dvif))==EOF) {
		eofdvifile = 1;
		return(0);
		}
	curloc++;
	return(c);
}

/* return signed version of next byte in dvifile */
int signedbyte()
{	register int c;
	if ((c = getc(dvif))==EOF) {
		eofdvifile = 1;
		return(0);
		}
	curloc++;
	if (c>127) return(c-256);
	return(c);
}

/* return unsigned value of next two bytes (high order first) */
int gettwobytes()
{
	register int a,b;
	a = getc(dvif);
	eofdvifile = ((b = getc(dvif))==EOF);
	curloc += 2;
	return((a << 8) | b);
}

/* return signed value of next two bytes (high order first) */
int signedpair()
{
	register int a,b;
	if ( (a = getc(dvif)) > 127) a -= 256; /* sign extend */
	eofdvifile = ((b = getc(dvif))==EOF);
	curloc += 2;
	return((a << 8) | b);
}

/* return unsigned value of next three bytes */
int getthreebytes()
{
	register int a,b,c;
	a = getc(dvif);
	b = getc(dvif);
	eofdvifile = ((c = getc(dvif))==EOF);
	curloc +=3;
	return((a << 16) | (b << 8) | c);
}

/* return signed value of next three bytes */
int signedtrio()
{
	register int a,b,c;
	if ( (a = getc(dvif)) > 127) a -= 256;
	b = getc(dvif);
	eofdvifile = ((c = getc(dvif))==EOF);
	curloc +=3;
	return((a << 16) | (b << 8) | c);
}

/* return value of next four bytes */
int signedquad()
{
	register int a,b,c,d;
	a = getc(dvif);
	b = getc(dvif);
	c = getc(dvif);
	eofdvifile = ((d = getc(dvif))==EOF);
	curloc += 4;
	return((a << 24) | (b << 16) | (c << 8) | d);
}

/* seek to byte n of file f: actually, assume f is for dvifile */
setdvipos(f,n)
	int f; /* not really, but we ignore it */
	int n;
{
	if (n>=0) {
	    fseek(dvif,(long) n,0); 
	    eofdvifile = 0;
	    }
	else {
	    fseek(dvif, (long) n, 2);
	    eofdvifile = 1;
	    }
}

/* return current byte offset in file f (again, assume dvifile) */
int curdvipos(f)
	int f;
{
	return((int) ftell(dvif));
}

/* added routines along the same lines */
static FILE *lgpf;
extern char lgpname[];

/* open the lgpfile, using the name in lgpname */
openlgpfile()
{
	char lcllgpname[BUFSIZ];	/* for a local copy of the dvi name */
	int ctr;

	strcpy(lcllgpname,lgpname);	/* a local copy to play with */

	/* the lgp name terminates on the first blank */
	for(ctr = 0;
		(ctr < BUFSIZ) && (lcllgpname[ctr] != '\0') && (lcllgpname[ctr] != ' ');
		ctr++)
			;
	lcllgpname[ctr] = '\0';
	if((lgpf=fopen(lcllgpname,"w"))==NULL)  {
		fprintf(stderr,"Sorry, cannot open %s for writing\n",lcllgpname);
		fflush(stderr);
		exit(1);
	}
}

/* output a byte to the lgp file */
outputbyte(b)
int b;
{
	char ch;

	putc((b&0377),lgpf);
}

/* ---and now, some routines borrowed and modified from TeX to handle reading
      pxl files--- */
/*
 * readbyte reads from the Pascal text file with iorec pointer filep
 * returning the byte read.  This is modified from lineread in TeX.
 * If end-of-file is encountered, the funit field of *filep is set
 * appropriately.
 */
int readbyte(filep)
struct iorec *filep;
{
	register c;
	register FILE *iop; /* stdio-style FILE pointer */
	
	iop = filep->fbuf;
	  /* overflow when next char would go into buffer[lastlim] */
	c = getc(iop);
	if (c<=0)
	    filep->funit |= EOFF; /* we hit end-of-file */
	return(c);
}

/*
**	testeof(filep)
**
**  Test whether or not the Pascal text file with iorec pointer filep
**  has reached end-of-file (when the only I/O on it is done with
**  lineread, above).
**  We may have to read the next character and unget it to see if perhaps
**  the end-of-file is next.
*/

bool
testeof(filep)
register struct iorec *filep;
{
	register char c;
	register FILE *iop; /* stdio-style FILE pointer */
	if (filep->funit & EOFF)
		return(TRUE);
	else { /* check to see if next is EOF */
		iop = filep->fbuf;
		c = getc(iop);
		if (c<0)
			return(TRUE);
		else {
			ungetc(c,iop);
			return(FALSE);
			}
		}
}


/* and now, some routines to implement paths for file names.  These routines
   are taken from Howard Trickey's port of dvitype (2/19/84).  Howard
   adapted them from his port of TeX, ext.c */

#include "../../texpaths.h"	/* defines default TEXFONTS path */
/* ---fixme--- use the TeX default here!!! */

char *fontpath;

char *getenv();

/*
 * setpaths is called to set up the pointer fontpath
 * as follows:  if the user's environment has a value for TEXFONTS
 * then use it;  otherwise, use defaultfontpath.
 */
setpaths()
{
	register char *envpath;
	
	if ((envpath = getenv("TEXFONTS")) != NULL)
	    fontpath = envpath;
	else
	    fontpath = defaultfontpath;
}

#define namelength 100   /* should agree with DISTdvi2lgp.ch */
extern char curname[],realnameoffile[]; /* these have size namelength */

/*
 *	testaccess(amode,filepath)
 *
 *  Test whether or not the file whose name is in the global curname
 *  can be opened for reading (if mode=READACCESS)
 *  or writing (if mode=WRITEACCESS).
 *
 *  The filepath argument is one of the ...FILEPATH constants defined below.
 *  If the filename given in curname does not begin with '/', we try 
 *  prepending all the ':'-separated areanames in the appropriate path to the
 *  filename until access can be made, if it ever can.
 *
 *  The realnameoffile global array will contain the name that yielded an
 *  access success.
 */

/* note that corresponding constants are defined in DISTdvi2lgp.ch */

#define READACCESS 4
#define WRITEACCESS 2

#define NOFILEPATH 0
#define FONTFILEPATH 3

bool
testaccess(amode,filepath)
    int amode,filepath;
{
    register bool ok;
    register char *p;
    char *curpathplace;
    int f;
    
    switch(filepath) {
	case NOFILEPATH: curpathplace = NULL; break;
	case FONTFILEPATH: curpathplace = fontpath; break;
	}
    if (curname[0]=='/')	/* file name has absolute path */
	curpathplace = NULL;
    do {
	packrealnameoffile(&curpathplace);
	if (amode==READACCESS)
	    /* use system call "access" to see if we could read it */
	    if (access(realnameoffile,READACCESS)==0) ok = TRUE;
	    else ok = FALSE;
	else {
	    /* WRITEACCESS: use creat to see if we could create it, but close
	    the file again if we're OK, to let pc open it for real */
	    f = creat(realnameoffile,0666);
	    if (f>=0) ok = TRUE;
	    else ok = FALSE;
	    if (ok)
		close(f);
	    }
    } while (!ok && curpathplace != NULL);
    if (ok) {  /* pad realnameoffile with blanks, as Pascal wants */
	for (p = realnameoffile; *p != '\0'; p++)
	    /* nothing: find end of string */ ;
	while (p < &(realnameoffile[namelength]))
	    *p++ = ' ';
	}
    return (ok);
}

/*
 * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
 * followed by '/', followed by the characters in curname up until the
 * first blank there, and finally a '\0'.  The cpp pointer is left pointing
 * at the next directory in the path.
 * But: if *cpp == NULL, then we are supposed to use curname as is.
 */
packrealnameoffile(cpp)
    char **cpp;
{
    register char *p,*realname;
    
    realname = realnameoffile;
    if ((p = *cpp)!=NULL) {
	while ((*p != ':') && (*p != '\0')) {
	    *realname++ = *p++;
	    if (realname == &(realnameoffile[namelength-1]))
		break;
	    }
	if (*p == '\0') *cpp = NULL; /* at end of path now */
	else *cpp = p+1; /* else get past ':' */
	*realname++ = '/';  /* separate the area from the name to follow */
	}
    /* now append curname to realname... */
    p = curname;
    while (*p != ' ') {
	if (realname >= &(realnameoffile[namelength-1])) {
	    fprintf(stderr,"! Full file name is too long\n");
	    break;
	    }
	*realname++ = *p++;
	}
    *realname = '\0';
}
SHAR_EOF
fi # end of overwriting check
if test -f '4.2-Note'
then
	echo shar: will not over-write existing file "'4.2-Note'"
else
cat << \SHAR_EOF > '4.2-Note'
Make sure to
	cp 4.2Makefile Makefile
before you begin
SHAR_EOF
fi # end of overwriting check
if test -f '4.2Makefile'
then
	echo shar: will not over-write existing file "'4.2Makefile'"
else
cat << \SHAR_EOF > '4.2Makefile'
#
# Makefile for Berkeley UNIX version of dvi2lgp, 4.2 bsd
#
# Note:  The -J option on the 'pc' commands below is the only way to
#        'pc' to use the -J option to the assembler (which makes it
#        do the sensible thing with branches).  This flag is not
#        documented, which is correct, since 'pc' should really be
#        using it all of the time...  (Grrr..)
#
# Note:  dvi2lgp uses the ../../texpaths.h file defined for use by
#	 TeX.  It refers to the TEXFONTS environment variable to
#	 decide where to look for font bit maps.
#
#	Also, the local variable FIXOTHERS is set to ``pxp -O -f'' for 4.2
#	since you are not going to use a hacked pc compiler that recognizes
#	the ``others:'' clause.  You will need the new Berkeley pxp program
#	that has the -O option to transform case statements with such
#	clauses into code that doesn't use ``others:''.
#	We use ``pxp -O -f'' here instead of ``pxp -O'' because we
#	want to have expressions fully parenthesized to avoid a pxp bug.
#	If you are using a hacked pc compiler, then set FIXOTHERS
#	to ``cat''.
#

TEXPC=pc
PCLIB=
PFLAGS= -J -O
CFLAGS = -O
FIXOTHERS= pxp -O -f

all: dvi2lgp

#
# Compile dvi2lgp
#

dvi2lgp: dvi2lgp.o lgpext.o
	${TEXPC} $(PFLAGS) -o dvi2lgp dvi2lgp.o lgpext.o ${PCLIB}

dvi2lgp.o: dvi2lgp.p
	$(TEXPC) $(PFLAGS) -c dvi2lgp.p

dvi2lgp.p: dvi2lgp.web dvi2lgp.ch
	tangle dvi2lgp.web dvi2lgp.ch
	$(FIXOTHERS) dvi2lgp.p > dvi2lgp.temp
	mv dvi2lgp.temp dvi2lgp.p

lgpext.o: lgpext.c lgpext.h ../../h00vars.h ../../texpaths.h
#
#
clean:
	rm -f *.p *.o dvi2lgp.pool
SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
Converts dvi files into a form that can be fed to the lgp with the lgp
command.  Expected usage would be
	tex foo
	dvi2lgp foo
	lgp foo.lgp

Make sure that ./tex82/h00vars.h is in place before making dvi2lgp.

If you are running 4.1bsd then run the shell script 4.1-setup.  If you are
running 4.2 bsd, run the shell script 4.2-setup.  Do this before trying to
make the program.

This program was written at the University of Washington, Department of
Computer Science.  Contact Richard Furuta or Pierre MacKay if problems arise.
SHAR_EOF
fi # end of overwriting check
if test -f 'dvi2lgp.ch'
then
	echo shar: will not over-write existing file "'dvi2lgp.ch'"
else
cat << \SHAR_EOF > 'dvi2lgp.ch'
% Copyright 1983 by Richard Furuta and Pierre MacKay. Copying without
% fee is permitted provided that the copies are not made or distributed
% for direct commercial advantage and credit to the source is given.
% Altered versions of this program are not to be copied 
% or distributed without permission from the authors.
% There are no restrictions on the use of this program.
%
% corresponds to DVI2LGP, version 2.2 and uses Tangle, version 2.5
% Richard Furuta, University of Washington

% things that need to be attended to are flagged with --fixme--

%
% [0] -- Changes to the preamble
%
@x
\def\title{DVI2LGP}
@y
\def\title{DVI2LGP for Berkeley Unix}
@z
@x [0] -- continued -- weave only prints changes
\pageno=\contentspagenumber \advance\pageno by 1
@y
\pageno=\contentspagenumber \advance\pageno by 1

\let\maybe=\iffalse

@z

% [10] program header
@x
@ The binary input comes from |dvi_file|, and symbolic output can be written
on \PASCAL's standard |output| file. The term |print| is used instead of
|write| when this program writes on |output|, so that all such output
can easily be redirected if desired.
We use the terms |tprint| and |tprint_ln|
for terminal output, in order to make any necessary
change easier.  The |term_in| and |term_out| files are defined later.

@d print(#)==write(#)
@d print_ln(#)==write_ln(#)
@d tprint(#)==write(term_out,#) {|write| will work in most environments}
@d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise}
@y
@ The binary input comes from |dvi_file|, and symbolic output can be written
on \PASCAL's standard |output| file. The term |print| is used instead of
|write| when this program writes on |output|, so that all such output
can easily be redirected if desired.
We use the terms |tprint| and |tprint_ln|
for terminal output, in order to make any necessary
change easier.  The |term_in| and |term_out| files are defined
to be input and output for the moment since we can't quite figure out
how to direct things to |stdout| in Unix in a reasonable way.

@d print(#)==write(#)
@d print_ln(#)==write_ln(#)
@d term_in==input {the terminal as an input file (stdin)}
@d term_out==output {the terminal as an output file (stdout)}
@d tprint(#)==write(term_out,#) {|write| will work in most environments}
@d tprint_ln(#)==write_ln(term_out,#) {|write_ln| likewise}
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

@x --- continuing from [10] --- the program declaration
program DVI2LGP(@!output);
@y
program DVI2LGP(@!input, @!output);
@z

@x --- continuing from [10] --- the #include directive
var@?@<Globals in the outer block@>@/
@y
var@?@<Globals in the outer block@>@/
@#
@\@=#include "lgpext.h"@>@\ {declarations for external C procedures}
@#
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

@x --- continuing from [10] --- initialize paths
  @<Set initial values@>@/
@y
  setpaths; {read environment, to find TEXFONTS, if there}
  @<Set initial values@>@/
@z

% [11] -- including or excluding debugging code--may not actually change
%        but reproduced here for convenience.
@x
@ Some of this code is optional for use when debugging only;
such material is enclosed between the delimiters |debug| and $|gubed|$.
Other parts, delimited by |stat| and $|tats|$, are optionally included
if statistics about \.{DVI2LGP}'s memory usage are desired.

@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
@f debug==begin
@f gubed==end
@#
@d stat==@{ {change this to `$\\{stat}\equiv\null$'
  when gathering usage statistics}
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
  when gathering usage statistics}
@f stat==begin
@f tats==end
@y
@ Some of this code is optional for use when debugging only;
such material is enclosed between the delimiters |debug| and $|gubed|$.
Other parts, delimited by |stat| and $|tats|$, are optionally included
if statistics about \.{DVI2LGP}'s memory usage are desired.

@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
@f debug==begin
@f gubed==end
@#
@d stat==@{ {change this to `$\\{stat}\equiv\null$'
  when gathering usage statistics}
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
  when gathering usage statistics}
@f stat==begin
@f tats==end
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [12] compiler directives
@x
@ The \PASCAL\ compiler used to develop this system has ``compiler
directives'' that can appear in comments whose first character is a dollar sign.
In production versions of \.{DVI2LGP} these directives tell the compiler that
@^system dependencies@>
it is safe to avoid range checks and to leave out the extra code it inserts
for the \PASCAL\ debugger's benefit, although interrupts will occur if
there is arithmetic overflow. There is also an directive to ensure that
the ``\&{extended addressing}'' feature in TOPS-20 \PASCAL\ is included.

@<Compiler directives@>=
@{@&$X+,C-,A+,D-@} {extended adr., no range check, catch overflow, no debug}
debug @{@&$X+,C+,D+@}@+ gubed {but turn everything on when debugging}
@y
@ The \PASCAL\ compiler used to develop this system has ``compiler
directives'' that can appear in comments whose
first character is a dollar sign.
The
directives shown below specify full checking and inclusion of the \PASCAL\
debugger when \.{DVI2LGP}
is being debugged, but they cause range checking and other
redundant code to be eliminated when the production system is being generated.
Arithmetic overflow will be detected in all cases.
We use verbatim \PASCAL\ mode to make sure the case of the options
is unchanged.
@^Overflow in arithmetic@>

@<Compiler directives@>=
@=(*$C-*)@> {no range check}
debug @=(*$C+*)@> @+ gubed {but turn everything back on when debugging}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [14] Increase name_length.  Note that this definition is repeated in
% lgpext.c and that the two should match.
@x
@!name_length=50; {a file name shouldn't be longer than this}
@y
@!name_length=100; {a file name shouldn't be longer than this}
@z

% [21] changing the case of names---yes or no
@x
@d system_case_of(#)==fold_upper(#) {leave out the adjustment for {\mc UNIX}}
@y
@d system_case_of(#)==xchr[#] {leave out the adjustment for {\mc UNIX}}
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [33] definitions for binary files
@x
We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
even if such simplicity is sometimes unrealistic.
@y
Here with Berkeley {\mc UNIX}, it turns out that asking for
a `|packed file of -128..127|' does what we want.  We do have to convert
values when read, but the external files look fine.
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z
%
% [33] continued
%
@x
@!byte_file=packed file of eight_bits; {files that contain binary data}
@y
@!byte_file=packed file of -128..127; {files that contain binary data}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [35]
@x
@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
begin reset(dvi_file,dvi_name);
cur_loc:=0;
end;
@#
procedure open_lgp_file; {prepares \.{LGP} file for output}
begin rewrite(lgp_file,lgp_name);
end;
@y
On Berkeley {\mc UNIX}, use external procedures for opening the
|dvi_file| and |lgp_file|, so that we can do random reading and sequential
writing without mucking with
the pc runtime data structures.
This also means that the |eof| tests for |dvi_file| need to be
handled externally.  This routine was borrowed from Howard Trickey's
|dvip| program.
We do use this opportunity, though, to define some file opening
related constants although they are not used here (they get used later
when the \.{PXL} files are opened).  These definitions are repeated in
lgpext.c and should be kept constant.

@d read_access_mode=4  {``read'' mode for |test_access|}
@d write_access_mode=2 {``write'' mode for |test_access|}

@d no_file_path=0    {no path searching should be done}
@d font_file_path=3  {path specifier for \.{PXL} files}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [37] reading bytes from dvi_file--routines borrowed from Trickey's dvip
@x
@ We shall use another set of simple functions to read the next byte or
bytes from |dvi_file|. There are seven possibilities, each of which is
treated as a separate function in order to minimize the overhead for
subroutine calls.
@^system dependencies@>

@p function get_byte:integer; {returns the next byte, unsigned}
var b:eight_bits;
begin if eof(dvi_file) then get_byte:=0
else  begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
  end;
end;
@#
function signed_byte:integer; {returns the next byte, signed}
var b:eight_bits;
begin read(dvi_file,b); incr(cur_loc);
if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
end;
@#
function get_two_bytes:integer; {returns the next two bytes, unsigned}
var a,@!b:eight_bits;
begin read(dvi_file,a); read(dvi_file,b);
cur_loc:=cur_loc+2;
get_two_bytes:=a*256+b;
end;
@#
function signed_pair:integer; {returns the next two bytes, signed}
var a,@!b:eight_bits;
begin read(dvi_file,a); read(dvi_file,b);
cur_loc:=cur_loc+2;
if a<128 then signed_pair:=a*256+b
else signed_pair:=(a-256)*256+b;
end;
@#
function get_three_bytes:integer; {returns the next three bytes, unsigned}
var a,@!b,@!c:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
cur_loc:=cur_loc+3;
get_three_bytes:=(a*256+b)*256+c;
end;
@#
function signed_trio:integer; {returns the next three bytes, signed}
var a,@!b,@!c:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
cur_loc:=cur_loc+3;
if a<128 then signed_trio:=(a*256+b)*256+c
else signed_trio:=((a-256)*256+b)*256+c;
end;
@#
function signed_quad:integer; {returns the next four bytes, signed}
var a,@!b,@!c,@!d:eight_bits;
begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
cur_loc:=cur_loc+4;
if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
end;
@y
@ In this module, the \.{.WEB} file defines some simple functions to get the
next byte or bytes from the |dvi_file|.
For {\mc UNIX} we, use external |@!get_byte|, etc., procedures for efficiency
reasons.  Consequently, we will
use this module to declare the |eof_dvi_file| variable that will be set
by those procedures.

@<Globals...@>=
@!eof_dvi_file:boolean; {|true| when \.{DVI} file has ended}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [38] Positioning within the dvi file
@x
Such routines are, of course, highly system dependent. They are implemented
here in terms of two assumed system routines called |set_pos| and |cur_pos|.
The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
negative or larger than the total number of items in |f|; in the latter
case, |set_pos(f,n)| moves to the end of file |f|.
The call |cur_pos(f)| gives the total number of items in |f|, if
|eof(f)| is true; we use |cur_pos| only in such a situation.

@p function dvi_length:integer;
begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
end;
@#
procedure move_to_byte(n:integer);
begin set_pos(dvi_file,n); cur_loc:=n;
end;
@y
Such routines are, of course, highly system dependent. They are implemented
in Berkeley Unix with four assumed system routines.
The two called |set_pos| and |cur_pos| are typical.
The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
negative or larger than the total number of items in |f|; in the latter
case, |set_pos(f,n)| moves to the end of file |f|.
The call |cur_pos(f)| gives the total number of items in |f|, if
|eof(f)| is true; we use |cur_pos| only in such a situation.
Berkeley Unix also defines |cur_dvi_pos| and |set_dvi_pos| because
the |dvi_file| routines are defined using external C procedures which
are not happy when given a Pascal file descriptor.

@p function dvi_length:integer;
begin set_dvi_pos(dvi_file,-1); dvi_length:=cur_dvi_pos(dvi_file);
end;
@#
procedure move_to_byte(n:integer);
begin set_dvi_pos(dvi_file,n); cur_loc:=n;
end;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [39] converting internal to external representations for byte files
@x
@ The attempt to output |eight_bits| bytes is virtually certain to
require some sort of system-dependent code.  What we show here is far too
simple-minded to work on any computer system we have access to, and
it is intended primarily to provide a place for the appropriate
routine in a \.{WEB} change file.

@p procedure output_byte(b:integer);

begin
 if b<0 then b:=b+@'400; {for an |eight_bits| value, remove sign}
 write(lgp_file,b);
end;
@^system dependencies@>
@y
@ The attempt to output |eight_bits| bytes is virtually certain to
require some system dependent code.
For Berkeley {\mc UNIX}, the numbers output to |lgp_file| must be in the range
|-128..127|.  If we subtract 256 from numbers greater than 127, all will
work out fine.  This is all done in an external procedure for performance
reasons

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [43] Storage for pxl font file names
@x
@ When we are inputting from \.{PXL} files,
it is usually preferable to read four bytes at a time. 
The input goes into global variables
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
the fourth. We also need |cur_name|, a string variable that will be set
to the current \.{PXL} font file name before each of the elements in the 
|pxl_files| array is opened or |reset|.


@<Glob...@>=
@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
@!cur_name:packed array[1..name_length] of char; {external name,
  with no lower case letters}
@y
@ When we are inputting from \.{PXL} files,
it is usually preferable to read four bytes at a time. 
The input goes into global variables
|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
the fourth. We also need |cur_name|, a string variable that will be set
to the current \.{PXL} font file name before each of the elements in the 
|pxl_files| array is opened or |reset|.
Under UNIX, we also have a |real_name_of_file| string, that gets
set by the external |test_access| procedure after path searching.

@<Glob...@>=
@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
@!cur_name,@!real_name_of_file:packed array[1..name_length] of char;
	 {external name}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [44] reading bytes from the input pxl file
@x
@p procedure read_pxl_bytes;
begin read(pxl_files[active_font],b0); read(pxl_files[active_font],b1);
read(pxl_files[active_font],b2); read(pxl_files[active_font],b3);
end;
@y
@p procedure read_pxl_bytes;
var byte: -128..127;
begin byte:=readbyte(pxl_files[active_font]);
if byte<0 then b0:=byte+256 else b0:=byte;
byte:=readbyte(pxl_files[active_font]);
if byte<0 then b1:=byte+256 else b1:=byte;
byte:=readbyte(pxl_files[active_font]);
if byte<0 then b2:=byte+256 else b2:=byte;
byte:=readbyte(pxl_files[active_font]);
if byte<0 then b3:=byte+256 else b3:=byte;
end;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [53] use our eof tester, not Pascal
@x
get_pxl_word(pxl_id); if not eof(pxl_files[active_font]) then goto 9997;
@y
get_pxl_word(pxl_id); if not test_eof(pxl_files[active_font]) then goto 9997;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [64] font area definition
@x
The string |cur_name| is supposed to be set to the external name of the
\.{PXL} or \.{VRL} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the four digits which indicate the magnification applied to
this font when it was created, and to add the suffix `\.{PXL}' 
or \.{VRL} after these.
In most environments, we should also change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
@y
The string |cur_name| is supposed to be set to the external name of the
\.{PXL} file for the current font. This usually means that we need to
prepend the name of the default directory, and
to append the four digits which indicate the magnification applied to
this font when it was created, and to add the suffix `\.{PXL}' after these.
In most environments, we should also change lower case letters
to upper case, since |cur_name| is a \PASCAL\ string.
However, in Berkeley {\mc UNIX}, we retain the lower case letters
since case is significant in file names.
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z
%
% [64] continued--font area definition
%
@x
@d default_directory_name=='TEXFONTS:' {change this to the correct name}
@d default_directory_name_length=9 {change this to the correct length}
@d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''}

@<Glob...@>=
@!default_directory:packed array[1..default_directory_name_length] of char;
@y
However, most of this is not needed for Berkeley Unix since we are going to
run down an externally defined path.  The definition for this path is found
in the TEXFONTS environment variable.  If none is given, a default is used,
defined
in the ``texpaths.h'' file which is included by lgpext.c.


@d extension_length=8 {length of the string (e.g.) ``\.{.1200PXL}''}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [65] initialize the variable we just eliminated
@x
@ @<Set init...@>=
default_directory:=default_directory_name;

@^system dependencies@>
@y
@ Nothing happens here, either, since there is nothing to initialize.  This
module is kept to preserve numbering.
@z

% [66] The work associated with opening the font file.
@x
@ Now for the real work of opening the font file.

@<Move font name...@>=
for k:=1 to name_length do cur_name[k]:=' ';
p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1];
if p=0 then
  begin for k:=1 to default_directory_name_length do
    cur_name[k]:=default_directory[k];
  r:=default_directory_name_length;
  end
else r:=0;
for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do
  begin incr(r);
  if r+4>name_length then
    abort('DVI2LGP capacity exceeded (max font name length=',
      name_length:1,')!');
@.DVI2LGP capacity exceeded...@>
  if (names[k]>="a")and(names[k]<="z") then
    cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}}
  else cur_name[r]:=xchr[names[k]]; 
  end;
fm:=font_mag[this_font];
cur_name[r+1]:='.';
if fm > 999 then begin
  a:=(fm div 1000)+ord('0');
  cur_name[r+2]:=xchr[a];
  fm:=fm mod 1000; 
end else decr(r); {for diminished fonts such as \.{999PXL}}
a:=(fm div 100)+ord('0');
cur_name[r+3]:=xchr[a];
fm:=fm mod 100; 
a:=(fm div 10)+ord('0');
cur_name[r+4]:=xchr[a];
fm:=fm mod 10;
a:=fm+ord('0');
cur_name[r+5]:=xchr[a];
cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x");
cur_name[r+8]:=system_case_of("l");
if not take_defaults then
begin
  if in_postamble then print(' == ') else
   if out_mode>errors_only then print(' requeue ');
@.requeue (a previously queued font)@>
  if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin
     for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' ');
     end;
end;
reset(pxl_files[p_f^.cache_number],cur_name);
p_f^.font_ptr:=this_font
@^system dependencies@>
@y
@ Now for the real work of opening the font file.

@<Move font name...@>=
for k:=1 to name_length do cur_name[k]:=' ';
p:= names[font_name[this_font]]; n:=names[font_name[this_font]+1];
r:=0;
for k:=font_name[this_font]+2 to font_name[this_font+1]-1 do
  begin incr(r);
  if r+4>name_length then
    abort('DVI2LGP capacity exceeded (max font name length=',
      name_length:1,')!');
@.DVI2LGP capacity exceeded...@>
  if (names[k]>="a")and(names[k]<="z") then
    cur_name[r]:=system_case_of(names[k]) {a harmless do-nothing in {\mc UNIX}}
  else cur_name[r]:=xchr[names[k]]; 
  end;
fm:=font_mag[this_font];
cur_name[r+1]:='.';
if fm > 999 then begin
  a:=(fm div 1000)+ord('0');
  cur_name[r+2]:=xchr[a];
  fm:=fm mod 1000; 
end else decr(r); {for diminished fonts such as \.{999PXL}}
a:=(fm div 100)+ord('0');
cur_name[r+3]:=xchr[a];
fm:=fm mod 100; 
a:=(fm div 10)+ord('0');
cur_name[r+4]:=xchr[a];
fm:=fm mod 10;
a:=fm+ord('0');
cur_name[r+5]:=xchr[a];
cur_name[r+6]:=system_case_of("p"); cur_name[r+7]:=system_case_of("x");
cur_name[r+8]:=system_case_of("l");
if not take_defaults then
begin
  if in_postamble then print(' == ') else
   if out_mode>errors_only then print(' requeue ');
@.requeue (a previously queued font)@>
  if (in_postamble or (out_mode>errors_only)) and (not take_defaults) then begin
     for k:=1 to r+extension_length do print(cur_name[k]);print_ln(' ');
     end;
end;
if test_access(read_access_mode,font_file_path) then
    reset(pxl_files[p_f^.cache_number],real_name_of_file)
else begin
    k:=1;
    print('Problem: font ');
    while((k <= name_length) and (cur_name[k] <> ' ')) do begin
       print(cur_name[k]);
       incr(k);
    end;
    print_ln(' cannot be found');
    abort('---aborting');
end;
p_f^.font_ptr:=this_font

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [70] printing an integer in octal
@x
@ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property
list in octal notation, and it is helpful to be able to check
errors against the information provided by \.{TFtoPL}, so if
any |check_sum| discrepancies appear, we print the information 
in octal notation. This is not the same routine as the one in
\.{PLtoTF} because, in this program, the value to be printed
may already have been altered by sign-extension. The value of
|kill_sign| is legitimate in \.{WEB} but may cause trouble in some
environments.
@^system dependencies@>

@d kill_sign==(-@'17777777777-1) {so that we can have an unsigned value}

@p procedure print_octal(@!a:integer); {outputs an integer in octal}
var
@!s:integer; {surrogate sign-bit}
@!i:integer; {what we are working on}
@!j:0..11; {one more than the number of digits of output}
@!dig:array[0..10] of "0".."7"; {eleven digit maximum}
begin print(' O '); {specify octal format}
s:=0;
if a < 0 then begin i:=a + kill_sign; s:= 2; end else i:=a;
for j:=0 to 10 do dig[j]:="0"; j:=0;
while (i>0)or(j=0) do
  begin dig[j]:=(i mod 8) + "0"; i:=i div 8; incr(j);
  end;
if s>0 then begin
  dig[10]:=dig[10] + s; {restore lost ``sign-bit'' if any}
  j:=11; {picks up effects such as \.{O} 20000012345}
end;
repeat decr(j); print(xchr[dig[j]]); until j=0;
end;
@y
@ The |check_sum| in |tfm_file|s is recorded in a \.{PL} property
list in octal notation, and it is helpful to be able to check
errors against the information provided by \.{TFtoPL}, so if
any |check_sum| discrepancies appear, we print the information
in octal notation.  In this routine, we just use Berkeley {\mc UNIX}'s
extension which allows printing of an octal number.

@d oct_output_digits=11

@p procedure print_octal(@!a:integer); {outputs an integer in octal}
begin print(' O '); {specify octal format}
print(a:oct_output_digits oct);
end;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [75] another use of our private eof function
@x
if eof(pxl_files[active_font]) then begin
@y
if test_eof(pxl_files[active_font]) then begin
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [82] terminal input and output files
@x
@!buffer:array[0..terminal_line_length] of ascii_code;
@!term_in:text_file; {the terminal, considered as an input file}
@!term_out:text_file; {the terminal, considered as an output file}
@y
@!buffer:array[0..terminal_line_length] of ascii_code;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [83] updating the terminal
@x
@d update_terminal == break(term_out) {empty the terminal output buffer}
@y
@d update_terminal == flush(term_out) {empty the terminal output buffer}

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [84] getting lines from the terminal
@x
@ During the dialog, \.{DVI2LGP} will treat the first blank space in a
line as the end of that line. Therefore |input_ln| makes sure that there
is always at least one blank space in |buffer|.
@^system dependencies@>

@p procedure input_ln; {inputs a line from the terminal}
var k:0..terminal_line_length;
begin update_terminal; reset(term_in);
if eoln(term_in) then read_ln(term_in);
k:=0;
while (k<terminal_line_length)and not eoln(term_in) do
  begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
  end;
buffer[k]:=" ";
end;
@y
@ During the dialog, \.{DVI2LGP} will treat the first blank space in a
line as the end of that line. Therefore |input_ln| makes sure that there
is always at least one blank space in |buffer|.  |first_get| is true
initially.
@^system dependencies@>

@p procedure input_ln; {inputs a line from the terminal}
var k:0..terminal_line_length;
begin update_terminal;
if first_get then first_get:=false
else begin
	if eoln(term_in) then read_ln(term_in)
end;
k:=0;
while (k<terminal_line_length)and not eoln(term_in) do
  begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
  end;
buffer[k]:=" ";
end;

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [87] top level for user dialog
@x
@ The selected options are put into global variables by the |dialog|
procedure, which is called just as \.{DVI2LGP} begins.
@^system dependencies@>

@p procedure dialog;
label 0,1,2,3,4,5;
var k:integer; {loop variable}
    ctr, tctr, lctr, dctr: integer;  {loop variables for getting file names}
begin rewrite(term_out); {prepare the terminal for output}
@y
@ The selected options are put into global variables by the |dialog|
procedure, which is called just as \.{DVI2LGP} begins.
@^system dependencies@>

@p procedure dialog;
label 0,1,2,3,4,5;
var k:integer; {loop variable}
    ctr, tctr, lctr, dctr: integer;  {loop variables for getting file names}
begin

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [94] parsing the file name argument
@x
@d area_marker==':' {or whatever your system uses} 
@d alt_marker=='>' {change this to things like the $\rangle$ directory bracket} 
@d ext_marker=='.' {the normal character for file-name extensions} 
@y
@d area_marker=='/' {or whatever your system uses} 
@d alt_marker=='/' {change this to things like the $\rangle$ directory bracket} 
@d ext_marker=='.' {the normal character for file-name extensions} 

@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [124] change for eof(dvi_file)
@x
if eof(dvi_file) then bad_dvi('the file ended prematurely!');
@y
if eof_dvi_file then bad_dvi('the file ended prematurely!');
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [148] change for eof(dvi_file)
@x
  begin if eof(dvi_file) then bad_dvi('the file ended prematurely!');
@y
  begin if eof_dvi_file then bad_dvi('the file ended prematurely!');
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z

% [156] change for eof(dvi_file)
@x
while (m=223)and not eof(dvi_file) do m:=get_byte;
if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
@y
while (m=223)and not eof_dvi_file do m:=get_byte;
if not eof_dvi_file then bad_dvi('signature in byte ',cur_loc-1:1,
@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
@z
SHAR_EOF
fi # end of overwriting check
if test -f '4.1-setup'
then
	echo shar: will not over-write existing file "'4.1-setup'"
else
cat << \SHAR_EOF > '4.1-setup'
# /bin/sh
# set the symbolics area up for 4.1bsd
cp 4.1Makefile Makefile
SHAR_EOF
fi # end of overwriting check
if test -f '4.1Makefile'
then
	echo shar: will not over-write existing file "'4.1Makefile'"
else
cat << \SHAR_EOF > '4.1Makefile'
#
# Makefile for Berkeley UNIX version of dvi2lgp, 4.1 bsd
#
# Note:  The -J option on the 'pc' commands below is the only way to
#        'pc' to use the -J option to the assembler (which makes it
#        do the sensible thing with branches).  This flag is not
#        documented, which is correct, since 'pc' should really be
#        using it all of the time...  (Grrr..)
#
# Note:  dvi2lgp uses the ../../texpaths.h file defined for use by
#	 TeX.  It refers to the TEXFONTS environment variable to
#	 decide where to look for font bit maps.
#

TEXPC=texpc
PCLIB=../../=4.1support/pclib.a
PFLAGS= -J -O
CFLAGS = -O

all: dvi2lgp

#
# Compile dvi2lgp
#

dvi2lgp: dvi2lgp.o lgpext.o
	${TEXPC} $(PFLAGS) -o dvi2lgp dvi2lgp.o lgpext.o ${PCLIB}

dvi2lgp.o: dvi2lgp.p
	$(TEXPC) $(PFLAGS) -c dvi2lgp.p

dvi2lgp.p: dvi2lgp.web dvi2lgp.ch
	tangle dvi2lgp.web dvi2lgp.ch

lgpext.o: lgpext.c lgpext.h ../../h00vars.h ../../texpaths.h
#
#
clean:
	rm -f *.p *.o dvi2lgp.pool
SHAR_EOF
fi # end of overwriting check
cd ..
#	End of shell archive
exit 0