§1330  TEX82  PART 51: THE MAIN PROGRAM 465

1330. The main program. This is it: the part of TEX that executes all those procedures we have written.

Well—almost. Let’s leave space for a few more routines that we may have forgotten.

⟨ Last-minute procedures 1333 ⟩

1331. We have noted that there are two versions of TEX82. One, called , has to be run first; it initializes everything from scratch, without reading a format file, and it has the capability of dumping a format file. The other one is called ‘ ’; it is a “virgin” program that needs to input a format file in order to get started. typically has more memory capacity than , because it does not need the space consumed by the auxiliary hyphenation tables and the numerous calls on primitive, etc.

The program cannot read a format file instantaneously, of course; the best implementations therefore allow for production versions of TEX that not only avoid the loading routine for Pascal object code, they also have a format file pre-loaded. This is impossible to do if we stick to standard Pascal; but there is a simple way to fool many systems into avoiding the initialization, as follows: (1) We declare a global integer variable called ready already . The probability is negligible that this variable holds any particular value like 314159 when is first loaded. (2) After we have read in a format file and initialized everything, we set ready already ← 314159. (3) Soon will print ‘ ’, waiting for more input; and at this point we interrupt the program and save its core image in some form that the operating system can reload speedily. (4) When that core image is activated, the program starts again at the beginning; but now ready already = 314159 and all the other global variables have their initial values too. The former chastity has vanished!

In other words, if we allow ourselves to test the condition ready already = 314159, before ready already has been assigned a value, we can avoid the lengthy initialization. Dirty tricks rarely pay off so handsomely.

466  PART 51: THE MAIN PROGRAM  TEX82 §1332

On systems that allow such preloading, the standard program called should be the one that has format preloaded, since that agrees with The TEXbook. Other versions, e.g., , should also be provided for commonly used formats. ⟨ Global variables 13 ⟩ +≡ ready already : integer ; { a sacrifice of purity for economy } 1332. Now this is really it: TEX starts and ends here.

The initial test involving ready already should be deleted if the Pascal runtime system is smart enough to detect such a “mistake.”

begin { start here } history ← fatal error stop; { in case we quit during initialization } t open out ; { open the terminal for output } if ready already = 314159 then goto start of TEX ; ⟨ Check the “constant” values for consistency 14 ⟩ if bad > 0 then

begin wterm ln(´ ␣ ␣ ␣ ␣ ␣ ´ , ´ ␣´ , bad : 1); goto final end ; end;

initialize; { set global variables to their starting values } init if ¬get strings started then goto final end ; init prim; { call primitive for each primitive } init str ptr ← str ptr ; init pool ptr ← pool ptr ; fix date and time; tini ready already ← 314159;

start of TEX : ⟨ Initialize the output routines 55 ⟩; ⟨ Get the first line of input and prepare to start 1337 ⟩; history ← spotless ; { ready to go! } main control ; { come to life } final cleanup; { prepare for death }

end of TEX : close files and terminate; final end : ready already ← 0;

end.

1333. Here we do whatever is needed to complete TEX’s job gracefully on the local operating system. The code here might come into play after a fatal error; it must therefore consist entirely of “safe” operations that cannot produce error messages. For example, it would be a mistake to call str room or make string at this time, because a call on overflow might lead to an infinite loop.

Actually there’s one way to get error messages, via prepare mag ; but that can’t cause infinite recursion. This program doesn’t bother to close the input files that may still be open.

⟨ Last-minute procedures 1333 ⟩ ≡ procedure close files and terminate;

var k: integer ; { all-purpose index } begin ⟨ Finish the extensions 1378 ⟩; stat if tracing stats > 0 then ⟨ Output statistics about this job 1334 ⟩; tats wake up terminal ; ⟨ Finish the file 642 ⟩;

if log opened then

begin wlog cr ; a close(log file); selector ← selector − 2;

if selector = term only then begin print nl ( ␣ ␣ ␣ ); slow print (log name); print char ( ); end;

end; end;

See also sections 1335, 1336, and 1338. This code is used in section 1330.

1334. The present section goes directly to the log file instead of using print commands, because there’s no need for these strings to take up str pool memory when a non-stat version of TEX is being used. ⟨ Output statistics about this job 1334 ⟩ ≡

if log opened then

