24  PART 5: ON-LINE AND OFF-LINE PRINTING  TEX82 §54

54. On-line and off-line printing. Messages that are sent to a user’s terminal and to the transcriptlog file are produced by several ‘print ’ procedures. These procedures will direct their output to a variety of places, based on the setting of the global variable selector , which has the following possible values: term and log , the normal setting, prints on the terminal and on the transcript file. log only , prints only on the transcript file. term only , prints only on the terminal. no print , doesn’t print at all. This is used only in rare cases before the transcript file is open. pseudo, puts output into a cyclic buffer that is used by the show context routine; when we get to that routine

we shall discuss the reasoning behind this curious mode. new string , appends the output to the current string in the string pool. 0 to 15, prints on one of the sixteen files for output. The symbolic names ‘term and log ’, etc., have been assigned numeric codes that satisfy the convenient relations no print + 1 = term only , no print + 2 = log only , term only + 2 = log only + 1 = term and log .

Three additional global variables, tally and term offset and file offset , record the number of characters that have been printed since they were most recently cleared to zero. We use tally to record the length of (possibly very long) stretches of printing; term offset and file offset , on the other hand, keep track of how many characters have appeared so far on the current line that has been output to the terminal or to the transcript file, respectively.

define no print = 16 { selector setting that makes data disappear }

define term only = 17 { printing is destined for the terminal only }

define log only = 18 { printing is destined for the transcript file only }

define term and log = 19 { normal selector setting }

define pseudo = 20 { special selector setting for show context }

define new string = 21 { printing is deflected to the string pool }

define max selector = 21 { highest selector setting } ⟨ Global variables 13 ⟩ +≡ log file: alpha file; { transcript of TEX session } selector : 0 . . max selector ; { where to print a message } dig : array [0 . . 22] of 0 . . 15; { digits in a number being output } tally : integer ; { the number of characters recently printed } term offset : 0 . . max print line; { the number of characters on the current terminal line } file offset : 0 . . max print line; { the number of characters on the current file line } trick buf : array [0 . . error line] of ASCII code; { circular buffer for pseudoprinting } trick count : integer ; { threshold for pseudoprinting, explained later } first count : integer ; { another variable for pseudoprinting }

55. ⟨ Initialize the output routines 55 ⟩ ≡ selector ← term only ; tally ← 0; term offset ← 0; file offset ← 0;

See also sections 61, 528, and 533. This code is used in section 1332.

56. Macro abbreviations for output to the terminal and to the log file are defined here for convenience. Some systems need special conventions for terminal output, and it is possible to adhere to those conventions by changing wterm, wterm ln, and wterm cr in this section.

