summaryrefslogtreecommitdiff
path: root/dviware/vutex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /dviware/vutex
Initial commit
Diffstat (limited to 'dviware/vutex')
-rw-r--r--dviware/vutex/cp6.chg715
-rw-r--r--dviware/vutex/cp6.hlp115
-rw-r--r--dviware/vutex/cp6.ins18
-rw-r--r--dviware/vutex/nonascii.fnt26
-rw-r--r--dviware/vutex/test.tex68
-rw-r--r--dviware/vutex/vaxvms.chg632
-rw-r--r--dviware/vutex/vaxvms.hlp129
-rw-r--r--dviware/vutex/vaxvms.ins20
-rw-r--r--dviware/vutex/vutex.web3533
9 files changed, 5256 insertions, 0 deletions
diff --git a/dviware/vutex/cp6.chg b/dviware/vutex/cp6.chg
new file mode 100644
index 0000000000..b5f8367961
--- /dev/null
+++ b/dviware/vutex/cp6.chg
@@ -0,0 +1,715 @@
+ CP6 changes file for the ASCII driver written by Warren Wolfe, Dec 10, 1987
+ Copyright 1987 CUBE Software, Victoria, B.C., Canada
+
+@x [2]
+@d banner=='This is ',clone,', Version 1.00'
+@y
+@d banner=='This is ',clone,', DPS8/CP6 Version 1.00'
+@z
+
+@x [3]
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {CP6 default for cases not listed explicitly}
+@z
+
+@x [4] add tfm_file,gen_input,and input to program header
+@p program vutex(dvi_file,bit_file,input,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@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program vutex(dvi_file,bit_file,tfm_file,gen_input,input,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@>@/
+@<DPS8/CP-6 procedures@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin open_output ; { must explicitly open output }
+ print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@z
+
+@x [9]
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+@y
+@!ASCII_code=0.."~"; {a subrange of the integers}
+@z
+
+@x [10]
+@!text_file=packed file of text_char;
+@y
+@!text_file=text;
+@z
+
+@x [30]
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@!word_file=packed file of integer; {for pixel file words}
+@y
+{later we'll define files that contain binary data}
+@z
+
+@x [31] fix defn of dvi_file and pxl_file, add tfm_file,bit_file
+@<Glob...@>=
+@!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
+@!tfm_file:byte_file; {a font metric file}
+@y
+@<Glob...@>=
+@!dvi_file:packed file of block; {the stuff we are \.{DVI}typing}
+@!tfm_file:packed file of block; {a font metric file}
+@!dvi_count:integer; {number of bytes read from current block of |dvi_file|}
+@!tfm_count:integer; {number of bytes read from current block of |tfm_file|}
+@!eof_tfm:boolean; { true when end of \.{TFM} file reached }
+@z
+
+@x [32] fix up opens and closes
+@ To prepare these files for input, we |reset| them. An extension of
+\PASCAL\ is needed in the case of |tfm_file|, since we want to associate
+it 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.
+@^system dependencies@>
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_bit_file; {prepares final output for writing}
+begin rewrite(bit_file);
+bit_is_open := true ;
+end;
+@#
+procedure open_tfm_file; {opens \.{TFM} file}
+begin reset(tfm_file,cur_name);
+eof_tfm := eof(tfm_file);
+end;
+@#
+procedure open_input_text ; {prepares to read from general purpose input}
+begin reset(gen_input, cur_name) ;
+end ;
+@y
+@ To prepare these files for input, we |reset| them. An extension of \PASCAL\ is
+needed in the case of |tfm_file|, since we want to associate it with external
+files whose names are specified dynamically (i.e., not known at compile time).
+On CP6, we use the |set_file_parameter| extension to name the file before it is
+|reset|. We also turn on FSFA for better performance. This means output files
+must be explicitly closed. If |eof(f)| is true immediately after |reset(f)| has
+acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@d close_file == @= close_file@>
+@d close_bit_file == close_file(bit_file)
+@d close_tfm_file == close_file(tfm_file)
+@d close_input_text == close_file(gen_input)
+@d set_file_parameters== @= set_file_parameters @>
+@d file_status == @= file_status @>
+
+@p procedure open_output; @= {explicitly opens output to assign the dcb to #4}@>
+begin close_file(output) ;
+set_file_parameters(output,' ',@='dcb=#4'@>) ;
+rewrite(output) ; end ;
+
+procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin set_file_parameters(dvi_file,' ',@='error=continue,fsfa=no,dcb=#1'@>);
+reset(dvi_file); if file_status(dvi_file)>0 then abort('Cannot open dvi file!');
+dvi_count := 0; cur_loc := 0;
+end;
+@#
+procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan}
+begin reset(dvi_file); dvi_count := 0;
+cur_loc:=0;
+end;
+@#
+procedure open_bit_file; {prepares final output for writing}
+begin set_file_parameters(bit_file,' ','error=continue,fsfa=yes,dcb=#3');
+rewrite(bit_file);
+if file_status(bit_file)>0 then
+ abort('Cannot open bit file or printer output!') ;
+bit_is_open := true ;
+end;
+@#
+procedure open_tfm_file; {opens \.{TFM} file}
+begin set_file_parameters(tfm_file,cur_name,'error=continue,fsfa=yes,scrub=yes');
+reset(tfm_file);
+ eof_tfm := file_status(tfm_file)>0;
+ if eof_tfm then print_ln('Couldn''t find ',cur_name);
+ @.Couldn't find ...@>
+ tfm_count := 0 ;
+end;
+@#
+procedure open_input_text ; {prepares to read from general purpose input}
+begin
+ set_file_parameters(gen_input,cur_name,'error=continue,fsfa=yes,scrub=yes');
+ reset(gen_input);
+ if file_status(gen_input) <> 0 then
+ print_ln('Couldn''t open ',cur_name);
+ @.Couldn't open ...@>
+end;
+@z
+
+@x [34]
+@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
+@d read_dvi_file(#)==begin
+ if dvi_count=block_size then begin
+ get(dvi_file); dvi_count:=0;
+ end;
+ #:=dvi_file^[dvi_count];
+ incr(dvi_count);
+ end
+
+@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 get_word:integer; {returns the next four bytes, unsigned}
+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;
+get_word:=((a*256+b)*256+c)*256+d;
+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;
+@z
+
+@x [37]
+@ We need a function that will read in a word from the \.{TFM} file. If
+the particular system
+@^system dependencies@>
+requires buffering, here is the place to do it. It also sets a global flag
+|eof_tfm| when it reaches the end of the file. If this flag is set on
+entrance to |load_tfm_file|, it is assumed that the file is bad.
+
+@p function tfm_integer : integer ;
+var i:integer;
+begin read(tfm_file, i);
+eof_tfm:=eof(tfm_file);
+tfm_integer:=i;
+end;
+
+@ There is nothing wrong with defining |eof_tfm| here.
+
+@<Glob...@>=
+@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.}
+@y
+@ We need a function that will read in a word from the \.{TFM} file. If
+the particular system
+@^system dependencies@>
+requires buffering, here is the place to do it. It also sets a global flag
+|eof_tfm| when it reaches the end of the file. If this flag is set on
+entrance to |load_tfm_file|, it is assumed that the file is bad.
+
+Since |tfm_file| is blocked, we need a function to read an integer
+from it.
+
+@d read_tfm_file(#)==begin
+ if tfm_count=block_size then begin
+ get(tfm_file); tfm_count:=0;
+ end;
+ #:=tfm_file^[tfm_count];
+ incr(tfm_count);
+ end
+@#
+@p function tfm_integer : integer;
+var i,@!result,@!temp:integer;
+begin
+ result := 0;
+ for i:=1 to 4 do begin@/
+ read_tfm_file(temp); result := result*two_8+temp;
+ end;
+ eof_tfm := eof(tfm_file);
+ tfm_integer := result ;
+end;
+@z
+
+@x [41]
+@ And, finally, we have the procedure which will extract the information from
+the \.{TFM} file. This routine is for input from the \.{TFM} file, which is (or
+should be) a |packed file of integer|, so this should cause no grief. See the
+documentation on |tftopl| for the structure of the file.
+
+
+@ @p procedure load_tfm_file ; {read in the width data}
+label 9997, {used for bad format}
+ 9999; {used for normal exit}
+@y
+@ For input and output from the \.{TFM} file, we have this procedure to
+read the \.{TFM} file into memory after the current |next_mem_free|.
+See the documentation on |tftopl| for the structure of the file.
+
+
+@ @p procedure load_tfm_file ; {read in the width data}
+@z
+
+@x [42]
+ if eof_tfm then goto 9997 ;
+ while not eof_tfm do begin
+ mem[k] := tfm_integer ;
+ k := k + 1 ;
+ if k > max_mem_size then
+ abort(clone,' memory size exceeded on load of tfm file!') ;
+ end ;
+@y
+ while not eof_tfm do begin
+ mem[k] := tfm_integer ;
+ k := k + 1 ;
+ if k > max_mem_size then
+ abort(clone,' memory size exceeded on load of tfm file!') ;
+ @.memory size exceeded ... tfm file!@>
+ end ;
+ close_tfm_file;
+ if k = next_mem_free then
+ print_ln('---not loaded, TFM file is bad')
+ @.---not loaded, TFM file is bad@>
+ else begin
+@z
+
+@x [42]
+ goto 9999 ;
+9997: print_ln('---not loaded, TFM file is bad') ;
+9999: end ;
+@y
+ end;
+end;
+@z
+
+@x [43] add CP6 routine to get options from command line
+@d errors_only=0 {value of |out_mode| when minimal printing occurs}
+@y
+@d CP6_parse_command == @= TEX_LIB$PARSE_COMMAND@>
+@d CP6_interaction_mode == @= TEX_LIB$INTERACTION_MODE@>
+@d errors_only=0 {value of |out_mode| when minimal printing occurs}
+@z
+
+@x [47]
+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}
+@y
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in==input {the terminal, considered as an input file}
+@d term_out==output {the terminal, considered as an output file}
+
+@<Glob...@>=
+@!buffer: packed array[0..terminal_line_length] of ASCII_code;
+@!buf_length: integer ; {length of command line}
+@z
+
+@x [48] use break_file on CP6
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == @= break_file@>(term_out) {empty the output buffer}
+@z
+
+@x [49] fix input_ln procedure
+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]:=" ";
+@y
+begin update_terminal;
+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]:=" ";
+read_ln(term_in);
+@z
+
+@x [52]
+@p procedure dialog;
+var k:integer; {loop variable}
+begin rewrite(term_out); {prepare the terminal for output}
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the truncation |print_width|@>;
+@<Determine the compression mode@>;
+@<Print all the selected options@>;
+if compress and (print_width > (page_width * 3 div 4)) then
+ page_width := 4 * print_width div 3
+else if (not compress) and (print_width > page_width - hh_offset) then
+ page_width := print_width + hh_offset;
+if page_width > max_p_width then
+ begin
+ page_width := max_p_width;
+ print_width := page_width - hh_offset;
+ end;
+num_lines := total_rast div page_width;
+end;
+@y
+
+@d batch == 0 { batch mode response}
+@d online == 1 { online mode response}
+
+@p procedure dialog;
+var i, k:integer; {loop variable}
+interaction: integer; { to contain response from mode query}
+command_ptr: com_buf_ptr; { pointer to command line}
+
+begin rewrite(term_out); {prepare the terminal for output}
+CP6_parse_command(command_ptr,buf_length); {get command line}
+CP6_interaction_mode(interaction,batch,online); { what access mode are we?}
+if (interaction=online) and (buf_length=0) then begin
+ @<Determine the desired |start_count| values@>;
+ @<Determine the desired |max_pages|@>;
+ @<Determine the truncation |print_width|@>;
+ @<Determine the compression mode@>;
+ @<Print all the selected options@>;
+end else @<Determine the options from the command buffer@>;
+if compress and (print_width > (page_width * 3 div 4)) then
+ page_width := 4 * print_width div 3
+else if (not compress) and (print_width > page_width - hh_offset) then
+ page_width := print_width + hh_offset;
+if page_width > max_p_width then
+ begin
+ page_width := max_p_width;
+ print_width := page_width - hh_offset;
+ end;
+num_lines := total_rast div page_width;
+end;
+
+
+@ Here are two new sections to support getting the options from the
+command line.
+
+@<Determine the options...@>==
+ begin
+ for i := 0 to buf_length-1 do buffer[i] := xord[command_ptr^[i]];
+ buffer[i+1] := "?" ; { end it with a question mark }
+ buf_ptr := 0 ;
+ repeat
+ case buffer[buf_ptr] of
+ " ",",","G","g" : ;
+ "S","s" : begin
+ @<Skip to parameter@> ; k := 0 ;
+ @<Parse the desired |start_vals|@> ;
+ end ;
+ "P","p" : begin
+ @<Skip to parameter@> ;
+ @<Parse the desired |max_pages|@> ;
+ end ;
+ "W","w" : begin
+ @<Skip to parameter@> ;
+ @<Parse the desired |print_width|@> ;
+ end ;
+ "T","t" : compress := false ;
+ "D","d" : out_mode := 1 ;
+ othercases
+ print_ln('Error on command line. Skipping: ',xchr[buffer[buf_ptr]]);
+ @.Error on command line...:@>
+ endcases ;
+ incr(buf_ptr);
+ until buf_ptr >= buf_length;
+ @<Print all the selected options@>;
+end
+
+@ @<Skip to parameter@>==
+while (buf_ptr < buf_length) and (buffer[buf_ptr] <> " ") and
+ (buffer[buf_ptr] <> "=") and (buffer[buf_ptr] <> ",") do
+ incr(buf_ptr);
+if buffer[buf_ptr] = "=" then incr(buf_ptr);
+@z
+
+@x [67] fix problem reading checksum from dvi file
+c:=signed_quad; font_check_sum:=c;@/
+@y
+c:=get_word; font_check_sum:=c;@/
+@z
+
+@x [68] set up directory names
+@d tfm_directory_name=='TeXfonts:'
+@d tfm_directory_name_length=9
+@y
+@d tfm_directory_name=='.:TEX'
+@d tfm_directory_name_length=5
+@z
+
+@x [70] fix the routine for the tfm name
+@<Move tfm name into the |cur_name| string@>=
+begin
+for k:=1 to name_length do cur_name[k]:=' ';
+r := 1 ; k := font_name ;
+if names[k]=0 then
+ begin for i:=1 to tfm_directory_name_length do
+ cur_name[i]:=tfm_directory[i];
+ r:=tfm_directory_name_length+1;
+ end else begin
+ while names[k] > 0 do begin
+ cur_name[r] := xchr[names[k]] ;
+ r := r + 1 ; k := k + 1 ;
+ end ;
+ end ;
+k := k + 1 ;
+while names[k] > 0 do begin
+ cur_name[r] := xchr[names[k]] ;
+ r := r + 1 ; k := k + 1 ;
+end ;
+cur_name[r] := '.' ; r := r + 1 ;
+cur_name[r] := 'T' ; r := r + 1 ;
+cur_name[r] := 'F' ; r := r + 1 ;
+cur_name[r] := 'M' ; r := r + 1 ;
+for k := 1 to r do
+ if (cur_name[k]>='a')and(cur_name[k]<='z') then
+ cur_name[k]:=xchr[xord[cur_name[k]]-@'40] ;
+end ;
+@y
+@<Move tfm name into the |cur_name| string@>=
+begin
+for k:=1 to name_length do cur_name[k]:=' ';
+r := 1; k := font_name + 1 ;
+while names[k] > 0 do begin
+ cur_name[r] := xchr[names[k]] ; incr(r); incr(k); end;
+cur_name[r] := ':' ; incr(r);
+cur_name[r] := 't' ; incr(r);
+cur_name[r] := 'f' ; incr(r);
+cur_name[r] := 'm' ; incr(r);
+ for k := 1 to tfm_directory_name_length do
+ begin cur_name[r] := tfm_directory[k] ; incr(r); end ;
+end;
+@z
+
+@x [121]
+First, this routine reads the file \.{nonASCII.tex.fnt} to determine which fonts
+are non ASCII, i.e. not printable on the screen. This list is shorter than the
+list of ASCII fonts.
+
+@p procedure base_font ;
+var i, j, k : integer ;
+ f_type : integer ; {type for listed fonts}
+ lf,lh,bc,ec,nw,np : integer ; {help in decoding the \.{TFM} file}
+ value, x : integer ; {a \.{TFM} |fix_word|}
+@<|base_font| variables@>
+begin
+ @<Load non ASCII font information@> ;
+ @<Determine base font@> ;
+end ;
+@y
+First, this routine reads the file \.{nonASCII\_tex\_fonts.:TEX} to determine
+which fonts are non ASCII, i.e. not printable on the screen. This list is
+shorter than the list of ASCII fonts.
+
+@p procedure base_font ;
+var i, j, k : integer ;
+ f_type : integer ; {type for listed fonts}
+ lf,lh,bc,ec,nw,np : integer ; {help in decoding the /.{TFM} file}
+ value, x : integer ; {a /.{TFM} |fix_word|}
+@!r : integer ; {local necessary for font name manipulations}
+@<|base_font| variables@>
+begin
+ @<Load non ASCII font information@> ;
+ @<Determine base font@> ;
+end ;
+@z
+
+@x [122]
+@d font_list_file=='nonASCII.tex.fnt' {change this to correct name}
+@d list_len==16
+@y
+@d font_list_file=='nonASCII_tex_fonts.:TEX'
+@d list_len==23
+@z
+
+@x [124] don't change file name to uppercase, and close input file
+for k := 1 to list_len do
+ if (list_fonts[k] <= 'z') and (list_fonts[k] >= 'a') then
+ cur_name[k] := xchr[xord[list_fonts[k]]-@'40]
+ else cur_name[k] := list_fonts[k] ;
+open_input_text ;
+num_asc_fonts := 0 ;
+while not eof(gen_input) do
+ @<Process line@> ;
+
+@y
+for k := 1 to list_len do
+ cur_name[k] := list_fonts[k] ;
+open_input_text ;
+num_asc_fonts :=0 ;
+while not eof(gen_input) do
+ @<Process line@> ;
+close_input_text ;
+@z
+
+@x [133]
+@p procedure clean_printer ;
+begin
+ write_ln(bit_file);
+ write_ln(bit_file,'The end...');
+ write_ln;
+end ;
+@y
+@p procedure clean_printer ;
+begin
+ write_ln(bit_file);
+ write_ln(bit_file,'The end...'); close_bit_file ;
+end ;
+@z
+
+@x [137]
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \vutex\ work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+@y
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \vutex\ work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+Here are the remaining changes to the program
+that are necessary to make \.{DVItype} work on CP6
+
+@<Const...@>==
+@!block_size=1024;
+@!bs_minus_1=1023;
+@!two_8= @'400;
+
+@ @<Types...@>==
+@!block = packed array [0..bs_minus_1] of 0..511;
+@!buf_type = packed array[0..terminal_line_length] of char;
+@!com_buf_ptr = ^buf_type; {pointer to command buffer}
+@!name_file_type = packed array[1..9] of char;
+
+@ Here are some special procedures that are external to Pascal
+written in PL6 to support operating system interfacing. They
+come from the |tex_lib| library supplied by Carlton University
+when they distributed \TeX.
+
+ In order to support random file positioning, we use a PL6
+routine to move the file to a particular record. This only
+works on files that are blocked, in this case |dvi_file|.
+@^system dependencies@>
+
+@f external == end
+
+@<DPS8/CP-6 procedures@>==
+procedure CP6_parse_command(var buf_ptr: com_buf_ptr;
+ buf_length: integer); external;
+procedure CP6_interaction_mode(var interaction:integer;
+ batch_mode, error_mode: integer); external;
+@z
+
+
+
+
+
+
+
diff --git a/dviware/vutex/cp6.hlp b/dviware/vutex/cp6.hlp
new file mode 100644
index 0000000000..324de76a58
--- /dev/null
+++ b/dviware/vutex/cp6.hlp
@@ -0,0 +1,115 @@
+Help File for CP6 installations:
+
+
+syntax:
+!vutex f1 [over f2] [(p1 [,p2]...)]
+
+
+purpose:
+vutex processor will print a TeX produced dvi file to an ordinary ASCII device
+
+f1 - name of an existing dvi file created by TeX.
+
+f2 - name of a valid ASCII output device.
+ If no file name for f2 is entered, output will default to user console
+
+p1,p2,... options to control output
+
+Options:
+
+The available options are given by:
+ S or s : for the starting page number
+ P or p : for the number of pages, beginning at S
+ W or w : for the width of the page in characters before truncation
+ T or t : for true TeX spacing as opposed to compressed
+ G or g : to initiate processing without further dialog
+
+
+If no option is entered, you will be prompted for 4 parameters, all of which may
+be defaulted by simply hitting RETURN.
+
+For example,
+
+!vutex file_dvi over LP@LASER
+This is vutex, DPS8/CP6 Version 1.00
+Starting page (default=*): *
+Maximum number of pages (default=1000000): *2
+Output truncated at column (default=160): *80
+Compress interword spaces, Y or N (default=Y): *
+' TeX output 1989.02.28:1029'
+- Page [1] [2]
+
+The first two are identical to those for dvilg8 and dvism, namely starting page
+number and number of pages to type. The third prompt is for the page width in
+characters (on most terminals, this is 80; on printers, this is 132).
+
+The fourth option allows the user to compress the spacing between words to one
+space. The text for each line will begin in the proper column at the left
+margin but all other TeX spacing between words will be reduced to one space.
+If the user should not select the compress option, each separate word will be
+left justified in the the space provided by TeX, and sup and super scripts will
+usually appear on separate lines. This option is most useful for tables and
+mathematical equations.
+
+An example of a command line invocation with options is given by:
+
+vutex file_dvi over file_vut (s=2,p=4,t)
+
+
+
+Seeing TeX output!
+
+One of the more frustrating things about TeX and its precise positioning of
+characters is that ``what you get is far more than what you can see'' on an
+ordinary ASCII terminal. The only devices for TeX output on the mainframe have
+been the laser printers, and that usually equates to running to get ( and
+waiting for ) your output, and excessive waste of paper. Note that at most
+installations, laser output is costed at about 10 times that produced by
+ordinary hammer printers.
+
+So, a program has been written to allow the TeX user to preview the output
+before sending it to the laser printer. Of course, the terminal screen (or an
+ordinary printer) still cannot handle the fine spacing of TeX, nor can it
+display all the characters you might wish to print. But, you will see an
+approximation to the spacing of words across the line and lines down the page.
+
+
+If your output was to a file, such as *vut in the example, you may copy that
+file to an ordinary line printer, or you may use a screen editor to view it.
+Note the following:
+
+ 1. Some characters may not appear because they landed in a place already
+occupied. Two characters from a small TeX font may fit into the space provided
+on the screen, so vutex can only print one such character.
+
+ 2. The output with true TeX spacing, may be wider than an ordinary screen
+of 80 characters, or even a printer page of 132 characters. With ordinary text
+you can compress the text and substantially reduce the width of the page, but
+text with lots of off-line text will appear best in true TeX mode. A screen
+editor may be used to view the output by switching from left to right side of
+the page. The present installation limits the width to 160 characters per line.
+
+ 3. Lines of small type or with extraordinary small interline spacing may
+appear superimposed in spaces in the previous line.
+
+ 4. \special has not been implemented and will be ignored by vutex.
+
+ 5. If characters are missing from a line because of truncation or
+superimposition, then a > sign will appear in the first position of that line.
+
+ 6. Unprintable characters from ASCII fonts, such as Greek symbols,
+and special math symbols, etc. will appear as # marks. However, as much as
+possible, vutex will try to print a meaningful character to suggest what is in
+the TeX output e.g. S is used to replace an integral sign.
+
+ 7. A + sign in the first position of a line means that it contains super
+scripts to the next line down, while a - sign means that it contains sub
+scripts to the previous line up.
+
+ 8. At the end of each TeX page, a ruler is drawn (in centimeters) to
+indicate the horizontal scale (this will not appear on the TeX output to a
+printer). Usually the vertical scale is only an approximation of the true TeX
+spacing, and some pages may appear physically shorter than others. If it is
+desired that all pages be the same length, perhaps with a form feed, this
+alteration can be made to the code.
+
diff --git a/dviware/vutex/cp6.ins b/dviware/vutex/cp6.ins
new file mode 100644
index 0000000000..e923cfa7ca
--- /dev/null
+++ b/dviware/vutex/cp6.ins
@@ -0,0 +1,18 @@
+To install vutex on your CP6 system:
+
+ 1. ``weave'' vutex.web with vutex.chg to produce a printed document
+
+ 2. check those items marked ``system dependencies'' for desired alterations
+ at your site. Make the necessary changes to the vutex.chg file.
+ For example, directory names should be changed, page_length should be
+ determined, and the end of page actions in the routine finish_page may be
+ altered to include a form feed.
+
+ 3. ``tangle'' vutex.web with vutex.chg to produce a pascal source file
+
+ 4. compile and link the source
+
+ 5. alter and install the nonascii.fnt file in the desired directory. This
+ file lists the names of non roman tfm files with a classification code.
+
+That's all, folks.
diff --git a/dviware/vutex/nonascii.fnt b/dviware/vutex/nonascii.fnt
new file mode 100644
index 0000000000..14c8faa50e
--- /dev/null
+++ b/dviware/vutex/nonascii.fnt
@@ -0,0 +1,26 @@
+amsy 2
+ambsy 2
+amcsc 2
+amex 2
+circle 2
+circlew 2
+lasy 2
+nlasy 2
+amex 3
+amsy 4
+ammi 5
+amtt 6
+amsltt 6
+cmsy 2
+cmbsy 2
+cmcsc 2
+cmex 2
+circle 2
+circlew 2
+lasy 2
+nlasy 2
+cmex 3
+cmsy 4
+cmmi 5
+cmtt 6
+cmsltt 6
diff --git a/dviware/vutex/test.tex b/dviware/vutex/test.tex
new file mode 100644
index 0000000000..22e78bc52d
--- /dev/null
+++ b/dviware/vutex/test.tex
@@ -0,0 +1,68 @@
+\global\newcount\n \global\newcount\m \global\newcount\i
+\nopagenumbers
+\rm Roman Font
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+\bf Bold Face Font
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+\sl Slant Font
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+\tt Teletype Font
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\ \advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+\font\mmi=ammi10 scaled \magstep0\mmi
+Math\thinspace Italics\thinspace Font
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\thinspace \advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+{\rm Symbols Font}
+\font\msy=amsy10 scaled \magstep0\msy
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\thinspace\advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\vfill\eject
+{\rm Extension Font}
+\font\mex=amex10 scaled \magstep0\mex
+
+\m=0
+\loop\ifnum\m<128
+\n=\m
+\i=0
+{\loop\ifnum\i<8 \char\n\thinspace\advance \n by 1 \advance\i by 1\repeat}
+\vskip11pt\advance\m by 8\message{\the\m}\repeat
+\bye
diff --git a/dviware/vutex/vaxvms.chg b/dviware/vutex/vaxvms.chg
new file mode 100644
index 0000000000..a3d7aebd2b
--- /dev/null
+++ b/dviware/vutex/vaxvms.chg
@@ -0,0 +1,632 @@
+ VMS changes file for the ASCII driver written by Warren Wolfe, Aug, 1988
+ Copyright 1987 CUBE Software, Victoria, B.C., Canada
+
+@x [2]
+@d banner=='This is ',clone,', Version 1.00'
+@y
+@d banner=='This is ',clone,', VAX/VMS Version 1.00'
+@z
+
+@x
+@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
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+@z
+
+@x [4] add tfm_file,gen_input,and input to program header
+@p program vutex(dvi_file,bit_file,input,output);
+@y
+@p
+@\@=[inherit('sys$library:starlet')]@>@\
+ {allows us to use system symbols and routines}
+program vutex(dvi_file,bit_file,tfm_file,gen_input,input,output);
+@z
+
+@x
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+@y
+@<Procedures for initialization@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin
+ @<Preset initial values@>@/
+ print_ln(banner);@/
+@z
+
+@x
+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.
+@^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|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+@y
+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.
+@^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|}
+
+@<Types...@>=
+@!text_file=text;
+@z
+
+@x
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+{later we'll define files that contain binary data}
+@z
+
+@x
+@!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
+@!tfm_file:byte_file; {a font metric file}
+@y
+@!dvi_file:packed file of byte_block; {the stuff we are \.{DVI}typing}
+@!tfm_file:packed file of byte_block; {a font metric file}
+@!dvi_count:integer; {number of bytes read from current block of |dvi_file|}
+@!tfm_count:integer; {number of bytes read from current block of |tfm_file|}
+@!dvi_blocks:integer; {number of blocks in |dvi_file|}
+@z
+
+@x Initialize byte counter for block in open_dvi_file
+begin reset(dvi_file);
+@y
+begin reset(dvi_file);
+dvi_count:=0;
+@z
+
+@x And again in reopen_dvi_file
+begin reset(dvi_file);
+@y
+begin reset(dvi_file);
+dvi_count:=0;
+@z
+
+@x
+begin reset(tfm_file,cur_name);
+@y
+begin close(tfm_file,@=error@>:=@=continue@>); {stupid Vax/VMS run-times}
+open(tfm_file,cur_name,@=old@>,@=error@>:=@=continue@>);
+reset(tfm_file,@=error@>:=@=continue@>);
+tfm_count:=0;
+@z
+
+@x
+begin reset(gen_input, cur_name) ;
+@y
+begin close(gen_input,@=error@>:=@=continue@>);
+open(gen_input,cur_name,@=old@>,@=error@>:=@=message@>);
+reset(gen_input,@=error@>:=@=message@>);
+@z
+
+@x
+@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
+@d read_dvi_file(#)==begin
+if dvi_count=block_size then begin
+get(dvi_file,@=error:=continue@>); dvi_count:=0;
+end;
+#:=dvi_file^[dvi_count];
+incr(dvi_count);
+end
+
+@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;
+@z
+
+@x [37]
+@ We need a function that will read in a word from the \.{TFM} file. If
+the particular system
+@^system dependencies@>
+requires buffering, here is the place to do it. It also sets a global flag
+|eof_tfm| when it reaches the end of the file. If this flag is set on
+entrance to |load_tfm_file|, it is assumed that the file is bad.
+
+@p function tfm_integer : integer ;
+var i:integer;
+begin read(tfm_file, i);
+eof_tfm:=eof(tfm_file);
+tfm_integer:=i;
+end;
+
+@ There is nothing wrong with defining |eof_tfm| here.
+
+@<Glob...@>=
+@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.}
+@y
+@ We need a function that will read in a word from the \.{TFM} file. If
+the particular system
+@^system dependencies@>
+requires buffering, here is the place to do it. It also sets a global flag
+|eof_tfm| when it reaches the end of the file. If this flag is set on
+entrance to |load_tfm_file|, it is assumed that the file is bad.
+
+Since |tfm_file| is blocked, we need a function to read an integer
+from it.
+
+@d read_tfm_file(#)==begin
+ if tfm_count=block_size then begin
+ get(tfm_file); tfm_count:=0;
+ end;
+ #:=tfm_file^[tfm_count];
+ incr(tfm_count);
+ end
+@#
+@p function tfm_integer : integer;
+var i,@!result,@!temp:integer;
+begin
+ result := 0;
+ for i:=1 to 4 do begin@/
+ read_tfm_file(temp); result := result*two_8+temp;
+ end;
+ eof_tfm := eof(tfm_file);
+ tfm_integer := result ;
+end;
+
+@ There is nothing wrong with defining |eof_tfm| here.
+
+@<Glob...@>=
+@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.}
+@z
+
+
+@x
+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}
+@y
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in==input {the terminal, considered as an input file}
+@d term_out==output {the terminal, considered as an output file}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!buf_length:integer;
+@z
+
+@x
+@ 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, \vutex\ 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
+@ 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 == do_nothing {empty the terminal output buffer}
+
+@ During the dialog, \vutex\ 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) ;
+k:=0;
+if eoln(term_in) then write_ln
+else begin
+ while (k<terminal_line_length)and not eoln(term_in) do begin
+ buffer[k]:=xord[term_in^];
+ incr(k); get(term_in);
+ end;
+ end;
+buffer[k]:=" ";
+end;
+@z
+
+
+@x [52]
+@p procedure dialog;
+var k:integer; {loop variable}
+begin rewrite(term_out); {prepare the terminal for output}
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the truncation |print_width|@>;
+@<Determine the compression mode@>;
+@<Print all the selected options@>;
+if compress and (print_width > (page_width * 3 div 4)) then
+ page_width := 4 * print_width div 3
+else if (not compress) and (print_width > page_width - hh_offset) then
+ page_width := print_width + hh_offset;
+if page_width > max_p_width then
+ begin
+ page_width := max_p_width;
+ print_width := page_width - hh_offset;
+ end;
+num_lines := total_rast div page_width;
+end;
+@y
+
+@d batch == 0 { batch mode response}
+@d online == 1 { online mode response}
+
+@p procedure dialog;
+var i, k:integer; {loop variable}
+interaction: integer; { to contain response from mode query}
+
+begin rewrite(term_out); {prepare the terminal for output}
+if buf_length<=0 then begin
+ @<Determine the desired |start_count| values@>;
+ @<Determine the desired |max_pages|@>;
+ @<Determine the truncation |print_width|@>;
+ @<Determine the compression mode@>;
+ @<Print all the selected options@>;
+end else @<Determine the options from the command buffer@>;
+if compress and (print_width > (page_width * 3 div 4)) then
+ page_width := 4 * print_width div 3
+else if (not compress) and (print_width > page_width - hh_offset) then
+ page_width := print_width + hh_offset;
+if page_width > max_p_width then
+ begin
+ page_width := max_p_width;
+ print_width := page_width - hh_offset;
+ end;
+num_lines := total_rast div page_width;
+end;
+
+
+@ Here are two new sections to support getting the options from the
+command line.
+
+@<Determine the options...@>==
+ begin
+ buffer[buf_length+1] := "?" ; { end it with a question mark }
+ buf_ptr := 0 ;
+ repeat
+ case buffer[buf_ptr] of
+ " ",",","G","g" : ;
+ "S","s" : begin
+ @<Skip to parameter@> ; k := 0 ;
+ @<Parse the desired |start_vals|@> ;
+ end ;
+ "P","p" : begin
+ @<Skip to parameter@> ;
+ @<Parse the desired |max_pages|@> ;
+ end ;
+ "W","w" : begin
+ @<Skip to parameter@> ;
+ @<Parse the desired |print_width|@> ;
+ end ;
+ "T","t" : compress := false ;
+ "D","d" : out_mode := 1 ;
+ othercases
+ print_ln('Error on command line. Skipping: ',xchr[buffer[buf_ptr]]);
+ @.Error on command line...:@>
+ endcases ;
+ incr(buf_ptr);
+ until buf_ptr >= buf_length;
+ @<Print all the selected options@>;
+end
+
+@ @<Skip to parameter@>==
+while (buf_ptr < buf_length) and (buffer[buf_ptr] <> " ") and
+ (buffer[buf_ptr] <> "=") and (buffer[buf_ptr] <> ",") do
+ incr(buf_ptr);
+if buffer[buf_ptr] = "=" then incr(buf_ptr);
+@z
+
+
+@x [68] set up directory names
+@d tfm_directory_name=='TeXfonts:'
+@d tfm_directory_name_length=9
+@y
+@d tfm_directory_name=='staff:[wolfe]'
+@d tfm_directory_name_length=13
+@z
+
+
+@x [122]
+@d font_list_file=='nonASCII.tex.fnt' {change this to correct name}
+@d list_len==16
+@y
+@d font_list_file=='nonASCIItex.dat'
+@d list_len==23
+@z
+
+@x [207]
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \vutex\ work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+@y
+Here are the remaining changes to the program
+that are necessary to make \.{DVItype} work on Vax/VMS.
+
+@<Const...@>==
+@!block_size=512;
+@!two_8= @'400;
+
+@ @<Types...@>==
+@!byte_block=packed array [0..block_size-1] of 0..255;
+
+@ On Vax/VMS we need the following special definitions, types, variables
+and procedures to be able to get the file name from the command line,
+or to prompt for them.
+
+@d VAX_direct==@=direct@>
+@d VAX_fixed==@=fixed@>
+@d VAX_volatile==@=volatile@>
+@d VAX_immed==@=%immed @>
+@d VAX_external==@=external@>
+@d VAX_stdescr==@=%stdescr @>
+@d VAX_lib_get_foreign==@= lib$get_foreign@>
+@d VAX_length==@=length @>
+@d VAX_fab_type==@= FAB$TYPE @>
+@d VAX_rab_type==@= RAB$TYPE @>
+@d VAX_xab_type==@= XAB$TYPE @>
+@d VAX_fab_xab==@= FAB$L_XAB @>
+@d VAX_xab_nxt==@= XAB$L_NXT @>
+@d VAX_xab_cod==@= XAB$B_COD @>
+@d VAX_xab_fhc==@= XAB$C_FHC @>
+@d VAX_xab_ebk==@= XAB$L_EBK @>
+
+@ @<Types...@>=
+@!sixteen_bits= 0..65535;
+
+@ @<Glob...@>==
+@!type_file: text;
+@!com_line:packed array[1..300] of char;
+@!cmd_len:sixteen_bits;
+@!cmd_i,cmd_e,cmd_o:integer;
+@!dvi_fname,@!bit_fname:varying [300] of char;
+@!got_file_name: boolean;
+
+@ @<Preset init...@>=
+open(output,'SYS$OUTPUT',@=error:=continue@>); {FIX ME! JUNK FOR RUN-TIME BUG}
+cmd_i:=0;
+VAX_lib_get_foreign(com_line,,cmd_len,cmd_i);
+cmd_i:=1;
+while (cmd_i<=cmd_len) and (com_line[cmd_i]=' ') do incr(cmd_i);
+
+cmd_o:=cmd_i;
+while (cmd_o<cmd_len) and (com_line[cmd_o]<>'\') do incr(cmd_o);
+if cmd_o<cmd_len then begin
+ for i:=1 to cmd_len-cmd_o do buffer[i-1] := ord(com_line[cmd_o+i]);
+ buf_length := cmd_len - cmd_o;
+ end
+else buf_length := 0;
+if cmd_o<cmd_len then cmd_len := cmd_o-1;
+
+got_file_name:=(cmd_i<=cmd_o) and (cmd_len>0);
+if got_file_name then @<Parse names of files@>
+else @<Input names of files@>;
+open(dvi_file,dvi_fname,@=readonly@>,
+ @=user_action:=@>dvi_open,@=error:=continue@>);
+if status(dvi_file)<>0 then begin
+ write_ln('Cannot open dvi file!');
+ goto 9999;
+ end;
+
+open(bit_file,bit_fname,@=new,32767@>,@=error:=continue@>);
+if status(bit_file)<>0 then begin
+ write_ln('Cannot open bit file or printer output');
+ goto 9999;
+ end;
+
+@ If the name of the |dvi_file| appears in the command line, we parse it.
+
+@<Parse names of files@>=
+begin cmd_e:=cmd_i+1;
+while (cmd_e<=cmd_len) and (com_line[cmd_e]<>' ') do incr(cmd_e);
+decr(cmd_e);
+dvi_fname:=substr(com_line,cmd_i,cmd_e-cmd_i+1)+'.DVI';
+
+cmd_i:=cmd_e+1;
+while (cmd_i<=cmd_len) and (com_line[cmd_i]=' ') do incr(cmd_i);
+if cmd_i<cmd_o then begin
+ cmd_e:=cmd_i;
+ while (cmd_e<=cmd_len) and (com_line[cmd_e]<>' ') do incr(cmd_e);
+ decr(cmd_e);
+ bit_fname:=substr(com_line,cmd_i,cmd_e-cmd_i+1)+'.VUT';
+ end
+else bit_fname:='SYS$OUTPUT';
+end
+
+
+@ If the name of the |dvi_file| does not appear in the command line, we must
+get one from the user.
+
+@<Input names of files@>=
+begin
+write('DVI file: ');
+if eof then goto 9999;
+read_ln(dvi_fname);
+
+write('VUT file: ');
+if eof then goto 9999;
+read_ln(bit_fname);
+if bit_fname.VAX_length=0 then bit_fname:='SYS$OUTPUT';
+end
+
+@ Here is the library procedure that gets the user's command line.
+
+@<Procedures for ...@>=
+[VAX_external] function VAX_lib_get_foreign(
+ VAX_stdescr cmdlin:[VAX_volatile] packed array [$l1..$u1:integer] of char
+ := VAX_immed 0;
+ VAX_stdescr prompt:[VAX_volatile] packed array [$l2..$u2:integer] of char
+ := VAX_immed 0;
+ var len : [VAX_volatile] sixteen_bits := VAX_immed 0;
+ var flag : [VAX_volatile] integer := VAX_immed 0)
+ :integer; extern;
+
+@ Here is how we intervene to find out the length of the |dvi_file|.
+
+@<Procedures for ...@>=
+function dvi_open(var fab:VAX_fab_type; var rab:VAX_rab_type):integer;
+type XAB_ptr = ^VAX_xab_type;
+var user_status:integer;
+ xab,fhc:XAB_ptr;
+begin
+user_status:=@= $OPEN@>(fab);
+if odd(user_status) then @= $CONNECT@>(rab);
+xab:=fab.VAX_fab_xab::XAB_ptr;
+fhc:=nil;
+while (xab<>nil) and (fhc=nil) do
+ if xab^.VAX_xab_cod=VAX_xab_fhc then fhc:=xab
+ else xab:=xab^.VAX_xab_nxt::XAB_ptr;
+if fhc<>nil then dvi_blocks:=int(fhc^.VAX_xab_ebk)
+else dvi_blocks:=0;
+
+dvi_open:=user_status;
+end;
+
+@z
+
+
diff --git a/dviware/vutex/vaxvms.hlp b/dviware/vutex/vaxvms.hlp
new file mode 100644
index 0000000000..70032f6308
--- /dev/null
+++ b/dviware/vutex/vaxvms.hlp
@@ -0,0 +1,129 @@
+Help File for vutex VMS installations:
+
+
+syntax:
+!vutex [f1] [f2] [\p1 [,p2]...)]
+
+
+purpose:
+vutex processor will print a TeX produced dvi file to an ordinary ASCII device
+
+f1 - name of an existing dvi file created by TeX. The string '.dvi' will be
+ appended to the end of the input name.
+
+f2 - name of a valid ASCII output device. The string '.vut' will be appended to
+ the end of the input name. If no file name for f2 is entered, output will
+ default to SYS$OUTPUT.
+
+If neither f2 nor f2 is entered, the user will be prompted for both. f1 must be
+entered at that time, while a null response to the prompt for f2 will default
+output to SYS$OUTPUT.
+
+p1,p2,... options to control output
+
+Options:
+
+The available options are given by:
+ S or s : for the starting page number
+ P or p : for the number of pages, beginning at S
+ W or w : for the width of the page in characters before truncation
+ T or t : for true TeX spacing as opposed to compressed
+ G or g : to initiate processing without further dialog
+
+
+If no option is entered, you will be prompted for 4 parameters, all of which may
+be defaulted by simply hitting RETURN.
+
+For example,
+
+$vutex out
+This is vutex, DPS8/CP6 Version 0.01
+Starting page (default=*): 3
+Maximum number of pages (default=1000000): <cr>
+Output truncated at column (default=160): 80
+Compress interword spacing, Y or N (default=Y): <cr>
+
+The first two are identical to those for dvilg8 and dvism, namely starting page
+number and number of pages to type. The third prompt is for the page width in
+characters (on most terminals, this is 80; on printers, this is 132).
+
+The fourth option allows the user to compress the spacing between words to one
+space. The text for each line will begin in the proper column at the left
+margin but all other TeX spacing between words will be reduced to one space.
+If the user should not select the compress option, each separate word will be
+left justified in the the space provided by TeX, and sup and super scripts will
+usually appear on separate lines. This option is most useful for tables and
+mathematical equations.
+
+
+Other invocations would include:
+
+$vutex \t,w=80
+DVI file: test.dvi
+VUT file: <cr>
+
+or
+
+$vutex test junk\w=132,s=4
+
+
+
+Seeing TeX output!
+
+One of the more frustrating things about TeX and its precise positioning of
+characters is that ``what you get is far more than what you can see'' on an
+ordinary ASCII terminal. The only devices for TeX output on the mainframe have
+been the laser printers, and that usually equates to running to get ( and
+waiting for ) your output, and excessive waste of paper. Note that at most
+installations, laser output is costed at about 10 times that produced by
+ordinary hammer printers.
+
+So, a program has been written to allow the TeX user to preview the output
+before sending it to the laser printer. Of course, the terminal screen (or an
+ordinary printer) still cannot handle the fine spacing of TeX, nor can it
+display all the characters you might wish to print. But, you will see an
+approximation to the spacing of words across the line and lines down the page.
+
+
+If your output was to a file, such as *vut in the example, you may copy that
+file to an ordinary line printer, or you may use a screen editor to view it.
+Note the following:
+
+ 1. Some characters may not appear because they landed in a place already
+occupied. Two characters from a small TeX font may fit into the space provided
+on the screen, so vutex can only print one such character.
+
+ 2. The output with true TeX spacing, may be wider than an ordinary screen
+of 80 characters, or even a printer page of 132 characters. With ordinary text
+you can compress the text and substantially reduce the width of the page, but
+text with lots of off-line text will appear best in true TeX mode. A screen
+editor may be used to view the output by switching from left to right side of
+the page. The present installation limits the width to 160 characters per line.
+
+ 3. Lines of small type or with extraordinary small interline spacing may
+appear superimposed in spaces in the previous line.
+
+ 4. \special has not been implemented and will be ignored by vutex.
+
+ 5. If characters are missing from a line because of truncation or
+superimposition, then a > sign will appear in the first position of that line.
+
+ 6. Unprintable characters from ASCII fonts, such as Greek symbols,
+and special math symbols, etc. will appear as # marks. However, as much as
+possible, vutex will try to print a meaningful character to suggest what is in
+the TeX output e.g. S is used to replace an integral sign.
+
+ 7. A + sign in the first position of a line means that it contains super
+scripts to the next line down, while a - sign means that it contains sub
+scripts to the previous line up.
+
+ 8. At the end of each TeX page, a ruler is drawn (in centimeters) to
+indicate the horizontal scale (this will not appear on the TeX output to a
+printer). Usually the vertical scale is only an approximation of the true TeX
+spacing, and some pages may appear physically shorter than others. If it is
+desired that all pages be the same length, perhaps with a form feed, this
+alteration can be made to the code.
+
+
+Note: In the examples given above, it is assumed that a suitable CLD file has
+ been implemented. (See vutex.cld on the program disk)
diff --git a/dviware/vutex/vaxvms.ins b/dviware/vutex/vaxvms.ins
new file mode 100644
index 0000000000..bc4647edd6
--- /dev/null
+++ b/dviware/vutex/vaxvms.ins
@@ -0,0 +1,20 @@
+To install vutex on your Vax VMS system:
+
+ 1. ``weave'' vutex.web with vutex.chg to produce a printed document
+
+ 2. check those items marked ``system dependencies'' for desired alterations
+ at your site. Make the necessary changes to the vutex.chg file.
+ For example, directory names should be changed, page_length should be
+ determined, and the end of page actions in the routine finish_page may be
+ altered to include a form feed.
+
+ 3. ``tangle'' vutex.web with vutex.chg to produce a pascal source file
+
+ 4. compile and link the source
+
+ 5. alter and install the nonascii.fnt file in the desired directory. This
+ file lists the names of non roman tfm files with a classification code.
+
+ 6. generate the appropriate command procedure
+
+That's all, folks.
diff --git a/dviware/vutex/vutex.web b/dviware/vutex/vutex.web
new file mode 100644
index 0000000000..c8fe0386e4
--- /dev/null
+++ b/dviware/vutex/vutex.web
@@ -0,0 +1,3533 @@
+% vutex:web
+%
+% This program was written by Warren Wolfe, and all rights are reserved.
+% Copyright 1987 CUBE Software, Victoria, B.C., Canada.
+
+% Version 1.00 ASCII terminal driver: December 1987
+% 1.01 Modification for horizontal and vertical shifts: June 1989
+% 1.02 Modification for page action: November 1989
+
+% 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\vutex{{\bf vu\TeX}}
+\def\tamu{Texas A\char38 M}
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{{\tenrm \vutex}}
+\def\contentspagenumber{0}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont \vutex} processor}
+ \vskip 15pt
+ \centerline{(Version 1.02, Dec 1989)}
+ \vfill}
+\def\botofcontents{
+\bigskip
+\centerline{This report was written by Warren Wolfe.}
+\centerline{based on the original by Tomas Rokicki}
+\centerline{\copyright 1988}
+}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This report documents the program designed to output \TeX\ processed data to any
+ASCII terminal. The program is based on \.{DVIgen} which is intended as a
+generic driver for \.{DVI} files created by \TeX82 and other document
+preparation systems and much of this documentation is extracted directly from
+the Rokicki original. It should be relatively easy to modify for a particular
+system.
+
+\vutex\ uses the 95 printable ASCII characters and the rather limited row and
+column format of an ordinary screen or printer to mimic somehow the typeset
+output of \TeX\ and fine resolution printers. Understandably the result is much
+less than other \.{DVIgen} processors, but it is a lot more acessible.
+
+The overall plan of \vutex\ is to scan the \.{DVI} file and to determine what
+fonts are being used. For most of these fonts, a table is available which maps
+a character in the font to a corresponding printable ASCII character. Then,
+with each page, a page grid is created corresponding to the raster grid of the
+screen or printer. Each character is placed in a location in that grid
+according to the horizontal and vertical coordinates determined by \TeX.
+Subscripts or superscripts are associated with their baseline. Finally the page
+grid is output to the device in either of two forms --- compressed or \TeX\
+spaced. In the compressed mode, all extraneous spaces between words are
+deleted, while the alternate mode places words so that the characters are
+left-justified in the space and at the location determined by \TeX.
+
+@d clone=='vutex'
+
+@ The |banner| string defined here should be changed whenever \vutex\
+gets modified.
+
+@d banner=='This is ',clone,', Version 1.02'
+ {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \vutex\ must read 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@>
+
+@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
+
+@ The binary input comes from |dvi_file|, and the final output is written
+to |bit_file|. On line interaction and error messages are 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
+could easily be redirected if desired.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program vutex(dvi_file,bit_file,input,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@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ 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}
+@d pdone=31 {go here when finished with a subtask during prescan}
+
+@<Labels...@>=final_end,done,pdone;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \vutex 's capacity.
+
+@<Constants...@>=
+@!max_mem_size=68000; {the major array used for almost everything.}
+@!name_size=1000; {total length of all font file names, special commands, and
+other miscellaneous strings.}
+@!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_length=50; {maximum length of a file name}
+@!total_rast=56000; {maximum number of rasters on page}
+@!max_p_width=280; {maximum width of page in rasters}
+@!dfl_p_width=200; {default width of page in rasters}
+@!dfl_n_lines=280; {maximum number of lines in a page }
+@!dfl_screen_width=80; {default width of screen in rasters}
+@!dfl_screen_height=22; {default height of screen in rasters}
+@!hor_overlap=6; {overlap for horizontal page action}
+@!vert_overlap=2; {overlap for vertical page action}
+@!page_length=36; {maximum physical length of a page (in cm)}
+@!hh_offset=18; {offset at left side of page}
+@!vv_offset=36; {offset at top of page}
+
+@ 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;
+\vutex\ 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 \vutex.
+@^system dependencies@>
+
+@d abort(#)==begin print_ln(' ',#); 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, \vutex\ 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 \.{DVI} files use ASCII code for file names
+and certain other strings.
+
+@d dfl_chr == '#' {default character ASCII code for unprintable fonts}
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {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 \vutex,
+So we shall assume that the \PASCAL\ system being used for \vutex\
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+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.
+@^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|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \vutex\ 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. However, the fonts used by \TeX\ do not
+all use the same character table and so we define other arrays to handle output
+for the major font families.
+
+@<Globals...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters for tty fonts}
+@!rchr: array [0..255] of text_char;
+ {specifies conversion of output characters for roman type fonts}
+@!mchr: array [0..255] of text_char;
+ {specifies conversion of output characters for math italic fonts}
+@!schr: array [0..255] of text_char;
+ {specifies conversion of output characters for math symbol fonts}
+@!echr: array [0..255] of text_char;
+ {specifies conversion of output characters for math extension fonts}
+
+@ 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 @'12 do xchr[i]:=dfl_chr;
+xchr[@'13]:='^';
+xchr[@'14]:='v';
+xchr[@'15]:='''';
+xchr[@'16]:=dfl_chr;
+xchr[@'17]:=dfl_chr;@/
+xchr[@'20]:='i';
+xchr[@'21]:='j';
+xchr[@'22]:='`';
+xchr[@'23]:='''';
+xchr[@'24]:=' ';
+xchr[@'25]:=' ';
+xchr[@'26]:=' ';
+xchr[@'27]:=' ';@/
+xchr[@'30]:=' ';
+xchr[@'31]:=dfl_chr;
+xchr[@'32]:=dfl_chr;
+xchr[@'33]:=dfl_chr;
+xchr[@'34]:=dfl_chr;
+xchr[@'35]:=dfl_chr;
+xchr[@'36]:=dfl_chr;
+xchr[@'37]:=dfl_chr;
+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]:=dfl_chr;
+
+@ Many of the \TeX\ standard fonts use a character layout slightly different
+from that given above. Therefore, we define a new array to handle output
+from these fonts. Modern Roman fonts are given below.
+
+@<Set init...@>=
+for i:=0 to @'17 do rchr[i]:=dfl_chr;
+rchr[@'20]:='i';
+rchr[@'21]:='j';
+rchr[@'22]:='`';
+rchr[@'23]:='''';
+rchr[@'24]:=' ';
+rchr[@'25]:=' ';
+rchr[@'26]:=' ';
+rchr[@'27]:=' ';@/
+rchr[@'30]:=' ';
+rchr[@'31]:='s';
+rchr[@'32]:='a';
+rchr[@'33]:='o';
+rchr[@'34]:='o';
+rchr[@'35]:='A';
+rchr[@'36]:='O';
+rchr[@'37]:='O';@/
+rchr[@'40]:=' ';
+rchr[@'41]:='!';
+rchr[@'42]:='"';
+rchr[@'43]:='#';
+rchr[@'44]:='$';
+rchr[@'45]:='%';
+rchr[@'46]:='&';
+rchr[@'47]:='''';@/
+rchr[@'50]:='(';
+rchr[@'51]:=')';
+rchr[@'52]:='*';
+rchr[@'53]:='+';
+rchr[@'54]:=',';
+rchr[@'55]:='-';
+rchr[@'56]:='.';
+rchr[@'57]:='/';@/
+rchr[@'60]:='0';
+rchr[@'61]:='1';
+rchr[@'62]:='2';
+rchr[@'63]:='3';
+rchr[@'64]:='4';
+rchr[@'65]:='5';
+rchr[@'66]:='6';
+rchr[@'67]:='7';@/
+rchr[@'70]:='8';
+rchr[@'71]:='9';
+rchr[@'72]:=':';
+rchr[@'73]:=';';
+rchr[@'74]:=dfl_chr;
+rchr[@'75]:='=';
+rchr[@'76]:=dfl_chr;
+rchr[@'77]:='?';@/
+rchr[@'100]:='@@';
+rchr[@'101]:='A';
+rchr[@'102]:='B';
+rchr[@'103]:='C';
+rchr[@'104]:='D';
+rchr[@'105]:='E';
+rchr[@'106]:='F';
+rchr[@'107]:='G';@/
+rchr[@'110]:='H';
+rchr[@'111]:='I';
+rchr[@'112]:='J';
+rchr[@'113]:='K';
+rchr[@'114]:='L';
+rchr[@'115]:='M';
+rchr[@'116]:='N';
+rchr[@'117]:='O';@/
+rchr[@'120]:='P';
+rchr[@'121]:='Q';
+rchr[@'122]:='R';
+rchr[@'123]:='S';
+rchr[@'124]:='T';
+rchr[@'125]:='U';
+rchr[@'126]:='V';
+rchr[@'127]:='W';@/
+rchr[@'130]:='X';
+rchr[@'131]:='Y';
+rchr[@'132]:='Z';
+rchr[@'133]:='[';
+rchr[@'134]:='"';
+rchr[@'135]:=']';
+rchr[@'136]:='^';
+rchr[@'137]:=' ';@/
+rchr[@'140]:='`';
+rchr[@'141]:='a';
+rchr[@'142]:='b';
+rchr[@'143]:='c';
+rchr[@'144]:='d';
+rchr[@'145]:='e';
+rchr[@'146]:='f';
+rchr[@'147]:='g';@/
+rchr[@'150]:='h';
+rchr[@'151]:='i';
+rchr[@'152]:='j';
+rchr[@'153]:='k';
+rchr[@'154]:='l';
+rchr[@'155]:='m';
+rchr[@'156]:='n';
+rchr[@'157]:='o';@/
+rchr[@'160]:='p';
+rchr[@'161]:='q';
+rchr[@'162]:='r';
+rchr[@'163]:='s';
+rchr[@'164]:='t';
+rchr[@'165]:='u';
+rchr[@'166]:='v';
+rchr[@'167]:='w';@/
+rchr[@'170]:='x';
+rchr[@'171]:='y';
+rchr[@'172]:='z';
+rchr[@'173]:='-';
+rchr[@'174]:='-';
+rchr[@'175]:='"';
+rchr[@'176]:='~';
+for i:=@'177 to 255 do rchr[i]:=dfl_chr;
+
+@ The standard \TeX\ math italics fonts use quite a different layout than
+either of the above, and we include a separate array for these useful fonts.
+
+@<Set init...@>=
+for i:=0 to @'47 do mchr[i]:=dfl_chr;
+mchr[@'50]:='-';
+mchr[@'51]:='-';
+mchr[@'52]:='-';
+mchr[@'53]:='-';
+mchr[@'54]:=' ';
+mchr[@'55]:=' ';
+mchr[@'56]:='>';
+mchr[@'57]:='<';@/
+mchr[@'60]:='0';
+mchr[@'61]:='1';
+mchr[@'62]:='2';
+mchr[@'63]:='3';
+mchr[@'64]:='4';
+mchr[@'65]:='5';
+mchr[@'66]:='6';
+mchr[@'67]:='7';@/
+mchr[@'70]:='8';
+mchr[@'71]:='9';
+mchr[@'72]:='.';
+mchr[@'73]:=',';
+mchr[@'74]:='<';
+mchr[@'75]:='/';
+mchr[@'76]:='>';
+mchr[@'77]:='*';@/
+mchr[@'100]:=dfl_chr;
+mchr[@'101]:='A';
+mchr[@'102]:='B';
+mchr[@'103]:='C';
+mchr[@'104]:='D';
+mchr[@'105]:='E';
+mchr[@'106]:='F';
+mchr[@'107]:='G';@/
+mchr[@'110]:='H';
+mchr[@'111]:='I';
+mchr[@'112]:='J';
+mchr[@'113]:='K';
+mchr[@'114]:='L';
+mchr[@'115]:='M';
+mchr[@'116]:='N';
+mchr[@'117]:='O';@/
+mchr[@'120]:='P';
+mchr[@'121]:='Q';
+mchr[@'122]:='R';
+mchr[@'123]:='S';
+mchr[@'124]:='T';
+mchr[@'125]:='U';
+mchr[@'126]:='V';
+mchr[@'127]:='W';@/
+mchr[@'130]:='X';
+mchr[@'131]:='Y';
+mchr[@'132]:='Z';
+mchr[@'133]:=dfl_chr;
+mchr[@'134]:=dfl_chr;
+mchr[@'135]:=dfl_chr;
+mchr[@'136]:=dfl_chr;
+mchr[@'137]:=dfl_chr;@/
+mchr[@'140]:='l';
+mchr[@'141]:='a';
+mchr[@'142]:='b';
+mchr[@'143]:='c';
+mchr[@'144]:='d';
+mchr[@'145]:='e';
+mchr[@'146]:='f';
+mchr[@'147]:='g';@/
+mchr[@'150]:='h';
+mchr[@'151]:='i';
+mchr[@'152]:='j';
+mchr[@'153]:='k';
+mchr[@'154]:='l';
+mchr[@'155]:='m';
+mchr[@'156]:='n';
+mchr[@'157]:='o';@/
+mchr[@'160]:='p';
+mchr[@'161]:='q';
+mchr[@'162]:='r';
+mchr[@'163]:='s';
+mchr[@'164]:='t';
+mchr[@'165]:='u';
+mchr[@'166]:='v';
+mchr[@'167]:='w';@/
+mchr[@'170]:='x';
+mchr[@'171]:='y';
+mchr[@'172]:='z';
+mchr[@'173]:='i';
+mchr[@'174]:='j';
+mchr[@'175]:='p';
+mchr[@'176]:=' ';
+mchr[@'177]:=' ';@/
+for i:=@'200 to 255 do mchr[i]:=dfl_chr;
+
+@ The math symbol font is, for the most part, unprintable in ASCII, but we
+shall map some of the characters onto meaningful representations.
+
+@<Set init...@>=
+schr[0]:='-';
+schr[1]:='.';
+schr[2]:='x';
+schr[3]:='*';
+schr[4]:=':';
+schr[5]:=dfl_chr;
+schr[6]:='+';
+schr[7]:='-';@/
+schr[@'10]:='+';
+schr[@'11]:='-';
+schr[@'12]:='x';
+schr[@'13]:='o';
+schr[@'14]:='o';
+schr[@'15]:='O';
+schr[@'16]:='o';
+schr[@'17]:='o';@/
+schr[@'20]:='=';
+schr[@'21]:='=';
+schr[@'22]:='<';
+schr[@'23]:='>';
+schr[@'24]:='<';
+schr[@'25]:='>';
+schr[@'26]:='<';
+schr[@'27]:='>';@/
+schr[@'30]:='~';
+schr[@'31]:='~';
+schr[@'32]:='<';
+schr[@'33]:='>';
+schr[@'34]:='<';
+schr[@'35]:='>';
+schr[@'36]:='<';
+schr[@'37]:='>';@/
+schr[@'40]:='-';
+schr[@'41]:='-';
+schr[@'42]:='|';
+schr[@'43]:='|';
+schr[@'44]:='-';
+schr[@'45]:='/';
+schr[@'46]:='\';
+schr[@'47]:='~';@/
+schr[@'50]:='-';
+schr[@'51]:='-';
+schr[@'52]:='|';
+schr[@'53]:='|';
+schr[@'54]:='-';
+schr[@'55]:='\';
+schr[@'56]:='/';
+schr[@'57]:=dfl_chr;@/
+schr[@'60]:='''';
+schr[@'61]:=dfl_chr;
+schr[@'62]:=dfl_chr;
+schr[@'63]:=dfl_chr;
+schr[@'64]:=dfl_chr;
+schr[@'65]:=dfl_chr;
+schr[@'66]:='/';
+schr[@'67]:=dfl_chr;@/
+schr[@'70]:=dfl_chr;
+schr[@'71]:=dfl_chr;
+schr[@'72]:='-';
+schr[@'73]:='0';
+schr[@'74]:='R';
+schr[@'75]:='I';
+schr[@'76]:='|';
+schr[@'77]:='|';@/
+schr[@'100]:=dfl_chr;
+schr[@'101]:='A';
+schr[@'102]:='B';
+schr[@'103]:='C';
+schr[@'104]:='D';
+schr[@'105]:='E';
+schr[@'106]:='F';
+schr[@'107]:='G';@/
+schr[@'110]:='H';
+schr[@'111]:='I';
+schr[@'112]:='J';
+schr[@'113]:='K';
+schr[@'114]:='L';
+schr[@'115]:='M';
+schr[@'116]:='N';
+schr[@'117]:='O';@/
+schr[@'120]:='P';
+schr[@'121]:='Q';
+schr[@'122]:='R';
+schr[@'123]:='S';
+schr[@'124]:='T';
+schr[@'125]:='U';
+schr[@'126]:='V';
+schr[@'127]:='W';@/
+schr[@'130]:='X';
+schr[@'131]:='Y';
+schr[@'132]:='Z';
+schr[@'133]:=dfl_chr;
+schr[@'134]:=dfl_chr;
+schr[@'135]:=dfl_chr;
+schr[@'136]:=dfl_chr;
+schr[@'137]:=dfl_chr;@/
+schr[@'140]:='-';
+schr[@'141]:='-';
+schr[@'142]:='|';
+schr[@'143]:='|';
+schr[@'144]:='|';
+schr[@'145]:='|';
+schr[@'146]:='{';
+schr[@'147]:='}';@/
+schr[@'150]:='<';
+schr[@'151]:='>';
+schr[@'152]:='|';
+schr[@'153]:='|';
+schr[@'154]:='|';
+schr[@'155]:='|';
+schr[@'156]:='\';
+schr[@'157]:=dfl_chr;@/
+schr[@'160]:='|';
+schr[@'161]:=dfl_chr;
+schr[@'162]:=dfl_chr;
+schr[@'163]:='S';
+schr[@'164]:=dfl_chr;
+schr[@'165]:=dfl_chr;
+schr[@'166]:='<';
+schr[@'167]:='>';@/
+for i:=@'170 to @'176 do schr[i]:=dfl_chr;
+for i:=@'177 to 255 do schr[i]:=dfl_chr;
+
+@ And then there is the math extension font which is used to create extended
+symbols. We do the best that we can.
+
+@<Set init...@>=
+echr[0]:='(';
+echr[1]:=')';
+echr[2]:='[';
+echr[3]:=']';
+echr[4]:='|';
+echr[5]:='|';
+echr[6]:='|';
+echr[7]:='|';@/
+echr[@'10]:='{';
+echr[@'11]:='}';
+echr[@'12]:='<';
+echr[@'13]:='>';
+echr[@'14]:='|';
+echr[@'15]:='|';
+echr[@'16]:='/';
+echr[@'17]:='\';@/
+echr[@'20]:='(';
+echr[@'21]:=')';
+echr[@'22]:='(';
+echr[@'23]:=')';
+echr[@'24]:='[';
+echr[@'25]:=']';
+echr[@'26]:='|';
+echr[@'27]:='|';@/
+echr[@'30]:='|';
+echr[@'31]:='|';
+echr[@'32]:='{';
+echr[@'33]:='}';
+echr[@'34]:='<';
+echr[@'35]:='>';
+echr[@'36]:='/';
+echr[@'37]:='\';@/
+echr[@'40]:='(';
+echr[@'41]:=')';
+echr[@'42]:='[';
+echr[@'43]:=']';
+echr[@'44]:='|';
+echr[@'45]:='|';
+echr[@'46]:='|';
+echr[@'47]:='|';@/
+echr[@'50]:='{';
+echr[@'51]:='}';
+echr[@'52]:='<';
+echr[@'53]:='>';
+echr[@'54]:='/';
+echr[@'55]:='\';
+echr[@'56]:='/';
+echr[@'57]:='\';@/
+echr[@'60]:='(';
+echr[@'61]:=')';
+echr[@'62]:='|';
+echr[@'63]:='|';
+echr[@'64]:='|';
+echr[@'65]:='|';
+echr[@'66]:='|';
+echr[@'67]:='|';@/
+echr[@'70]:='(';
+echr[@'71]:=')';
+echr[@'72]:='(';
+echr[@'73]:=')';
+echr[@'74]:='{';
+echr[@'75]:='}';
+echr[@'76]:='|';
+echr[@'77]:='|';@/
+echr[@'100]:='(';
+echr[@'101]:=')';
+echr[@'102]:='|';
+echr[@'103]:='|';
+echr[@'104]:='<';
+echr[@'105]:='>';
+echr[@'106]:=dfl_chr;
+echr[@'107]:=dfl_chr;@/
+echr[@'110]:='S';
+echr[@'111]:='S';
+echr[@'112]:='o';
+echr[@'113]:='O';
+echr[@'114]:='+';
+echr[@'115]:='+';
+echr[@'116]:='x';
+echr[@'117]:='X';@/
+echr[@'120]:='E';
+echr[@'121]:=dfl_chr;
+echr[@'122]:='S';
+for i:=@'123 to @'127 do echr[i]:=dfl_chr;
+echr[@'130]:='E';
+echr[@'131]:=dfl_chr;
+echr[@'132]:='S';
+for i:=@'133 to @'141 do echr[i]:=dfl_chr;
+echr[@'142]:='^';
+echr[@'143]:='^';
+echr[@'144]:='^';
+echr[@'145]:='~';
+echr[@'146]:='~';
+echr[@'147]:='~';@/
+echr[@'150]:='[';
+echr[@'151]:=']';
+echr[@'152]:='|';
+echr[@'153]:='|';
+echr[@'154]:='|';
+echr[@'155]:='|';
+echr[@'156]:='{';
+echr[@'157]:='}';@/
+echr[@'160]:='|';
+echr[@'161]:='|';
+echr[@'162]:='|';
+echr[@'163]:='|';
+echr[@'164]:='|';
+echr[@'165]:='|';
+echr[@'166]:='|';
+echr[@'167]:='|';@/
+echr[@'170]:='|';
+echr[@'171]:='\';
+echr[@'172]:=dfl_chr;
+echr[@'173]:=dfl_chr;
+echr[@'174]:=dfl_chr;
+echr[@'175]:=dfl_chr;
+echr[@'176]:='|';
+echr[@'177]:='|';@/
+for i:=@'200 to 255 do echr[i]:=dfl_chr;
+
+@ 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 \vutex, we need to know exactly
+what \.{DVI} files are. The form of such files was designed by David R.
+@^Fuchs, David Raymond@>
+Fuchs in 1979. Almost any reasonable typesetting 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 a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), 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 character whose number
+matches the opcode, 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 \vutex\ will allow character codes greater than 255, assuming that
+they all have the same width as the character whose code is $c \bmod 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$.
+
+\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]$ $\ldots$ $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} $\ldots$ \.{\\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. \vutex\ does not do such sorting.)
+
+\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 short enough
+\.{\\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<256|; 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^{31}$@>|.
+
+\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. (For example, there are
+exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
+points where there are $2^{16}$ sp in a point, so \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 postable.
+
+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|.
+\vutex\ prints this comment out, which, in \TeX82 files, contains the date
+and time of execution.
+
+@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 and \\{xxx} 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. \vutex\ does not make use of this capability,
+however.
+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$[{\G}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.
+Unfortunately, since characters can be set outside of the page boundaries,
+these numbers cannot be used to set limits on the page area.
+
+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 next; 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@>
+
+@* Plan of attack.
+
+A \.{DVI} to device program must deal with a large amount of data. The
+\.{DVI} files in most applications tend to be quite large.
+For this reason, one of the primary goals in the design of this program was
+to reduce memory requirements as much as possible. After much consideration
+and examination of several other \.{DVI} drivers, it was decided that operation
+of this program would occur in three distinct phases---prescan, font loading,
+and page drawing.
+
+Every \.{DVI} file is prescanned. During this phase, information about font
+usage is collected. A very simple interpreter simply notices the |bop| and
+|eop| tokens (to allow a subset of the pages to be printed), all of the
+|font_def| commands (the name and other information is stored), and the
+|set_char| commands (certain counters are incremented). For every font that is
+declared, space is allocated on the main |mem| array for a table of information
+regarding that font. This table contains that information that is necessary as
+determined from the \TeX\ font metric (\.{TFM}) files.
+
+During the second phase, the fonts are given a priority rating and the \.{TFM}
+information table is created. At this point, all the information about
+font usage in the file is located in the |mem| array. Therefore, a
+routine named |base_font| is called. This routine scans the declared fonts
+and assesses them as either printable ASCII fonts or as unprintable fonts.
+The printable fonts are given priority ratings based on the use of characters in
+each font. The font with priority 0 is the |base_font| and the horizontal
+raster resolution is based on spacings determined by that font.
+
+The vertical raster resolution is determined by the arbitrary assignment of a
+set number of lines to the printed page. Each character or word is then
+assigned a line in the page according to its vertical coordinate.
+
+The routine |load_tfm_file| creates a table for each font, declaring the width
+of each character in \.{DVI} units. Then a companion table is created with a
+raster unit width for each character.
+
+During phase three, the \.{DVI} file is reread one page at a time, and the
+characters are ``drawn'' onto a |page|. This is a character array which
+will be output to the device when the page is completed. Because of the coarse
+raster resolution of the device, several characters may be placed within the
+same array space, but the priority rating is used to determine which character
+shall appear there. Thus, a |priority| array is kept to determine the
+priority of any character appearing on the |page|. Also, because of
+differences in \TeX\ font spacing, blank spaces may appear in words if the
+characters were placed exactly in their raster positions. Instead, a |word|
+array positions a sequence of characters into adjacent raster positions and then
+attempts to reset the sequence into the space allocated by \TeX\ in the
+|page|. Such words are left-justified in the allocated space so that
+large spaces (or, possibly, one space) will appear between words in the
+|page|.
+
+We note that, if a sequence of characters is set from a font with smaller
+character sizes than the |base_font|, then the space allocated by \TeX\ for that
+word will be smaller than the raster width required. Hence, words consisting of
+characters from small fonts may be truncated.
+
+\vutex\ output is in one of two modes --- compressed or pure \TeX. In the pure
+\TeX\ mode, each page is printed as it was constructed in the array, i.e. each
+word is left-justified in the space provided by the \.{DVI} commands. Again,
+a number of spaces may appear between word, and the output can require a wide
+palette. Sub/super script lines will appear on separate associated lines.
+
+The compression mode will shrink inter-word spacing to one space, with the first
+character on each line appearing in its proper \TeX\ position, and all
+subsequent words are shifted to the left. Difficulties in associating sub/super
+script lines with the proper word in the baseline has forced a vertical
+compression of the associated lines into the baseline before the horizontal
+compression takes place. This may lead to ambiguous text, particularly with
+mathematical expressions, but it is recommended that such text be viewed in the
+unaltered non-compressed mode.
+
+The format of the |mem| array reflects these phases. It is organized as
+a large array of 32-bit words. The first 256 words are used as font pointers.
+They are initialized to zero at the beginning of the program. Then, whenever
+font number |k| is defined, 262 words are allocated in the |mem| array for the
+font, and |mem[k]| is set to point to the sixth word of the allocated space.
+
+The first allocated word, or |mem[mem[k]-6]|, consists of two sixteen bit
+integers. The least significant sixteen bits are the external or directory
+size of the font. The high order sixteen bits contain a pointer to the |names|
+array, which contains the name of the font.
+
+The next word (|mem[mem[k]-5]|) contains the |font_scaled_size| followed by the
+font design size. Then comes the |font_space|. The next word is |font_type|
+which determines the output array to use, followed by |font_status| to indicate
+if the font is a printable font or not.
+
+Finally comes the tabular information for the individual characters. They each
+use two words. The first word, |mem[mem[k]+2*f]|, contains the |tfm_width| of
+the character in \.{DVI} units. The next contains the |use_count| for the first
+pass, and the |raster_width| during the second pass.
+
+During the first pass, the first word of the first character in the table
+contains the checksum of the \.{TFM} file loaded from the \.{DVI} file.
+Hopefully this will match the checksum in the \.{TFM} file.
+
+The |names| array will contain all of the string information in the file. It
+is used as the input buffer from the terminal, the storage area for font names,
+temporary space for special's, etc. A `string' consists of a pointer into
+this array. The end of every string will be the value 0 as a marker.
+|next_names_free| indicates the end of the table.
+
+@ The following macroes should make the program slightly more readable, as they
+calculate the offsets, etc. automatically.
+
+@d font_desc_size=262
+@d hi(#)==(# div 65536)
+@d lo(#)==(# mod 65536)
+@d raster_round(#)==round((#)/resol) + hh_offset
+@d line_round(#)==round((#)/vresol) + vv_offset
+@d set_line(#)==
+ if line_for[#]<0 then
+ begin line_for[#] := next_line_free;
+ next_line_free:=next_line_free + page_width;
+ end
+@d @!font_check_sum == mem[cur_fptr] {Temporary location for checksum}
+@d @!font_scaled_size == mem[cur_fptr-5] {Scaled size for current font}
+@d @!font_design_size == mem[cur_fptr-4] {Design size for current font}
+@d @!font_space == mem[cur_fptr-3] {Size of a thinspace in the font}
+@d @!font_type == mem[cur_fptr-2] {Code to determine type of font}
+@d @!declared == 1
+@d @!other == 2
+@d @!mexten == 3
+@d @!msymbol == 4
+@d @!mitalic == 5
+@d @!tty == 6
+@d @!roman == 7
+@d @!font_status == mem[cur_fptr-1] {Priority of the current font}
+@d @!font_nsave == mem[cur_fptr-6] {For storing the name of the font}
+@d @!font_name == hi(mem[cur_fptr-6]) {High sixteen bits for font name ptr}
+@d @!directory_size == lo(mem[cur_fptr-6]) {Low sixteen bits for external
+ size pointer}
+@d @!tfm_width == mem[cur_char_ptr] {\.{TFM} width of current character}
+@d @!use_count == mem[cur_char_ptr+1] {Use count used mostly in phase two.}
+@d @!raster_width == mem[cur_char_ptr+1] {character width in rasters, phase
+ three}
+@d @!temp_ar(#) == mem[next_mem_free+#] {`array' used for temporary storage}
+
+@ We also need to define some of these variables, and initialize them.
+
+@<Glob...@>=
+@!mem : array [0..max_mem_size] of integer ; {major memory array}
+@!names : array [0..name_size] of 0..127 ; {string array}
+@!cur_fptr, @!cur_char_ptr : integer ; {temporary pointers}
+@!next_mem_free, @!next_names_free : integer; { used for allocation }
+i,j : integer ; {plain old index variable}
+
+@ @<Set init...@>=
+for i := 0 to 255 do
+ mem[i] := 0 ; { no fonts defined yet.}
+next_mem_free := 256 ; { next available position in mem array }
+next_names_free := 1 ; { next available position in names array }
+
+@* Input from binary files.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@!word_file=packed file of integer; {for pixel file words}
+
+@ The program deals with two binary file variables: |dvi_file| is the main input
+file that we are translating into symbolic form, and |tfm_file| is the current
+\.{TFM} file from which character raster information is being read. The third
+file is the final output file, a text file.
+
+@<Glob...@>=
+@!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
+@!tfm_file:byte_file; {a font metric file}
+@!bit_file:text_file; {where the final output goes}
+@^system dependencies@>
+
+@ To prepare these files for input, we |reset| them. An extension of
+\PASCAL\ is needed in the case of |tfm_file|, since we want to associate
+it 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.
+@^system dependencies@>
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure reopen_dvi_file; {reopens the |dvi_file| for the next scan}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_bit_file; {prepares final output for writing}
+begin rewrite(bit_file);
+bit_is_open := true ;
+end;
+@#
+procedure open_tfm_file; {opens \.{TFM} file}
+begin reset(tfm_file,cur_name);
+eof_tfm := eof(tfm_file);
+end;
+@#
+procedure open_input_text ; {prepares to read from general purpose input}
+begin reset(gen_input, cur_name) ;
+end ;
+
+@ |cur_loc| and |cur_name| are global variables: |cur_loc| is the number of the
+byte about to be read next from |dvi_file|, and |cur_name| is a string variable
+that will be set to the current \.{TFM} file name before |open_tfm_file| is
+called.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+
+@ 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;
+
+@* 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 a program that reads a
+\.{DVI} file is supposed to know the widths of the characters that appear in
+\\{set\_char} commands. Therefore \vutex\ reads in a \.{TFM} file for each
+font that is used.
+
+@ It is, of course, a simple matter to print the name of a given font.
+
+@p procedure print_font(@!f:integer);
+var k:0..name_size; {index into |names|}
+ cur_fptr : integer ;
+begin cur_fptr := mem[f]; if font_name=0 then print_ln('UNDEFINED!')
+@.UNDEFINED@>
+else begin k := font_name ;
+ if names[k]>0 then begin
+ while names[k]>0 do begin
+ print(xchr[names[k]]) ;
+ k := k + 1 ;
+ end ;
+ print(':');
+ end ;
+ k := k + 1 ;
+ while names[k]>0 do begin
+ print(xchr[names[k]]);
+ k := k + 1 ;
+ end ;
+ print_ln(' ');
+ end;
+end;
+
+@ We need a function that will read in a word from the \.{TFM} file. If
+the particular system
+@^system dependencies@>
+requires buffering, here is the place to do it. It also sets a global flag
+|eof_tfm| when it reaches the end of the file. If this flag is set on
+entrance to |load_tfm_file|, it is assumed that the file is bad.
+
+@p function tfm_integer : integer ;
+var i:integer;
+begin read(tfm_file, i);
+eof_tfm:=eof(tfm_file);
+tfm_integer:=i;
+end;
+
+@ There is nothing wrong with defining |eof_tfm| here.
+
+@<Glob...@>=
+@!eof_tfm:boolean; {true when end of \.{TFM} file is reached.}
+
+
+@ The most important part of |load_tfm_file| is the conversion to \.{DVI} units,
+which involves multiplying the relative values in the \.{TFM} 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 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.
+
+@p function tfm_to_int(val:integer) : integer ;
+var alpha, beta, x, z:integer ; {help in converting widths}
+ b0, b1, b2, b3:eight_bits ; {temporary byte storage}
+begin z := font_scaled_size ;
+@<Split tfm value up into bytes@>;
+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+ z:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta ;
+ if b0=255 then z := z - alpha ;
+ tfm_to_int := z ;
+end ;
+
+@ @<Split tfm value up...@>=
+begin x:=val ;
+if x>=0 then b0:=x div @'100000000
+else begin x:=(x+@'10000000000)+@'10000000000; b0:=x div @'100000000+128;
+ end ;
+x:=x mod @'100000000;
+b1:=x div @'200000;x:=x mod @'200000;b2:=x div @'400;b3:=x mod @'400; end;
+
+@ @<Replace |z|...@>=
+begin alpha:=16*z; beta:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; beta:=beta div 2;
+ end;
+end ;
+
+@ And, finally, we have the procedure which will extract the information from
+the \.{TFM} file. This routine is for input from the \.{TFM} file, which is (or
+should be) a |packed file of integer|, so this should cause no grief. See the
+documentation on |tftopl| for the structure of the file.
+
+
+@ @p procedure load_tfm_file ; {read in the width data}
+label 9997, {used for bad format}
+ 9999; {used for normal exit}
+var r:integer; {used for |cur_name| manipulation}
+ i, k : integer ; {used for general pointer}
+ bc, ec : integer ; {first and last characters in font}
+ @!ptr : integer; {pointer to width table}
+
+begin
+ @<Move tfm name into the |cur_name| string@> ;
+ if out_mode > 0 then print_ln('Trying to load ',cur_name) ;
+ open_tfm_file ;
+ k := next_mem_free ;
+ if eof_tfm then goto 9997 ;
+ while not eof_tfm do begin
+ mem[k] := tfm_integer ;
+ k := k + 1 ;
+ if k > max_mem_size then
+ abort(clone,' memory size exceeded on load of tfm file!') ;
+ end ;
+ bc := hi(mem[next_mem_free+1]) ;
+ ec := lo(mem[next_mem_free+1]) ;
+ if mem[next_mem_free+6]<>font_check_sum then
+ print_ln('Checksum in tfm file does not match that in dvi file!') ;
+ k := next_mem_free + lo(mem[next_mem_free]) + 6 - bc ;
+ cur_char_ptr := cur_fptr ;
+ for i := 0 to bc-1 do begin
+ tfm_width := 0 ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+ end ;
+ for i := bc to ec do begin
+ if mem[k+i] > 0 then
+ ptr := mem[k+i] div @'100000000
+ else
+ ptr := ((mem[k+i] + one_fourth) + one_fourth) div @'100000000 + 128 ;
+ tfm_width := tfm_to_int(mem[ptr + ec + k + 1 ]) ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+ end ;
+ for i := ec+1 to 127 do begin
+ tfm_width := 0 ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+ end ;
+ goto 9999 ;
+9997: print_ln('---not loaded, TFM file is bad') ;
+9999: end ;
+
+@* User interface.
+\vutex\ is normally considered a filter, and therefore operates very
+quietly unless some error is encountered. However, for debugging purposes
+or the inquisitive, one can access the variable |out_mode|. This variable
+will allow the tracing of \vutex. It has two possible values: 0, and 1.
+If it is zero, then the program is very quiet, only informing the user of
+errors. If it is one, a trace is executed for debugging purposes.
+
+There are a few optional parameters requested when this program is invoked. It
+is possible to print only a restricted subset of the pages by specifying the
+desired starting page and the maximum number of pages. You also may select the
+number of rasters across a page and the compression mode for output.
+
+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,
+\vutex\ will begin with the earliest such page in the file. The
+default specification `\.*' (which matches all pages) therefore denotes
+the page at the beginning of the file.
+
+When \vutex\ begins, it engages the user in a brief dialog so that the
+options will be specified. This part of \vutex\ 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
+(|out_mode=terse|, starting page `\.*', |max_pages=1000000|, |print_width=160|,
+and |compress=false|). On other hand, the system-dependent routines that are
+needed are not complicated, so it will not be terribly difficult to introduce
+them. Normally, however, the necessary parameters should be taken from the
+command line which invokes the program (if this is possible.) Then, this part
+of the program would not even be used. This is in the domain of the change
+file, however.
+
+@^system dependencies@>
+
+@d errors_only=0 {value of |out_mode| when minimal printing occurs}
+@d terse=1 {value of |out_mode| for abbreviated output}
+
+@<Glob...@>=
+@!out_mode:errors_only..terse; {controls the amount of output}
+@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
+@!page_width:integer; {page width for setting characters}
+@!num_lines:integer; {number of lines in a page}
+@!print_width: integer; {the number of raster units across screen before
+ truncation}
+@!print_height: integer; {the number of lines printed on each screen}
+@!compress: boolean; {true if interword spacing to be compressed to one blank}
+@!batch_mode: boolean; {true if pages to be processed with no interaction}
+
+@ The starting page specification is recorded in two global arrays called
+|start_count| and |start_there|. 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. The other values of |start_count| and |start_there| are not
+important, in this example.
+
+@<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}
+
+@ @<Set init...@>=
+out_mode := errors_only;
+max_pages:=1000000; start_vals:=0; start_there[0]:=false;
+page_width:= dfl_p_width; num_lines:=dfl_n_lines;
+print_width:= dfl_screen_width; print_height:= dfl_screen_height;
+compress:=false;batch_mode:=false;
+
+@ 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. 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, \vutex\ 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 \vutex\ begins.
+@^system dependencies@>
+
+@p procedure dialog;
+var k:integer; {loop variable}
+ dum_width:integer;
+ dum_height:integer;
+begin rewrite(term_out); {prepare the terminal for output}
+dum_width := 0; dum_height := 0;
+@<Determine the desired interactive mode@>;
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the number of printed rasters |dum_width|@>;
+@<Determine the number of printed rows |dum_height|@>;
+@<Determine the compression mode@>;
+@<Print all the selected options@>;
+@<Print the page action options@>;
+@<Adjust page dimensions@>;
+end;
+
+@ @<Determine the desired interactive mode@>=
+write(term_out,'Interactive mode (default=Y): ');
+batch_mode := false; input_ln; buf_ptr:=0;
+if (buffer[buf_ptr] = "N") or (buffer[buf_ptr] = "n") then batch_mode:=true ;
+
+@ @<Determine the desired |start...@>=
+write(term_out,'Starting page (default=*): ');
+start_vals:=0; start_there[0]:=false;
+input_ln; buf_ptr:=0; k:=0;
+@<Parse the desired |start_vals|@>
+
+@ These two routines have been split in half so it is easier to accept these
+options from the command line if your operating system makes that possible.
+Put a question mark at the end of the line to end it when doing it this way.
+
+@<Parse the desired |start_vals|@>=
+if (buffer[buf_ptr] <> "?") and (buffer[buf_ptr] <> " ") and
+ (buffer[buf_ptr] <> "/") then begin
+ repeat if buffer[buf_ptr]="*" then
+ begin start_there[k]:=false; incr(buf_ptr);
+ end
+ else begin start_there[k]:=true; start_count[k]:=get_integer;
+ end;
+ if (k<9)and(buffer[buf_ptr]=".") then
+ begin incr(k); incr(buf_ptr);
+ end
+ else if (buffer[buf_ptr]=" ") or (buffer[buf_ptr]="/") or
+ (buffer[buf_ptr]="?") then
+ start_vals:=k
+ else begin k := 0 ; start_vals := 0 ; end ;
+ until start_vals=k ;
+end
+
+@ @<Determine the desired |max_pages|@>=
+write(term_out,'Maximum number of pages (default=1000000): ');
+max_pages := 1000000; input_ln; buf_ptr:=0;
+@<Parse the desired |max_pages|@>
+
+@ @<Parse the desired |max_pages|@>=
+ begin max_pages:=get_integer;
+ if max_pages=0 then
+ max_pages := 1000000 ;
+ end
+
+@ @<Determine the number of printed rasters |dum_width|@>=
+write(term_out,'Width of output in characters',
+ ' (default=160/batch, 80/interactive): ');
+input_ln; buf_ptr:=0; dum_width:=get_integer
+
+@ @<Determine the number of printed rows |dum_height|@>=
+write(term_out,'Number of printed lines per screen',
+ ' (default=280/batch, 22/interactive): ');
+input_ln; buf_ptr:=0;
+dum_height:=get_integer
+
+@ @<Determine the compression mode@>=
+write(term_out,'Compress interword spaces, Y or N (default=N): ');
+input_ln; buf_ptr:=0; compress:=false ;
+if (buffer[buf_ptr] = "Y") or (buffer[buf_ptr] = "y") then compress:=true ;
+
+
+@ We may have to adjust the page dimensions to fit on the internal array. If
+the compression mode has been chosen, we must allow extra room for the
+uncompressed text.
+
+@ @<Adjust page dimensions@>=
+if dum_width=0 then
+ if batch_mode then print_width := page_width
+ else print_width := dfl_screen_width
+else print_width := dum_width;
+if compress and (print_width > (page_width * 3 div 4)) then
+ page_width := 4 * print_width div 3
+else if (not compress) and (print_width > page_width - hh_offset) then
+ page_width := print_width + hh_offset;
+if page_width > max_p_width then
+ begin
+ page_width := max_p_width;
+ print_width := page_width - hh_offset;
+ end;
+num_lines := total_rast div page_width;
+if dum_height=0 then
+ if batch_mode then print_height := num_lines
+ else print_height := dfl_screen_height
+else print_height := dum_height;
+if print_height > num_lines then print_height := num_lines
+
+
+@ After the dialog is over, we print the options so that the user
+can see what \vutex\ thought was specified. This is only done if the
+|out_mode| is greater than 0, so we can run quietly usually.
+
+@<Print all the selected options@>=
+if out_mode > 0 then begin
+print_ln('Options selected:');
+@.Options selected@>
+print(' Starting page = ');
+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;
+print_ln(' Maximum number of pages = ',max_pages:1);
+print_ln(' Number of printed characters = ',print_width:1);
+print_ln(' Number of printed lines = ',print_height:1);
+end
+
+@ If the user has selected interactive mode, then a summary of the the page
+action options is printed for the user's information.
+
+@<Print the page action options@>=
+if not batch_mode then
+ begin print_ln(' ');
+ print_ln(' At the end of each printed screen, enter one of:');
+ print_ln(' L - left, R - right, U - up, P - new page, <cr> - down');
+ print_ln(' ');
+ end;
+
+@ Another procedure that is included for debugging and monitoring purposes
+is the |diagnostics| procedure. This procedure prints out the memory usage
+of \vutex\. Again, this is only done if the |out_mode| is greater than
+zero.
+
+@p procedure diagnostics ;
+begin
+if out_mode > 0 then begin
+print_ln('Number of pages processed: ', actual_page_count:1) ;
+print_ln('String pool usage: ',next_names_free:1, ' out of ', name_size:1,
+ ' bytes') ;end ;
+end ;
+
+@* Defining fonts.
+In \vutex, a font definition should only be recognized while the scanning
+is not in the postamble, and when the pass is one.
+
+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 following subroutine does the necessary things when a \\{fnt\_def}
+command is being processed.
+
+@p procedure define_font(@!f:integer); {|f| is an external font number}
+var
+@!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}
+@!k:0..name_size; {indices into |names|}
+begin if not in_postamble then begin
+ if (f>255) then abort('Only font definitions 0..255 are valid in ',
+ clone) ;
+@.vutex capacity exceeded...@>
+ if (mem[f]<>0) then abort('Font number ',f:1,' already defined!') ;
+@.Font already defined@>
+ cur_fptr := next_mem_free+6 ;
+ mem[f]:=cur_fptr ;
+ next_mem_free := next_mem_free + font_desc_size ;
+@<Read the font parameters into position for font |nf|, and
+ print the font name@>;
+end ;
+end ;
+
+@ This routine takes the data from the \.{DVI} file about the current font,
+and loads it into memory. It also initializes the |use_count| for each
+character.
+
+@<Read the font parameters into position for font |nf|...@>=
+c:=signed_quad; font_check_sum:=c;@/
+q:=signed_quad; font_scaled_size := q ;
+d:=signed_quad; font_design_size := d ;
+font_space:=d div 6;@/
+p:=get_byte; n:=get_byte;
+if next_names_free + p + n + 2 > name_size then
+ abort(clone,' capacity exceeded (name size=',name_size:1,')!');
+@.vutex capacity exceeded...@>
+font_nsave := next_names_free * 65536 ;
+if out_mode > 0 then print('Font ',f:1,': ');
+for k := 1 to p do begin
+ names[next_names_free] := get_byte ;
+ next_names_free := next_names_free + 1 ;
+end ;
+names[next_names_free] := 0 ;
+next_names_free := next_names_free + 1 ;
+for k := 1 to n do begin
+ names[next_names_free] := get_byte ;
+ next_names_free := next_names_free + 1 ;
+end ;
+names[next_names_free] := 0 ;
+next_names_free := next_names_free + 1 ;
+cur_char_ptr := cur_fptr ;
+for k := 0 to 127 do begin {initialize use counts}
+ use_count := 0 ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+end ;
+font_type := declared ;
+if out_mode > 0 then print_font(f) ;
+
+@ If |p=0|, i.e., if no font directory has been specified, \vutex\
+is supposed to use the default font directory, which is a
+system-dependent place where the standard font \.{TFM} files are kept.
+@^system dependencies@>
+
+@d tfm_directory_name=='TeXfonts:'
+@d tfm_directory_name_length=9
+
+@<Glob...@>=
+@!tfm_directory:packed array[1..tfm_directory_name_length] of char;
+
+@ We stick this initial value into the |names| array for later use.
+
+@<Set init...@>=
+tfm_directory:=tfm_directory_name;
+
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font.
+@^system dependencies@>
+
+@<Move tfm name into the |cur_name| string@>=
+begin
+for k:=1 to name_length do cur_name[k]:=' ';
+r := 1 ; k := font_name ;
+if names[k]=0 then
+ begin for i:=1 to tfm_directory_name_length do
+ cur_name[i]:=tfm_directory[i];
+ r:=tfm_directory_name_length+1;
+ end else begin
+ while names[k] > 0 do begin
+ cur_name[r] := xchr[names[k]] ;
+ r := r + 1 ; k := k + 1 ;
+ end ;
+ end ;
+k := k + 1 ;
+while names[k] > 0 do begin
+ cur_name[r] := xchr[names[k]] ;
+ r := r + 1 ; k := k + 1 ;
+end ;
+cur_name[r] := '.' ; r := r + 1 ;
+cur_name[r] := 'T' ; r := r + 1 ;
+cur_name[r] := 'F' ; r := r + 1 ;
+cur_name[r] := 'M' ; r := r + 1 ;
+for k := 1 to r do
+ if (cur_name[k]>='a')and(cur_name[k]<='z') then
+ cur_name[k]:=xchr[xord[cur_name[k]]-@'40] ;
+end ;
+
+
+@* Special execution.
+In this implementation, special commands are ignored. However, the structure to
+handle such remains as a skeleton if some special procedures are desired.
+
+We need a procedure that will handle any special commands that might appear.
+Commands must have the following format:
+
+\medskip
+\line{\hskip20pt command(parameters$\ldots$)\hfil}
+\medskip
+
+If there is more than one command in a special, they must be separated by
+spaces. The parameters are also separated by spaces. The string that was
+contained in the special command is in the names array starting at
+|next_names_free| and ending with a 0. Also, the |prescan| boolean should be
+checked to see if this is the first or second time that this string was
+encountered.
+
+@p procedure do_special ;
+begin
+end ;
+
+@ This is where the special commands are interpreted. We accumulate the
+string, and pass a pointer to it to a routine called |do_special|. Note that
+this routine is called twice, once for the prescan, and once for the actual
+processing. This way, special commands that set up the device for a particular
+mode can be included and processed during the second pass. During pass one,
+specials on pages before the first page to be processed are also passed, as
+they might contain configuration information.
+
+@<Translate an |xxx| command@>=
+begin
+bad_char:=false;
+if p+next_names_free > name_size then
+ abort('Out of string space during special!') ;
+@.Out of string space during special@>
+for k:=1 to p do
+ begin q:=get_byte;
+ if (q<" ")or(q>"~") then
+ bad_char:=true;
+ end;
+if bad_char then print_ln('non-ASCII character in xxx command!');
+@.non-ASCII character...@>
+do_special ;
+end ;
+
+@* Interpreting the page commands.
+
+The main work of \vutex\ 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. Of course, this is for the second pass. A |prescan| routine
+is still necessary to load the font information.
+
+@ 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 raster analogs of $h$ and $v$,
+since it is not always true that |hh=raster_round(h)| or
+|vv=line_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 rasters}
+
+@ 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; \vutex\
+checks to see if this total is accurate.
+
+@<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}
+@!page_count:integer; {the total number of pages seen so far}
+@!hpos_ed, @!vpos_ed : boolean ; {are we positioned in this respect?}
+
+@ @<Set init...@>=
+max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
+max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
+
+@ 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):
+ abort('Can''t call first_par like this.') ;
+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;
+
+@ We need a routine to skip over a font definition. This is very simple,
+just requires skipping over the parameters:
+
+@p procedure skip_font_def ;
+var @!dumq, @!dumi, i : integer ;
+begin
+ dumq := signed_quad ; dumq := signed_quad ; dumq := signed_quad ;
+ dumi := get_byte + get_byte ;
+ for i := 1 to dumi do dumi := get_byte ;
+end ;
+
+@ A useful constant:
+
+@<Constants...@>=
+one_fourth = 1073741824 ;
+
+@ Strictly speaking, the |do_page| procedure is really a function with
+side effects, not a `\&{procedure}'; it returns the value |false| if
+\vutex\ 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|}
+
+@ Now we have the actual routine that draws a character. It simply sets the
+character pointer and calls the routine to set the character in the page.
+
+@p procedure draw_char(p:integer) ;
+@<Character drawing procedures@>
+begin cur_char_ptr := cur_fptr + 2*p ;
+ dev_char_draw ;
+ hpos_ed := false ;
+end ;
+
+@ Simple horizontal or vertical rules can be created from underscores and
+vertical bars. They are given low priority and will be replaced by all other
+characters. If a vertical line segment is the only character to appear on a line
+in the page, then that line will not be printed.
+
+@p procedure dev_rule_draw(rh, rw: integer) ;
+label done ;
+var position : integer ;
+i,j : integer ;
+begin if line_for[vv] < 0 then begin
+ line_for[vv] := next_line_free ;
+ next_line_free := next_line_free + page_width ;
+ end;
+ position := line_for[vv] ; j := position + hh + 1 ;
+ if j < position + page_width then
+ if rw >= rh then
+ begin for i := 1 to rw do
+ begin if (page[j] = xchr[32]) and (j > position) then
+ page[j] := xchr[95]
+ else if page[position] = xchr[32] then page[position] := xchr[62] ;
+ incr(j) ;
+ if (j >= position + page_width) then
+ begin if page[position] = xchr[32] then
+ page[position] := xchr[62] ;
+ goto done ;
+ end
+ end ;
+ end else if j > position then
+ begin for i := 1 to rh do
+ begin if page[j] = xchr[32] then
+ page[j] := xchr[124]
+ else page[j-hh-1] := xchr[62] ;
+ if line_for[vv-i] < 0 then
+ begin line_for[vv-i] := next_line_free ;
+ next_line_free := next_line_free + page_width ;
+ end ;
+ position := line_for[vv-i] ;
+ j := position + hh + 1 ;
+ if j < 0 then goto done ;
+ end ;
+ end ;
+done: end ;
+
+@ On entrance to this routine, the character to be drawn will be in |p|, the
+font in |cur_fptr|. The routine should be responsible for insuring that the
+device is set to the correct location on the page (|hh| and |vv|) and that the
+current font is selected. The characters are drawn sequentially into a word
+array.
+
+@<Character drawing procedures@>=
+procedure dev_char_draw ;
+var position : integer ; {points to beginning of raster row}
+i : integer ;
+c : char ; {the ASCII character to be set}
+begin if not set_word then @<Initialize word@> ;
+incr(in_word) ; position := line_for[word_vv] ;
+hpos_ed := true ; vpos_ed := true ;
+if (hh >= 0 ) then
+ if (word_hh+in_word < page_width) then
+ begin i := position+ word_hh + in_word ;
+ if (i < total_rast) then
+ if (ord(priority[i]) > font_status) and
+ (ord(word_priority[in_word]) > font_status) then
+ begin case font_type of
+ roman: @<Cases for the roman fonts@> ;
+ tty: c := xchr[p];
+ mitalic: c := mchr[p];
+ msymbol: c := schr[p];
+ mexten: c := echr[p];
+ othercases c := dfl_chr
+ endcases ;
+ word[in_word] := c ;
+ word_priority[in_word] := xchr[font_status] ;
+ end
+ else if page[position] = xchr[32] then
+ page[position] := xchr[62]
+ else if page[position] = xchr[32] then
+ page[position] := xchr[62] ;
+ end else if page[position] = xchr[32] then
+ page[position] := xchr[62]
+else if page[position] = xchr[32] then
+ page[position] := xchr[62] ;
+if font_status > 0 then
+ if (page[position] = xchr[32]) or (page[position]=xchr[62]) then
+ page[position] := xchr[42];
+end ;
+
+@ Variables must be initialized at the beginning of a new word. If the word is
+appearing on a new line, the first space in the priority array is used to flag
+the line as a baseline or an associated sub/super script line.
+
+@<Initialize word@>=
+begin set_word := true ;
+word_hh := hh; word_vv := vv ;
+in_word := 0;
+if line_for[vv] < 0 then
+ begin line_for[vv] := next_line_free ;
+ next_line_free := next_line_free + page_width ;
+ end ;
+if prev_vv >= 0 then
+ begin if (priority[line_for[vv]]=xchr[126]) then
+ if prev_vv=vv-1 then
+ begin page[line_for[vv]] := '-' ;
+ priority[line_for[vv]] := '-' ;
+ end
+ else if prev_vv=vv+1 then
+ begin page[line_for[vv]] := '+' ;
+ priority[line_for[vv]] := '+' ;
+ end
+ else priority[line_for[vv]] := 'b'
+ end
+else priority[line_for[vv]] := 'b' ;
+prev_vv := vv ;
+for i := 1 to page_width do
+ begin word[i] := ' ' ;
+ word_priority[i] := xchr[126] ;
+ end ;
+end
+
+@ The layout for the standard roman fonts is slightly different from the ASCII
+table.
+
+@d ff == @'13 {these define the ligatures in roman fonts}
+@d fi == @'14
+@d fl == @'15
+@d ffi == @'16
+@d ffl == @'17
+@d ss == @'31
+@d ae == @'32
+@d oe == @'33
+@d AE == @'35
+@d OE == @'36
+
+@<Cases for the roman fonts@>=
+case p of
+ ff: begin word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[102] ; end ;
+ fi: begin word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[105] ; end ;
+ fl: begin word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[108] ; end ;
+ ffi: begin word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[105] ; end ;
+ ffl: begin word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; word[in_word] := rchr[102] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[108] ; end ;
+ ss: begin word[in_word] := rchr[115] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[115] ; end ;
+ ae: begin word[in_word] := rchr[97] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[101] ; end ;
+ oe: begin word[in_word] := rchr[111] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[101] ; end ;
+ AE: begin word[in_word] := rchr[65] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[69] ; end ;
+ OE: begin word[in_word] := rchr[79] ;
+ word_priority[in_word] := xchr[font_status] ;
+ incr(in_word) ; c := rchr[69] ; end ;
+ othercases c:= rchr[p]
+ endcases
+
+@ We need the global page arrays
+
+@<Glob...@>=
+@!page : array [0..total_rast] of text_char ; {the page of data}
+@!priority : array [0..total_rast] of text_char ; {priority array}
+@!line_for : array [1..dfl_n_lines] of integer ; {points to line in array}
+@!next_line_free : integer ; {points to the next free position in array}
+@!word : array [0..max_p_width] of text_char ; {string to hold complete word}
+@!word_priority : array [0..max_p_width] of text_char ; {priority of each char}
+@!in_word : integer ; {points to location in word}
+@!set_word : boolean ; {set to true if a word has begun}
+@!word_hh : integer ; {hh value where word began}
+@!word_vv : integer ; {vv value of first character in word}
+@!min_hh,@!min_vv : integer ; {minimum hh, vv values on the page}
+@!prev_vv: integer ;
+
+@ This routine will reset the characters in a word if the positioning has
+left holes or missing characters.
+
+@p procedure reset_word ;
+var last:integer; {position of last character in word to be reset}
+i,j,position : integer ;
+begin set_line(vv) ;
+if word_hh < min_hh then min_hh := max(0,word_hh) ;
+if word_vv < min_vv then min_vv := max(0,word_vv) ;
+if hh > word_hh then
+ begin position := line_for[word_vv] ;
+ j := position + word_hh + 1 ;
+ last := hh - word_hh + 1 ; if last > in_word then last := in_word ;
+ for i := 1 to last do
+ begin if j > position then
+ if priority[j] > word_priority[i] then
+ begin page[j] := word[i] ;
+ priority[j] := word_priority[i] ;
+ end
+ else if page[position] = xchr[32] then
+ page[position] := xchr[62]
+ else if page[position] = xchr[32] then
+ page[position] := xchr[62] ;
+ incr(j) ;
+ end ;
+ end ;
+ set_word := false ;
+end ;
+
+@ This routine is called at the beginning of each page, before anything
+is processed.
+
+@p procedure start_page ;
+var i : integer ;
+begin
+ hpos_ed := false ; vpos_ed := false ;
+ for i := 0 to total_rast do
+ begin page[i] := xchr[32];
+ priority[i] := xchr[126] ;
+ end;
+ for i := 1 to num_lines do line_for[i] := -1;
+ next_line_free := 0;
+ min_hh :=hh_offset ; min_vv := vv_offset ;
+ prev_vv := -1 ;
+end ;
+
+@ These constants define the key strokes for interactive paging action.
+
+@<Constants...@>=
+@!u_left = "L"; l_left="l"; {key for move left on page}
+@!u_right= "R"; l_right="r"; {key for move right on page}
+@!u_up= "U"; l_up="u"; {key for move up on page}
+@!u_page= "P"; l_page="p"; {key for move to next page}
+@!u_comp= "C"; l_comp="c"; {key for compress text on page}
+
+@ When this routine is called, a full page has been processed, and the
+page array is sent to the output device.
+
+@p procedure finish_page ;
+label done;
+var i,j,k: integer ; {index counters}
+ last_hh: integer ; {raster column of furthest right character}
+ last_vv: integer ; {raster row of furthest down character}
+ first_p_hh: integer ; {raster column of first printed character}
+ last_p_hh: integer ; {raster column of last printed character}
+ first_p_row: integer ; {row number of first printed row}
+ last_p_row: integer ; {row number of last printed row}
+ rows_printed: integer ;
+ raster: integer ;
+ jspace: integer ;
+ shift: integer ;
+ next_row: integer ;
+ position: integer ;
+ average: integer; {average spacing between baselines}
+ menu : integer ; {used to interactively select page-action}
+ printing_page : boolean ;
+begin first_p_hh := min_hh ; first_p_row := min_vv ;
+last_hh := raster_round(max_h_so_far) + 1 ;
+last_vv := line_round(max_v_so_far) ;
+if last_hh >= page_width then
+ last_hh := page_width - 1 ;
+@<Determine average spacing between nonempty lines@> ;
+if compress then @<Compress spaces between words@> ;
+printing_page := true;
+position := first_p_row;
+while (printing_page) do
+ begin last_p_hh := first_p_hh + print_width-2;
+ if last_p_hh >= page_width then last_p_hh := page_width;
+ write_ln(bit_file);
+ i := first_p_row;
+ rows_printed := 0;
+ while (i <= last_vv) and (rows_printed < print_height) do
+ begin if line_for[i] >= 0 then
+ begin position := i ;
+ raster := line_for[i] ;
+ if raster > total_rast-last_hh-1 then goto done ;
+ write(bit_file,page[raster]) ;
+ k := raster + last_p_hh ;
+ while page[k] = xchr[32] do decr(k) ;
+ k := k - raster - 1 ;
+ raster := raster + first_p_hh ;
+ for j := first_p_hh to k do
+ begin write(bit_file,page[raster]) ;
+ incr(raster) ;
+ end ;
+ write_ln(bit_file,page[raster]) ;
+ incr(rows_printed) ;
+ end
+ else if (average >0) and (i-position > average) then
+ begin write_ln(bit_file) ;
+ position := i ; incr(rows_printed) ;
+ end ;
+ incr(i);
+ end;
+ done: @<Determine page action@> ;
+ end;
+end ;
+
+@ Since each physical line is represented by perhaps several lines in the page
+arrays, we try to determine the spacing between physical lines by taking the
+average number of blank lines in the array between nonempty lines. This is used
+to approximate the number of blank lines in a vertical shift in \TeX.
+Note that a page number at the bottom of a sparse page may distort the average.
+Hence if the last line is set apart substantially, we will ignore it.
+
+@<Determine average spacing between nonempty lines@>=
+begin
+ average := 0 ; k:= 0 ;
+ i := 1 ; j := line_for[i] ;
+ while (i <= last_vv) and ((j < 0) or (priority[j] <>'b')) do
+ begin incr(i); j:= line_for[i];
+ end;
+ position := i ;
+ incr(i) ;
+ while i <= last_vv do
+ begin j := line_for[i] ;
+ if (j>=0) then
+ if (priority[j]='b') then
+ begin shift := i - position ;
+ average := average + shift ;
+ incr(k) ; position := i ;
+ end;
+ incr(i);
+ end ;
+ if k > 1 then
+ begin if shift > 1.1*round(average*1.0/k) then
+ begin k := k - 1 ;
+ average := average - shift ;
+ end;
+ if k > 1 then
+ average := round(average*1.0 / k )
+ end;
+end
+
+@ If the compress option is chosen, the text is compressed vertically so that
+associated sub/super script lines are pushed into the baseline. Then the
+horizontal spacing is abandonned and interword spacing is compressed to one
+blank space.
+
+@<Compress spaces between words@>=
+begin
+for i := min_vv to last_vv do
+ if line_for[i]>=0 then
+ if priority[line_for[i]]='+' then begin
+ raster := line_for[i] ;
+ position := line_for[i+1] ;
+ for j := min_hh to last_hh do
+ if (page[raster+j]<>xchr[32]) then
+ if (page[position+j]=xchr[32]) or
+ (priority[raster+j]<priority[position+j]) then
+ begin
+ page[position+j] := page[raster+j] ;
+ priority[position+j] := priority[raster+j];
+ end;
+
+ line_for[i] := -2
+ end ;
+for i := last_vv downto min_vv do
+ if line_for[i]>=0 then
+ if priority[line_for[i]]='-' then begin
+ raster := line_for[i] ;
+ position := line_for[i-1] ;
+ for j := min_hh to last_hh do
+ if (page[raster+j]<>xchr[32]) then
+ if (page[position+j]=xchr[32]) or
+ (priority[raster+j]<priority[position+j]) then
+ begin
+ page[position+j] := page[raster+j] ;
+ priority[position+j] := priority[raster+j];
+ end;
+ line_for[i] := -2
+ end ;
+for i := min_vv to last_vv do
+ if line_for[i]>=0 then
+ begin raster := line_for[i] + 1 ;
+ next_row := raster + page_width - 1;
+ while (page[raster] = ' ') and (raster < next_row) do
+ incr(raster) ;
+ while (page[raster] <> ' ') and (raster < next_row) do
+ incr(raster) ;
+ jspace := raster ;
+ while raster < next_row do
+ begin
+ while (page[raster] = ' ') and (raster < next_row) do
+ incr(raster) ;
+ shift := raster - jspace - 1 ;
+ while (page[raster] <> ' ') and (raster < next_row) do
+ begin page[raster-shift] := page[raster] ;
+ incr(raster) ;
+ end ;
+ for k := raster - shift to raster - 1 do page[k] := ' ' ;
+ jspace := raster - shift ;
+ end ;
+ end ;
+last_hh := min(last_hh,jspace);
+end
+
+@ When the portion of the page is printed, either the interactive user will be
+given a menu of options to chose from or the batch job will continue.
+
+@d null = 0
+@d left_arrow = 1
+@d right_arrow = 2
+@d up_arrow = 3
+@d down_arrow = 4
+@d comp = 5
+@d next_page = 6
+
+@<Determine page action@> =
+begin if batch_mode then
+ @<Draw graded horizontal rule@>
+else begin last_p_row := i-1 ;
+ @<Select from menu@>;
+ case menu of
+ left_arrow: begin first_p_hh := first_p_hh - print_width + hor_overlap;
+ if first_p_hh < min_hh then first_p_hh := min_hh;
+ end;
+ right_arrow: begin first_p_hh := last_p_hh - hor_overlap;
+ if first_p_hh > last_hh - print_width then
+ first_p_hh := last_hh - print_width ;
+ end;
+ up_arrow: begin k := 0;
+ while (k < print_height-vert_overlap) and
+ (first_p_row > min_vv) do
+ begin first_p_row := first_p_row - 1 ;
+ j := line_for[first_p_row] ;
+ if (j >= 0) and (priority[j] ='b') then incr(k) ;
+ end;
+ end;
+ down_arrow: if last_p_row = last_vv then
+ printing_page := false
+ else
+ begin first_p_row := last_p_row - vert_overlap;
+ if first_p_row > last_vv - print_height then
+ first_p_row := last_vv - print_height + 1;
+ end;
+ comp: if (not compress) then @<Compress spaces between words@>;
+ next_page: @<Draw graded horizontal rule@>;
+ othercases @<Draw graded horizontal rule@>
+ endcases
+ end;
+end
+
+@ The interactive user will be given a choice of re-drawing the page, shifted
+right, left, up, or down, or in the compressed mode. Any other selection will
+cause the process to go to the next page.
+
+@ @<Select from menu@> =
+begin rewrite(term_out); write(term_out,'Hit key for page action.. ');
+input_ln;
+buf_ptr :=0;
+menu := null;
+if (buffer[buf_ptr] = u_left) or (buffer[buf_ptr] = l_left) then
+ menu := left_arrow
+else if (buffer[buf_ptr] = u_right) or (buffer[buf_ptr] = l_right) then
+ menu := right_arrow
+else if (buffer[buf_ptr] = u_up) or (buffer[buf_ptr] = l_up) then
+ menu := up_arrow
+else if (buffer[buf_ptr] = u_page) or (buffer[buf_ptr] = l_page) then
+ menu := next_page
+else if (buffer[buf_ptr] = u_comp) or (buffer[buf_ptr] = l_comp) then
+ menu := comp
+else menu := down_arrow;
+incr(buf_ptr);
+end
+
+@ At the end of each page, a graded horizontal rule will be drawn. The
+gradations will be every 2 cm. measured from point with dvi value 0.
+
+@<Draw graded horizontal rule@> =
+begin write_ln(bit_file);
+position := trunc(200000.0/num*den/resol) ;
+write(bit_file,' _ cm --> ');
+i := 0;
+while i <= last_p_hh do
+ begin
+ if i > first_p_hh + 9 then
+ if i mod position = 0 then
+ begin
+ if 2*(i div position) < 10 then
+ write(bit_file,(2*(i div position)):1,'_')
+ else
+ write(bit_file,(2*(i div position)):2);
+ incr (i);
+ end
+ else write(bit_file,'_') ;
+ incr (i);
+ end;
+write_ln(bit_file);
+printing_page := false;
+end
+
+
+@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
+so we shall split |do_page| into two parts, one of which is
+called |spcl_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}
+@!cur_font:integer; {current internal font number}
+@!a:integer; {byte number of the current command}
+
+@ Here is the overall setup.
+
+@p @t\4@>@<Declare the function called |spcl_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}
+@!hhh:integer; {|h|, rounded to the nearest pixel}
+begin start_page; cur_font:=0; {set current font undefined}
+while mem[cur_font]>0 do cur_font := cur_font + 1 ;
+s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0;
+hh:=raster_round(0); vv:=line_round(0);
+ {initialize the state variables}
+cur_fptr := 0 ; set_word := false ;
+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;
+
+@ @<Translate the next command...@>=
+begin a:=cur_loc ;
+o:=get_byte; if o > 127 then 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 |h|, |v|, |w|, |x|, |y|, |z|,
+ |hh|, and |vv|; then |goto done|@>;
+done: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 draw_char(o)
+else case o of
+ four_cases(set1), four_cases(put1): begin draw_char(o); goto fin_set;
+ end;
+ set_rule, put_rule: begin q:=signed_quad ; dev_rule_draw(round(p/vresol),
+ round(q/resol)); goto fin_rule;
+ end;
+ @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
+ @t\4@>@<Cases for horizontal motion@>@;
+ othercases if spcl_cases(o,p) then goto done@+else goto 9998
+endcases ;
+
+@ @<Declare the function called |spcl_cases|@>=
+function spcl_cases(@!o:eight_bits;@!p:integer):boolean;
+label change_font,move_down,done,9998;
+var q:integer; {parameter of the current command}
+@!k:integer; {loop index}
+@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
+@!pure:boolean; {is the command error-free?}
+@!vvv:integer; {|v|, rounded to the nearest raster}
+begin pure:=true;
+case o of
+@t\4@>@<Cases for vertical motion@>@;
+@t\4@>@<Cases for fonts@>@;
+four_cases(xxx1): begin @<Translate an |xxx| command@>; goto done; end;
+pre: begin print_ln('preamble command within a page!'); goto 9998;
+ end;
+@.preamble command within a page@>
+post,post_post: begin print_ln('postamble command within a page!'); goto 9998;
+@.postamble command within a page@>
+ end;
+othercases begin print_ln('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: spcl_cases:=pure;
+end;
+
+@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
+nop: goto done;
+bop: begin print_ln('bop occurred before eop'); goto 9998;
+@.bop occurred before eop@>
+ end;
+eop: begin finish_page ;
+ if s<>0 then print_ln('stack not empty at end of page (level ',
+ s:1,')!');
+@.stack not empty...@>
+ do_page:=true; goto 9999;
+ end;
+push: begin
+ if s=max_s_so_far then
+ begin max_s_so_far:=s+1;
+ if s=max_s then print_ln('deeper than claimed in postamble!');
+@.deeper than claimed...@>
+@.push deeper than claimed...@>
+ if s=stack_size then
+ begin print_ln(clone, ' 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); goto show_state;
+ end;
+pop: begin
+ if s=0 then print_ln('(illegal at level zero)!')
+ else begin if set_word then reset_word ;
+ 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;
+ goto show_state;
+ end;
+
+@ Our screen resolution is coarse, and we shall reset characters in words so
+that the characters are left justified in the space \TeX\ provides. We shall do
+our position calculations in the same manner that they would be done on the
+printer. Rounding to the nearest raster 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. We allow a larger space in the negative direction than in the
+positive one, because \TeX\ makes comparatively large backspaces when it
+positions accents.
+
+Note that, if the horizontal movement is large, the word is reset.
+
+@d out_space(#)==if cur_fptr=0 then hh := raster_round(h+p)
+ else begin
+ if (abs(p) >= round(vresol / 3)) and (set_word) then reset_word ;
+ if (p>=font_space)or(p<=-4*font_space) then
+ hh:=raster_round(h+p)
+ else hh:=hh+round(p/resol);
+ end;
+ q:=p; hpos_ed := false ; 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.
+
+Again, if the the vertical movement is large, the word is reset.
+
+@d out_vmove(#)==if cur_fptr=0 then vv := line_round(v+p)
+ else begin
+ if (p <> 0) and (set_word) then reset_word ;
+ if abs(p)>=5*font_space then
+ vv:=line_round(v+p)
+ else vv:=vv+round(p/vresol);
+ end;
+ vpos_ed := false ;
+ 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), four_cases(fnt1): goto change_font;
+four_cases(fnt_def1): begin
+ skip_font_def;
+ goto done;
+ end;
+
+@ @<Finish a command that either sets or puts a character...@>=
+q:=tfm_width ;
+if o>=put1 then goto done;
+hh:=hh+raster_width;
+goto move_right
+
+@ @<Finish a command that either sets or puts a rule...@>=
+if o=put_rule then goto done;
+hh:=hh+round(q/resol); goto move_right
+
+@ A sequence of consecutive rules, or consecutive characters in a fixed-width
+font whose width is not an integer number of rasters, can cause |hh| to drift
+far away from a correctly rounded value. \vutex\ ensures that the
+amount of drift will never exceed |max_drift| rasters.
+
+@d infinity==@'17777777777 {$\infty$ (approximately)}
+@d max_drift=1 {we insist that abs|(hh-raster_round(h))<=max_drift|}
+
+@<Finish a command that sets |h:=h+q|, then |goto done|@>=
+hhh:=raster_round(h+q);
+if abs(hhh-hh)>max_drift then begin hpos_ed := false ;
+ if hhh>hh then hh:=hhh-max_drift
+ else hh:=hhh+max_drift;
+end ;
+h:=h+q;
+if abs(h)>max_h_so_far then
+ begin if abs(h)>max_h+99 then
+ begin print_ln('warning: |h|>',max_h:1,'!');
+@.warning: |h|...@>
+ max_h:=abs(h);
+ end;
+ max_h_so_far:=abs(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 print_ln('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 print_ln('arithmetic overflow! parameter changed from ',
+ p:1, ' to ',(-v)-infinity:1);
+ p:=(-v)-infinity;
+ end;
+vvv:=line_round(v+p);
+if abs(vvv-vv)>max_drift then begin
+ if vvv>vv then vv:=vvv-max_drift
+ else vv:=vvv+max_drift;
+ end;
+v:=v+p;
+if abs(v)>max_v_so_far then
+ begin if abs(v)>max_v+99 then
+ begin print_ln('warning: |v|>',max_v:1,'!');
+@.warning: |v|...@>
+ max_v:=abs(v);
+ end;
+ max_v_so_far:=abs(v);
+ end;
+goto done
+
+@ @<Show the values of |h|, |v|, |w|, |x|, |y|, |z|...@>=
+goto done
+
+@ @<Finish a command that changes the current font...@>=
+cur_fptr := mem[p] ;
+goto done
+
+@* 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.
+
+@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}
+bad_char:boolean ;
+begin while true do
+ begin if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+ k:=get_byte;
+ if k > 127 then begin 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: if prescan then begin
+ define_font(p);
+ end else skip_font_def ;
+ xxx1,xxx1+1,xxx1+2,xxx1+3: if prescan then begin
+ @<Translate an |xxx| command@>
+ end else
+ 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 ;
+ end;
+9999:end;
+
+@ Now we need a routine that will scan a page from the |bop| to the |eop|,
+maintaining information about font usage. This routine also catches errors
+like illegal font numbers or characters. It also counts the number of pages
+actually processed.
+
+@p function scan_page : boolean ;
+label 9999; {end of this subroutine}
+var p:integer; {a parameter}
+@!k:0..255; {command code}
+ bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
+@!down_the_drain:integer; {garbage}
+begin actual_page_count := actual_page_count + 1 ;
+ while true do
+ begin a:=cur_loc ;
+ if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+ k:=get_byte;
+ if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+ if k<set_char_0+128 then begin
+ cur_char_ptr := cur_fptr + 2*k ;
+ incr(use_count);
+ end else begin
+ 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;
+ eop: goto 9999 ;
+ four_cases(set1),four_cases(put1): begin if p > 128 then
+ abort('character greater than 128 encountered!')
+@.character greater than...@>
+ else begin cur_char_ptr := cur_fptr + 2*p ; incr(use_count); end; end ;
+ set_rule,put_rule: down_the_drain:=signed_quad;
+ fnt_def1,fnt_def1+1,fnt_def1+2,fnt_def1+3: define_font(p);
+ sixty_four_cases(fnt_num_0),four_cases(fnt1): begin
+ if p>255 then abort(clone, ' cannot handle font numbers > 255!') ;
+@.vutex capacity exceeded...@>
+ cur_fptr := mem[p] ;
+ if cur_fptr=0 then abort('Undefined font!') ;
+@.Undefined font@>
+ end ;
+ four_cases(xxx1): @<Translate an |xxx| command@>
+ post: begin in_postamble:=true; goto 9999;
+ end;
+ othercases do_nothing
+ endcases;
+ end ;
+ end;
+9999:scan_page := true; end;
+
+@ The global variable |started| indicates that we have found the
+starting page. |last_page| points to the most recent page processed,
+and |prev_page| points to the page before it. These two globals can be
+used for processing a file in the reverse output, if the printer being
+driven stacks the pages backwards, for instance.
+
+@<Glob...@>=
+@!started:boolean; {has the starting page been found?}
+@!last_page: integer; {what is the most recent page processed?}
+@!prev_page: integer; {what is the page before it?}
+@!actual_page_count: integer; {how many pages to actually process}
+
+@ @<Set init...@>=
+started:=false;
+last_page:=-1;
+prev_page:=-1;
+actual_page_count:=0;
+
+@ @<Pass a |bop|...@>=
+last_page:=cur_loc;
+incr(page_count);
+for k:=0 to 9 do count[k]:=signed_quad;
+prev_page:=signed_quad ; {skip over the back pointers}
+
+@ We need a procedure to open the output file, and to send out any
+initialization that the printer might need.
+
+@<Open bit file and initialize device@>=
+open_bit_file ; init_device ;
+
+@* Page scanning routines.
+Here are a few miscellaneous routines which will scan the page, calling
+the |pre_scan| and |do_page| routines as necessary.
+
+@<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@>
+@<Read the conversion units@>;
+p:=get_byte; {fetch the length of the introductory comment}
+print('''');
+while p>0 do
+ begin decr(p); print(xchr[get_byte]);
+ end;
+print_ln('''')
+
+@ We also need a routine to skip over the preamble after the file has been
+reset again.
+
+@<Skip over the preamble@>=
+reopen_dvi_file;
+p:=get_byte; {fetch the first byte}
+p:=get_byte; {fetch the identification byte}
+p:=signed_quad; p:=signed_quad; p:=signed_quad; {skip over the mag stuff}
+p:=get_byte; {fetch the length of the introductory comment}
+while p>0 do begin
+ i:=get_byte; decr(p); end;
+
+@ Read in the conversion units and magnification for the font.
+
+@<Glob...@>=
+@!num,den : integer; {numerator and denominator for conversion of dvi units}
+
+@ @<Read the conversion units@>=
+num:=signed_quad; if num<=0 then bad_dvi('numerator is ',num:1);
+@.numerator is wrong@>
+den:=signed_quad; if den<=0 then bad_dvi('denominator is ',den:1);
+@.denominator is wrong@>
+p:=signed_quad;
+
+@ 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.
+
+@<Translate up to |max_pages| pages@>=
+begin print('- Page ');
+ while max_pages>0 do
+ begin decr(max_pages);
+ for k:=0 to start_vals do
+ begin print('[',count[k]:1);
+ if k<start_vals then print('.')
+ else print ('] ');
+ end ;
+ if not do_page then bad_dvi('page ended unexpectedly');
+@.page ended unexpectedly@>
+ begin
+ 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 ;
+ end;
+done: end;
+
+@ This routine will prescan the file, using the |scan_page| macro to get
+the information out of each page.
+
+@<Prescan up to |max_pages| pages@>=
+begin while max_pages>0 do
+ begin decr(max_pages);
+ if not scan_page then bad_dvi('page ended unexpectedly');
+@.page ended unexpectedly@>
+ if in_postamble then goto pdone ;
+ repeat k:=get_byte;
+ if (k>=fnt_def1)and(k<fnt_def1+4) then
+ begin p:=first_par(k); skip_font_def; k:=nop;
+ end;
+ until k<>nop;
+ if k=post then
+ begin in_postamble:=true; goto pdone;
+ end;
+ if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop',k);
+@.byte n is not bop@>
+ @<Pass a |bop|...@>;
+ end;
+pdone:end
+
+@* Font loading.
+Now we have the procedure which determines the vertical and
+horizontal raster resolution. On entrance to this procedure, all the font usage
+information will have been loaded into the |use_count| entries for each font
+descriptor. Based on this, |base_font| will determine the resolution to use
+for assigning screen raster positions.
+
+First, this routine reads the file \.{nonASCII.tex.fnt} to determine which fonts
+are non ASCII, i.e. not printable on the screen. This list is shorter than the
+list of ASCII fonts.
+
+@p procedure base_font ;
+var i, j, k : integer ;
+ f_type : integer ; {type for listed fonts}
+ lf,lh,bc,ec,nw,np : integer ; {help in decoding the \.{TFM} file}
+ value, x : integer ; {a \.{TFM} |fix_word|}
+@<|base_font| variables@>
+begin
+ @<Load non ASCII font information@> ;
+ @<Determine base font@> ;
+end ;
+
+@ The non ASCII font information is assumed to be in the following format:
+\medskip
+{\def\ff#1\cr{\line{\hskip\parindent\tt #1\hfil}}
+\ff amsy 2\cr
+\ff ambsy 2\cr
+\ff amcsc 2\cr
+\ff amit 4\cr
+\ff amtt 5\cr
+\medskip
+
+The only item on a line is the font name and the type code.
+
+@d font_list_file=='nonASCII.tex.fnt' {change this to correct name}
+@d list_len==16
+
+@<Glob...@>=
+@!resol, vresol : real; {number of dvi units per raster or line}
+@!gen_input : text_file ; {input path for text}
+@!list_fonts : packed array [1..list_len] of char ; {non ASCII fonts name}
+@!num_asc_fonts : integer ; {number of ASCII fonts declared}
+
+@ @<Set init...@>=
+list_fonts := font_list_file ;
+
+@ @<Load non ASCII font information@>=
+for k := 1 to name_length do cur_name[k] := ' ' ;
+for k := 1 to list_len do
+ if (list_fonts[k] <= 'z') and (list_fonts[k] >= 'a') then
+ cur_name[k] := xchr[xord[list_fonts[k]]-@'40]
+ else cur_name[k] := list_fonts[k] ;
+open_input_text ;
+num_asc_fonts := 0 ;
+while not eof(gen_input) do
+ @<Process line@> ;
+
+@ At this point, we are at the beginning of a line in the non ASCII font
+information file. We read in the name of a non printable font, and then scan
+through our fonts looking for a match. By default all fonts are assumed to be
+roman unless named in this file.
+
+@<Process line@>=
+begin
+ buf_ptr := 1 ;
+ while not eoln(gen_input) do begin
+ buffer[buf_ptr] := xord[gen_input^] ;
+ get(gen_input) ;
+ buf_ptr := buf_ptr + 1 ;
+ end ;
+ read_ln (gen_input) ;
+ buffer[buf_ptr] := " " ;
+ buf_ptr := 1 ;
+ while buffer[buf_ptr] <> " " do buf_ptr := buf_ptr + 1 ;
+ while buffer[buf_ptr] = " " do buf_ptr := buf_ptr + 1 ;
+ f_type := get_integer ;
+ for i := 0 to 255 do if mem[i] > 0 then begin
+ cur_fptr := mem[i] ;
+ j := font_name ; k := 1 ;
+ if names[j] = 0 then begin
+ if (font_type)=declared then
+ begin font_type := roman ;
+ incr(num_asc_fonts) ;
+ end ;
+ j := j + 1 ;
+ while (names[j] = buffer[k]) do begin
+ j := j + 1 ; k := k + 1 ; end ;
+ if (buffer[k] = " ") then begin
+ font_type := f_type ;
+ if f_type = other then decr(num_asc_fonts) ;
+ end ;
+ end ;
+ end ;
+end
+
+@ This section of code figures out which font is to determine the resolution.
+It goes through the list of fonts, and, for each font that is ASCII, it
+calculates the |use_count| and the font number into a list built at the top of
+memory. This list is then sorted, and the fonts are given priorities.
+Horizontal resolution normally is slightly less than the width of normal
+interword space, except for fonts where this parameter is 0 in which case one
+third of a quad is used.
+
+The vertical resolution is determined by placing |num_lines| on the physical
+page.
+
+@d dd_s_k(#)==mem[temp_ar(#)-1] {sort key for advantages}
+
+@<Determine base font@>=
+@<Make list of declared fonts and calculate advantages@> ;
+@<Sort by advantages and build linked list@> ;
+@<Load the font data@> ;
+
+@ Making a list of the declared fonts is relatively easy, as is calculating
+the advantages. The advantage is the use count for all fonts.
+
+@<Make list of declared fonts...@>=
+asc_num := 0 ;
+nasc_num := num_asc_fonts ;
+for i := 0 to 255 do if mem[i] > 0 then begin
+ cur_fptr := mem[i] ;
+ cur_char_ptr := cur_fptr ;
+ dd_total := 0 ;
+ for j := 0 to 127 do begin
+ if use_count > 0 then
+ dd_total := dd_total + use_count ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+ end ;
+ if (dd_total >0) then
+ begin if (font_type > other) then
+ begin temp_ar(asc_num) := cur_fptr ;
+ incr(asc_num) ;
+ end else if (font_type = other) then
+ begin temp_ar(nasc_num) := cur_fptr ;
+ incr(nasc_num) ;
+ end ;
+ end ;
+ mem[cur_fptr + 1] := dd_total ;
+end
+
+@ The sort to determine the priority of the fonts is a simple insertion sort.
+Since this sort is only performed once, and it usually has less than a dozen
+elements to sort, simplicity is called for. Note that the ASCII fonts are
+sorted at the top, and the non-ASCII fonts are sorted last.
+
+@<Sort by advantages and build linked list@>=
+for i := asc_num-1 downto 1 do
+ for j := 0 to i-1 do
+ if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin
+ t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ;
+ end ;
+for i := nasc_num-1 downto num_asc_fonts+1 do
+ for j := num_asc_fonts to i-1 do
+ if mem[temp_ar(j)+1] < mem[temp_ar(i)+1] then begin
+ t := temp_ar(i) ; temp_ar(i) := temp_ar(j) ; temp_ar(j) := t ;
+ end ;
+for j := 0 to asc_num-1 do dd_s_k(j) := j ;
+for j := num_asc_fonts to nasc_num-1 do dd_s_k(j) := j ;
+
+@ Some of |base_font|' locals:
+
+@<|base_font| variables@>=
+@!asc_num : integer ; {positions the ASCII fonts used}
+@!nasc_num : integer ; {positions the non-ASCII fonts used}
+@!t : integer ; {temporary holding place for exchange swaps}
+@!dd_total : integer ; {contains total number of different characters}
+
+@ We now will load the \.{TFM} file for each font. We also will determine the
+resolution from the |base_font| and will scale all \.{TFM} widths accordingly.
+
+@<Load the font data@>=
+cur_fptr := mem[next_mem_free] ;
+load_tfm_file ; @<Determine vertical and horizontal resolution@> ;
+for i := 0 to 255 do
+ begin if (mem[i] > 0) then
+ begin cur_fptr := mem[i] ;
+ if font_status <> 0 then load_tfm_file ;
+ cur_char_ptr := cur_fptr ;
+ for j := 0 to 127 do begin
+ if (use_count > 0) then begin
+ raster_width := round(tfm_width/resol) ;
+ if raster_width < 1 then raster_width := 1;
+ end ;
+ cur_char_ptr := cur_char_ptr + 2 ;
+ end ;
+ end ;
+end
+
+@ The vertical resolution is determined to place |num_lines| on a page of
+dimension |page_length|. The horizontal resolution is given by the inter-word
+space for the |base_font|. Note that this section assumes that the \.{TFM} data
+for the |base_font| is exactly as it was initally read into |mem|.
+
+@<Determine vertical and horizontal resolution@>=
+begin lf := hi(mem[next_mem_free]) ; lh := lo(mem[next_mem_free]) ;
+ bc := hi(mem[next_mem_free+1]) ; ec := lo(mem[next_mem_free+1]) ;
+ nw := hi(mem[next_mem_free+2]) ; np := lo(mem[next_mem_free+5]) ;
+ k := next_mem_free + 6 + lh - bc ;
+ vresol := den / (num / 100000.0) * page_length / num_lines ;
+ value := mem[next_mem_free + lf - np + 1] ;
+ x := tfm_to_int(value) ;
+ resol := x * 1.0 ;
+ if resol <= 0 then
+ begin value := mem[next_mem_free + lf - np + 5] ;
+ x := tfm_to_int(value) ;
+ resol := x / 3.0 ;
+ end;
+end ;
+
+@ This routine may be used to initialize the output device to begin receiving
+information. This section could be personalized to a particular system.
+
+@p procedure init_device ;
+begin
+end ;
+
+@ Now for the antithesis of the above. This routine cleans up the \TeX\
+job, and prepares for the next.
+
+@p procedure clean_printer ;
+begin
+ write_ln(bit_file);
+ write_ln(bit_file,'The end...');
+ write_ln;
+end ;
+
+@* The main program.
+Now we are ready to put it all together. This is where \vutex\ starts,
+and where it ends.
+
+@p begin initialize; {get all variables initialized}
+dialog; {set up all the options}
+prescan := true ;
+@<Process the preamble@>;
+skip_pages;
+if not in_postamble then
+ @<Prescan up to |max_pages| pages@>;
+@<Open bit file and initialize device@>;
+base_font ;
+prescan:=false ;
+in_postamble := false ;
+started:=false ;
+max_pages:=actual_page_count ;
+@<Skip over the preamble@>;
+skip_pages;
+@<Translate up to |max_pages| pages@>;
+final_end:
+if bit_is_open then clean_printer;
+diagnostics;
+end.
+
+@ The main program needs a few global variables in order to do its work.
+
+@<Glob...@>=
+@!k,@!p,@!q:integer; {general purpose registers}
+@!prescan:boolean; {indicates that we are in prescan phase}
+@!bit_is_open : boolean ; {indicates that output file was opened.}
+
+@ @<Set init...@>=
+bit_is_open := false ;
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \vutex\ work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
+