(c) Copyright 1983, 1984  by Charles Karney and Phil Andrews
This is the changes file produced by Phil Andrews and Charles Karney
for TeX, based on the one for Tops-20 by DRF

When compiling for INITEX, it is necessary to reduce the following constants:
	mem_max=58000		to	mem_max=45000
	string_vacancies=15000	to	string_vacancies=13000
	pool_size=40000		to	pool_size=37000

Here is a description of the changes
	Print banner line
	Initialization procedure
	Compile time constants
	Make all characters readable
	File opening and closing
	File input
	Open TTY
	Implement wake_up_terminal
	Initialize terminal
	Enter editing hooks
	36-bit words; quarter and half words start at zero
	Date and time
	Establish system areas and parse file names
	Type out CPU time & run editor

Besides implementing the various system dependent parts of TeX, this
implements the following features

(1) All control characters (except null) in files are read
(2) Command line is scanned and remembered
(3) CPU time is printed at the end of the run
(4) The E option for the editor
(5) Finds system PPNs from logical names

Record of changes (started a bit late)
83/02/19 Reads all control characters
	Scans command line
83/03/23 Remembers command line
83/03/26 Updated for 0.96 (hi, ho, pool_size)
	Print out CPU time
83/03/28 Removed sensitive debug, stats, compile time constants,
	bad_fonts_code; increased save_size (to match TEX.WEB)
83/03/29 Changed some directories to aid in porting versions
83/04/04 Changed PPPL to MFE in Banner
	Introduced first hooks for calling editor
	Changed input_ln so that form_feed acts as end-of-line
	(needed for consistency with FINE)
83/04/08 Cleaned up editing stuff
83/04/13 Moved fonts to [120,44,TFM]
83/04/14 Made INITEX set the system PPNs from logical names
	(still need to do it for formats)
83/04/19 Updated for TeX 0.97
	wake_up_terminal implemented