define wterm(#) ≡ write(term out , #)

define wterm ln(#) ≡ write ln(term out , #)

define wterm cr ≡ write ln(term out )

define wlog (#) ≡ write(log file, #)

define wlog ln(#) ≡ write ln(log file, #)

define wlog cr ≡ write ln(log file)

§57  TEX82  PART 5: ON-LINE AND OFF-LINE PRINTING 25

57. To end a line of text output, we call print ln. ⟨ Basic printing procedures 57 ⟩ ≡ procedure print ln; { prints an end-of-line }

begin case selector of

term and log : begin wterm cr ; wlog cr ; term offset ← 0; file offset ← 0;

end; log only : begin wlog cr ; file offset ← 0;

end; term only : begin wterm cr ; term offset ← 0;

end; no print , pseudo, new string : do nothing ; othercases write ln(write file[selector ]) endcases; end; { tally is not affected }

See also sections 58, 59, 60, 62, 63, 64, 65, 262, 263, 518, 699, and 1355. This code is used in section 4.

58. The print char procedure sends one character to the desired destination, using the xchr array to map it into an external character compatible with input ln. All printing comes through print ln or print char . ⟨ Basic printing procedures 57 ⟩ +≡ procedure print char (s : ASCII code); { prints a single character }

label exit ; begin if ⟨ Character s is the current new-line character 244 ⟩ then

if selector < pseudo then begin print ln; return; end;

case selector of

term and log : begin wterm(xchr [s]); wlog (xchr [s]); incr (term offset ); incr (file offset );

if term offset = max print line then begin wterm cr ; term offset ← 0; end;

if file offset = max print line then begin wlog cr ; file offset ← 0; end;

end; log only : begin wlog (xchr [s]); incr (file offset );

if file offset = max print line then print ln;

end; term only : begin wterm(xchr [s]); incr (term offset );

if term offset = max print line then print ln; end;

no print : do nothing ; pseudo: if tally < trick count then trick buf [tally mod error line] ← s; new string : begin if pool ptr < pool size then append char (s);

end; { we drop characters if the string space is full } othercases write(write file[selector ], xchr [s]) endcases; incr (tally );

exit : end; 59. An entire string is output by calling print . Note that if we are outputting the single standard ASCII character , we could call print ( ), since = 99 is the number of a single-character string, as explained above. But print char ( ) is quicker, so TEX goes directly to the print char routine when it knows that this is safe. (The present implementation assumes that it is always safe to print a visible ASCII character.) ⟨ Basic printing procedures 57 ⟩ +≡ procedure print (s : integer ); { prints string s }

label exit ;

var j: pool pointer ; { current character code position }

nl : integer ; { new-line character to restore } begin if s ≥ str ptr then s ← { this can’t happen } else if s < 256 then

if s < 0 then s ← { can’t happen } else begin if selector > pseudo then

begin print char (s); return; { internal strings are not expanded } end;

if (⟨ Character s is the current new-line character 244 ⟩) then

if selector < pseudo then begin print ln; return; end;

nl ← new line char ; new line char ← −1; { temporarily disable new-line character } j ← str start [s]; while j < str start [s + 1] do

begin print char (so(str pool [j])); incr (j);

end; new line char ← nl ; return; end;

j ← str start [s];

while j < str start [s + 1] do begin print char (so(str pool [j])); incr (j); end;

exit : end;

60. Control sequence names, file names, and strings constructed with might contain ASCII code values that can’t be printed using print char . Therefore we use slow print for them: ⟨ Basic printing procedures 57 ⟩ +≡ procedure slow print (s : integer ); { prints string s }

var j: pool pointer ; { current character code position } begin if (s ≥ str ptr ) ∨ (s < 256) then print (s) else begin j ← str start [s];

while j < str start [s + 1] do begin print (so(str pool [j])); incr (j); end; end;

end; 61. Here is the very first thing that TEX prints: a headline that identifies the version number and format package. The term offset variable is temporarily incorrect, but the discrepancy is not serious since we assume that the banner and format identifier together will occupy at most max print line character positions. ⟨ Initialize the output routines 55 ⟩ +≡

wterm(banner );

if format ident = 0 then wterm ln(´ ␣ ␣ ␣ ´ )

else begin slow print (format ident ); print ln;

end; update terminal ;

62. The procedure print nl is like print , but it makes sure that the string appears at the beginning of a new line. ⟨ Basic printing procedures 57 ⟩ +≡ procedure print nl (s : str number ); { prints string s at beginning of line }

begin if ((term offset > 0) ∧ (odd (selector ))) ∨ ((file offset > 0) ∧ (selector ≥ log only )) then print ln; print (s); end;

63. The procedure print esc prints a string that is preceded by the user’s escape character (which is usually a backslash). ⟨ Basic printing procedures 57 ⟩ +≡ procedure print esc(s : str number ); { prints escape character, then s }

var c: integer ; { the escape character code }

begin ⟨ Set variable c to the current escape character 243 ⟩;

if c ≥ 0 then

if c < 256 then print (c); slow print (s); end;

64. An array of digits in the range 0 . . 15 is printed by print the digs . ⟨ Basic printing procedures 57 ⟩ +≡ procedure print the digs (k : eight bits ); { prints dig [k − 1] … dig [0] }

begin while k > 0 do begin decr (k); if dig [k] < 10 then print char ( + dig [k]) else print char ( − 10 + dig [k]); end;

end; 65. The following procedure, which prints out the decimal representation of a given integer n, has been written carefully so that it works properly if n = 0 or if (−n) would cause overflow. It does not apply mod or div to negative arguments, since such operations are not implemented consistently by all Pascal compilers. ⟨ Basic printing procedures 57 ⟩ +≡ procedure print int (n : integer ); { prints an integer in decimal form }

var k: 0 . . 23; { index to current digit; we assume that n < 10^{23} }

m: integer ; { used to negate n in possibly dangerous cases } begin k ← 0; if n < 0 then

begin print char ( );

if n > −100000000 then negate(n)

else begin m ← −1 − n; n ← m div 10; m ← (m mod 10) + 1; k ← 1; if m < 10 then dig [0] ← m else begin dig [0] ← 0; incr (n);

end; end;

end; repeat dig [k] ← n mod 10; n ← n div 10; incr (k); until n = 0; print the digs (k); end;

66. Here is a trivial procedure to print two digits; it is usually called with a parameter in the range 0 ≤ n ≤ 99. procedure print two(n : integer ); { prints two least significant digits }

begin n ← abs (n) mod 100; print char ( + (n div 10)); print char ( + (n mod 10)); end;

67. Hexadecimal printing of nonnegative integers is accomplished by print hex . procedure print hex (n : integer ); { prints a positive integer in hexadecimal form }

var k: 0 . . 22; { index to current digit; we assume that 0 ≤ n < 16^{22} } begin k ← 0; print char ( ); repeat dig [k] ← n mod 16; n ← n div 16; incr (k); until n = 0; print the digs (k); end;

68. Old versions of TEX needed a procedure called print ASCII whose function is now subsumed by print . We retain the old name here as a possible aid to future software archæologists.

define print ASCII ≡ print 69. Roman numerals are produced by the print roman int routine. Readers who like puzzles might enjoy trying to figure out how this tricky code works; therefore no explanation will be given. Notice that 1990 yields , not . procedure print roman int (n : integer );

label exit ; var j, k: pool pointer ; { mysterious indices into str pool }

u, v: nonnegative integer ; { mysterious numbers } begin j ← str start [ ]; v ← 1000; loop begin while n ≥ v do

begin print char (so(str pool [j])); n ← n − v;

end; if n ≤ 0 then return; { nonpositive input produces no output } k ← j + 2; u ← v div (so(str pool [k − 1]) − ); if str pool [k − 1] = si ( ) then

begin k ← k + 2; u ← u div (so(str pool [k − 1]) − ); end;

if n + u ≥ v then begin print char (so(str pool [k])); n ← n + u; end

else begin j ← j + 2; v ← v div (so(str pool [j − 1]) − );

end; end;

exit : end;

70. The print subroutine will not print a string that is still being created. The following procedure will. procedure print current string ; { prints a yet-unmade string }

var j: pool pointer ; { points to current character code } begin j ← str start [str ptr ]; while j < pool ptr do

begin print char (so(str pool [j])); incr (j);

end; end;

71. Here is a procedure that asks the user to type a line of input, assuming that the selector setting is either term only or term and log . The input is placed into locations first through last − 1 of the buffer array, and echoed on the transcript file if appropriate.

This procedure is never called when interaction < scroll mode. define prompt input (#) ≡

begin wake up terminal ; print (#); term input ; end { prints a string and gets a line of input }

procedure term input ; { gets a line from the terminal } var k: 0 . . buf size; { index into buffer } begin update terminal ; { Now the user sees the prompt for sure } if ¬input ln(term in, true) then fatal error ( ␣ ␣ ␣ ␣ ␣ ); term offset ← 0; { the user’s line ended with ⟨return⟩ } decr (selector ); { prepare to echo the input } if last ≠ first then

for k ← first to last − 1 do print (buffer [k]); print ln; incr (selector ); { restore previous status } end;