30  PART 6: REPORTING ERRORS  TEX82 §72

72. Reporting errors. When something anomalous is detected, TEX typically does something like this:

print err (  ␣         ␣   ␣    ␣  );

                   help3 (  ␣  ␣   ␣     ␣    ␣  ␣  ␣     ␣  ␣  )
                   (  ␣  ␣   ␣      ␣     ␣ ´ ␣      ␣  )
                   (  ␣   ␣    ␣   ␣   ␣   ␣  ␣  );

error ;

A two-line help message would be given using help2 , etc.; these informal helps should use simple vocabulary that complements the words used in the official error message that was printed. (Outside the U.S.A., the help messages should preferably be translated into the local vernacular. Each line of help is at most 60 characters long, in the present implementation, so that max print line will not be exceeded.)

The print err procedure supplies a ‘ ’ before the official message, and makes sure that the terminal is awake if a stop is going to occur. The error procedure supplies a ‘ ’ after the official message, then it shows the location of the error; and if interaction = error stop mode, it also enters into a dialog with the user, during which time the help message may be printed.

73. The global variable interaction has four settings, representing increasing amounts of user interaction: define batch mode = 0 { omits all stops and omits terminal output } define nonstop mode = 1 { omits all stops } define scroll mode = 2 { omits error stops } define error stop mode = 3 { stops at every opportunity to interact } define print err (#) ≡

begin if interaction = error stop mode then wake up terminal ; print nl ( ␣ ); print (#); end

⟨ Global variables 13 ⟩ +≡ interaction: batch mode . . error stop mode; { current level of interaction }

74. ⟨ Set initial values of key variables 21 ⟩ +≡ interaction ← error stop mode;

75. TEX is careful not to call error when the print selector setting might be unusual. The only possible values of selector at the time of error messages are no print (when interaction = batch mode and log file not yet open); term only (when interaction > batch mode and log file not yet open); log only (when interaction = batch mode and log file is open); term and log (when interaction > batch mode and log file is open). ⟨ Initialize the print selector based on interaction 75 ⟩ ≡

if interaction = batch mode then selector ← no print else selector ← term only

This code is used in sections 1265 and 1337.

§76  TEX82  PART 6: REPORTING ERRORS 31

76. A global variable deletions allowed is set false if the get next routine is active when error is called; this ensures that get next and related routines like get token will never be called recursively. A similar interlock is provided by set box allowed .

The global variable history records the worst level of error that has been detected. It has four possible values: spotless , warning issued , error message issued , and fatal error stop.

Another global variable, error count , is increased by one when an error occurs without an interactive dialog, and it is reset to zero at the end of every paragraph. If error count reaches 100, TEX decides that there is no point in continuing further.

define spotless = 0 { history value when nothing has been amiss yet }

define warning issued = 1 { history value when begin diagnostic has been called }

define error message issued = 2 { history value when error has been called }

define fatal error stop = 3 { history value when termination was premature } ⟨ Global variables 13 ⟩ +≡ deletions allowed : boolean; { is it safe for error to call get token? } set box allowed : boolean; { is it safe to do a assignment? } history : spotless . . fatal error stop; { has the source input been clean so far? } error count : −1 . . 100; { the number of scrolled errors since the last paragraph ended }

77. The value of history is initially fatal error stop, but it will be changed to spotless if TEX survives the initialization process. ⟨ Set initial values of key variables 21 ⟩ +≡

deletions allowed ← true; set box allowed ← true; error count ← 0; { history is initialized elsewhere }

78. Since errors can be detected almost anywhere in TEX, we want to declare the error procedures near the beginning of the program. But the error procedures in turn use some other procedures, which need to be declared forward before we get to error itself.

It is possible for error to be called recursively if some error arises when get token is being used to delete a token, and/or if some fatal error occurs while TEX is trying to fix a non-fatal one. But such recursion is never more than two levels deep. ⟨ Error handling procedures 78 ⟩ ≡ procedure normalize selector ; forward ; procedure get token; forward ; procedure term input ; forward ; procedure show context ; forward ; procedure begin file reading ; forward ; procedure open log file; forward ; procedure close files and terminate; forward ; procedure clear for error prompt ; forward ; procedure give err help; forward ; debug procedure debug help; forward ; gubed

See also sections 81, 82, 93, 94, and 95. This code is used in section 4.

79. Individual lines of help are recorded in the array help line, which contains entries in positions 0 . . (help ptr − 1). They should be printed in reverse order, i.e., with help line[0] appearing last.

define hlp1 (#) ≡ help line[0] ← #; end define hlp2 (#) ≡ help line[1] ← #; hlp1 define hlp3 (#) ≡ help line[2] ← #; hlp2 define hlp4 (#) ≡ help line[3] ← #; hlp3 define hlp5 (#) ≡ help line[4] ← #; hlp4 define hlp6 (#) ≡ help line[5] ← #; hlp5 define help0 ≡ help ptr ← 0 { sometimes there might be no help } define help1 ≡ begin help ptr ← 1; hlp1 { use this with one help line } define help2 ≡ begin help ptr ← 2; hlp2 { use this with two help lines } define help3 ≡ begin help ptr ← 3; hlp3 { use this with three help lines } define help4 ≡ begin help ptr ← 4; hlp4 { use this with four help lines } define help5 ≡ begin help ptr ← 5; hlp5 { use this with five help lines } define help6 ≡ begin help ptr ← 6; hlp6 { use this with six help lines }

⟨ Global variables 13 ⟩ +≡ help line: array [0 . . 5] of str number ; { helps for the next error } help ptr : 0 . . 6; { the number of help lines present } use err help: boolean; { should the err help list be shown? }

80. ⟨ Set initial values of key variables 21 ⟩ +≡ help ptr ← 0; use err help ← false;

81. The jump out procedure just cuts across all active procedure levels and goes to end of TEX . This is the only nontrivial goto statement in the whole program. It is used when there is no recovery from a particular error.

Some Pascal compilers do not implement non-local goto statements. In such cases the body of jump out should simply be ‘close files and terminate; ’ followed by a call on some system procedure that quietly terminates the program. ⟨ Error handling procedures 78 ⟩ +≡ procedure jump out ;

begin goto end of TEX ; end;

82. Here now is the general error routine. ⟨ Error handling procedures 78 ⟩ +≡ procedure error ; { completes the job of error reporting }

label continue, exit ; var c: ASCII code; { what the user types }

s1 , s2 , s3 , s4 : integer ; { used to save global variables when deleting tokens } begin if history < error message issued then history ← error message issued ; print char ( ); show context ; if interaction = error stop mode then ⟨ Get user’s advice and return 83 ⟩; incr (error count ); if error count = 100 then

begin print nl ( ␣ ␣ ␣ ␣ ␣ ␣ ); history ← fatal error stop; jump out ; end;

⟨ Put help message on the transcript file 90 ⟩; exit : end; 83. ⟨ Get user’s advice and return 83 ⟩ ≡

loop begin continue: clear for error prompt ; prompt input ( ␣ ); if last = first then return; c ← buffer [first ]; if c ≥ then c ← c + − ; { convert to uppercase } ⟨ Interpret code c and return if done 84 ⟩; end

This code is used in section 82.

84. It is desirable to provide an ‘ ’ 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 the name of the file that should be edited and the relevant line number.

There is a secret ‘ ’ option available when the debugging routines haven’t been commented out. ⟨ Interpret code c and return if done 84 ⟩ ≡

case c of

, , , , , , , , , : if deletions allowed then

⟨ Delete c − tokens and goto continue 88 ⟩; debug : begin debug help; goto continue; end; gubed : if base ptr > 0 then

begin print nl ( ␣ ␣ ␣ ␣ ␣ ); slow print (input stack [base ptr ].name field ); print ( ␣ ␣ ␣ ); print int (line); interaction ← scroll mode; jump out ; end;

: ⟨ Print the help information and goto continue 89 ⟩; : ⟨ Introduce new material from the terminal and return 87 ⟩; , , : ⟨ Change the interaction level and return 86 ⟩; : begin interaction ← scroll mode; jump out ;

end;

othercases do nothing

endcases; ⟨ Print the menu of available options 85 ⟩

This code is used in section 83.

85. ⟨ Print the menu of available options 85 ⟩ ≡ begin print ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); print nl ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); print nl ( ␣ ␣ ␣ ␣ ); if base ptr > 0 then print ( ␣ ␣ ␣ ␣ );

if deletions allowed then

  print nl ( ␣  ␣   ␣  ␣ ␣  ␣      ␣   ␣    ␣ ␣  ␣ ␣      ␣  ␣  );

print nl ( ␣ ␣ ␣ ␣ ␣ ); end

This code is used in section 84.

86. Here the author of TEX apologizes for making use of the numerical relation between , , , and the desired interaction settings batch mode, nonstop mode, scroll mode. ⟨ Change the interaction level and return 86 ⟩ ≡

begin error count ← 0; interaction ← batch mode + c − ; print ( ␣ ␣ );

case c of

: begin print esc( ); decr (selector );

end; : print esc( ); : print esc( ); end; { there are no other cases } print ( ); print ln; update terminal ; return; end

This code is used in section 84.

87. When the following code is executed, buffer [(first + 1) . . (last − 1)] may contain the material inserted by the user; otherwise another prompt will be given. In order to understand this part of the program fully, you need to be familiar with TEX’s input stacks. ⟨ Introduce new material from the terminal and return 87 ⟩ ≡

begin begin file reading ; { enter a new syntactic level for terminal input }

{ now state = mid line, so an initial blank space will count as a blank } if last > first + 1 then

begin loc ← first + 1; buffer [first ] ← ␣ ;

end else begin prompt input ( ); loc ← first ;

end; first ← last ; cur input .limit field ← last − 1; { no end line char ends this line } return; end

This code is used in section 84.

88. We allow deletion of up to 99 tokens at a time. ⟨ Delete c − tokens and goto continue 88 ⟩ ≡

begin s1 ← cur tok ; s2 ← cur cmd ; s3 ← cur chr ; s4 ← align state; align state ← 1000000; OK to interrupt ← false; if (last > first + 1) ∧ (buffer [first + 1] ≥ ) ∧ (buffer [first + 1] ≤ ) then

c ← c ∗ 10 + buffer [first + 1] − ∗ 11 else c ← c − ; while c > 0 do

begin get token; { one-level recursive call of error is possible } decr (c); end;

cur tok ← s1 ; cur cmd ← s2 ; cur chr ← s3 ; align state ← s4 ; OK to interrupt ← true;

help2 ( ␣    ␣    ␣       ␣    ␣     ␣  ␣   ␣  )

(  ␣   ␣   ␣      ␣     ␣  ␣       ␣  ␣  ); show context ; goto continue;

end

This code is used in section 84.

89. ⟨ Print the help information and goto continue 89 ⟩ ≡

begin if use err help then

begin give err help; use err help ← false;

end

else begin if help ptr = 0 then help2 (  ␣ ␣   ´ ␣    ␣   ␣  ␣    ␣  ␣    ␣  )

    (  ␣   ␣      ␣   ␣      ␣ ␣  );

repeat decr (help ptr ); print (help line[help ptr ]); print ln; until help ptr = 0; end;

help4 (  ␣ ␣       ␣    ␣    ␣    ␣ ␣  )
(  ␣   ␣      ␣   ␣      ␣ ␣  )
( ␣     ␣     ␣    ␣        ␣      ␣ ␣       ␣   ␣  )
( ` ` ␣   ␣    ␣      ␣    ␣   ␣            ´  ´ );

goto continue; end

This code is used in section 84.

90. ⟨ Put help message on the transcript file 90 ⟩ ≡ if interaction > batch mode then decr (selector ); { avoid terminal output }

if use err help then

begin print ln; give err help;

end else while help ptr > 0 do

begin decr (help ptr ); print nl (help line[help ptr ]); end;

print ln; if interaction > batch mode then incr (selector ); { re-enable terminal output } print ln

This code is used in section 82.

91. A dozen or so error messages end with a parenthesized integer, so we save a teeny bit of program space by declaring the following procedure: procedure int error (n : integer );

begin print ( ␣ ); print int (n); print char ( ); error ; end;

92. In anomalous cases, the print selector might be in an unknown state; the following subroutine is called to fix things just enough to keep running a bit longer. procedure normalize selector ;

begin if log opened then selector ← term and log else selector ← term only ; if job name = 0 then open log file; if interaction = batch mode then decr (selector ); end; 93. The following procedure prints TEX’s last words before dying.

define succumb ≡

begin if interaction = error stop mode then interaction ← scroll mode;

{ no more interaction } if log opened then error ; debug if interaction > batch mode then debug help; gubed history ← fatal error stop; jump out ; { irrecoverable error } end

⟨ Error handling procedures 78 ⟩ +≡ procedure fatal error (s : str number ); { prints s, and that’s it }

begin normalize selector ;

print err ( ␣ ); help1 (s); succumb;

end;

94. Here is the most dreaded error message. ⟨ Error handling procedures 78 ⟩ +≡ procedure overflow (s : str number ; n : integer ); { stop due to finiteness }

begin normalize selector ; print err ( ␣ ␣ ␣ ␣ ); print (s); print char ( ); print int (n); print char ( ); help2 ( ␣ ␣ ␣ ␣ ␣ ␣ ) ( ␣ ␣ ␣ ␣ ␣ ␣ ␣ ); succumb; end;

95. The program might sometime run completely amok, at which point there is no choice but to stop. If no previous error has been detected, that’s bad news; a message is printed that is really intended for the TEX maintenance person instead of the user (unless the user has been particularly diabolical). The index entries for ‘this can’t happen’ may help to pinpoint the problem. ⟨ Error handling procedures 78 ⟩ +≡ procedure confusion(s : str number ); { consistency check violated; s tells where }

begin normalize selector ;

if history < error message issued then begin print err ( ␣ ´ ␣ ␣ ); print (s); print char ( );

    help1 ( ´ ␣       ␣      ␣    ␣    ␣  ␣       ␣   ␣   ␣   ␣   ␣  );

end else begin print err ( ␣ ´ ␣ ␣ ␣ ␣ ␣ ␣ );

    help2 (  ␣  ␣    ␣    ␣   ␣     ␣  ␣    ␣       ␣  ␣  )
    ( ␣     ␣ ´ ␣      ␣          ␣      ␣   ␣  ␣   ␣   ␣  );

end; succumb; end;

96. Users occasionally want to interrupt TEX while it’s running. If the Pascal runtime system allows this, one can implement a routine that sets the global variable interrupt to some nonzero value when such an interrupt is signalled. Otherwise there is probably at least a way to make interrupt nonzero using the Pascal debugger.

define check interrupt ≡ begin if interrupt ≠ 0 then pause for instructions ; end

⟨ Global variables 13 ⟩ +≡ interrupt : integer ; { should TEX pause for instructions? } OK to interrupt : boolean; { should interrupts be observed? } 97. ⟨ Set initial values of key variables 21 ⟩ +≡

interrupt ← 0; OK to interrupt ← true;

98. When an interrupt has been detected, the program goes into its highest interaction level and lets the user have nearly the full flexibility of the error routine. TEX checks for interrupts only at times when it is safe to do this. procedure pause for instructions ;

begin if OK to interrupt then

begin interaction ← error stop mode; if (selector = log only ) ∨ (selector = no print ) then incr (selector );

print err (  ); help3 (  ␣  )

(  ␣  ␣      ␣    ␣           ␣   ␣  ␣      `  ´  )

(  ␣   ␣    ␣    ␣  ␣    ␣  ␣      ␣` ´  ); deletions allowed ← false; error ;

deletions allowed ← true; interrupt ← 0; end;

end;