83/04/20 Use normal TTY for output (fixes 'Improper mode for file
	TTY:TTY' bug with detached jobs)
83/05/01 Use TMP:TEX instead of nnnTEX.TMP, since DELETE no longer
	works with the new COMPIL.
83/07/07 Updated for TeX 0.99 and TANGLE 1.7
83/07/29 Updated for TeX 0.999.
83/08/02 Updated for TeX 0.9999.
83/08/03 Allowed fonts names of greater than 6 chars by take first 3
	and last 3 characters.
83/08/18 Finish jump-to-editor.
83/08/22 Support devices in file names.  Changed TEX.POO and PLAIN.FMT
	to be on TEXFMT:.  (TEX_area and TEX_font_area can be switched
	to logical device names when they are eslablished at the system
	level).
83/09/09 Change all devices to TeX:
83/09/31 Remove DISPOSE---not worth the overhead.
83/11/29 Updated for TeX 1.0.  Remove change for line length (since
	FINE now handles 79 char lines on a 80 wide terminal).  Change
	MFE to TOPS-10 in banner.
84/01/05 Increased various limits for LaTeX.  Values taken from Tops-20
	change file.
84/01/14 Changed TEX to LATEX in set_up_editing.  Added copyright notice.
84/04/12 Change set_up_editing to read program name at run-time.
Print banner line

@x
@d banner=='This is TeX, Version 1.0' {printed when \TeX\ starts}
@y
@d banner=='This is TeX82, TOPS-10 Version 1.0' {printed when \TeX\ starts}
@z
Initialization procedure

@x
@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
@y CHANGE FOR INITEX
@d init==@{  {change this to `$\\{init}\eqv\.{@@\{}$' in the production version}
@d tini==@}  {change this to `$\\{tini}\eqv\.{@@\}}$' in the production version}
@z

Compile-time constants

@x [1] Compile-time constants:
@!mem_max=30000; {greatest index in \TeX's internal |mem| array,
  must be strictly less than |max_halfword|}
@!buf_size=500; {maximum number of characters simultaneously present in
  current lines of open files and in control sequences between
  \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
@!error_line=72; {width of context lines on terminal error messages}
@!half_error_line=42; {width of first lines of contexts in terminal
  error messages, should be between 30 and |error_line-15|}
@!max_print_line=79; {width of longest text lines output, should be at least 60}
@!stack_size=200; {maximum number of simultaneous input sources}
@!max_in_open=6; {maximum number of input files and error insertions that
  can be going on simultaneously}
@!font_max=75; {maximum internal font number, must not exceed |max_quarterword|
  and must be at most |font_base+256|}
@!font_mem_size=20000; {number of words of |font_info| for all fonts}
@!param_size=60; {maximum number of simultaneous macro parameters}
@!nest_size=40; {maximum number of semantic levels simultaneously active}
@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
@!string_vacancies=8000; {the minimum number of characters that should be
  available for the user's control sequences and font names,
  after \TeX's own error messages are stored}
@!pool_size=32000; {maximum number of characters in strings, including all
  error messages and help texts, and the names of all fonts and
  control sequences; must exceed |string_vacancies| by the total
  length of \TeX's own strings, which is currently about 23000}
@!save_size=600; {space for saving values outside of current group, must be
  at most |max_halfword|}
@!trie_size=8000; {space for hyphenation patterns, should be larger for
  \.{INITEX} than it is in production versions of \TeX}
@!dvi_buf_size=800; {size of the output buffer, must be a multiple of 8}
@!file_name_size=40; {file names shouldn't be longer than this}
@!pool_name='TeXformats:TEX.POOL                     ';
  {string of length |file_name_size|, tells where the string pool appears}
@y CHANGE FOR INITEX
@!mem_max=58000; {greatest index in \TeX's internal |mem| array,
  must be strictly less than |max_halfword|}
@!buf_size=500; {maximum number of characters simultaneously present in
  current lines of open files and in control sequences between
  \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
@!error_line=79; {width of context lines on terminal error messages}
@!half_error_line=50; {width of first lines of contexts in terminal
  error messages, should be between 30 and |error_line-15|}
@!max_print_line=79; {width of longest text lines output, should be at least 60}
@!stack_size=200; {maximum number of simultaneous input sources}
@!max_in_open=6; {maximum number of input files and error insertions that
  can be going on simultaneously}
@!font_max=100;
  {maximum internal font number, must not exceed |max_quarterword|}
@!font_mem_size=25000; {number of words of |font_info| for all fonts}
@!param_size=60; {maximum number of simultaneous macro parameters}
@!nest_size=40; {maximum number of semantic levels simultaneously active}
@!max_strings=4400; {maximum number of strings}
@!string_vacancies=15000; {the minimum number of characters that should be
  available for the user's control sequences and font names,
  after \TeX's own error messages are stored}
@!pool_size=40000; {maximum number of characters in strings, including all
  error messages and help texts, and the names of all fonts and
  control sequences; must exceed |string_vacancies| by the total
  length of \TeX's own strings, which is currently about 23000}
@!save_size=600; {space for saving values outside of current group, must be
  at most |max_halfword|}
@!trie_size=8000; {space for hyphenation patterns, should be larger for
  \.{INITEX} than it is in production versions of \TeX}
@!dvi_buf_size=800; {size of the output buffer, must be a multiple of 8}
@!file_name_size=40; {file names shouldn't be longer than this}
@!pool_name='TeX:TEX.POO                             ';
  {string of length |file_name_size|, where string pool appears}
@z

@x [1] TANGLE-time constants:
@d mem_base=0 {smallest index in the |mem| array, must not be less
  than |min_halfword|}
@d hi_mem_base=13000 {smallest index in the single-word area of |mem|,
  must be substantially larger than |mem_base| and smaller than |mem_max|}
@d font_base=0 {smallest internal font number, must not be less
  than |min_quarterword|}
@d hash_size=2100 {maximum number of control sequences; it should be at most
  about |(mem_max-hi_mem_base)/6|, but 2100 is already quite generous}
@d hash_prime=1777 {a prime number equal to about 85\% of |hash_size|}
@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
@y
@d mem_base=0 {smallest index in the |mem| array, must not be less
  than |min_halfword|}
@d hi_mem_base=25000 {smallest index in the single-word area of |mem|,
  must be substantially larger than |mem_base| and smaller than |mem_max|}
@d font_base=0 {smallest internal font number, must not be less
  than |min_quarterword|}
@d hash_size=2500 {maximum number of control sequences; it should be at most
  about |(mem_max-hi_mem_base)/6|, but 2500 is already quite generous}
@d hash_prime=2129 {a prime number equal to about 85\% of |hash_size|}
@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
@z

Make all characters readable.

@x
for i:=1 to @'37 do xchr[i]:=' ';
@y
for i:=1 to @'37 do xchr[i]:=chr(i);
@z
File opening and closing

@x
@d reset_OK(#)==erstat(#)=0
@y
@d reset_OK(#)==(erstat(#)=0) or (erstat(#)=@'20000) {empty file}
@z

@x
@p function a_open_in(var f:alpha_file):boolean;
  {open a text file for input}
begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
@y
@p function erstat(var f:file):integer; extern; {in the runtime library}
function a_open_in(var f:alpha_file):boolean;
  {open a text file for input}
begin reset(f,name_of_file,'/O/E'); a_open_in:=reset_OK(f);
{/E turns off end-of-line censor}
@z

@x
function b_open_in(var f:byte_file):boolean;
  {open a binary file for input}
begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
@y
function b_open_in(var f:byte_file):boolean;
  {open a binary file for input}
begin reset(f,name_of_file,'/O/B:8'); b_open_in:=reset_OK(f);
@z

@x
function b_open_out(var f:byte_file):boolean;
  {open a binary file for output}
begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
@y
function b_open_out(var f:byte_file):boolean;
  {open a binary file for output}
begin rewrite(f,name_of_file,'/O/B:8'); b_open_out:=rewrite_OK(f);
@z
File input

@x
@ Input from text files is read one line at a time, using a routine called
|input_ln|. This function is defined in terms of global variables called
|buffer|, |first|, and |last| that will be described in detail later; for
now, it suffices for us to know that |buffer| is an array of |ASCII_code|
values, and that |first| and |last| are indices into this array
representing the beginning and ending of a line of text.

@<Glob...@>=
@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
@!first:0..buf_size; {the first unused position in |buffer|}
@!last:0..buf_size; {end of the line just input to |buffer|}
@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
@y
@ Input from text files is read one line at a time, using a routine called
|input_ln|. This function is defined in terms of global variables called
|buffer|, |first|, and |last| that will be described in detail later; for
now, it suffices for us to know that |buffer| is an array of |ASCII_code|
values, and that |first| and |last| are indices into this array
representing the beginning and ending of a line of text.

We will read the lines first into an auxiliary buffer, in order to
save the running time of procedure-call overhead.
@^Knuth, Donald Ervin@>
@d line_feed=@'12 {ASCII code for line feed}
@d form_feed=@'14 {ditto for form feed}
@d ASCII_space=@'40 {ditto for space}
@d aux_buf_size=80

@<Glob...@>=
@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
@!first:0..buf_size; {the first unused position in |buffer|}
@!last:0..buf_size; {end of the line just input to |buffer|}
@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
@!aux_buf:array[0..aux_buf_size-1] of text_char; {where the characters go
		first}
@!cpu_time:nonnegative_integer; {cumulative runtime in ms}
@z

@x
@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
  {inputs the next line or returns |false|}
var last_nonblank:0..buf_size; {|last| with trailing blanks removed}
begin if bypass_eoln then if not eof(f) then get(f);
  {input the first character of the line into |f^|}
last:=first; {cf.\ Matthew 19:30}
if eof(f) then input_ln:=false
else  begin last_nonblank:=first;
  while not eoln(f) do
    begin if last>=max_buf_stack then
      begin max_buf_stack:=last+1;
      if max_buf_stack=buf_size then
        overflow("buffer size",buf_size);
@:TeX capacity exceeded buffer size}{\quad buffer size@>
      end;
    buffer[last]:=xord[f^]; get(f); incr(last);
    if buffer[last-1]<>" " then last_nonblank:=last;
    end;
  last:=last_nonblank; input_ln:=true;
  end;
end;
@y
@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
  {inputs the next line or returns |false|}
label 1,2,3;
var n: integer; @!full,@!fake_eol,@!form_eol:boolean;
@!k,@!m: 0..buf_size; {indices into |buffer|}
begin if bypass_eoln then {input the first character of the line into |f^|}
  begin if not eof(f) then get(f);
  if not eof(f) then if f^=chr(line_feed) then get(f);
    {skip past a |line_feed|}
  end;
last:=first;
if eof(f) then input_ln:=false
else	begin
1:	full:=false; fake_eol:=false; form_eol:=false;
	read(f,aux_buf:n);
2:	if (n>aux_buf_size) then begin n:=n-1; full:=true end;
	    {aux_buf overflowed}
	if last+n>max_buf_stack then
	    if last+n>=buf_size then
		    begin max_buf_stack:=buf_size;
		    overflow("buffer size",buf_size);
@:TeX capacity exceeded buffer size}{\quad buffer size@>
		    end
	    else max_buf_stack:=last+n;
    if n>0 then
	begin m:=last;
	    last:=m+n;
	    for k:=m to last-1 do buffer[k]:=xord[aux_buf[k-m]];
	    if full then {there's more on this line} goto 1;
	end;
    if form_eol then goto 3; {simulate new line with formfeed}
    if fake_eol then goto 1;
    if (f^<>chr(carriage_return)) and (f^<>chr(ASCII_space)) then
	{only allow carriage_return or space (for TTY) as end-of-line}
    begin aux_buf[0]:=f^; n:=1;
	  if f^=chr(form_feed) then form_eol:=true else
	    begin get(f); fake_eol:=true end;
	  goto 2 end;
    while last>first do if buffer[last-1]<>" " then goto 3
					       else decr(last);
3:	input_ln:=true;
    end;
end;
@z

Open TTY

@x
@ The user's terminal acts essentially like other files of text, except
that it is used both for input and for output. When the terminal is
considered an input file, the file variable is called |term_in|, and when it
is considered an output file the file variable is |term_out|.
@^system dependencies@>

@<Glob...@>=
@!term_in:alpha_file; {the terminal as an input file}
@!term_out:alpha_file; {the terminal as an output file}
@y
@ The user's terminal acts essentially like other files of text, except
that it is used both for input and for output. When the terminal is
considered an input file, the file variable is called |term_in|, and when it
is considered an output file the file variable is |term_out|.
Here, we use the built-in |TTY| file for output, but open a file
explicitly for input.  On input this allows ^Z to be used as an
end-of-file character.  If a file is explicity opened for terminal
output, then the output happens even when the \TeX82 is |CCONT|ed.
If the job is then detached, you get an `Improper mode' error.
@^system dependencies@>

@d term_out==TTY {the terminal as an output file}

@<Glob...@>=
@!term_in:alpha_file; {the terminal as an input file}
@z

@x
@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
@y
@d t_open_out==do_nothing {open the terminal for text output}
@z

Implement wake_up_terminal and call_fine

@x
@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
@y
@d wake_up_terminal == undo_ctrl_O {cancel the user's cancellation of output}

@p procedure undo_ctrl_O;
var val:integer; arg:array[0..2] of integer; success:boolean;
begin
calli(@'115,,-1,val,success);{TRMNO gets UDX code for 0=this terminal}
if (val<>0) then begin	{0 is returned for detached jobs}
    arg[0]:=@'2045;		{.TOOSU=1045+.TOSET=1000 sets the ^O state}
    arg[1]:=val;		{UDX code}
    arg[2]:=0;		{0 means output switched on}
    {TRMOP does the action, 3 is the number of arguments}
    calli(@'116,3,arg,val,success)
end
end;

procedure call_fine; {procedure to call the editor FINE on exit}
mtype name_type = (dev, fil_name, ext, dummy, ppn_no, core);
pack6 = packed array[1..6] of char;
var val: integer; skipp : boolean;
run_name : array[name_type] of integer;

function conv_six_bit(line_in : pack6):integer;
mtype
six_bit = 0..63;
word = record case boolean of
    true : ( line : packed array[1..6] of six_bit;);
    false : ( int : integer;);
end;
var temp : word;
i :integer;
begin
    for i := 1 to 6 do temp.line[i] := ord(line_in[i]) - @'40;
    conv_six_bit := temp.int;
end;
begin
    run_name[dev] := conv_six_bit('SYS   ');
    run_name[fil_name] := conv_six_bit('FINE  ');
    run_name[ext] := conv_six_bit('EXE   ');
    run_name[dummy] := 0;
    run_name[ppn_no] := 0;
    run_name[core] := 0;
    calli(@'35, 1, run_name, val, skipp);
end;
@z

Initialize terminal

@x
@ The following program does the required initialization
without retrieving a possible command line.
It should be clear how to modify this routine to deal with command lines,
if the system permits them.
@^system dependencies@>

@p function init_terminal:boolean; {gets the terminal input started}
label exit;
begin t_open_in;
loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
@.**@>
  if not input_ln(term_in,true) then {this shouldn't happen}
    begin write_ln(term_out);
    write(term_out,'! End of file on the terminal... why?');
@.End of file on the terminal@>
    init_terminal:=false; return;
    end;
  loc:=first;
  while (loc<last)and(buffer[loc]=" ") do incr(loc);
  if loc<last then
    begin init_terminal:=true;
    return; {return unless the line was all blank}
    end;
  write_ln(term_out,'Please type the name of your input file.');
  end;
exit:end;
@y
@ The following program does the required initialization
retrieves a possible command line using rescan or remembers
the comand line.  The way this is done is:

(a) Rescan command line.  (Two variants are considered:  (1) .TEX PAPER
and (2) .R TEX;PAPER.  The first line then consists of everything after
the "TEX " or "R TEX;".)

(b) If the rescanning finds something it is put in the file TMP:TEX.
An exception is if the line just consists of "\".  In that case
TMP:TEX is deleted.  (This allows TEX to forget the remembered command
line.)

(c) If no command line is found and if TMP:TEX contains something,
then TMP:TEX is used.

(d) Otherwise prompt for input from the terminal.

@^system dependencies@>

@p function rescan:boolean; extern; @t\2@>@;
    {puts the command line into the terminal buffer,
    or returns |false| if there was no command line}
@#
function init_terminal:boolean; {gets the terminal files started}
mtype
@!lin=record blank:integer; txt:packed array[0..aux_buf_size-1] of char end;
@!args=packed record nam:halfword;blank:halfword;
		 len:halfword;ptr:^lin end;
var @!xblock:args;@!lina:^lin;
@!val,i,j:integer;
@!success:boolean;
@!delim_char:char;
@#
procedure init_tmp_cor;
begin
new(lina);
xblock.nam:=@'644570;	{SIXBIT /TEX/}
xblock.blank:=0;		{zero out rest of word}
xblock.ptr:=lina;		{pointer to word before text}
end;
@#
function read_tmp_file:boolean;
begin
val:=aux_buf_size div 5;
xblock.len:=@'1000000-val;
calli(@'44,1,xblock,val,success);	{TMPCOR call, 1 => read}
if val=0 then success:=false;
if success then begin
    j:=5*val;			{number of characters in file}
    loc:=first;
    last:=first+j;
    for i:=0 to j-1 do buffer[loc+i]:=ord(xblock.ptr^.txt[i]);
end;
read_tmp_file:=success;
end;
@#
procedure write_tmp_file;
begin
j:=last-loc;
if j>aux_buf_size then j:=aux_buf_size;
for i:=0 to j-1 do xblock.ptr^.txt[i]:=chr(buffer[loc+i]);
val:=(j+4) div 5;
for i:=j to 5*val-1 do xblock.ptr^.txt[i]:=' ';{pad to end of word}
xblock.len:=@'1000000-val;			{-length in words}
if val<>0 then				{if empty, delete file}
    calli(@'44,3,xblock,val,success)	{TMPCOR call, 3 => write}
else
    calli(@'44,2,xblock,val,success);	{TMPCOR call, 2 => delete}
end;
@#
function read_jcl:boolean;
begin
read_jcl:=false;
debug if false then@;@+gubed@;@/
if rescan then begin
loc:=first;
last:=first;
read_ln(term_in); {get first character into |term_in^|}
while (not eoln(term_in)) and (term_in^=' ') do get(term_in);
	    {skip leading spaces}
if ((term_in^='R') or (term_in^='r')) then delim_char:=';'
				      else delim_char:=' ';
while (not eoln(term_in)) and (term_in^<>delim_char) do
    get(term_in);	{skip TEX or R TEX}
if not eoln(term_in) then get(term_in); {skip delim_char}
while not eoln(term_in) do begin
    buffer[last]:=xord[term_in^];
    incr(last); get(term_in)
end;
end;
while (loc<last) and (buffer[loc]=" ") do incr(loc); {skip spaces}
if  (loc=last-1) and (buffer[loc]="\") then begin
last:=loc; write_tmp_file {a lone \ causes temp file to be forgotten}
end;
if loc<last then begin write_tmp_file; read_jcl:=true end
end;
@#
procedure read_first_line;
label exit;
begin
loop@+	begin
    wake_up_terminal; write(term_out,'**'); update_terminal;
@.**@>
    if not input_ln(term_in,true) then {this shouldn't happen}
	    begin write_ln(term_out);
	    write(term_out,'! End of file on the terminal... why?');
@.End of file on the terminal@>
	    init_terminal:=false; return;
	    end;
    loc:=first;
    while (loc<last)and(buffer[loc]=" ") do incr(loc);
    if loc<last then return; {return unless the line was all blank}
    write_ln(term_out,'Please type the name of your input file.');
    end;
exit:end;
@#
begin
cpu_time:=runtime;
t_open_in;
init_tmp_cor;
init_terminal:=true;
if not read_jcl then if not read_tmp_file then read_first_line;
end;
@z

Enter editing hooks

@x
@!error_count:-1..100; {the number of scrolled errors since the
  last paragraph ended}
@y
@!error_count:-1..100; {the number of scrolled errors since the
  last paragraph ended}
@!run_editor:boolean; {should the editor be run on exiting?}
@z

@x
deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
@y
deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
run_editor:=false;
@z

@x
@<Error hand...@>=
procedure jump_out;
begin goto end_of_TEX;
end;
@y
The procedure |set_up_editing|, writes out a TMP: file for FINE to
read.  This causes FINE to read in the file to be edited and the
position the cursor at the right line. It will also cause FINE to run TEX
when exited.

@<Error hand...@>=
procedure jump_out;
begin goto end_of_TEX;
end;
@#
{Declare this here for use by error procedures}
procedure undo_ctrl_O; forward;
@#
procedure set_up_editing;
const
this_buf_size = 150;
mtype
halfword = 0..@'777777;
lin=record blank:integer; txt:packed array[0..this_buf_size] of char end;
args=packed record nam:halfword;blank:halfword;
		     len:halfword;ptr:^lin end;
var xblock:args;lina:^lin;

procedure init_commands;
begin
    new(lina);
    xblock.nam:= @'454464;	{SIXBIT /EDT/}
    xblock.blank:=0;		{zero out rest of word}
    xblock.ptr:=lina;		{pointer to word before text}
end;

procedure write_commands;
const ctrl_u = @'25; ctrl_n = @'16; esc = @'33;
mtype
six_bit = 0..63;
word = record case boolean of
    true : ( line : packed array[1..6] of six_bit;);
    false : ( int : integer;);
end;
var  success : boolean; i, s, val : integer;
dummy : file of char; j:pool_pointer; temp : word;
begin
    strwrite(dummy, xblock.ptr^.txt);	{fake file}
    write(dummy, 's');	{precursor to file name}
    s:=input_stack[base_ptr].name_field;
    for j:=str_start[s] to str_start[s+1]-1 do
	  write(dummy,xchr[str_pool[j]]);	{file name}
    write(dummy, chr(@'15), chr(@'12));
    calli(@'41,-1,3,temp.int,success);	{GETTAB call to read program name}
    for i:=1 to 6 do if (temp.line[i]>0)
		 then write(dummy, chr(@'40+temp.line[i]));
    write(dummy, '!',chr(@'15), chr(@'12)); {run tex on exit from fine}
    write(dummy, chr(esc), '<', chr(ctrl_u), (line-1):1,
	chr(ctrl_n), chr(0));	{ go down line-1 lines }
    getindex(dummy, i);
    xblock.len:=@'1000000-((i+4) div 5);		{-length in words}
    calli(@'44,3,xblock,val,success)	{TMPCOR call, 3 => write}
end;

begin
    init_commands;
    write_commands;
    run_editor:=true;
end;
@z

@x
@ It is desirable to provide an `\.E' option here that gives the user
an easy way to return from \TeX\ to the system editor, with the offending
line ready to be edited. But such an extension requires some system
wizardry, so the present implementation simply types out what file should be
edited and the relevant line number.
@y
@ It is desirable to provide an `\.E' option here that gives the user
an easy way to return from \TeX\ to the system editor, with the offending
line ready to be edited. What we do here is write a TMP: file which will
be read by FINE when we call with an offset of 1 on leaving \TeX.
FINE will then read in the source file and position the cursor at the
correct line. Note that on leaving FINE \TeX\ will be run automatically.
@z

@x
  begin print_nl("You want to edit file ");
@.You want to edit file x@>
  print(input_stack[base_ptr].name_field);
  print(" at line "); print_int(line);
  interaction:=scroll_mode; jump_out;
@y
  begin print_nl("Calling FINE to edit file ");
@.You want to edit file x@>
  print(input_stack[base_ptr].name_field);
  print(" at line "); print_int(line);
  interaction:=scroll_mode; set_up_editing; jump_out;
@z

@x
if base_ptr>0 then print("E to edit your file,");
@y
if base_ptr>0 then print("E to edit your file using FINE,");
@z

36-bit words; quarter and half words start at zero

@x
It is usually most efficient to have |min_quarterword=min_halfword=0|,
so one should try to achieve this unless it causes a severe problem.
The values defined here are recommended for most 32-bit computers.

@d min_quarterword=0 {smallest allowable value in a |quarterword|}
@d max_quarterword=255 {largest allowable value in a |quarterword|}
@d min_halfword==0 {smallest allowable value in a |halfword|}
@d max_halfword==65535 {largest allowable value in a |halfword|}
@y
It is usually most efficient to have |min_quarterword=min_halfword=0|,
so one should try to achieve this unless it causes a severe problem.
The values defined here are recommended for most 36-bit computers.

@d min_quarterword=0 {smallest allowable value in a |quarterword|}
@d max_quarterword=511 {largest allowable value in a |quarterword|}
@d min_halfword==0 {smallest allowable value in a |halfword|}
@d max_halfword==262143 {largest allowable value in a |halfword|}
@z

@x
@d qi(#)==#+min_quarterword
  {to put an |eight_bits| item into a quarterword}
@d qo(#)==#-min_quarterword
  {to take an |eight_bits| item out of a quarterword}
@d hi(#)==#+min_halfword
  {to put a sixteen-bit item into a halfword}
@d ho(#)==#-min_halfword
  {to take a sixteen-bit item from a halfword}
@y
@d qi(#)==# {to put an |eight_bits| item into a quarterword}
@d qo(#)==# {to take an |eight_bits| item from a quarterword}
@d hi(#)==# {to put a sixteen-bit item into a halfword}
@d ho(#)==# {to take a sixteen-bit item from a halfword}
@z

Date and time

@x
Since standard \PASCAL\ cannot provide such information, something special
is needed. The program here simply specifies July 4, 1776, at noon; but
users probably want a better approximation to the truth.

@p procedure fix_date_and_time;
begin time:=12*60; {minutes since midnight}
day:=4; {fourth day of the month}
month:=7; {seventh month of the year}
year:=1776; {Anno Domini}
end;
@y
@d ptime==t@&i@&m@&e
@p procedure do_date(var min_since_midnite:integer);
{
     DATE result is a PACKED ARRAY [1..9] OF CHAR.  The date
          is returned in the form 'DD-Mmm-YY'.
     Here we use CALLI 14,ac.  This means we don't have to decode the Mmm.
     TIME current time in msec (type integer)
								}
var success:boolean; date_code : integer;
begin
    calli(@'14,0,0,date_code,success);
		{ returns 31*(12*(year-1964)+(month-1))+(day-1) in date_code }
    day := (date_code mod 31)+1;
    month := ((date_code div 31) mod 12)+1;
    year := (date_code div (31*12))+1964;
    min_since_midnite := ptime div (1000*60);
    end;
procedure fix_date_and_time;
begin
do_date(time);
end;
@z

Establish system areas and parse file names

@x
@ The file names we shall deal with have the following structure:
If the name contains `\.>' or `\.:', the file area consists of all characters
up to and including the final such character; otherwise the file area is null.
If the remaining file name contains `\..', the file extension consists of all
such characters from the first `\..' to the end, otherwise the file extension
is null.
@^system dependencies@>

We can scan such file names easily by using two global variables that keep track
of the occurrences of area and extension delimiters:

@<Glob...@>=
@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
@y
@ The file names we shall deal with have the following structure:
If the name contains `\.:' or `\.[', the file area consists of all
characters before and including the `\.:' concatenated with everything
beyond (and including) the `.['.  If the remaining file name contains
`\..', the file extension consists of all such characters from this
character to the @\.[' or end, otherwise the file extension
is null. We can assume that there is at most one of each of \.:', `\.['
and `\..'.

This is all compliciated by the presence of device and PPN specifications
at opposite ends of the file names.  The trick is to combine them together
for internal TeX consumption, and split them apart in |print_file_name| and
|pack_file_name|.

We can scan such file names easily by using three global variables that keep
track of the occurrences of area and extension delimiters:

@d file_buf_size=40

@<Global...@>=
@!dev_delimiter:-1..file_buf_size; {the most recent `\.:', if any}
@!area_delimiter:-1..file_buf_size; {the most recent `\.[', if any}
@!ext_delimiter:-1..file_buf_size; {the relevant `\..', if any}
@!file_buf:packed array[0..file_buf_size-1] of 0..127; {where the file names
		first}
@!file_index:0..file_buf_size; {index into file_buf}
@^system dependencies@>
@z

@x
@d TEX_area=="TeXinputs:"
@.TeXinputs@>
@d TEX_font_area=="TeXfonts:"
@.TeXfonts@>
@y
@d TEX_area=="TeX:"
@.TeXinputs@>
@d TEX_font_area=="TeX:[,,TFM]"
@.TeXfonts@>
@z

@x
@p procedure begin_name;
begin area_delimiter:=0; ext_delimiter:=0;
end;

@ And here's the second.
@^system dependencies@>

@p function more_name(@!c:ASCII_code):boolean;
begin if c=" " then more_name:=false
else  begin if (c=">")or(c=":") then
    begin area_delimiter:=pool_ptr; ext_delimiter:=0;
    end
  else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
  str_room(1); append_char(c); {contribute |c| to the current string}
  more_name:=true;
  end;
end;

@ The third.
@^system dependencies@>

@p procedure end_name;
begin if str_ptr+3>max_strings then
  overflow("number of strings",max_strings-init_str_ptr);
@:TeX capacity exceeded number of strings}{\quad number of strings@>
if area_delimiter=0 then cur_area:=""
else  begin cur_area:=str_ptr; incr(str_ptr);
  str_start[str_ptr]:=area_delimiter+1;
  end;
if ext_delimiter=0 then
  begin cur_ext:=""; cur_name:=make_string;
  end
else  begin cur_name:=str_ptr; incr(str_ptr);
  str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
  end;
end;
@y
@p procedure begin_name;
begin
 dev_delimiter:=-1;
 area_delimiter:=-1;
 ext_delimiter:=-1;
 file_index:=0
end;

@ And here's the second.
@^system dependencies@>

@p function more_name(@!c:ASCII_code):boolean;
begin if (c<=" ") or (c>"~") or (file_index=file_buf_size) then
   more_name:=false
else
  begin if (c=":") and (ext_delimiter<0) and (area_delimiter<0)
	 then dev_delimiter:=file_index
    else if (c=".") and (area_delimiter<0) then ext_delimiter:=file_index
      else if (c="[") then area_delimiter:=file_index;
  file_buf[file_index]:=c; incr(file_index);
  more_name:=true;
  end;
end;

@ The third.
@^system dependencies@>

@p procedure end_name;
var j:0..file_buf_size-1;
begin if str_ptr+3>max_strings then
  overflow("number of strings",max_strings-init_str_ptr);
@:TeX capacity exceeded number of strings}{\quad number of strings@>
if (area_delimiter<0) and (dev_delimiter<0) then cur_area:=""
else  begin if dev_delimiter>=0 then begin
      str_room(dev_delimiter+1);
      for j:=0 to dev_delimiter do append_char(file_buf[j]) end;
    if area_delimiter>=0 then begin
      str_room(file_index-area_delimiter);
      for j:=area_delimiter to file_index-1 do append_char(file_buf[j]) end;
    cur_area:=make_string
end;
if area_delimiter<0 then area_delimiter:=file_index;
if (ext_delimiter<0) then
  begin cur_ext:=""; ext_delimiter:=area_delimiter end
else begin
  str_room(area_delimiter-ext_delimiter);
  for j:=ext_delimiter to area_delimiter-1 do append_char(file_buf[j]);
  cur_ext:=make_string
end;
str_room(ext_delimiter-dev_delimiter-1);
for j:=dev_delimiter+1 to ext_delimiter-1 do append_char(file_buf[j]);
cur_name:=make_string
end;
@z

@x
procedure print_file_name(@!n,@!a,@!e:integer);
begin print(a); print(n); print(e);
end;
@y
procedure print_file_name(@!n,@!a,@!e:str_number);
var delim,j:pool_pointer;
begin delim:=str_start[a+1]-1;
  while (delim>=str_start[a]) and (str_pool[delim]<>":") do decr(delim);
  for j:=str_start[a] to delim do print_char(str_pool[j]);
  print(n); print(e);
  for j:=delim+1 to str_start[a+1]-1 do print_char(str_pool[j]);
end;
@z

@x
@p procedure pack_file_name(@!n,@!a,@!e:str_number);
var k:integer; {number of positions filled in |name_of_file|}
@!c: ASCII_code; {character being packed}
@!j:pool_pointer; {index into |str_pool|}
begin k:=0;
for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
end;
@y
@p procedure pack_file_name(@!n,@!a,@!e:str_number);
var k:integer; {number of positions filled in |name_of_file|}
@!c: ASCII_code; {character being packed}
@!delim,j:pool_pointer; {index into |str_pool|}
begin
 delim:=str_start[a+1]-1;
 while (delim>=str_start[a]) and (str_pool[delim]<>":") do decr(delim);
 k:=0;
 for j:=str_start[a] to delim do append_to_name(str_pool[j]);
{Handle fonts whose names are longer than 6 characters by taking the
first 3 and last 3 characters only.}
if (e=".tfm") and (length(n)>6) then
    begin for j:=str_start[n] to str_start[n]+2 do
	append_to_name(str_pool[j]);
    for j:=str_start[n+1]-3 to str_start[n+1]-1 do
	append_to_name(str_pool[j]);
    end
else for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
for j:=delim+1 to str_start[a+1]-1 do append_to_name(str_pool[j]);
if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
end;
@z

@x
@d format_default_length=20 {length of the |TEX_format_default| string}
@d format_area_length=11 {length of its area part}
@d format_ext_length=4 {length of its `\.{.fmt}' part}
@y
@d format_default_length=13 {length of the |TEX_format_default| string}
@d format_area_length=4 {length of its area part}
@d format_ext_length=4 {length of its `\.{.fmt}' part}
@z

@x
TEX_format_default:='TeXformats:PLAIN.fmt';
@y
TEX_format_default:='TeX:plain.fmt';
@z

Type out CPU time & run editor

@x
wake_up_terminal; @<Finish the \.{DVI} file@>;
if job_name>0 then
  begin wlog_cr; a_close(log_file); selector:=selector-2;
  if selector=term_only then
    begin print_nl("Transcript written on ");
    print(log_name); print_char(".");
    end;
  end;
end;
@y
wake_up_terminal; @<Finish the \.{DVI} file@>;
cpu_time:=runtime-cpu_time;
print_nl("CPU time = "); print_int(cpu_time div 1000); print_char(".");
print_two(cpu_time div 10); print(" s.");
if job_name>0 then
  begin wlog_cr; a_close(log_file); selector:=selector-2;
  if selector=term_only then
    begin print_nl("Transcript written on ");
    print(log_name); print_char(".");
    end;
  end;
  if run_editor then call_fine;
end;
@z