begin wlog ln(´ ␣´ ); wlog ln(´ ␣ ␣ ␣ ␣ ␣ ´ ´ ␣ ´ , ´ ␣ ␣ ´ ); wlog (´ ␣´ , str ptr − init str ptr : 1, ´ ␣ ´ ); if str ptr ≠ init str ptr + 1 then wlog (´ ´ ); wlog ln(´ ␣ ␣ ␣´ , max strings − init str ptr : 1); wlog ln(´ ␣´ , pool ptr − init pool ptr : 1, ´ ␣ ␣ ␣ ␣ ␣´ , pool size − init pool ptr : 1); wlog ln(´ ␣´ , lo mem max − mem min + mem end − hi mem min + 2 : 1,

´ ␣ ␣ ␣ ␣ ␣ ␣´ , mem end + 1 − mem min : 1); wlog ln(´ ␣´ , cs count : 1, ´ ␣ ␣ ␣ ␣ ␣ ␣´ , hash size : 1); wlog (´ ␣´ , fmem ptr : 1, ´ ␣ ␣ ␣ ␣ ␣ ␣´ , font ptr − font base : 1, ´ ␣ ´ ); if font ptr ≠ font base + 1 then wlog (´ ´ ); wlog ln(´ ␣ ␣ ␣´ , font mem size : 1, ´ ␣ ␣´ , font max − font base : 1); wlog (´ ␣´ , hyph count : 1, ´ ␣ ␣ ´ ); if hyph count ≠ 1 then wlog (´ ´ ); wlog ln(´ ␣ ␣ ␣´ , hyph size : 1); wlog ln(´ ␣´ , max in stack : 1, ´ ´ , max nest stack : 1, ´ ´ , max param stack : 1, ´ ´ ,

max buf stack + 1 : 1, ´ ´ , max save stack + 6 : 1, ´ ␣ ␣ ␣ ␣ ␣´ , stack size : 1, ´ ´ , nest size : 1, ´ ´ , param size : 1, ´ ´ , buf size : 1, ´ ´ , save size : 1, ´ ´ );

end

This code is used in section 1333.

1335. We get to the final cleanup routine when  or  has been scanned and its all over.

⟨ Last-minute procedures 1333 ⟩ +≡ procedure final cleanup;

label exit ; var c: small number ; { 0 for , 1 for } begin c ← cur chr ; if job name = 0 then open log file; while input ptr > 0 do

if state = token list then end token list else end file reading ; while open parens > 0 do

begin print ( ␣ ); decr (open parens ); end;

if cur level > level one then begin print nl ( ); print esc( ␣ ␣ ); print ( ␣ ␣ ␣ ␣ ␣ ); print int (cur level − level one); print char ( ); end;

while cond ptr ≠ null do begin print nl ( ); print esc( ␣ ␣ ); print ( ␣ ); print cmd chr (if test , cur if ); if if line ≠ 0 then

begin print ( ␣ ␣ ␣ ); print int (if line);

end; print ( ␣ ␣ ); if line ← if line field (cond ptr ); cur if ← subtype(cond ptr ); temp ptr ← cond ptr ; cond ptr ← link (cond ptr ); free node(temp ptr , if node size); end;

if history ≠ spotless then

if ((history = warning issued ) ∨ (interaction < error stop mode)) then

if selector = term and log then begin selector ← term only ; print nl ( ␣ ␣ ␣ ␣ ␣ ␣ ); selector ← term and log ; end;

if c = 1 then

begin init for c ← top mark code to split bot mark code do

if cur mark [c] ≠ null then delete token ref (cur mark [c]); store fmt file; return; tini print nl ( ␣ ␣ ␣ ␣ ␣ ); return; end;

exit : end;

1336. ⟨ Last-minute procedures 1333 ⟩ +≡ init procedure init prim; { initialize all the primitives } begin no new control sequence ← false; ⟨ Put each of TEX’s primitives into the hash table 226 ⟩; no new control sequence ← true; end; tini 1337. When we begin the following code, TEX’s tables may still contain garbage; the strings might not even be present. Thus we must proceed cautiously to get bootstrapped in.

But when we finish this part of the program, TEX is ready to call on the main control routine to do its work. ⟨ Get the first line of input and prepare to start 1337 ⟩ ≡

begin ⟨ Initialize the input routines 331 ⟩; if (format ident = 0) ∨ (buffer [loc] = ) then

begin if format ident ≠ 0 then initialize; { erase preloaded format } if ¬open fmt file then goto final end ; if ¬load fmt file then

begin w close(fmt file); goto final end ; end;

w close(fmt file);

while (loc < limit ) ∧ (buffer [loc] = ␣ ) do incr (loc);

end; if end line char inactive then decr (limit ) else buffer [limit ] ← end line char ; fix date and time; ⟨ Compute the magic offset 765 ⟩; ⟨ Initialize the print selector based on interaction 75 ⟩; if (loc < limit ) ∧ (cat code(buffer [loc]) ≠ escape) then start input ; { assumed } end

This code is used in section 1332.