summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/doc/generic/knuth/errata/tex82.bug
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2009-05-23 00:17:44 +0000
committerKarl Berry <karl@freefriends.org>2009-05-23 00:17:44 +0000
commitcfd5aee575d84f09834ffcf92e5ad51c7cac38ca (patch)
tree9bb827d3c2a6f3d57148a6b4c9312a9f559acaaa /Master/texmf-dist/doc/generic/knuth/errata/tex82.bug
parentac7c62d7555a6cebca926dc51ebc39cb235982db (diff)
move tex-refs and knuth out of texmf-doc
git-svn-id: svn://tug.org/texlive/trunk@13409 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/doc/generic/knuth/errata/tex82.bug')
-rw-r--r--Master/texmf-dist/doc/generic/knuth/errata/tex82.bug9094
1 files changed, 9094 insertions, 0 deletions
diff --git a/Master/texmf-dist/doc/generic/knuth/errata/tex82.bug b/Master/texmf-dist/doc/generic/knuth/errata/tex82.bug
new file mode 100644
index 00000000000..607ffff358d
--- /dev/null
+++ b/Master/texmf-dist/doc/generic/knuth/errata/tex82.bug
@@ -0,0 +1,9094 @@
+This file has been updated periodically ever since TeX82 was born;
+it has been summarized in "The errors of TeX," Software Practice &
+Experience, July 1989. Entries are in chronological order;
+thus the most recent news (including all bugfixes made since
+that article was published) appears at the bottom of the file.
+
+[Add 519 to these entry numbers to get the corresponding number in
+the published article. The article also translates all module
+numbers to their final form; what you are about to read is
+"authentic source material" from the early days before TeX converged.]
+
+-------------------------------------------------------------------------------
+
+First updates to the TeX82 listing published in September, 1982.
+(These changes were included in the original Version 0 of TeX, but they
+were discovered after the listing went to press.)
+
+1. Module 943, line 6 (bug discovered 9/28)
+change "if cur_cmd=char_num then" to
+ if (cur_cmd=letter) or (cur_cmd=other_char) then r:=qi(cur_chr)
+ else if cur_cmd=char_num then
+
+2. "pause" changed to "pausing" and "pause_code" to "pausing code", throughout.
+
+3. Module 719, lines 8 and 11 (bug discovered 9/28)
+insert "rule_save:=overfull_rule; overfull_rule:=0;" after "save_ptr-2;"
+insert "overfull_rule:=rule_save;" before "q:=p+list_offset;"
+and insert a declaration of "overfull_rule: scaled" in module 716.
+
+4. Module 1128, lines 6 and following (bug discovered 9/28)
+change "while n<>0 do" to "loop"
+change "goto done" to
+ begin scan_left_brace; new_save_level(false_group);
+ goto done;
+ end
+change "... return 1130>;" to
+ ... return 1130>
+ else if n=0 then
+ begin new_save_level(case_group); goto done;
+ end;
+and change "done: ... (false_group);" to "done:".
+
+5. Module 182, line 10 (suggestion by DRF on 9/30)
+change "0.0" to "?.?"
+
+6. Module 682, new definition of math_spacing (decision of 10/2)
+"0234000122*4000133**3**344*0400400*000000234000111*4111112341011"
+
+7. Module 684, new code for case "4" (decision of 10/2)
+"4": if cur_style<script_style then x:=thick_mu_skip_code else x:=0;
+[Also in module 682 line 7, say "...a conditional thick space (\nonscript..."]
+
+8. Module 11, trie_size changed from 7000 to 8000 because of new
+improved (but longer) hyphenation patterns (10/4)
+
+9. Module 453, forgot to change this when 454 changed (noted by DRF 10/6)
+format_default_length=20 (not 22)
+format_area_length=11 (not 13)
+
+ Changes to TEX.WEB made after Version 0 was released in October, 1982.
+
+Since copies of TEX.WEB are not supposed to be edited, there are
+two ways to make your version of TeX bug-free:
+ 1. Get a new copy of TEX.WEB.
+ 2. Put the corrections into your change file(s).
+Some people will find (1) easier than (2), except the main TeX sources at
+SCORE won't be updated quite as fast as the sources at SAIL (which have to
+be translated into ascii before they are sent to the outside world).
+Actually (2) will be quite easy, unless the list of changes becomes quite
+long, so it is the recommended procedure. In case (2) it would be useful
+to include a comment like "this is fix number xx" (using the numbering
+scheme in this file), so that the change could readily be deleted at some
+future time.
+
+10. Module 857 line -5 (bug discovered 10/8/82 by HWT)
+change it to: if h>0 then decr(h) else h:=trie_op_hash_size;
+
+11. Module 457 line 3 (typo discovered 10/9/82)
+change `\.!' to `\.\&'
+
+12. Module 1245 line 8 (bug fixed 10/9/82, discovered by MMD)
+insert the following between "begin" and "if":
+if format_ident<>0 then initialize; {erase preloaded format}
+
+** Version 0.1 incorporates the above changes.
+
+13. This is an extension to the language, put in to satisfy people who
+objected to the fact that \write (and \openout and \closeout) only
+caused action after being deferred to the next \shipout. Some applications
+call for immediate output, hence a new feature: \immediate followed by
+\openout or \write or \closeout causes the output action to take place
+without delay. For example, \immediate\write{x} is equivalent to
+\shipout\vbox{\write{x}} except that the latter also puts an empty
+page into the DVI file.
+
+The extension requires the following new code:
+13a. Insert `\immediate' after `\closeout' in module 1248.
+13b. Define immediate_code=4 and include the following in module 1252:
+ primitive("immediate",extension,immediate_code);
+13c. Include the following in module 1254:
+ immediate_code:print_esc("immediate");
+13d. And, in module 1256:
+ immediate_code:@<Implement \.{\\immediate}@>;
+13e. Finally, there's a new module inserted after old module 1280.
+Here is the WEB coding for this module:
+
+@ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+@^recursion@>
+
+@<Implement \.{\\immediate}@>=
+begin get_nc_token;
+if (cur_cmd=extension)and(cur_chr<=close_node) then
+ begin p:=tail; do_extension; {append a whatsit node}
+ out_what(tail); {do the action immediately}
+ flush_node_list(tail); tail:=p; link(p):=null;
+ end
+else back_input;
+end;
+
+** Version 0.2 incorporates the above changes.
+
+14. Like change 11, this one doesn't affect the program, it just improves
+the documentation: Insert the following definitions in module 106:
+define set_glue_ratio_zero(#) == #:=0.0 {assign representation of zero ratio}
+define set_glue_ratio_one(#) == #:=1.0 {assign representation of unit ratio}
+
+These macros are now introduced in a dozen or so future modules, thereby
+eliminating most of the system-dependent changes needed elsewhere for ratios.
+(Note: I also changed 0 to 0.0 in two places of module 182, where a glue_ratio
+comparision was being made.)
+
+15. Change of module 576 (discovered by HWT, 10/14/82)
+"hd:quarterword" should be "hd:eight_bits".
+(The same error occurs in module 522, but in that module the remedy is
+simply to delete the declaration of hd, since this variable is no longer used.)
+
+16. A most embarrassing bug (discovered by DRF, 10/14/82)
+Replace module 531 by:
+
+@ A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+@p procedure dvi_pop(@!l:integer);
+begin if (l=dvi_offset+dvi_ptr)and(dvi_ptr>0) then decr(dvi_ptr)
+else dvi_out(pop);
+end;
+
+Now we need to make a few changes to subsequent modules:
+16a. In 549, after "incr(cur_s)", insert
+ if cur_s>0 then dvi_out(push);
+and before "decr(cur_s)", insert
+ if cur_s>0 then dvi_pop(save_loc);
+
+16b. Delete "dvi_out(push);" and "dvi_pop;" from modules 553, 558, 562, 567.
+
+16c. Change module 559 just as in 16a.
+
+17. Module 605, line 6 (discovered 10/15/82)
+The test should be "prev_depth>ignore_depth"
+
+** Version 0.3 incorporates the above changes.
+
+18. Module 11 (noticed by WLS, 10/16/82)
+Delete the definition of align_size (it's harmless but never used)
+
+19. (This change and the next cause major changes to the TRIP output;
+file TRIP.LOG and its relatives are being kept up to date on area [tug,dek].)
+The change avoids error messages when vpackage is called during output.
+Such messages can occur when there was no error, because the page is being
+boxed without the \skip glue from its insertions; so they should be omitted.
+The user who really wants such messages can still get them by saying
+"\setbox255=\vbox to 1ht255{\unbox255}".
+In module 903, insert the followng before the declaration of "wait":
+ save_vbadness:integer; {saved value of |vbadness|}
+ save_vfuzz: scaled; {saved value of |vfuzz|}
+Then in module 924, insert
+ save_vbadness:=vbadness; vbadness:=inf_bad;
+ save_vfuzz:=vfuzz; vfuzz:=max_dimen; {inhibit error messages}
+before the call on vpackage, and
+ vbadness:=save_vbadness; vfuzz:=save_vfuzz;
+after.
+
+20. Module 917 computes |page_size| improperly. (Noticed 10/21/82)
+Delete the statement "page_so_far[1]:=page_so_far[1]+width(q);"
+and change the preceding statement to:
+ page_size:=page_size-h-width(q);
+(The comment about page_so_far in the first paragraph of module 895 is
+correct; I mistakenly introduced a bug in module 917 some months after
+writing the first draft of the code, believing that I was making the
+algorithm more elegant or something.)
+
+** Version 0.4 incorporates the above changes.
+
+21. Since TeX82 applied to (the "woven" documentation) TEX.TEX uses
+about 11500 words of variable-size memory, I'm increasing hi_mem_size
+(in module 12) from 12000 to 13000. Actually, I recommend using considerably
+larger values for mem_max and hi_mem_size, whenever possible.
+
+22. Addition of the \boxmaxdepth parameter (10/22/82):
+This involves renumbering hfuzz_code through dimen_pars, in module 234,
+to numbers 9 through 18; inserting the lines
+ @d box_max_depth_code=8 {maximum depth of explicit vboxes}
+ @d box_max_depth==dimen_par(box_max_depth_code)
+ box_max_depth_code:print_esc("boxmaxdepth");
+to module 234 and
+ primitive("boxmaxdepth",assign_dimen,box_max_depth_code);
+to module 235; and changing the call on vpack in module 996 to
+ vpackage(link(head),saved(2),saved(1),box_max_depth);
+
+** Version 0.5 incorporates the above changes.
+
+23. Module 1224, line 4 (bug found by GMK/HWT on 10/26/82)
+change "(k+x>eqtb_size)" to "(k+x>eqtb_size+1)"
+
+24. Module 247, line 13 (bug found 10/26/82)
+(bug was reflected in TRIP.LOG but not noticed)
+change "ch_code(p)" to "ch_code(p-single_base)"
+
+25. Module 670, line 18 (bug found 10/27/82)
+interchange the statements "fetch(...)" and "math_type(...):=..."
+(since the fetch routine sometimes has a side-effect of changing math_type)
+ A major change (Version 0.6) made on October 28:
+The following list of changes counts as "number 26" on the list.
+Fonts now have identifiers instead of code numbers; the "\:" primitive
+has disappeared; and there are associated new features for "\the".
+
+a. In module 11, delete bad_font_code.
+b. In modules 170 and 172, delete print_esc(":"), and change
+ `print_int(font_code' to `sprint_cs(font_ident'.
+c. In module 205, the comment for set_font is revised.
+d. In module 217, there are now five locations for control sequences
+ that are perpetually defined; undefined_control_sequence
+ is therefore defined to be frozen_control_sequence+5.
+e. In module 248, sprint_cs is now included among <Basic printing procedures>.
+f. Delete the primitive ":" in modules 250 and 251.
+g. Add to module 376: <Declare procedures that scan font-related stuff>
+h. In module 377, seven levels are now distinguished; we define
+ font_val=4, ident_val=5, tok_val=6.
+i. In module 380, delete the previous cases for def_family and set_font,
+ and the following takes the place of case assign_toks:
+ assign_toks,def_family,set_font,def_font: <Fetch a token list or
+ font number or font identifier, provided that level=tok_val>;
+j. Module 382 (which now has a new name) has this new ending:
+ else if cur_cmd=assign_toks then scanned_result(equiv(m))(tok_val)
+ else <Fetch a font number or a font identifier>
+k. Module 389 becomes
+ <Fetch a font number or a font identifier>=
+ if cur_cmd=set_font then scanned_result(cur_chr)(font_val)
+ else if cur_cmd=def_font then
+ scanned_result(font_ident[cur_font])(ident_val)
+ else begin scan_four_bit_int;
+ scanned_result(font_ident[equiv(m+cur_val)])(ident_val);
+ end
+l. In module 393, the relation `<>tok_val' becomes `<=mu_val'.
+m. In module 426, the relation `=tok_val' becomes '>=ident_val'.
+ Also add a new case to the case statement:
+ font_val: begin print(font_name[cur_val]);
+ if font_size[cur_val]<>font_dsize[cur_val] then
+ begin print(" at "); print_scaled(font_size[cur_val]);
+ print("pt");
+ end;
+ end;
+n. The body of module 427 becomes:
+ begin p:=temp_head; link(p):=null;
+ if cur_val_level=ident_val then store_new_token(cs_token_flag+cur_val)
+ else if cur_val<>null then
+ begin r:=link(cur_val); {do not copy the reference count}
+ while r<>null do
+ begin store_new_token(info(r)); r:=link(r);
+ end;
+ end;
+ the_toks:=p;
+ end
+o. In module 480, delete user_font_code; there's a new comment:
+ When the user defines \font\f, say, TeX assigns an internal number
+ to the user's font \f. For example, if this internal number is 13,
+ we will have font_ident[13]=p and equiv(p)=13, where p is the eqtb
+ location of the control sequence \f.
+p. In module 481, delete the declaration of font_number, and
+ replace the declaration of font_code by
+ font_ident:array[internal_font_number] of pointer;
+q. New stuff in module 483 (also delete references to font_number, font_code):
+ define bad_font_ident=frozen_control_sequence+4 {denotes a null font}
+ font_name[undefined_font]:="nullfont";
+ font_ident[undefined_font]:=bad_font_ident;
+ text(bad_font_ident):="nullfont"; eq_level(bad_font_ident):=level_one;
+ eq_type(bad_font_ident):=set_font;
+ equiv(bad_font_ident):=undefined_font;
+r. In module 490, parameter u is new of type pointer, and the
+ read_font_info subroutine is changed to a function that
+ returns an internal_font_number. There's a new local variable
+ g:internal_font_namber; {the number to return}
+ and we set g:=undefined_font immediately upon entering read_font_info.
+ Also set read_font_info:=g just before exiting.
+s. In module 491, print_int(u) becomes sprint_cs(u).
+t. Delete the statements involving font_code and font_number in module 506,
+ and set g:=f at the end of that module.
+u. The body of module 507 becomes:
+ <Declare procedures that scan font-related stuff>=
+ procedure scan_font_ident;
+ var f:internal_font_number;
+ begin <Get the next non-blank non-call...>;
+ if cur_cmd=set_font then f:=cur_chr
+ else begin print_nl("! Missing font identifier");
+ help2("I was looking for a control sequence whose")
+ ("current meaning has been defined by \font.");
+ back_error; f:=undefined_font;
+ end;
+ end;
+v. New beginning of module 508:
+ The following routine is used to implement `\texinfo f n'.
+ The boolean parameter writing is set true if the calling program
+ intends to change the parameter value.
+ <Declare procedures that scan font-related stuff>=
+and "scan_font_number" is changed to "scan_font_ident".
+w. In module 509, "print_int(font_code" becomes "sprint_cs(font_ident".
+ Also "font code is defined" becomes "\font is loaded" in the help.
+x. In module 940, the first line of help is changed to
+ "You have to specify a font identifier,"
+y. The set_font case in module 1138 reduces to
+ set_font: define(cur_font_loc,data,cur_chr);
+z. In module 1153, delete all the complicated stuff starting with "scan_int"
+ and substitute simply this:
+ scan_font_ident; define(p,data,cur_val);
+ end;
+aa. Change new_font to new_font(a) in module 1169, and add parameter
+ (a:small_number)
+ in module 1170. Also add the label common_ending, and declare
+ variable u to have type pointer. The code beginning with "scan_int"
+ is changed to the following:
+ get_token;
+ if cs_ptr=0 then
+ <Complain about the missing control sequence and return>;
+ u:=cs_ptr; scan_optional_equals; scan_file_name;
+ <Scan the ``at'' size specification>;
+ <If this font has already been loaded, set f to the internal
+ font number and goto common_ending>;
+ f:=read_font_info(u,cur_name,cur_area,s);
+ common_ending: define(u,set_font,f); font_ident[f]:=u;
+ exit:end;
+bb. New module 1172:
+ When the user gives a new identifier to a font that was previously
+ loaded, the new value becomes the font_ident of record.
+ Font names `xyz' and `XYZ' are considered to be different.
+ <If this font has already been loaded...>=
+ for f:=font_base+1 to font_ptr do
+ if [the test previously in module 1174] then goto common_ending
+cc. New module 1173:
+ <Complain about the missing control sequence and return>=
+ begin print_nl("! A font identifier must be a control sequence");
+ help2("You should say, e.g., `\font\f=fontfilename'.")
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end
+dd. New module 1174:
+ <Cases of print_cmd_chr...>=
+ set_font:begin print("select font "); print(font_name[chr_code]);
+ if font_size[chr_code]<>font_dsize[chr_code] then
+ begin print(" at "); print_scaled(font_size[chr_code]);
+ print("pt");
+ end;
+ end;
+ee. Delete the comment at the beginning of module 1227, and delete the
+ loop for k:=0 to bad_font_code-1, and delete dump_int(undefined_font).
+ff. Delete the repeat loop in module 1228.
+gg. Change font_code to font_ident in module 1229, and change
+ print_int(font_code to sprint_int(font_ident.
+hh. In module 1230,
+ begin undump(single_base)(undefined_control_sequence)(font_ident[k]);
+ undump_qqqq(font_check[k]); [and continue as before]
+
+The changes above have been incorporated into Version 0.6.
+ The ill-fated Version 0.7
+
+27. (Here's a change that I retracted shortly after making it, since I
+discovered that the source was flaky after all; and I also found a reliable
+source [NBS Circular 570] confirming my original information. I include
+the note here only for historical purposes...)
+
+"After years of searching, I've finally found a definitive definition of
+the printer's point; and (unfortunately) my previous conjecture was wrong.
+The truth is that 83pc=35cm, exactly; so I am changing TeX to conform.
+This means some changes in the comments of modules 101 and 517, and the
+following changes to the program:
+
+"27a. In modules 547 and 571, the appropriate DVI numbers are now:
+ dvi_four(109375); dvi_four(2039808); {conversion ratio for sp}
+"27b. The guts of module 418 become:
+ if scan_keyword("in") then set_conversion(63246)(875)
+ else if scan_keyword("pc") then set_conversion(12)(1)
+ else if scan_keyword("cm") then set_conversion(996)(35)
+ else if scan_keyword("mm") then set_conversion(498)(175)
+ else if scan_keyword("bp") then set_conversion(10541)(10500)
+ else if scan_keyword("dd") then set_conversion(996)(931)
+ else if scan_keyword("cc") then set_conversion(11952)(931)
+ else..."
+
+*** Version 0.7 of TeX incorporated the changes above (82/10/30);
+this version was withdrawn on 82/11/2.
+ Changes subsequent to Version 0.6
+
+28. The experience with #27 did lead me to one improvement, thanks
+to Chuck Bigelow, with respect to Didot points. (11/4/82)
+Add the following comment to module 418:
+ According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+ this agrees well with the value $\rm1000.333/,mm$ cited by Bosshard
+ in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+And change two lines of the code there as follows:
+ else if scan_keyword("dd") then set_conversion(1238)(1157)
+ else if scan_keyword("cc") then set_conversion(14856)(1157)
+
+29. The new font material is made more robust by ensuring that \the\font
+always returns a pointer to a control sequence whose command code is
+set_font. (Change 11/4/82)
+
+29a. The code changed in 26aa is changed again, to the following:
+ <If the next token isn't a control sequence, issue a complaint and
+ return; otherwise set u:=cs_ptr, and set hash_used to the
+ location of a ``frozen'' copy of the new font identifier>;
+ define(u,set_font,undefined_font);
+ scan_optional_equals; scan_file_name;
+ <Scan the ``at'' size specification>;
+ <If this font has already been loaded, set f to the internal
+ font number and goto common_ending>;
+ f:=read_font_info(u,cur_name,cur_area,s);
+ common_ending: equiv(u):=f; geq_define(hash_used,set_font,f);
+ font_ident[f]:=hash_used;
+ exit:end;
+29b. Instead of 26cc, here's the new module 1173:
+ We reserve a special control sequence for the font identifier;
+ this one cannot be redefined by the user, so it is safe to
+ return it as a value of \the\font.
+ <If the next token isn't a control sequence...>=
+ get_token;
+ if cs_ptr<hash_base then
+ begin print_nl(
+ "! A font identifier must be a multiletter control sequence");
+ help2("You should say, e.g., `\font\ffn=fontfilename'.")
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end;
+ repeat if hash_is_full then overflow("hash size",hash_size);
+ decr(hash_used);
+ until text(hash_used)=0; {search for an empty location in hash}
+ u:=cs_ptr; text(hash_used):=text(u); {copy the name}
+ stat incr(cs_count); tats
+
+30. Module 993, replace lines 5 to 7 (11/5/82):
+else begin if k=vmode then new_save_level(vbox_group)
+ else begin new_save_level(vtop_group); k:=vmode;
+ end;
+ if looseness<>0 then eq_word_define(int_base+looseness_code,0);
+ if hanging_indent<>0 then
+ eq_word_define(dimen_base+hanging_indent_code,0);
+ if par_shape_ptr<>null then eq_define(par_shape_loc,shape_ref,null);
+ end;
+
+31. Module 403, replace line 5 (11/6/82):
+if cur_tok<cs_token_flag then
+ begin cur_val:=cur_chr;
+ if cur_cmd<=right_brace then
+ if cur_cmd=right_brace then incr(align_state)
+ else decr(align_state);
+ end
+
+32. Improvements to error recovery for bad alignments (11/6/82):
+In module 1039, replace "begin align_error; goto reswitch; end" by "align_error"
+In module 1040, delete "cur_cmd:=left_brace;" and "cur_cmd:=right_brace;"
+ and change "error" to "back_error"
+In module 1041, delete "cur_cmd:=relax;"
+
+33. More power to \let (11/8/82):
+33a. Module 1141 becomes:
+ <Assignments>+=
+ let: begin get_token;
+ if cs_ptr<>0 then <Carry out the \let operation>
+ else begin print_nl
+ ("! You can use \let only with control sequences");
+ help2("I'm not \let-ting anything change here,")
+ ("since I can only do things like `\let\a=b'.");
+ back_error;
+ end;
+ end;
+33b. Module 1142 becomes:
+ <Carry out the \let...>=
+ begin p:=cs_ptr;
+ repeat get_token;
+ until cur_cmd<>spacer;
+ if cur_tok=other_token+"=" then
+ begin get_token;
+ if cur_cmd=spacer then get_token;
+ end;
+ if cur_cmd>=call then add_token_ref(cur_chr);
+ define(p,cur_cmd,cur_chr);
+ end
+
+34. This change helps you see an undefined control sequence
+in certain unusual cases (discovered by JDH, 11/8/82):
+Add the clause "(base_ptr=input_ptr) or" to module 294, line 2.
+
+35. Here's an improvement in the formula for demerits; previously
+more weight was given to minimizing bad spacing on lines with penalties,
+so that (slightly loose hyphenated line)(OK line) was considered worse
+than (OK hyphenated line)(quite loose line). (fixed 11/8/82)
+Change lines 2--7 of module 772 to the following:
+ d:=line_penalty+b; d:=d*d;
+ if pi<>0 then
+ if pi>0 then d:=d+pi*pi
+ else if pi>eject_penalty then d:=d-pi*pi;
+
+36. Minor change to save buffer space in non-INITEX (11/10/82):
+Enclose the declaration of pool_file in module 50 by init...tini.
+
+37. Minor improvement in format for the context of error messages (11/13/82):
+37a. Module 289, type "inserted" split into "backed_up=3", "inserted=4"
+and the other type numbers increase: "macro=5", etc.
+37b. In module 296, backed_up: if loc=null then print_nl("<recently read>")
+ else print_nl("<to be read again> ");
+ inserted: print_nl("<inserted text> ");
+37c. Minor changes to comments in modules 289 and 293.
+37d. Change "inserted" to "backed_up" in module 294.
+37e. Add definition "back_list(#)==begin_token_list(#,backed_up)" in module 305.
+37f. In module 306, first test should be ">=backed_up", second "<=inserted".
+37g. Change ins_list to back_list in modules 307, 318, 375, 1195.
+37h. Add definition to module 308:
+ ins_error==begin back_error; token_type:=inserted; end
+37i. Change back_error to ins_error in modules 362, 1040, 1046.
+37j. Change "<to be read again>" to "<inserted text>" in module 973 help.
+
+38. Module 407, the error message is changed (11/11/82) to:
+"! Missing number, treated as zero".
+
+39. Fix anomaly when hbadness or vbadness is small (11/11/82):
+Module 593, line 2, "if (-x-total_shrink[normal]>hfuzz) or (hbadness<100)"
+Module 603, line 2, "if (-x-total_shrink[normal]>vfuzz) or (vbadness<100)"
+
+40. Added the \tokens primitive (11/13/82):
+In module 223, defined tokens_loc and tokens (analogous to every_par).
+In module 225, defined the primitive.
+In module 1146, changed the comment to include tokens_loc as a possibility.
+
+41. Change get_nc_token to get_token in module 374 (11/13/82).
+(In particular \def\foo{...}\foo won't say "undefined c.s." now!)
+(This change later retracted during debugging; it was found that
+"endv" aborts the job, so this cure was worse than the disease. Then
+it was re-established as part of the major change to conditionals,
+because endv needed to be more robust anyway.)
+
+42. In module 699, we make \span expand in a preamble (11/13/82).
+Change lines -7 through -3 to:
+ begin get_nc_token; goto reswitch;
+ end;
+
+43. Modules 321 and 322 have been altered for better efficiency (11/12/82)
+and better exposition; the three conditions of module 322 are now
+in separate if tests.
+
+44. Frozen control sequences are now unredefinable. (11/16/82)
+A new procedure get_r_token has been introduced to give uniform error
+messages for \def, \let, \read, \font, \mathchardef, and to make them
+incapable of changing the frozen equivalents.
+
+Incidentally, get_nc_token is changed to get_x_token; and ch_code
+becomes cat_code.
+ A major change (Version 0.8) made on November 14--16:
+Conditional statements are taken from semantics to syntax.
+
+This change, which counts as number 45 on the list, made it necessary
+to renumber the modules. And it was such a drastic change, the
+differences can only be sketched here, using the old module numbers
+for reference. A variety of other things were cleaned up because this
+change made it more natural for them to be handled differently.
+
+45a. Module 158, inserted a permanently empty token list called null_list.
+45b. Module 204, deleted if_test, case_branch, else_code, convert;
+ inserted end_cs_name.
+45c. Module 206, inserted if_test, fi_or_else, cs_name, convert, and
+ end_template (the latter comes after long_outer_call).
+45d. Module 217, there are now nine frozen control sequences; also null_cs.
+45e. Modules 247 and 248, must be able to print a null control sequence.
+45f. Module 268 (leave_transparent_group) disappears; comments in the
+ previous modules also are appropriately simplified.
+45g. Module 273, delete endv_token.
+45h. Modules 277 and 278, delete references to endv (it no longer appears).
+45i. After module 281, a procedure show_cur_cmd_chr takes the
+ code for tracing commands from module 937.
+45j. Module 317 now forbids \outer control sequences when skipping,
+ and inserts \fi in front of them, for error recovery.
+45k. Module 334 now uses null_cs if carriage-return is an escape.
+45l. Module 343 no longer has endv test.
+45m. Module 344, major extensions to expand_calls make it several modules
+ longer. It saves global variables like cur_val. It changes
+ end_template to endv. It processes cs_name and convert.
+ It processes if_test by calling the "conditional" procedure.
+ It processes fi_or_else by checking their legality, and (if legal)
+ updating the if stack.
+45n. Module 373 (pass_block) disappears.
+45o. After module 400, radix and cur_val and cur_val_level are initialized.
+45p. Module 428 changes so that conv_toks is a procedure rather than a
+ function; this procedure is invoked by expand_calls.
+45q. Modules 436 and 1032, delete reference to conv_toks.
+45r. After module 443, the entire part 48 moves to this part of the program,
+ and the parts are renumbered accordingly. \case is changed to
+ \ifcase, and it resides under the if_test command.
+ New routines are inserted to maintain a linked stack that records
+ the current state of conditionals. There's a procedure
+ pass_text that skips text while looking for \fi or \else or \or
+ on level zero with respect to \if...\fi nesting. The previous
+ routines for skip control via handle_right_brace are eliminated,
+ and the new ones are somewhat simpler. The new \ifcat is mostly
+ combined with the old \if; they no longer ignore spaces.
+ The scanner_status is set to normal while processing \ifx.
+ \ifx will consider \a to equal 0 after \let\a=0.
+45s. Modules 701, 707: endv_token is replaced by a token for a frozen
+ control sequence whose command code end_template makes it behave
+ like an outer_call. After expand_calls, it is converted to another
+ frozen control sequence, whose command code is endv. This two-step
+ facilitates error recovery, instead of giving a fatal error stop.
+45t. Module 874, remove "pass_block(1); goto done".
+45u. After module 1047, routine for end_cs_name prints an error message.
+45v. Module 1049, get_nc_token becomes get_token. (else $\ifmmode fails)
+45w. Module 1202 gets another case (end_template).
+45x. Module 1243, new warning is printed if the if stack isn't empty.
+ Changes after version 0.8
+
+46. Declare c : ascii_code in module 825 (noted by DRF, 11/21/82)
+
+47. Module 776 lines 4-6 (suggested by DRF, 11/21/82)
+threshold:=pretolerance;
+if threshold>=0 then
+ begin stat if tracing_stats>2 then
+ begin begin_diagnostic; print_nl("@@firstpass");
+ end; tats
+ second_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+ end;
+
+48. Protection for DVI files (added 11/16/82)
+Replace lines 2 and 3 of module 570 by:
+ <Update the values of max_h and max_v; but if the page is too large,
+ goto done>;
+Move the statement "dead_cycles:=0" from module 570 to module 568.
+Declare "label done;" in module 568.
+And add the following new module after 570:
+
+@ Sometimes the user will generate a huge page because other error messages
+are being ignored. Such pages are not output to the \.{dvi} file, since they
+may confuse the printing software.
+
+@<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
+if (height(p)>max_dimen)or(depth(p)>max_dimen)or(width(p)>max_dimen) then
+ begin print_nl("! Huge page cannot be shipped out");
+ help2("The page just created is more than 18 feet tall or")@/
+ ("more than 18 feet wide, so I suspect something went wrong.");
+ error;
+ if tracing_output=0 then
+ begin begin_diagnostic;
+ print_nl("The following box has been deleted:");
+ show_box(p);
+ end_diagnostic(true);
+ end;
+ goto done;
+ end;
+if height(p)+depth(p)>max_v then max_v:=height(p)+depth(p);
+if width(p)>max_h then max_h:=width(p)
+
+49. New features \everymath and \everydisplay (12/2/82).
+Make changes analogous to those for "\tokens" (see change 40).
+Put the following just before the end of module 1050:
+ if every_math<>null then begin_token_list(every_math,every_math_text);
+And put the following just before the end of module 1056:
+ if every_display<>null then
+ begin_token_list(every_display,every_display_text);
+
+50. New feature \futurelet (12/2/82):
+Module 1141 changes again. (Also, "let" and "futurelet" share cmd code "let".)
+let: begin n:=cur_chr;
+ get_r_token; p:=cs_ptr;
+ if n=normal then
+ begin repeat get_token;
+ until cur_cmd<>spacer;
+ if cur_tok=other_token+"=" then
+ begin get_token;
+ if cur_cmd=spacer then get_token;
+ end;
+ end
+ else begin get_token; q:=cur_tok; get_token; back_input;
+ cur_tok:=q; back_input; {look ahead, then back up}
+ end; {|back_input| doesn't affect |cur_cmd|, |cur_chr|}
+and continue with "if cur_cmd>=call", as before.
+
+*** The changes above have been incorporated into version 0.9 of TeX.
+ Changes after version 0.9
+
+51. new \endinput primitive (suggested by FY, 12/7/82):
+\input and \endinput both use the same command code. The cases in module
+955 are deleted and replaced by those in module 965 (which disappears),
+because \input is now allowed in any mode. The code for any_mode(input)
+is "if cur_chr=0 then start_input else force_eof:=true", and it moves from
+module 952 to just before module 1197. The global variable force_eof
+is initially false, and module 340 becomes (after first:=start;)
+ if not force_eof then
+ begin if input_ln.... else force_eof:=true;
+ end;
+ if force_eof then begin print_char(")"); force_eof:=false;...
+
+52. Module 1005, line 4 (12/8/82)
+change "if align_state<0" to "if (mode<0)or(align_state<0)"
+(This avoids embarrassing case where TeX says "type a command or say \end"
+but when you type \end it says "You can't use \end in restricted horiz mode".)
+
+53. Patch to the new code for \csname (12/21/82)
+After eq_type(cs_ptr):=relax, also say equiv(cs_ptr):=256.
+(This corrects a bug that would appear only if \csname occurs right
+after a file name.)
+
+54. Change 47 introduced a bug when tracingonline=0 (12/20/82)
+when omitting firstpass, also do "if tracing_stats>2 then begin_diagnostic;"
+
+55. \hskip -1pt plus 2pt was parsed as \hskip -(1pt plus 2pt)! (12/20/82)
+In module 421, before line -4, insert the following:
+ if negative then
+ begin cur_val:=-cur_val; negative:=false;
+ end;
+But then realize that "negative" is always false on line -2; simplify.
+
+56. Cosmetic change to paragraph statistics (12/23/82)
+tight_fit..very_loose_fit codes have been renumbered very_loose_fit..tight_fit.
+
+57. Module 729 changes to make TeX language more consistent (12/23/82):
+else if type(tail)<>glue_node then tail_append(new_penalty(inf_penalty))
+else begin type(tail):=penalty_node; delete_glue_ref(glue_ptr(tail));
+ flush_node_list(leader_ptr(tail)); penalty(tail):=inf_penalty;
+
+58. Commas are allowed as alternates to radix points. (12/23/82)
+define continental_point_token=other_token+"," {decimal point, Eurostyle}
+in module 400, and insert the following twice in 409:
+ if cur_tok=continental_point_token then cur_tok:=point_token;
+
+59. \hangindent becomes a normal parameter. (12/23/82)
+This simplifies the code in obvious ways; for example, module 1148 disappears.
+The command code hang_indent goes away too; what was previously called
+hanging_indent is then renamed hang_indent.
+
+60. \prevgraf becomes accessible. (12/23/82)
+This involves renaming the "after" field "pg_field" in the nest array;
+making a new command code set_prev_graf;
+including the following loop into scan_the:
+ nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scanned_result(nest[p].pg_field)(int_val);
+and including the following active procedure for any_mode(set_prev_graf):
+ procedure change_prev_graf;
+ var p:0..nest_size; {index into |nest|}
+ begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scan_optional_equals; scan_int;
+ if cur_val<0 then
+ begin print_nl("! Bad \prevgraf");
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ end
+ else begin nest[p].pg_field:=cur_val; cur_list:=nest[nest_ptr];
+ end;
+ end;
+
+61. \clubpenalty is split off from \widowpenalty. (12/23/82)
+
+62. Bad bug in module 1005 (12/24/82)
+(must not go to reswitch if \par is a macro!)
+Instead of setting cur_cmd and cur_chr and goto reswitch, just back_input
+and set token_type:=inserted.
+
+63. \openin not to prompt if file not present (12/25/82)
+Change lines -4 and -3 of module 1183 to:
+ if a_open_in(read_file[n]) then read_open[n]:=just_open
+ else read_open[n]:=closed;
+
+64. New \jobname primitive (12/25/82)
+It is added to the "convert" command in the obvious way.
+In conv_toks, the relevant code is
+ if job_name=0 then open_log_file
+before "selector" is changed and
+ print(job_name)
+after.
+
+65. Better error recovery for math-only things (12/25/82):
+In module 952, don't goto reswitch after insert_dollar_sign.
+In module 954, first back_input, then set cur_tok, and
+don't bother to set cur_chr or cur_cmd; ins_error instead of back_error.
+
+66. Module 1163, line 3, insert "scan_optional_equals" (12/25/82).
+Also make \the\parshape allowed.
+
+67. The location where an \if begins is stacked (12/26/82)
+so that a better error message can be given for \end while \if is incomplete.
+This means two-word nodes instead of one-word nodes in the if stack.
+
+68. Change 30 is extended to \insert, \vadjust, \valign, \output (12/26/82)
+The one-time-only paragraph parameters are now cleared by a
+subroutine called normal_paragraph; hang_after is also set to 1.
+The essential change being made now is to call normal_paragraph in modules
+704, 932, 1009, and 1076.
+
+69. \pagetotal and \pagegoal are added (12/27/82)
+The changes are analogous to, but simpler than, those for \prevgraf.
+
+70. Tracing of page-optimization calculations (12/27/82)
+A bunch of print commands are added to modules 897, 914, and 918,
+activated if tracing_pages>0. Also, \tracingparagraphs is separated
+from \tracingstats.
+
+**** The changes above have been incorporated into version 0.91 of TeX
+
+71. The build_page procedure is broken in two parts (Dec 31, 1982)
+by making module 919 into a procedure called fire_up.
+
+72. \ifeven1\else is made legal by introducing if_code (Dec 31, 1982)
+This improves part of the code in change 45; if_limit has a specific
+value that recovers automatically from a former syntax error.
+
+73. Improvement to alignments when columns don't occur (Dec 31, 1982)
+Delete module 711; and where it was used in module 708, say this:
+ begin fin_col:=true; return;
+ end;
+Also, in module 717, replace the statement "width(q):=0" by
+"<Nullify width(q) and the tabskip glue following the current column>"
+where that new module has the following code:
+ begin width(q):=0; r:=link(q); s:=glue_ptr(r);
+ if s<>zero_glue then
+ begin add_glue_rel(zero_glue); delete_glue_ref(s);
+ glue_ptr(r):=zero_glue;
+ end;
+ end
+
+74. Better error message in overfull alignment (Dec 31, 1982)
+In module 717, don't set both height and width; set the height zero.
+Then in module 719, switch width to height if necessary. (People didn't
+understand the previous error messages, and I couldn't blame them.)
+
+*** The changes above have been incorporated into version 0.92 of TeX82
+*** (which was the last version of 1982, completed 11:59pm on December 31)
+ The first changes after 1982
+
+75. Modules 961 and 962 should be one module. (Jan 3, 1983)
+Also, the absolute constant 100 is replaced, and the test becomes
+ if ((page_head=page_tail)and(head=tail)and(dead_cycles=0))or
+ (dead_cycles>max_dead_cycles) then
+
+76. Surprise bug: module 1010. (Jan 3, 1983)
+The case "if head<>tail" needs an else clause: else pop_nest.
+Also remove the "<Scan an optional space>" in that module.
+
+77. Improvement to change 22 (Jan 4, 1983)
+In module 996, use box_max_depth from inside the \vbox:
+ @!d:scaled; {maximum depth}
+ begin d:=box_max_depth; unsave;... vpackage(...,d);
+
+78. \groupbegin and \groupend changed to \begingroup and \endgroup (Jan 4, 83)
+
+79. \deadcycles made accessible (Jan 4, 83)
+
+80. New calculations for split insertions (Jan 4, 83)
+In module 918, we now work with natural width, and add in the page depth too:
+The line "else w:=x_over_n..." is changed to
+ else begin w:=page_goal-page_total-page_depth;
+ if count(n)<>1000 then w:=x_over_n(w,count(n))*1000;
+ end;
+(Note that cur_page_height and cur_page_depth and page_size have been
+renamed page_total, page_depth, and page_goal, in accordance with new syntax.)
+
+*** The changes above have been incorporated into Version 0.93
+
+81. Old bug finally unearthed by PHY (Jan 6, 1983)
+insert "incompleat_node:=null;" after "push_nest;" in module 1097.
+
+82. Extension of change 69: \pageshrink, etc. added (Jan 6, 1983)
+
+83. \floatingpenalty and \insertpenalties added (Jan 6, 1983)
+Also, the insertion nodes now have a new format, so that the values of
+\floatingpenalty, \splittopskip, and \splitmaxdepth can be stored with
+each insertion; this requires the obvious changes in several places:
+a) Module 136, ins_node has fields float,depth,height,ins_ptr,split_top_ptr
+b) Module 184, these fields are displayed
+c) Module 198, ins_node case, also delete_glue_ref(split_top_ptr(p))
+d) Module 202, ins_node case, also add_glue_ref(split_top_ptr(p))
+e) Modules 226-228, new integer parameter \floatingpenalty
+f) Module 883, add parameter d to vert_break subroutine
+g) Module 885, use d instead of split_max_depth
+h) Module 890, argument split_max_depth to call of vert_break
+i) Module 894, change width to height (better name)
+j) Module 900, initialize insert_penalties here, not in 901
+k) Module 914, cost to be awful_bad if insert_penalties>=10000
+l) Module 916, add float(p) to insert_penalties if type(r)<>inserting
+m) Module 916, subtract page_depth from delta
+n) Module 918, subtract page_shrink from delta
+o) Module 918, argument depth(p) to call of vert_break
+p) Module 921, insert_penalties:=0, save split_top_skip before calling 925
+q) Module 921, restore split_top_skip before calling 924
+r) Module 928, set split_top_skip:=split_top_ptr(p) before prune_page_top
+s) Module 929, after q:=p, incr(insert_penalties)
+t) Module 929, before free_node, delete_glue_ref(split_top_ptr(p))
+u) Module 933, clear insert_penalties before calling 934
+v) Module 978, more local variables needed
+w) Module 1010, build the newfangled ins_node
+
+84. Scanner goes to new_line when <return> is category 13 (Jan 7, 1983)
+Insert state:=new_line in module 323 just before the reference to module 339;
+and delete "state:=new_line;" from modules 328 and 330 (and their names).
+
+85. Distinguish between user \kern and font \kern (Jan 9, 1983)
+The nontrivial parts of this are to change "type(s)<>kern_node" to
+"(type(s)<>kern_node)or(subtype(s)<>normal)" in modules 809 and 810;
+and to say "kern_node: if subtype(s)=explicit then goto done4" in module 812.
+The \kern primitive now has "explicit" instead of "normal" in 967.
+
+86. "ignorespace" becomes "ignorespaces" (Jan 9, 1983)
+
+87. Don't omit a blank space after \def, \message, \mark, etc. (Jan 9, 1983)
+<Scan an optional space> is removed from modules 431, 848, 874.
+"info(r):=space_token;" and "link(r):=get_avail; r:=link(r);" removed from 1278.
+
+*** The above changes appear in Version 0.94.
+ Version 0.95
+
+88. New active characters in math mode (Jan 12, 1983)
+In module 1062, add a label "restart" and change lines -3 and -2 as follows:
+fam(p):=(c div 256) mod 16;
+if c>=var_code then
+ if fam(p)<8 then fam(p):=cur_fam
+ else begin <Convert c to an active character whose equivalent is
+ ready to be scanned next>; goto restart;
+ end;
+In module 1064, the body of the procedure becomes
+begin if c>='4000 then <Convert c ...> else <the former body>; end
+And there's a new module:
+<Convert c...>=
+begin cs_ptr:=(c mod 128)+active_base;
+cur_cmd:=eq_type(cs_ptr); cur_chr:=equiv(cs_ptr); x_token; back_input;
+end
+
+89. Surprise bug: $1-$ treated the - as binary (Jan 15, 1983)
+New module <Convert a final bin_noad to an ord_noad>=
+ if r_type=bin_noad then type(r):=ord_noad
+is called in module 649 before the call of 678, and in module 651
+in the place where that code already appears.
+
+90. Another oversight (Jan 15, 1983)
+Add "space_factor:=1000;" after "push_nest" in modules 1027, 1029
+
+91. And a more embarrassing one (Jan 16, 1983)
+I forgot "spotless:=false;" at the beginning of the procedure in module 80.
+But while fixing this, I decided to make it more general since IBMers want
+a return code at the end of the job. So there's a history variable that has
+four values: spotless, warning_issued, error_message_issued, fatal_error_stop.
+a) module 75: declare history, define spotless etc.
+b) module 76: initialize history:=spotless
+c) module 80: if history<error_mess... then history:=error_mess...
+d) 80 and 90: history:=fatal_error_stop before jump_out
+e) 92: if history<error_message_issued then
+f) 232: if history=spotless then history:=warning_issued
+g) 1243: if history=warning_issued then print_nl("(see the log...)")
+
+92. "This can't happen" could happen, so there's new error recovery (Jan 16, 83)
+In module 933, replace the call on confusion by a call of the following module:
+ @ @<Recover from an unbalanced output routine@>=
+ begin print_nl("! Unbalanced \output routine");
+ help2("Your sneaky output routine has fewer real {'s than }'s.")@/
+ ("I can't handle that very well; good luck."); error;
+ repeat get_token;
+ until loc=null;
+ end
+In module 1278, replace the call on confusion by a call of:
+ @ @<Recover from an unbalanced write command@>=
+ begin print_nl("! Unbalanced \write command");
+ help2("On this page there's a \write with fewer real {'s than }'s.")@/
+ ("I can't handle that very well; good luck."); error;
+ repeat get_token;
+ until cur_tok=end_write_token;
+ end
+
+93. String overflow clobbered the log file (Jan 18, 1983)
+Also, "confusion" before log file open would cause problems. Also start_input
+calling open_log_file calling prompt_file_name calling fatal_error!
+To fix these anomalies, open_log_file no longer calls prompt_file_name,
+if interaction<scrollmode; instead, it terminates after printing what went
+wrong. Also, the fatal_error and overflow and confusion procedures call
+the following new subroutine:
+procedure normalize_selector;
+begin if job_name>0 then selector:=term_and_log else selector:=term_only;
+if interaction=batch_mode then decr(selector);
+if job_name=0 then open_log_file;
+end;
+
+94. \ifeof\fi loops infinitely (Jan 18, discovered by Lamport)
+Change 72 converted such a \fi to <space>\fi. Now it is converted to \relax\fi.
+
+95. \limitswitch changed to \displaylimits et al. (Jan 18, 1983)
+[Incidentally, this fixes a bug in the former positioning of \int\limitswitch]
+a) module 608: subtype of Op can be normal, limits, or no_limits.
+b) 620: display the subtype if not normal
+c) 667: new logic decides limits before looking at the operand (and the operand
+ is now called the nucleus); the italic correction is removed only if
+ it should not be put back
+d) 1069: subtype(tail):=cur_chr
+
+96. Minor changes to math in unusual cases (Jan 19, 1983)
+a:delete "if height(y)<=0 then height(y):=default_rule_thickness" in module 658
+b:move "if thickness(q)=default..." from module 664 to module 661
+c:delete module 1081, that error message isn't worth the bother
+d:in module 1062, char_num case, c:=math_code(cur_val) (if cur_val<128)
+e:in module 1064, a similar change
+
+97. Bad spacing from change 6 is corrected (Jan 19, 1983)
+underline, overline, radical, vcenter, and accent noads now revert to type
+Ord instead of type Inner.
+{...} produces type Ord also.
+There's a new primitive \mathinner.
+The new_noad function now produces an ord_noad (change its calls accordingly).
+And the default is changed in module 679 to t:=ord_noad; the fraction_noad
+case sets t:=inner_noad, and the (inner_noad,ord_noad) cases swap places.
+
+98. New \mathchoice primitive (Jan 19, 1983)
+a) module 204: new command
+b) 254: math_choice_group
+c) 614: style node three words long, so a choice node can be converted to it
+d) 614a: choice node has four subfields: display_mlist, text_mlits, etc.
+e) 1081a: routines to build a choice_node like 1027-1029 build discretionaries
+
+99. \input moves to syntax from semantics (Jan 19, 1983)
+a) 204, 206: renumber commands
+b) When input is to be expanded, if name_in_progress then insert_relax
+c) 459,464: name_in_pr:=true; begin_name;...end_name; name_in_pr:=false;
+d) 406,461: declare name_in_progress, a global boolean initially false
+
+100. \chardef joins \mathchardef (Jan 19, 1983)
+a) 204: math_only becomes math_given; add new command char_given
+b) 205: new command char_def
+c) 250,251: new primitive char_def
+d) 380: char_given,math_given yield integer after \the
+e) 401: char_given,math_given yield integer in context of integer
+f) 936,943: char_given treated like other_char
+g) 1062,1064: same, if cur_chr<128, else assume math_code(x)=x
+h) 1143,1144: char_def is analogous to math_char_def
+
+101. \unbox becomes \unhbox,\unvbox; also add \unhcopy (Jan 19, 1983)
+Module 1020 changes in the obvious way.
+
+102. \spacefactor, \pagetotal, etc. move to prefixed_command (Jan 20, 1983)
+
+103. \hrule in horizontal mode, \vrule in vertical mode: switch modes (Jan 20)
+
+104. \globaldefs parameter, affects prefixed_command (Jan 20, 1983)
+
+105. After looking at frequency counts, some optimizations made (Jan 21, 1983)
+a) "fast_get_avail" and "fast_store_new_token" introduced to speed up the loops
+in modules 360 and 431.
+b) some procedure call overhead eliminated in begin_token_list, end_token_list,
+back_input, and flush_node_list.
+c) a few if tests changed from "if a and b then" to "if a then if b then".
+
+106. Changes for space efficiency in math constructions (Jan 22, 1983)
+a) In module 1102, mlist_penalties:=(mode>0)
+b) The following code is inserted before "free_node" in module 639 (rebox):
+ if (is_char_node(p))and(link(p)=null) then
+ begin f:=font(p); v:=char_width(f)(char_info(f)(character(p)));
+ if v<>width(b) then link(p):=new_kern(width(b)-v);
+ end;
+c) A new module is called just before clean_box exits:
+<Simplify a trivial box>=
+q:=list_ptr(x);
+if is_char_node(q) then
+ begin r:=link(q);
+ if r<>null then if link(r)=null then if type(r)=kern_node then
+ begin free_node(r,small_node_size); link(q):=null;
+ end;
+ end
+
+107. Oversight in rebox routine (module 639) corrected (Jan 22, 1983)
+if type(b)=vlist_node then b:=hpack(b,natural);
+
+108. Module 217 clobbers eqtb[bad_font_ident] set in change 26q (Jan 22, 1983)
+Decided to fix this by making \nullfont a primitive.
+This means the procedure missing_font can be deleted, and the test for
+undefined font can be removed from the inner loop.
+(This reflects a rather dramatic change from TeX80, where
+a missing font was a fatal "Whoa" error!)
+Note: I thought I could delete module 646, but realized that it still provides
+a useful error message. The dump/undump routines (cf. change 26ee) now
+dump the null_font information too, as its parameters can be changed.
+
+109. End of program now lists all incomplete \ifs (Jan 24, 1983)
+
+110. Alignment preamble setup to allow \halign\lb (Jan 29, 1983)
+The statement "align_state:=-1000000;" is inserted near the beginning
+of module 695 (and the comment about align_state=-999999 is deleted).
+The constant -999999 is changed to -1000000 in modules 700 and 701.
+
+111. Forgot to test is_char_node(r) (Jan 30, 1983)
+in the <Simplify a trivial box> code of change 106c.
+By coincidence, this was caught since somebody used font number 11
+in the second character of a list of length 2!
+
+112. Improved format for stats at end of run (suggested by DRF, Jan 30, 1983)
+Module 1242 changes; nothing subtle.
+
+**** The changes above have been incorporated into version 0.95
+ Version 0.96
+
+113. space after one-symbol control sequences NOT to be ignored unless
+the catcode of that symbol is letter or spacer. (Jan 30, 1983)
+The name of module 334 changes slightly; the state is set based
+on cat_code(cur_chr), shortly after label start_cs.
+A new variable cat is introduced and set to cat_code(cur_chr) in modules
+334 and 336.
+
+114. trailing spaces removed on all lines of input (Jan 30, 1983)
+last_nonblank is added to input_ln routine (module 31)
+
+115. mmode+accent gives error but assumes math_accent (Feb 3, 1983)
+This goes into math_ac procedure (module 1075); module 1035 is eliminated.
+
+116. \iftrue and \iffalse (Feb 5, 1983)
+Simple addition of two new conditions.
+
+117. Bad calculation of size for \left and \right (Feb 6, 1983)
+In module 680, the first assignment to delta should be
+ delta:=(delta1 div 500)*delimiter_factor.
+
+118. \delimitershortfall (new name) replaces \delimiterlimit. (Feb 12)
+
+119. \abovewithdelims.. to be equivalent to \above (Feb 12)
+Module 1089 revised so that the delimiters are scanned before the dimension.
+
+120. Remove the kludgy math codes introduced in change 88 (Feb 12)
+\fam becomes a normal integer parameter
+and \mathcode stored with min_halfword added
+and \mathcode allowed to be 32768
+and current family is substituted only when it's in range.
+Also the initialization of mathcode for letters now specifies family 1.
+(This implies a dozen or so obvious revisions.)
+
+121. Bug in module 1152: max spacefactor is 32767 (Feb 12)
+also module 1013 gets a restricted range
+
+122. Octal output to be replaced by hexadecimal. (Feb 14)
+
+123. Forgot to include char_given in module 1037, re change 100 (Feb 14)
+
+124. vmode+valign,hmode+halign made legal transitions (Feb 17)
+
+125. \tracingrestores (Feb 18)
+This involves a lot of new, rather tedious code that's interspersed with the
+eqtb definitions (the body of a procedure called show_eqtb).
+
+126. New error message for hmode+hrule in -hmode. (Feb 25)
+head_for_vmode now suggests \leaders in this case. [improves change 103]
+
+127. under/overfull boxes in alignment: Better message. (Feb 27)
+par_begin_line becomes pack_begin_line, and it is set (negative) in
+module 719, read in modules 590 and 601.
+
+128. New \xcr feature. (suggested by Lamport comments, Mar 4)
+align_peek (module 702) ignores it, otherwise it acts like ordinary \cr.
+
+129. In stats, subtract out TeX's own string requirements (Mar 4)
+(cf. change 112)
+init_pool_ptr and init_str_ptr variables added in obvious way
+(declared in module 39, set in module 1240, used in module 1242)
+
+130. \everyhbox and \everyvbox. (Mar 6)
+The modifications are obvious; for example, module 1076 gets the statement
+ if every_vbox<>null then begin_token_list(every_vbox,every_vbox_text);
+and a similar pair of statements goes into module 993.
+
+131. Precaution in module 1105 error recovery (March 9, 1983)
+Change the first test to "if (a=null)or danger", in order to
+avoid accessing math_quad when the symbol fonts aren't known to be present.
+
+132. Installed float and unfloat to aid portability (suggested by HWT, 3/7/83)
+
+133. \dispskip becomes \abovedisplayskip and \belowdisplayskip (3/9/83)
+Also \dispaskip becomes \abovedisplayshortskip; \dispbskip similar.
+
+134. \romannumeral separated from \number (suggested by FY, March 10)
+Obvious changes; print_roman_int now accepts negative input but gives
+no output in such cases (the test in module 68 becomes "if n<=0 then return").
+
+135. scan_keyword to ignore leading spaces (Mar 12)
+In module 375, change "else begin" to
+"else if (cur_cmd<>spacer)or(p<>backup_head) then begin".
+
+136. Update to change 112 (Mar 14)
+Module 1242 now uses write and write_ln directly (saves space and time).
+
+137. Another change to page-break cost (suggested by Lamport, March 16)
+Cf. change 83k above; the relevant lines of module 914 now read
+ if b<awful_bad then
+ if pi<=eject_penalty then c:=pi
+ else if b<inf_bad then c:=b+pi+insert_penalties
+ else c:=deplorable
+ else c:=b;
+ if insert_penalties>=10000 then c:=awful_bad;
+and a similar change (but without insert_penalties) occurs in module 887.
+
+**** The changes above have been incorporated into version 0.96 (March 16, 1983)
+ Version 0.97
+
+138. \everyjob (suggested by FY, March 18)
+Module 936: main_control inserts every_job as its first action.
+
+139. Improved printout of macro definitions (March 19)
+In module 278, simply treat left_brace like right_brace.
+(This corresponds to the way the manual describes parameter matching.)
+
+140. Omit blanks as non-delimited parameters (March 19)
+In module 360, replace
+ else store_new_token(cur_tok);
+by
+ else @<Store the current token, but |goto continue| if it is
+ a blank space that would become an undelimited parameter@>;
+Then define @<Store the current token...@> to be:
+begin if cur_tok=space_token then
+ if info(r)<=end_match_token then
+ if info(r)>=match_token then goto continue;
+store_new_token(cur_tok);
+end
+
+141. Minor patch in module 386, catches mu_glue (March 21)
+begin cur_val:=zero_glue; cur_val_level:=glue_val;
+if not is_char_node(tail)and(mode<>0) then
+ begin if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end
+else if (mode=vmode)and(tail=head)and(last_page_glue<>max_halfword) then
+ cur_val:=last_page_glue;
+end
+
+142. Patch in module 699, \span expands only one level (March 21)
+(See previous change #42.) The "get_x_token" in the code
+while (cur_chr=span_code)and(cur_cmd=tab_mark) do get_x_token;
+should be replaced by:
+ begin get_token;
+ if cur_cmd>max_command then
+ begin expand_calls; get_token;
+ end;
+ end
+
+143. Single # in \tokens, \message, etc. (March 22)
+(The previous rule was really bad in connection with \uppercase,
+or with \write when #'s had to be given four times!)
+Module 437 should be prefaced with "if macro_def then".
+And get_token should become get_x_token there, if xpand is true.
+
+144. Keyword "to" required in \read. (March 22)
+This will avoid the common error of a missing space before the \cs.
+Also the stream number can be out of range, for terminal input.
+The stream number in a \write can be out of range too, for terminal output.
+Modules 1145 and 1258 and 1280, etc., change in the obvious ways.
+
+145. \ifeven<countnumber> replaced by \ifodd<number> (March 26)
+This makes the language more consistent.
+
+146. Big surprise bug relating to \if\if aabc\fi (March 26)
+(related to change 45; the possibility that cur_if might not be the
+correct one when the conditional is evaluated was discovered today)
+The main change is to add the change_if_limit procedure, and to add
+the variable save_cond_ptr and the code that now depends on it.
+
+147. \if and \ifcat should tolerate primitives (March 28)
+
+148. "absent" becomes "void", a better word (March 28)
+
+149. Module 990: \lastbox to clear the shift_amount (March 28)
+since I don't want to figure out what it means in all cases (\vsplit, etc)
+
+150. print_err("...") takes the place of print_nl("! ...") (DRF, March 29)
+And wake_up_terminal is introduced in module 34, and used in modules
+37, 70, 341, 441, 457, 463, 1210, 1240, 1246.
+
+151. \halign extended to periodic preambles (April 1)
+Modules 688--690: cur_loop introduced, stacked/unstacked
+Module 695: cur_loop initialized
+Module 700: cur_loop set up
+Module 708--709: cur_loop used and advanced
+
+152. \leaders to align by the smallest enclosing box. (April 1)
+Module 549: new local variable left_edge, initialized to cur_h
+Module 557: cur_h:=left_edge+leader_wd*((cur_h-left_edge) div leader_wd)
+Module 559: new local variable top_edge, initialized to cur_v-height(this_box)
+Module 566: analogous to 557
+
+153. hyphenation after whatsits is OK (April 1)
+In module 809, skip past whatsits
+
+154. \par in vertical mode should build_page (April 2)
+vmode+par_end moves from 952 to 1005
+
+155. Clear aux to zero in module 703 (April 2)
+
+156. Digits will switch families (April 4)
+(initialize their math codes differently in module 225)
+
+157. Refinement to correction 83m (April 7)
+the test for not splitting should be
+ if ((h<=0)or(h<=delta))and(height(p)+height(r)<=dimen(n)) then
+
+158. (re 128) \xcr renamed to \crcr, at Lamport's request. (April 8)
+
+159. Better error recovery in runaway preamble (April 11)
+Module 319, aligning: set align_state:=-1000000
+
+160. \read to get balanced braces (April 12)
+Module 440 changed to look more like 431.
+Module 443 gives error message if the \read goes off end of file.
+Module 319 removes \outer from forbidden control sequence that is \read.
+
+161. Bug found by Jim Sterken (April 14)
+Module 798 can make q a char_node, so module 794 needs this patch:
+ if not is_char_node(q) then
+to be inserted just before "if (type(q)=math_node)..."
+
+162. \uppercase and \lowercase to apply to all characters (April 15)
+module 1196 changes; also I put active_base before single_base in eqtb
+
+**** The changes above have been incorporated into version 0.97 (April 16, 1983)
+**** except the part of 144 about \read-1 was forgotten and put in 0.98 later
+ Version 0.98
+
+163. change small_number to 0..65 in module 814 (found by DRF, April 17)
+
+164. improved error recovery in module 1166 (suggested by DRF, April 17)
+after error: repeat get_token; until cur_cmd=right_brace; {flush the patterns}
+
+165. improved \read from terminal (suggested by Todd Allen at Yale, May 1)
+I had forgotten to implement the extended stream numbers in change 144.
+Also, the prompt is now omitted if n<0.
+
+166. \write n writes only to the log file, if n<0. (May 18)
+
+167. Unified syntax for parameters and registers. (May 18)
+a) Command code changes: def_font moved to before prefix;
+ register eliminated; set_register renamed register and
+ moved (with adv_register...div_register) to before prefix;
+ min_internal and max_internal defined.
+b) scan_the renamed scan_something_internal, and it acts on cur_tok.
+c) scan_int, scan_dimen, scan_glue simplified accordingly; instead of
+ testing for cur_cmd=the or cur_cmd=register, they now test
+ cur_cmd versus min_internal and max_internal.
+d) ins_the is removed.
+e) \minusthe is removed. Consequently "the_toks" needs no parameter.
+
+168. new parameters \hoffset, \voffset (May 18)
+
+169. \everycr (suggested by Spivak, installed May 24)
+
+170. \countdef, \dimendef, etc. (suggested by DRF long ago, installed May 25)
+Straightforward change to internal representation of assign_int and similar
+commands, so that the chr part is now a pointer to the eqtb location.
+
+171. \advance, \multiply, \divide (suggested by FY, installed May 25)
+
+172. \hyphenchar (May 25)
+A new command assign_font_int is introduced, and incorporated into
+scan_something_internal and prefixed_command in the usual way.
+The hyphen_char array entries are initialized in modules 483 and 506,
+allocated in 482, used in modules 809 [is hyphenation to be suppressed?],
+828 [insert the hyphen character], 946 [insert discretionary node
+following hyphen], 1027 [implementation of \-].
+hyphen_char[k] is dumped and undumped in modules 1229 and 1230.
+
+173. \skewchar (May 25)
+Loaded and fetched with the same command code as \hyphenchar.
+Module 659 gets a dozen more lines of program, to compute the skew.
+
+174. \noexpand (May 25)
+This involves: introduction of no_expand and dont_expand command codes;
+frozen_dont_expand as an internal marker; small change to get_next
+when that marker is sensed; change to processing of \if and \ifcat;
+implementation of no_expand in the expand_calls subroutine.
+I also changed the name of expand_calls to "expand".
+
+175. \meaning (May 25)
+This adds one case to the "convert" command.
+print_meaning is a new subroutine, using code that was in show_whatever.
+
+176. "dm" and "vu" are out, ".5\hsize" is in (May 25)
+Straightforward changes to module 416.
+
+177. \texinfo f n becomes \fontdimen n f (May 25)
+
+178. \afterassignment (suggested by ARK, May 27)
+
+179. \chardef\xx=5\xx shouldn't say that \xx is undefined (May 27)
+
+180. \relax to be ignored like spaces in math mode (May 28)
+and in a few other places: <Get next non-blank non-relax non-call token>
+is now used in modules 379, 988, 994, 1062, 1070, 1134, 1179
+(i.e., scan_left_brace, scan_box, scan_math, scan_delimiter, prefixed_command,
+do_assignments, and just after \leaders).
+
+181. improve \mathaccent wrt sub/superscripts (sugg by HWT, May 30)
+
+182. its_all_over: remove dead_cycles>max_dead_cycles (May 30)
+Modules 961 and 962 are combined and simplified.
+
+*** The changes above were installed into version 0.98 on May 31, 1983 ***
+ Version 0.99
+
+183. \mark and \insert and \vadjust allowed in restricted hmode (Jun 3)
+also in math; this is a comparatively big change
+[at present, \mark in a display causes TeX to crash with "can't happen"!]
+modules 652 and 679: mark_node,ins_node,adjust_node now permitted
+modules 576 and 578: t becomes a global variable adjust_tail
+modules 802 and 1110: new calling sequence to get the adjustments
+modules 254, 986, 993, 995, 996: adjusted_hbox_group for the new kind of hbox
+modules 698--690: cur_head and cur_tail added to alignment stack
+modules 703, 712, 715: adjustments gathered and appended during \halign
+
+184. \ht, \wd, and \dp (Jun 6)
+
+185. When displaying noads, use ^ and _ instead of ( and [ (Jun 6)
+
+186. A..F in hex constants could be otherchar as well as letter (Jun 6)
+
+187. Remove <scan optional space> from module 417 (Jun 7)
+(it was redundant code)
+
+188. \mkern .5\thinmuskip and \mkern\thinmuskip should be legal (Jun 7)
+
+189. 2.5\space\space\dimen0 should work (Jun 7)
+previously it worked after "plus" or "minus" only!
+
+190. <font identifier> to allow also \font for current font (Jun 7)
+"if cur_cmd=def_font then f:=cur_font else" added to module 507
+
+191. \gdef not to be global when \globaldefs<0 (Jun 7)
+"and (global_defs>=0)" added to module 1139
+
+192. \advance\spaceskip by-\spaceskip should yield zero_glue (Jun 7)
+the procedure trap_zero_glue is culled from module 1147
+
+193. \show should work with any token (Jun 7)
+
+194. \tokens to become 256 registers (Jun 8)
+\toks and \toksdef added in the straightforward way
+(this affects mainly eqtb, scan_something_internal, prefixed_command)
+
+195. allow \indent in math mode (Jun 8)
+also, \valign in math mode to give missing $ error
+modules 1001 and 1043 disappear; 1004 is generalized slightly.
+
+196. remove redundant code (Jun 8)
+In module 1044, there's no need to check cur_group and call off_save.
+Similarly in module 1053.
+
+197. new_write_whatsit shouldn't allow \openout-1, \closeout-1 (Jun 8)
+(simple change to module 1258)
+
+198. \lastbox should give error in math mode (Jun 8)
+(simple change to module 990)
+
+199. \leaders not followed by proper glue should be back_error (Jun 9)
+[I made the change; TRIP should test this error message!]
+
+200. Module 702, correction to beginning of \noalign (Jun 9)
+if mode=-vmode then normal_paragraph;
+
+201. After alphabetic constant, expand the optional space (Jun 10)
+
+202. Set space_factor:=1000 after rule or constructing an accent (Jun 12)
+That's in modules 964 and 1036.
+
+203. blunder in module 783 (caught by Jim Sterken, fixed Jun 14)
+disc_width:=0 needs to be set before testing if s=null
+(A real bug that existed since the beginning! It showed up on page 37
+ of the September 1982 TRIP manual; my hand-checking was incomplete...)
+
+204. Change to optional spaces after <dimen> (Jun 14)
+The optional space will now be analogous to that after <number>.
+
+205. Fix conflict between \output and \everydisplay (Jun 14)
+In change 49, I should have inserted every_display before calling build_page.
+
+206. Overflow errors to be consistent with statistics reporting (Jun 17)
+(See change 129.)
+
+207. \tracing switches to be all positive vs nonpositive (Jun 17)
+
+*** The changes above were installed into version 0.99 on June 19, 1983 ***
+ Version 0.999
+
+208. \catcode`\%=14 to be done by INITEX (Jun 20)
+
+209. \par in vmode to clear parshape etc. (Jun 21)
+Module 1005, which contains vmode+par_end, now calls normal_paragraph.
+
+210. Improvement to change 39 (Jun 21)
+Module 593, overfull_rule not appended if solely due to hbadness
+
+211. Alignment bug allows glue_set to be less than -1! (Jun 21)
+Modules 723 and 724 need to be patched.
+
+212. Correction to change 134 (found by Debby Clark, Jun 22)
+Module 68: n to be declared integer, not nonnegative_integer.
+
+213. "by" to be optional (suggested by Lamport, Jun 22)
+Module 1158 disappears
+
+214. Module 1235, slight change in format_ident message (Jun 24)
+
+215. New measures needed to thwart trickery (Jun 25)
+Glue set values computed by \span could have been brought into
+TeX's registers via, e.g., \valign and \vsplit; so the
+"kern" idea of module 722 is insufficient and should be abandoned.
+Extra boxes and glue are added; this has additional virtue
+of perfect accuracy in alignment of vertical rules.
+The main change is to introduce a new module after 722, in which s and t
+are updated for spans, and to eliminate the "equivalent kern" code from 719
+(the corresponding glue calculations are now done in the new module).
+
+216. leaders to affect height/width of their boxes (Jun 25)
+Module 583 splits into two parts (one for hpack, one for vpack).
+
+217. \unskip permitted a little more often (Jun 28)
+Module 1018 reports error only if last_page_glue<max_halfword.
+
+218. {\bf A} in math: not to make a box (Jul 1)
+Module 1093 changes as follows: info(saved(0)):=fin_mlist(null) becomes
+p:=fin_mlist(null); info(saved(0)):=p; if <simple case> then <simplify>.
+
+219. "scaled" feature added to \font input.
+
+220. \mathaccent still not working right? (Jul 2)
+Change 181, I forgot to correct delta when box x changed.
+
+221. Remove confusion possible on file of length 1 (found by Lamport)
+Correction suggested by DRF, July 2: introduce bypass_eoln
+
+222. Allow things like ^^b. (July 4)
+This simplifies modules 332 and 335; also affects 48 and 49.
+And (surprise) 58.
+
+223. \escapechar \defaulthyphenchar \defaultskewchar \endlinechar (July 4)
+to make TeX less dependent on the character set
+A lot of error messages are now broken up so that they use print_esc
+
+224. erstat added for file opening/closing (by DRF, July 7)
+That's module 27.
+
+225. \tracingpages output to show total glue too (July 11)
+Added a procedure called print_page_totals, used also by show_activities.
+Broke modules 214 and 896 into two modules each.
+
+226. Guard against insertion into an hbox (July 11)
+A new procedure ensure_vbox is called in modules 917 and 925.
+
+227. <tokenvar>=<tokenvar> allowed (July 11)
+
+228. \errhelp parameter added (July 11)
+
+229. get_preamble_token should look at global_defs (July 11)
+Module 699 is patched.
+
+*** The above changes installed in preliminary version 0.999 on July 12, 1983,
+but I decided later in the day to do a few more things:
+
+230. \string, \noexpand, \meaning to allow \outer (Todd Allen, July 12)
+
+231. \the to be an expandable control sequence (July 12)
+Several things in the language are cleaned up:
+a) \the\tenrm replaced by \fontname\tenrm [\fontname<font>]
+b) when expanding edef, etc., result of \the still expanded only only level
+c) expansion after \def not inhibited, since \noexpand is now present
+d) \the\the disallowed.
+
+232. \unhbox and \unhcopy allowed in math mode if the box is void (July 12)
+Module 1020 is extended to handle this case.
+
+233. Value of default_rule was incorrectly rounded (July 16)
+Module 424: default_rule=26214 {.39999 pt}
+
+234. \mathaccent still not right! (July 16)
+I need to make the final height the max of (height of accented letter w/o
+superscript, height of unaccented letter w superscript).
+
+235. \newlinechar parameter (July 16)
+simple change to print subroutine
+
+236. boxes and rules to be allowed in discretionaries (sugg by HP man, July 16)
+in fact this simply requires omitting of the prohibition (in module 1031)
+and a few more cases equivalent to kern_node in modules 754, 755, 784.
+
+237. \tracingcommands to show all expandable tokens (July 16)
+
+238. \char to be allowed in \hyphenation list (July 16)
+Module 848 changes in the obvious way.
+
+239. \aftergroup (July 16)
+A new save_type.
+
+240. Curtail running dimensions inside alignments (sugg by ARK, July 16)
+Module 720.
+
+241. Strange pattern data could cause PASCAL error (found July 17)
+Put "if hc[1]=127 then hyf[0]:=0;" at beginning of module 878.
+
+242. hc codes for hyphenation are one lower (July 17)
+e.g., hc[...]:=lc_code(...)-1. This makes code 127 impossible to match.
+
+243. whatsits also allowed after hyphenatable words (July 17)
+Module 812.
+
+244. \/ makes an explicit kern (July 17)
+"subtype(tail):=explicit" in module 1023.
+
+*** Version 0.999 installed July 17, 1983.
+But later in the day I decided to do a few more things:
+
+245. Lowercase letters allowed in file names (July 18)
+In module 452, omit the conversion to uppercase.
+
+246. "No output file" becomes "No pages of output." (July 18)
+Module 571.
+
+247. QRS on error leads to confirmation message (sugg by ARK, July 18)
+Module 84.
+
+*** The real version 0.999 finally installed July 18.
+ * Version 1.0
+
+(Changes made after the Version 0.999 listing of TeX82)
+(Henceforth the "final" module numbers are used)
+
+248. Module 1215, allow space in \read n to \cs (by FY, July 25, 1983)
+@x patch in get_r_token routine
+begin restart: get_token;
+@y
+begin restart: repeat get_token;
+until cur_tok<>space_token;
+@z
+
+249. Module 498, we must stack the current if type (FY, July 27)
+@x patch in conditional routine
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;@/
+@y
+@!this_if:small_number; {type of this conditional}
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;this_if:=cur_chr;@/
+@z
+Also replace cur_if by this_if in modules 501, 503, 506. The following
+patches do only what is necessary to make things work:
+@x
+ print_cmd_chr(if_test,cur_if);
+@y
+ print_cmd_chr(if_test,this_if);
+@z
+@x
+if cur_if=if_int_code then scan_int@+else scan_normal_dimen;
+@y
+if this_if=if_int_code then scan_int@+else scan_normal_dimen;
+@z
+@x
+if cur_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
+@y
+if this_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
+@z
+
+250. Module 507, \ifx need not put a control sequence in hash table (July 29)
+@x
+get_token; n:=cs_ptr; p:=cur_cmd; q:=cur_chr;
+get_token; if cur_cmd<>p then b:=false
+@y
+get_next; n:=cs_ptr; p:=cur_cmd; q:=cur_chr;
+get_next; if cur_cmd<>p then b:=false
+@z
+
+251. Module 86, message is lost (noticed by HWT, July 31)
+@x
+print("..."); print_ln; return;
+@y
+print("..."); print_ln; update_terminal; return;
+@z
+
+252. Don't put empty at end of \input file! (Aug 1)
+[This simplifies the rules and the program, and also gets around a bug
+that occurred at the end of files with \endlinechar<0.]
+@x Module 362:
+@ An empty line is inserted at the end of the file, if the last line
+wasn't already empty, because |input_ln| sets |last:=first| when
+it discovers an |eof|.
+@^empty line at end of file@>
+
+@<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else if limit<>start then firm_up_the_line
+ {if |pausing|, the user can add more lines}
+ else force_eof:=true;
+@y
+@ @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else force_eof:=true;
+@z
+
+*** The changes above went into Version 0.9999, which was widely distributed
+
+253. Ridiculous blunder made in change 146 (found by FY, August 16)
+@x Correction to module 497
+else loop@+begin q:=cond_ptr;
+ if link(q)=p then
+ begin type(p):=l; return;
+ end;
+ if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ q:=link(q);
+@y
+else begin q:=cond_ptr;
+ loop@+ begin if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ if link(q)=p then
+ begin type(q):=l; return;
+ end;
+ q:=link(q);
+ end;
+@z
+
+254. Minor amendment to stat(s) printing (cf. change 129) (August 16)
+@x in module 1334
+ wlog_ln(' ',str_ptr-init_str_ptr:1,' strings out of ',
+ max_strings-init_str_ptr:1);@/
+@y
+ wlog(' ',str_ptr-init_str_ptr:1,' string');
+ if str_ptr<>init_str_ptr+1 then wlog('s');
+ wlog_ln(' out of ',
+@z
+
+255. Bug in \xleader computations (found by FY, August 18)
+@x in module 592
+@!lq,@!lr:integer; {quantities used in calculations for leaders}
+@y
+@!lq,@!lr,@!lx:integer; {quantities used in calculations for leaders}
+@z
+@x in module 626
+ begin edge:=cur_h+rule_wd;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_h+leader_wd<=edge do
+ @<Output a leader box at |cur_h|,
+ then advance |cur_h| by |leader_wd|@>;
+@y
+ begin edge:=cur_h+rule_wd; lx:=0;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_h+leader_wd<=edge do
+ @<Output a leader box at |cur_h|,
+ then advance |cur_h| by |leader_wd+lx|@>;
+@z
+@x in module 627
+ leader_wd:=leader_wd+lx;
+@y
+@z
+@x in module 628
+cur_h:=save_h+leader_wd;
+@y
+cur_h:=save_h+leader_wd+lx;
+@z
+@x in module 635
+ begin edge:=cur_v+rule_ht;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_v+leader_ht<=edge do
+ @<Output a leader box at |cur_v|,
+ then advance |cur_v| by |leader_ht|@>;
+@y
+ begin edge:=cur_v+rule_ht; lx:=0;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_v+leader_ht<=edge do
+ @<Output a leader box at |cur_v|,
+ then advance |cur_v| by |leader_ht+lx|@>;
+@z
+@x in module 636
+ leader_ht:=leader_ht+lx;
+@y
+@z
+@x in module 637
+cur_v:=save_v-height(leader_box)+leader_ht;
+@y
+cur_v:=save_v-height(leader_box)+leader_ht+lx;
+@z
+Also insert the following in modules 619 and 629:
+@!lx:scaled; {extra space between leader boxes}
+
+256. \/ should apply to ligatures! (August 20)
+@x in module 1113
+var f:internal_font_number; {the font in the |char_node|}
+begin if is_char_node(tail)and(tail<>head) then
+ begin f:=font(tail);
+ tail_append(new_kern(char_italic(f)(char_info(f)(character(tail)))));
+@y
+label exit;
+var p:pointer; {|char_node| at the tail of the current list}
+@!f:internal_font_number; {the font in the |char_node|}
+begin if tail<>head then
+ begin if is_char_node(tail) then p:=tail
+ else if type(tail)=ligature_node then p:=lig_char(tail)
+ else return;
+ f:=font(p);
+ tail_append(new_kern(char_italic(f)(char_info(f)(character(p)))));
+@z
+@x later in that same module
+end;
+@y
+exit: end;
+@z
+
+257. Another debugging hack. (August 27)
+@x module 1339
+15: begin font_in_short_display:=null_font; short_display(n);
+ end;
+@y
+15: begin font_in_short_display:=null_font; short_display(n);
+ end;
+16: panicking:=not panicking;
+@z
+
+258. Redundant code eliminated (August 27)
+Module 531 needn't set and reset name_in_progress [but it's harmless].
+
+259. Bug: \input shouldn't occur during font size spec (Spivak; fixed August 27)
+@x module 1258
+@ @<Scan the font size specification@>=
+@y
+@ @<Scan the font size specification@>=
+name_in_progress:=true; {this keeps |cur_name| from being changed}
+@z
+@x module 1258
+else s:=-1000
+@y
+else s:=-1000;
+name_in_progress:=false
+@z
+
+260. \ifhbox and \ifvbox introduced. (August 27)
+@x module 487
+@d ifx_code=10 { `\.{\\ifx}' }
+@d if_eof_code=11 { `\.{\\ifeof}' }
+@d if_true_code=12 { `\.{\\iftrue}' }
+@d if_false_code=13 { `\.{\\iffalse}' }
+@d if_case_code=14 { `\.{\\ifcase}' }
+@y
+@d if_hbox_code=10 { `\.{\\ifhbox}' }
+@d if_vbox_code=11 { `\.{\\ifvbox}' }
+@d ifx_code=12 { `\.{\\ifx}' }
+@d if_eof_code=13 { `\.{\\ifeof}' }
+@d if_true_code=14 { `\.{\\iftrue}' }
+@d if_false_code=15 { `\.{\\iffalse}' }
+@d if_case_code=16 { `\.{\\ifcase}' }
+@z
+@x
+primitive("ifx",if_test,ifx_code);
+@y
+primitive("ifhbox",if_test,if_hbox_code);
+@!@:if_hbox_}{\.{\\ifhbox} primitive@>
+primitive("ifvbox",if_test,if_vbox_code);
+@!@:if_vbox_}{\.{\\ifvbox} primitive@>
+primitive("ifx",if_test,ifx_code);
+@z
+@x module 488
+ ifx_code:print_esc("ifx");
+@y
+ if_hbox_code:print_esc("ifhbox");
+ if_vbox_code:print_esc("ifvbox");
+ ifx_code:print_esc("ifx");
+@z
+@x module 501
+if_void_code: @<Test if a box is void@>;
+@y
+if_void_code, if_hbox_code, if_vbox_code: @<Test box register status@>;
+@z
+@x module 505
+@ @<Test if a box is void@>=
+begin scan_eight_bit_int; b:=(box(cur_val)=null);
+end
+@y
+@ @<Test box register status@>=
+begin scan_eight_bit_int; p:=box(cur_val);
+if this_if=if_void_code then b:=(p=null)
+else if p=null then b:=false
+else if this_if=if_hbox_code then b:=(type(p)=hlist_node)
+else b:=(type(p)=vlist_node);
+end
+
+261. Serious data structure error (found by Todd Allen, August 29)
+@x module 478 (an error introduced in change 231)
+ q:=the_toks; link(p):=link(temp_head); p:=q;
+@y
+ q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+@z
+
+262. Minor patch for efficiency (August 29)
+@x module 466
+ begin store_new_token(info(r)); r:=link(r);
+@y
+ begin fast_store_new_token(info(r)); r:=link(r);
+@z
+
+263. Minor patch to error message (August 29)
+@module 579
+ print(" has "); print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has n fontdimen...@>
+@y
+ print(" has only "); print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has only...@>
+@z
+
+*** The changes above comprise version 0.99999, used only at SAIL (August 29)
+
+264. funny blank spaces are showable (August 30)
+@x module 298
+spacer: print("blank space");
+@y
+spacer: chr_cmd("blank space ");
+@z
+
+265. \newlinechar [change 235] should affect print_char too (August 31)
+@x module 58
+procedure print_char(@!c:ascii_code); {prints a single character}
+begin case selector of
+term_and_log: begin wterm(xchr[c]); write(log_file,xchr[c]);
+ 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 write(log_file,xchr[c]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[c]); 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]:=c;
+new_string: begin if pool_ptr<pool_size then append_char(c);
+ end; {drop characters if the string space is full}
+othercases write(write_file[selector],xchr[c])
+endcases;@/
+incr(tally);
+end;
+@y
+procedure print_char(@!s:ascii_code); {prints a single character}
+label exit;
+begin if @<Character |s| is the current new-line character@> 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; {drop characters if the string space is full}
+othercases write(write_file[selector],xchr[s])
+endcases;@/
+incr(tally);
+exit:end;
+
+266. \lastkern,\lastpenalty,\unkern,\unpenalty (September 4)
+@x module 208
+@d unskip=25 {nullify glue ( \.{\\unskip} )}
+@y
+@d remove_item=25 {nullify last item ( \.{\\unpenalty},
+ \.{\\unkern}, \.{\\unskip} )}
+@z
+@x module 208
+@d last_skip=69 {most recent glue ( \.{\\lastskip} )}
+@y
+@d last_item=69 {most recent item ( \.{\\lastpenalty},
+ \.{\\lastkern}, \.{\\lastskip} )}
+@z
+@x module 265
+primitive("lastskip",last_skip,0);@/
+@!@:last_skip_}{\.{\\lastskip} primitive@>
+@y
+@z
+@x module 265
+primitive("the",the,0);
+@!@:the_}{\.{\\the} primitive@>
+primitive("toks",toks_register,0);
+@!@:toks_}{\.{\\toks} primitive@>
+primitive("unskip",unskip,0);@/
+@!@:unskip_}{\.{\\unskip} primitive@>
+@y
+primitive("the",the,0);@/
+@!@:the_}{\.{\\the} primitive@>
+primitive("toks",toks_register,0);@/
+@!@:toks_}{\.{\\toks} primitive@>
+@z
+@x module 266
+last_skip: print_esc("lastskip");
+@y
+@z
+@x module 266
+unskip: print_esc("unskip");
+@y
+@z
+@x module 413
+last_skip: @<Fetch the glue in the current node, if any@>;
+@y
+last_item: @<Fetch an item in the current node, if appropriate@>;
+@z
+@x module 416 [I also changed the comment]
+primitive("dp",set_box_dimen,depth_offset);
+@!@:dp_}{\.{\\dp} primitive@>
+@y
+primitive("dp",set_box_dimen,depth_offset);
+@!@:dp_}{\.{\\dp} primitive@>
+primitive("lastpenalty",last_item,int_val);
+@!@:last_penalty_}{\.{\\lastpenalty} primitive@>
+primitive("lastkern",last_item,dimen_val);
+@!@:last_kern_}{\.{\\lastkern} primitive@>
+primitive("lastskip",last_item,glue_val);
+@!@:last_skip_}{\.{\\lastskip} primitive@>
+@z
+@x module 417
+else print_esc("dp");
+@y
+else print_esc("dp");
+last_item: if chr_code=int_val then print_esc("lastpenalty")
+else if chr_code=dimen_val then print_esc("lastkern")
+else print_esc("lastskip");
+@z
+@x module 424
+@ Here is where \.{\\lastskip} is implemented. The reference count will be
+updated later.
+@:last_skip_}{\.{\\lastskip} primitive@>
+
+@<Fetch the glue in the current node, if any@>=
+begin cur_val:=zero_glue; cur_val_level:=glue_val;
+if not is_char_node(tail)and(mode<>0) then
+ begin if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end
+else if (mode=vmode)and(tail=head)and(last_page_glue<>max_halfword) then
+ cur_val:=last_page_glue;
+end
+@y
+@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\lastskip} are
+implemented. The reference count for \.{\\lastskip} will be updated later.
+
+@<Fetch an item in the current node...@>=
+begin if cur_chr=glue_val then cur_val:=zero_glue@+else cur_val:=0;
+cur_val_level:=cur_chr;
+if not is_char_node(tail)and(mode<>0) then
+ case cur_chr of
+ int_val: if type(tail)=penalty_node then cur_val:=penalty(tail);
+ dimen_val: if type(tail)=kern_node then cur_val:=width(tail);
+ glue_val: if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end {there are no other cases}
+else if (mode=vmode)and(tail=head) then
+ case cur_chr of
+ int_val: cur_val:=last_penalty;
+ dimen_val: cur_val:=last_kern;
+ glue_val: if last_glue<>max_halfword then cur_val:=last_glue;
+ end; {there are no other cases}
+end
+@z
+@x module 982 [also the comment changes]
+@!last_page_glue:pointer; {used to implement \.{\\lastskip}}
+@y
+@!last_glue:pointer; {used to implement \.{\\lastskip}}
+@!last_penalty:integer; {used to implement \.{\\lastpenalty}}
+@!last_kern:scaled; {used to implement \.{\\lastkern}}
+@z
+@x module 991
+last_page_glue:=max_halfword;
+@y
+last_glue:=max_halfword; last_penalty:=0; last_kern:=0;
+@z
+@x module 994
+@<Update the value of |last_page_glue|@>;
+@y
+@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+@z
+@x module 996
+@ @<Update the value of |last_page_glue|@>=
+if last_page_glue<>max_halfword then delete_glue_ref(last_page_glue);
+if type(p)=glue_node then
+ begin last_page_glue:=glue_ptr(p); add_glue_ref(last_page_glue);
+ end
+else last_page_glue:=max_halfword
+@y
+@ @<Update the values of |last_glue|...@>=
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+last_penalty:=0; last_kern:=0;
+if type(p)=glue_node then
+ begin last_glue:=glue_ptr(p); add_glue_ref(last_glue);
+ end
+else begin last_glue:=max_halfword;
+ if type(p)=penalty_node then last_penalty:=penalty(p)
+ else if type(p)=kern_node then last_kern:=width(p);
+ end
+@z
+@x module 1017
+if last_page_glue<>max_halfword then delete_glue_ref(last_page_glue);
+@<Start a new current page@>; {this sets |last_page_glue:=max_halfword|}
+@y
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+@<Start a new current page@>; {this sets |last_glue:=max_halfword|}
+@z
+@x module 1048
+vmode+vmove,hmode+hmove,mmode+hmove,any_mode(last_skip),
+@y
+vmode+vmove,hmode+hmove,mmode+hmove,any_mode(last_item),
+@z
+@x module 1104
+any_mode(unskip): delete_skip;
+@y
+any_mode(remove_item): delete_last;
+@z
+@x module 1105
+procedure delete_skip;
+var p:pointer; {runs through the current list}
+begin if (mode=vmode)and(tail=head) then
+ @<Apologize for inability to do \.{\\unskip} now,
+ unless the previous node was not glue@>
+else begin if not is_char_node(tail) then if type(tail)=glue_node then
+@y
+procedure delete_last;
+var p:pointer; {runs through the current list}
+begin if (mode=vmode)and(tail=head) then
+ @<Apologize for inability to do the operation now,
+ unless \.{\\unskip} follows non-glue@>
+else begin if not is_char_node(tail) then if type(tail)=cur_chr then
+@z
+@x module 1106
+@ @<Apologize for inability to do \.{\\unskip}...@>=
+begin if last_page_glue<>max_halfword then
+ begin you_cant;
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead."); error;
+@y
+@ @<Apologize for inability to do the operation...@>=
+begin if (cur_chr<>glue_node)or(last_glue<>max_halfword) then
+ begin you_cant;
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("page. Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("page. Perhaps you can make the output routine do it.");
+ error;
+@z
+@x module 1107
+primitive("unhbox",un_hbox,box_code);@/
+@y
+primitive("unpenalty",remove_item,penalty_node);@/
+@!@:un_penalty_}{\.{\\unpenalty} primitive@>
+primitive("unkern",remove_item,kern_node);@/
+@!@:un_kern_}{\.{\\unkern} primitive@>
+primitive("unskip",remove_item,glue_node);@/
+@!@:un_skip_}{\.{\\unskip} primitive@>
+primitive("unhbox",un_hbox,box_code);@/
+@z
+@x module 1108
+un_hbox: if chr_code=copy_code then print_esc("unhcopy")
+@y
+remove_item: if chr_code=glue_node then print_esc("unskip")
+ else if chr_code=kern_node then print_esc("unkern")
+ else print_esc("unpenalty");
+un_hbox: if chr_code=copy_code then print_esc("unhcopy")
+@z
+
+*** The above changes installed in version 0.999999 (September 5, 1983)
+
+267. Undo change 29: it was overkill and not needed (Sep 17)
+@x module 1257
+label exit, common_ending;
+var u:pointer; {user's font identifier}
+@!s:scaled; {stated ``at'' size, or negative of scaled magnification}
+@!f:internal_font_number; {runs through existing fonts}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+@<If the next token isn't a suitable control sequence, issue a complaint
+ and |return|; otherwise set |u:=cs_ptr|, and set |hash_used| to the
+ location of a ``frozen'' copy of the new font identifier@>;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; geq_define(hash_used,set_font,f);
+font_ident[f]:=hash_used;
+exit:end;
+@y
+label common_ending;
+var u:pointer; {user's font identifier}
+@!s:scaled; {stated ``at'' size, or negative of scaled magnification}
+@!f:internal_font_number; {runs through existing fonts}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; font_ident[f]:=u;
+end;
+@z
+@x module 1260
+@ We reserve a special control sequence for the font identifier; this one
+cannot be redefined by the user, so it is safe to return it as a value of
+\.{\\the\\font}.
+
+@<If the next token isn't a suitable control sequence...@>=
+get_r_token;
+if cs_ptr<hash_base then
+ begin print_err("A font identifier must be a multiletter control sequence");
+@.A font identifier...@>
+ help2("You should say, e.g., `\font\ffn=fontfilename'.")@/
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end;
+repeat if hash_is_full then overflow("hash size",hash_size);
+@:TeX capacity exceeded hash size}{\quad hash size@>
+decr(hash_used);
+until text(hash_used)=0; {search for an empty location in |hash|}
+u:=cs_ptr; text(hash_used):=text(u); {copy the name}
+@!stat incr(cs_count);@+tats@;@/
+@y
+@z
+Note: Since module 1260 has disappeared, module 1258 has been split into two.
+
+268. Minor change to diagnostic output format (September 18)
+@x module 211 [print_mode]
+ 2:print("displayed math");
+@y
+ 2:print("display math");
+@z
+
+269. Kerns inserted for accents must be explicit (September 20)
+@x module 1123
+@!p,@!q: pointer; {character and box nodes}
+@y
+@!p,@!q,@!r:pointer; {character, box, and kern nodes}
+@z
+@x module 1125
+link(tail):=new_kern(delta); link(link(tail)):=p;
+link(p):=new_kern(-a-delta); tail:=link(p); p:=q;
+@y
+r:=new_kern(delta); subtype(r):=explicit; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=explicit; link(p):=tail; p:=q;
+@z
+
+270. "log" changed to "transcript" in a few output messages (Sep 26)
+@x module 535
+prompt_file_name("log file name",".log");
+@y
+prompt_file_name("transcript file name",".log");
+@z
+@x module 1293
+ ("lists on your terminal as well as on the log file.");
+@y
+ ("lists on your terminal as well as in the transcript file.");
+@z
+@x module 1335
+ print_nl("(see the log file for additional information)");
+@.see the log file...@>
+@y
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+@z
+
+271. Uninitialized variable bug (found by Bernd Schulze, 1 Oct 83)
+@x module 944
+ begin if trie_op_ptr>=max_quarterword-1 then {overflow}
+ begin trie_op_ptr:=max_quarterword;
+ new_trie_op:=min_quarterword; return;
+@y we allow one more trie op (OK since trie_op_hash_size is big enough)
+ begin if trie_op_ptr=max_quarterword then {overflow}
+ begin new_trie_op:=min_quarterword; return;
+@z
+
+272. Spaces at end of lines ignored also in TEX.POOL (by DRF, 14 Oct 83)
+@x module 52
+ begin if eoln(pool_file) then bad_pool('! TEX.POOL line too short.');
+@.TEX.POOL line too short@>
+ read(pool_file,m); append_char(xord[m]);
+@y
+ begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
+ append_char(xord[m]);
+@z
+
+273. |history| updates (by DRF, 14 Oct 83)
+@x module 77
+deletions_allowed:=true; history:=spotless; error_count:=0;
+@y
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+@z
+@x module 1332
+@p begin {@!|start_here|}
+@y
+@p begin {@!|start_here|}
+history:=fatal_error_stop; {in case we quit during initialization}
+@z
+@x ibid.
+main_control; {come to life}
+@y
+history:=spotless; {ready to go!}
+main_control; {come to life}
+@z
+
+274. improved "runaway" messages (suggested by FY, October 18)
+@x module 305 (the introductory comment is changed too)
+@d aligning=4 {|scanner_status| when reading an alignment preamble}
+
+@<Glob...@>=
+@!scanner_status : normal..aligning; {can a subfile end now?}
+@y
+@d aligning=4 {|scanner_status| when reading an alignment preamble}
+@d absorbing=5 {|scanner_status| when reading a balanced text}
+
+@<Glob...@>=
+@!scanner_status : normal..absorbing; {can a subfile end now?}
+@z
+@x module 306 (string space is also now conserved slightly)
+ defining: begin print("definition?"); p:=def_ref;
+ end;
+ matching: begin print("argument?"); p:=temp_head;
+ end;
+ aligning: begin print("preamble?"); p:=hold_head;
+ end;
+ end; {there are no other cases}
+ print_ln; show_token_list(link(p),null,error_line-10);
+@y
+ defining: begin print("definition"); p:=def_ref;
+ end;
+ matching: begin print("argument"); p:=temp_head;
+ end;
+ aligning: begin print("preamble"); p:=hold_head;
+ end;
+ absorbing: begin print("text"); p:=def_ref;
+ end;
+ end; {there are no other cases}
+ print_char("?");print_ln; show_token_list(link(p),null,error_line-10);
+@x module 339 (I also changed the module name)
+end; {there are no other cases}
+@y
+absorbing:begin print("text"); info(p):=right_brace_token+"}";
+ end;
+end; {there are no other cases}
+@z
+@x module 473
+begin scanner_status:=defining; warning_index:=cs_ptr;
+def_ref:=get_avail; info(def_ref):=null;
+@y
+begin if macro_def then scanner_status:=defining
+@+else scanner_status:=absorbing;
+warning_index:=cs_ptr; def_ref:=get_avail; info(def_ref):=null;
+@z
+
+275. similar, but this corrects a real bug (found by FY, October 18)
+@x module 1226
+ cs_ptr:=q;
+ @<Get the next non-blank non-relax non-call token@>;
+ if cur_cmd<>left_brace then @<If the righthand side is a token parameter
+ or token register, finish the assignment and |goto done|@>;
+ back_input; q:=scan_toks(false,false);
+@y
+ @<Get the next non-blank non-relax non-call token@>;
+ if cur_cmd<>left_brace then @<If the righthand side is a token parameter
+ or token register, finish the assignment and |goto done|@>;
+ back_input; cs_ptr:=q; q:=scan_toks(false,false);
+@z
+
+276. Change #119 should have changed module 1090 too (by Barry Smith, Oct 24)
+@x module 1183 [which was module 1090 at the time of change 119]
+begin if c mod delimited_code=above_code then scan_normal_dimen;
+if c>=delimited_code then
+ begin scan_delimiter(garbage,false); scan_delimiter(garbage,false);
+ end;
+@y
+begin if c>=delimited_code then
+ begin scan_delimiter(garbage,false); scan_delimiter(garbage,false);
+ end;
+if c mod delimited_code=above_code then scan_normal_dimen;
+@z
+
+277. Changes for efficiency, based on empirical frequency data (Nov 9)
+@x module 45 [since the result is true almost always]
+begin j:=str_start[s]; result:=false;
+while j<str_start[s+1] do
+ begin if str_pool[j]<>buffer[k] then goto not_found;
+@y
+begin j:=str_start[s];
+while j<str_start[s+1] do
+ begin if str_pool[j]<>buffer[k] then
+ begin result:=false; goto not_found;
+ end;
+@z
+@x module 380 [many compilers don't handle "while true do" very well]
+label done;
+begin loop begin get_next;
+@^inner loop@>
+ if cur_cmd<=max_command then goto done;
+ if cur_cmd>=call then
+ if cur_cmd<end_template then macro_call
+ else begin cs_ptr:=frozen_endv; cur_cmd:=endv;
+ goto done; {|cur_chr=null_list|}
+ end
+ else expand;
+ end;
+@y
+label restart,done;
+begin restart: get_next;
+@^inner loop@>
+if cur_cmd<=max_command then goto done;
+if cur_cmd>=call then
+ if cur_cmd<end_template then macro_call
+ else begin cs_ptr:=frozen_endv; cur_cmd:=endv;
+ goto done; {|cur_chr=null_list|}
+ end
+else expand;
+goto restart;
+@z
+@x module 829 [preparation for change to module 852]
+label exit,done,continue,deactivate;
+@y
+label exit,done,done1,continue,deactivate;
+@z
+@x module 830 [ditto]
+@!save_link:pointer; {temporarily holds value of |link(cur_p)|}
+@y
+@!save_link:pointer; {temporarily holds value of |link(cur_p)|}
+@!shortfall:scaled; {used in badness calculations}
+@z
+@x modue 851 [ditto]
+if cur_active_width[1]<line_width then
+@y
+shortfall:=line_width-cur_active_width[1]; {we're this much too short}
+if shortfall>0 then
+@z
+@x module 852 [avoid calling |badness| in most common case]
+else begin b:=badness(line_width-cur_active_width[1],cur_active_width[2]);
+@y
+else begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
+ begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
+ end;
+ b:=badness(shortfall,cur_active_width[2]);
+@z
+@x module 852, continued
+ else fit_class:=decent_fit;
+@y
+ else fit_class:=decent_fit;
+ done1:
+@z
+@x module 853 [using |shortfall|, since we now have it]
+begin if cur_active_width[1]-line_width>cur_active_width[6] then
+ b:=inf_bad+1
+else b:=badness(cur_active_width[1]-line_width,cur_active_width[6]);
+@y
+begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
+else b:=badness(-shortfall,cur_active_width[6]);
+@z
+
+278. Forgotten |error| call (noticed by Gabi Kuper, December 3, 1983)
+@x module 500
+ help1("I'm ignoring this; it doesn't match any \if.");
+@y
+ help1("I'm ignoring this; it doesn't match any \if.");
+ error;
+@z
+
+*** Version 1.0 released on December 3, 1983 incorporates all of the above.
+ Version 1.1
+
+279. Problem with change 267 (found by Mike Urban, received 2 Feb 84)
+(I had overlooked many problems, e.g. `{\font\a=x \global\a}\the\font'
+and `\font\a=x \font\b=x \let\b=\undefined \the\a', etc.
+The remedy involves removal of the font_ident array, so there's a
+sprinkling of corrections in lots of modules. But basically the change
+is quite conservative, so it shouldn't spawn any new bugs (it says here).)
+
+@x module 174 (removing a reference to |font_ident|)
+ else sprint_cs(font_ident[font(p)]);
+@y
+ else @<Print the font identifier for |font(p)|@>;
+@z
+@x module 176 (removing a reference to |font_ident|)
+ else sprint_cs(font_ident[font(p)]);
+@y
+ else @<Print the font identifier for |font(p)|@>;
+@z
+@x module 222 (redefining and expanding the `frozen' area)
+@d frozen_null_font=frozen_control_sequence+9 {permanent `\.{\\nullfont}'}
+@d frozen_dont_expand=frozen_control_sequence+10
+ {permanent `\.{\\notexpanded:}'}
+@d undefined_control_sequence=frozen_control_sequence+11 {dummy location}
+@y
+@d frozen_dont_expand=frozen_control_sequence+9
+ {permanent `\.{\\notexpanded:}'}
+@d frozen_null_font=frozen_control_sequence+10
+ {permanent `\.{\\nullfont}'}
+@d font_id_base=frozen_null_font-font_base
+ {begins table of 257 permanent font identifiers}
+@d undefined_control_sequence=frozen_null_font+257 {dummy location}
+@z
+@x module 234 (removing a reference to |font_ident|, awkwardly)
+print_char("="); sprint_cs(font_ident[equiv(n)]);
+@y
+print_char("=");@/
+print_esc(hash[font_id_base+equiv(n)].rh);
+ {that's |text(font_id_base+equiv(n))|}
+@z
+@x module 256 (adding a new macro, font_id_text)
+@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied}
+@y
+@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied}
+@d font_id_text(#) == text(font_id_base+#) {a frozen font identifier's name}
+@z
+@x module 262 (relaxing a former restriction)
+else if (text(p)<128)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
+@y
+else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
+@z
+@x module 267 (pick up the changes from 174 and 176)
+@!@^Single-character primitives@>
+@y
+@!@^Single-character primitives@>
+
+Meanwhile, this is a convenient place to catch up on something we were unable
+to do before the hash table was defined:
+
+@<Print the font identifier for |font(p)|@>=
+print_esc(font_id_text(font(p)))
+@z
+@x module 415 (removing another reference to font_ident)
+ scanned_result(font_ident[cur_val])(ident_val);
+@y
+ scanned_result(font_id_base+cur_val)(ident_val);
+@z
+@x module 548 (change to the comment only)
+to the user's font~\.{\\f}. For example, if this internal number is 13,
+we will have |font_ident[13]=p| and |equiv(p)=13|, where |p| is the |eqtb|
+location of the control sequence~\.{\\f}.
+@y
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+@z
+@x module 549 (deleting the declaration of font_ident)
+@!font_ident:array[internal_font_number] of pointer; {the most recent user
+ font identifier corresponding to an internal font number}
+@y
+@z
+@x module 552 (deleting an unnecessary initialization of font_ident)
+font_ident[null_font]:=frozen_null_font;
+@y
+@z
+@x module 579 (removing another reference to font_ident)
+ begin print_err("Font "); sprint_cs(font_ident[f]);
+@y
+ begin print_err("Font "); print_esc(font_id_text(f));
+@z
+@x module 1257 (this is the real change!)
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; font_ident[f]:=u;
+@y
+@!t:str_number; {name for the frozen font identifier}
+@!old_setting:0..max_selector; {holds |selector| setting}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+if u>=hash_base then t:=text(u)
+else if u>=single_base then
+ if u=null_cs then t:="FONT"@+else t:=u-single_base
+else begin old_setting:=selector; selector:=new_string;
+ print("FONT"); print(u-active_base); selector:=old_setting;
+@.FONTx@>
+ str_room(1); t:=make_string;
+ end;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; eqtb[font_id_base+f]:=eqtb[u]; font_id_text(f):=t;
+@z
+@x module 1260 (change to the comment only)
+the new value becomes the |font_ident| of record. Font names `\.{xyz}' and
+@y
+the new name becomes the font identifier of record. Font names `\.{xyz}' and
+@z
+@x module 1322 (no need to dump font_ident)
+begin dump_int(font_ident[k]);
+dump_qqqq(font_check[k]);
+@y
+begin dump_qqqq(font_check[k]);
+@z
+@x module 1322 (or to print from it)
+print_nl("\font"); sprint_cs(font_ident[k]); print_char("=");
+@y
+print_nl("\font"); print_esc(font_id_text(k)); print_char("=");
+@z
+@x module 1323 (or to undump it)
+begin undump(active_base)(undefined_control_sequence)(font_ident[k]);
+undump_qqqq(font_check[k]);@/
+@y
+begin undump_qqqq(font_check[k]);@/
+@z
+
+280. Double interrupt possibility (found by Clint Cuzzo, received 9 Feb 84)
+@x module 1031
+if interrupt>0 then if OK_to_interrupt then
+ begin back_input; pause_for_instructions; goto big_switch;
+@y
+if interrupt<>0 then if OK_to_interrupt then
+ begin back_input; check_interrupt; goto big_switch;
+@z
+
+281. Improve spacing in $(A,<)$. (12 Feb 84)
+@x module 764
+"0234000122*4000133**3**344*0400400*000000234000111*4111112341011"
+@y
+"0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
+@z
+
+282. Bad goto! (diagnosis by Clint Cuzzo and George O'Connor, recd 13 Feb 84)
+@x module 344 (here we simply change the name of module 346)
+any_state_plus(invalid_char): @<Decry the invalid character and
+ |goto switch|@>;
+@y
+any_state_plus(invalid_char): @<Decry the invalid character and
+ |goto restart|@>;
+@z
+@x module 346 (because clear_for_error_prompt might make state=token_list)
+goto switch;
+@y
+goto restart;
+@z
+
+283. String pool economy (suggested by DRF, 13 Feb 84)
+@x module 537
+@<Read the first line of the new file@>;
+@y
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string; name:=cur_name;
+ end;
+@<Read the first line of the new file@>;
+@z
+
+284. Nicer scaled output (suggested by METAFONT development, 26 Feb 84)
+@x module 103
+be reproduced exactly. [{\sl Proof:\/} If round$(x)=\lfloor
+x+{1\over2}\rfloor$ and if $\alpha<1$, it is not difficult to verify that
+round$(\alpha\,\hbox{round}( \alpha^{-1}n))=n$ for all integers |n|. In
+our case $\alpha=2^{16}/10^5$.]
+
+@p procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+s:=((s mod unity) * 3125 + 1024) div 2048;
+ {now |0<=s<100000| is the fraction part}
+print_char(".");
+repeat print_char("0"+(s div 10000)); s:=10*(s mod 10000);
+until s=0;
+end;
+@y
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@p procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+var delta:scaled; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+print_char(".");
+s:=10*(s mod unity)+5; delta:=10;
+repeat if delta>unity then s:=s+@'100000-(delta div 2); {round the last digit}
+print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
+until s<=delta;
+end;
+@z
+
+*** Those six changes account for version 1.1 (February 29, 1984).
+*** And we put the following change in too, at the last minute:
+
+285. ".00000762939453126pt" didn't round correctly! (March 2, 1984)
+@x module 452
+ if k<16 then {digits for |k>=16| cannot affect the result}
+@y (Note, I also changed the comment in module 102: 16 -> 17)
+ if k<17 then {digits for |k>=17| cannot affect the result}
+@z
+
+*** Note: I changed "cs_ptr" to "cur_cs", since the name change is more
+indicative of the fact that this variable makes a trio with cur_cmd
+and cur_chr. The TEX.WEB source changed 78 times, as a result, but
+none of the author's change files were affected.
+(But note: "save_cs_ptr" was not changed to "save_cur_cs", because
+of a name conflict.)
+
+*** And the following two were slipped in at the last second:
+
+286. loophole permitted get_next recursion (March 16, 1984)
+@x module 336
+ begin @<Back up an outer control sequence so that it can be reread@>;
+@y
+ begin deletions_allowed:=false;
+ @<Back up an outer control sequence so that it can be reread@>;
+@z
+@x ibid
+ end;
+@y
+ deletions_allowed:=true;
+ end;
+@z [the changes to deletions_allowed in module 338 can now be removed]
+
+287. terminal not open when the program starts bad (March 24, 1984)
+@x module 1332
+initialize; {set global variables to their starting values}
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin wake_up_terminal;
+ wterm('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+@y
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin t_open_out;
+ wterm('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+initialize; {set global variables to their starting values}
+@z
+
+*** Note: "Cosmetic" changes were also made in module 363 ("print_ln"
+moved to after "wake_up_terminal") and in some commentary.
+
+*** Another last-minute correction:
+288. \patterns{xxx...xxxdxxxdxxx} anomaly found by JRD (Mar 27, 1984)
+@x module 962
+else begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
+@y
+else begin hyf[k]:=cur_chr-"0";
+ if k<63 then digit_sensed:=true;
+@z
+
+*** Yoicks, yet ANOTHER:
+289. a whole case of copy_node_list forgotten (Apr 11, 1984)
+@x module 206
+othercases confusion("copying")
+@y
+adjust_node: begin r:=get_node(small_node_size);
+ adjust_ptr(r):=copy_node_list(adjust_ptr(p));
+ end; {|words=1=small_node_size-1|}
+othercases confusion("copying")
+@z
+
+*** And "finally"...
+290. uninitialized variables could be accessed (found by Nick Briggs, June 11)
+@x module 552
+font_size[null_font]:=0; font_dsize[null_font]:=0;
+@y
+font_size[null_font]:=0; font_dsize[null_font]:=0;
+char_base[null_font]:=0; width_base[null_font]:=0;
+height_base[null_font]:=0; depth_base[null_font]:=0;
+italic_base[null_font]:=0; lig_kern_base[null_font]:=0;
+kern_base[null_font]:=0; exten_base[null_font]:=0;
+@z (actual values are immaterial except for debugging safeguards)
+
+291. same, cf. modules 355 and 389 (Nick Briggs, June 11)
+@x module 331
+scanner_status:=normal; first:=1;
+@y
+for first:=0 to buf_size do buffer[first]:=0;
+scanner_status:=normal; warning_index:=null; first:=1;
+@z (error was harmless except that it might trigger debugging checks)
+
+292. missing ligature/kern (found by JRD, June 21)
+@x module 1036
+ if (cur_cmd=letter)or(cur_cmd=other_char) then r:=qi(cur_chr)
+@y
+ if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given) then
+ r:=qi(cur_chr)
+@z
+
+293. quarterword constraint is made explicit (July 4)
+@x module 111
+if (buf_size>max_halfword) then bad:=18;
+@y
+if buf_size>max_halfword then bad:=18;
+if max_quarterword-min_quarterword<255 then bad:=19;
+@z
+
+294. trivial optimization made for consistency with MF (July 7)
+@x module 363
+if (pausing>0)and(interaction>nonstop_mode) then
+@y
+if pausing>0 then if interaction>nonstop_mode then
+@z
+
+295. \tracingmacros>1 for more diagnostics (July 8)
+@x module 323
+ if t=macro then param_start:=param_ptr@+else loc:=link(p);
+@y
+ if t=macro then param_start:=param_ptr
+ else begin loc:=link(p);
+ if tracing_macros>1 then
+ begin begin_diagnostic; print_nl("");
+ case t of
+ mark_text:print_esc("mark");
+ write_text:print_esc("write");
+ othercases print_cmd_chr(assign_toks,t-output_text+output_routine_loc)
+ endcases;@/
+ print("->"); token_show(p); end_diagnostic(false);
+ end;
+ end;
+@z
+
+*** The changes above were incorporated in Version 1.1, released July 9, 1984.
+ Version 1.2
+
+296. "see the transcript file" on offline show commands. (July 27, 1984)
+@x module 1293
+@ @d show_error==@;@/
+ if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+ else if tracing_online>0 then
+ begin@t@>@;@/
+ help3("This isn't an error message; I'm just \showing something.")
+ @t\4\4@>@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).");
+ end
+ else begin@t@>@;@/
+ help5("This isn't an error message; I'm just \showing something.")
+ @t\4\4@>@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).")@/
+ ("And type `I\tracingonline=1\show...' to show boxes and")@/
+ ("lists on your terminal as well as in the transcript file.");
+ end;
+ error
+
+@<Declare act...@>=
+procedure show_whatever;
+var p:pointer; {tail of a token list to show}
+begin case cur_chr of
+show_code: @<Show the current meaning of a token@>;
+show_box_code: @<Show the current contents of a box@>;
+show_the_code: @<Show the current value of some parameter or register@>;
+othercases @<Show the current semantic nest@>
+endcases;
+end;
+@y
+@ @<Declare act...@>=
+procedure show_whatever;
+label common_ending;
+var p:pointer; {tail of a token list to show}
+begin case cur_chr of
+show_lists: begin begin_diagnostic; show_activities;
+ end;
+show_box_code: @<Show the current contents of a box@>;
+show_code: @<Show the current meaning of a token, then |goto common_ending|@>;
+othercases @<Show the current value of some parameter or register,
+ then |goto common_ending|@>
+endcases;@/
+@<Complete a potentially long \.{\\show} command@>;
+common_ending: if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+else if tracing_online>0 then
+ begin@t@>@;@/
+ help3("This isn't an error message; I'm just \showing something.")@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).");
+ end
+else begin@t@>@;@/
+ help5("This isn't an error message; I'm just \showing something.")
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).")@/
+ ("And type `I\tracingonline=1\show...' to show boxes and")@/
+ ("lists on your terminal as well as in the transcript file.");
+ end;
+error;
+end;
+@z
+@x module 1294
+@ @<Show the current meaning of a token@>=
+begin get_token; print_nl("> ");
+if cur_cs<>0 then
+ begin sprint_cs(cur_cs); print_char("=");
+ end;
+print_meaning; show_error;
+@y
+@ @<Show the current meaning of a token...@>=
+begin get_token; print_nl("> ");
+if cur_cs<>0 then
+ begin sprint_cs(cur_cs); print_char("=");
+ end;
+print_meaning; goto common_ending;
+@z
+@x module 1296
+end_diagnostic(true);
+print_err("OK");
+@.OK@>
+show_error;
+@y
+@z
+@x module 1297
+flush_list(link(temp_head)); show_error;
+@y
+flush_list(link(temp_head)); goto common_ending;
+@z
+@x module 1298
+@ @<Show the current semantic nest@>=
+begin begin_diagnostic; show_activities; end_diagnostic(true);
+print_err("OK");
+show_error;
+@.OK@>
+end
+@y
+@ @<Complete a potentially long \.{\\show} command@>=
+end_diagnostic(true); print_err("OK");
+@.OK@>
+if selector=term_and_log then if tracing_online<=0 then
+ begin selector:=term_only; print(" (see the transcript file)");
+ selector:=term_and_log;
+ end
+@z
+
+297. allow `0' in response to error prompts (October 20, 1984)
+@x module 84
+"1","2","3","4","5","6","7","8","9": if deletions_allowed then
+@y
+"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
+@z
+ Versions 1.3 and 1.4
+
+298. Dirty PASCAL (two quarterword 0's were read as a halfword) (25 Nov 84)
+@x module 846
+print(" -> @@@@"); print_int(serial(prev_break(passive)));
+@y
+print(" -> @@@@");
+if prev_break(passive)=null then print_char("0")
+else print_int(serial(prev_break(passive)));
+@z
+
+299. Ditto! (25 Nov 84)
+@x module 934
+label reswitch, exit, found, not_found;
+@y
+label reswitch, exit, found, not_found, done;
+@z
+@x module 939
+while info(p)>n-3 do {eliminate hyphens \TeX\ doesn't like}
+ begin q:=link(p); free_avail(p); p:=q;
+ end;
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+@y
+loop@+ begin if p=null then goto done;
+ if info(p)<n-2 then goto done;
+ q:=link(p); free_avail(p); p:=q; {eliminate hyphens that \TeX\ doesn't like}
+ end;
+done: @<Insert the \(p)pair |(s,p)| into the exception table@>;
+@z
+
+300. Major change to memory management (25 Nov 84)
+[The following changes have two main merits: (1) TeX should run faster on
+virtual-memory systems when the jobs are small; (2) it should be possible
+to use the same production version of TeX both for jobs with lots of macros
+and few boxes as well as for jobs with lots of boxes and few macros, because
+the boundary line between the two types of storage is now chosen dynamically.]
+[The user is unaffected, except that there's only one "memory size" overflow
+error now instead of two.]
+[The TRIP test is now changed slightly: The correct settings of memory
+size parameters are now mem_min=mem_bot=1, mem_top=mem_max=3000.]
+
+@x module 11
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|}
+@y
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=0; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@z
+@x module 12
+@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|}
+@y
+@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@z
+@x module 12
+ about |(mem_max-hi_mem_base)/6|, but 2100 is already quite generous}
+@y
+ about |(mem_max-mem_min)/10|, but 2100 is already quite generous}
+@z
+@x module 14
+if (hi_mem_base<mem_base+100)or(hi_mem_base+100>mem_max) then bad:=4;
+@y
+if mem_bot+1100>mem_top then bad:=4;
+@z
+@x module 111
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+@y
+init if (mem_min<>mem_bot)or(mem_max<>mem_top) then bad:=10;@+tini@;@/
+if (mem_min>mem_bot)or(mem_max<mem_top) then bad:=10;
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+@z
+@x module 111
+if (mem_base<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@y
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@z
+@x module 115
+value represents a null pointer.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==mem_base {the null pointer}
+@y
+value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==min_halfword {the null pointer}
+@z
+@x module 116
+@ The |mem| array is divided once and for all into two regions that are
+allocated separately. Locations less than |hi_mem_base| are used for storing
+@y
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+@z
+@x module 116
+relevant size when a node is freed. The remaining region of |mem| is
+allocated in single words using a conventional \.{AVAIL} stack.
+
+Incidentally, it would be feasible to construct implementations of \TeX\ that
+are based on 16-bit words instead of 32-bit words, for machines having
+comparatively small memories. In such cases it might be desirable to have
+two parallel arrays for the upper part of memory, called say \\{mem\_link}|[p]|
+and \\{mem\_info}|[p]|,
+since the single-word region in the present implementation
+consists entirely of |memory_word| items of type |two_halves|.
+@^small computers@>
+@y
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
+of preloaded format files, by the \.{INITEX} preprocessor.
+@.INITEX@>
+Production versions of \TeX\ may extend the memory at both ends in order to
+provide more space; locations between |mem_min| and |mem_bot| are always
+used for variable-size nodes, and locations between |mem_top| and |mem_max|
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\hbox{|null<=mem_min<=mem_bot<lo_mem_max<
+ hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
+@z
+@x module 116
+@!mem : array[mem_base..mem_max] of memory_word; {the big dynamic storage area}
+@y
+@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
+@!lo_mem_max : pointer; {the largest location of variable-size memory in use}
+@!hi_mem_min : pointer; {the smallest location of one-word memory in use}
+@z
+@x module 117
+@!max_var_used : integer; {how much memory was in use}
+@y
+@z
+@x module 118
+occur between |hi_mem_base| and |mem_end|, inclusive, are of type
+@y
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+@z
+@x module 120
+else begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("macro memory size",mem_max+1-hi_mem_base);
+ {quit; all one-word nodes are busy}
+@:TeX capacity exceeded macro memory size}{\quad macro memory size@>
+ end;
+@y
+else begin decr(hi_mem_min); p:=hi_mem_min;
+ if hi_mem_min<=lo_mem_max then
+ begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("main memory size",mem_max+1-mem_min);
+ {quit; all one-word nodes are busy}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+ end;
+ end;
+@z
+@x module 125
+label found,exit;
+@y
+label found,exit,restart;
+@z
+@x module 125
+begin p:=rover; {start at some free node in the ring}
+@y
+begin restart: p:=rover; {start at some free node in the ring}
+@z
+@x module 125
+overflow("box memory size",hi_mem_base-mem_base);
+ {sorry, nothing satisfactory is left}
+@:TeX capacity exceeded box memory size}{\quad box memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+if var_used>max_var_used then max_var_used:=var_used;
+@y
+if lo_mem_max+2<hi_mem_min then
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size",mem_max+1-mem_min);
+ {sorry, nothing satisfactory is left}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+@z
+@x modules 126--127
+@ Empirical tests show that the routine in this section performs a
+|remove_node| operation about 0.75 times per allocation, on the average,
+after which it finds that |r>p+1| about 95\% of the time.
+
+@<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+@^inner loop@>
+while is_empty(q) do @<Merge node |p| with node |q|@>;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+
+@ @<Merge node |p| with node |q|@>=
+begin t:=rlink(q);
+@^inner loop@>
+if q=rover then rover:=t;
+llink(t):=llink(q); rlink(llink(q)):=t;@/
+q:=q+node_size(q);
+end
+@y
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \TeX\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+begin if lo_mem_max+1000<hi_mem_min then t:=lo_mem_max+1000
+else t:=(lo_mem_max+hi_mem_min+2) div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
+rover:=q; goto restart;
+end
+
+@ Empirical tests show that the routine in this section performs a
+node-merging operation about 0.75 times per allocation, on the average,
+after which it finds that |r>p+1| about 95\% of the time.
+
+@<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+@^inner loop@>
+while is_empty(q) do {merge node |p| with node |q|}
+ begin t:=rlink(q);
+ if q=rover then rover:=t;
+ llink(t):=llink(q); rlink(llink(q)):=t;@/
+ q:=q+node_size(q);
+ end;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+@z
+% module 129 no longer refers to remove_node; I don't recall when it changed!
+@x module 134
+@d is_char_node(#) == (#>hi_mem_base)
+@y
+@d is_char_node(#) == (#>=hi_mem_min)
+@z
+@x module 162
+example, locations |mem_base| to |mem_base+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Dynamic allocation of variable-size
+nodes is restricted to locations |first_mem| through |(hi_mem_base-1)|,
+and single-word nodes are dynamically allocated in locations |second_mem|
+through |mem_max|, inclusive. It is harmless to let |lig_trick|, |garbage|,
+and |backup_head| share the same location of |mem|.
+
+@d zero_glue==mem_base {specification for \.{0pt plus 0pt minus 0pt}}
+@y
+example, locations |mem_bot| to |mem_bot+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
+harmless to let |lig_trick|, |garbage|, and |backup_head| share the same
+location of |mem|.
+
+@d zero_glue==mem_bot {specification for \.{0pt plus 0pt minus 0pt}}
+@z
+@x module 162
+@d first_mem==fil_neg_glue+glue_spec_size {first dynamically allocatable word
+ in the variable-size |mem|}
+@#
+@d page_ins_head==hi_mem_base {list of insertion data for current page}
+@d contrib_head==hi_mem_base+1 {vlist of items not yet on current page}
+@d page_head==hi_mem_base+2 {vlist for current page}
+@d temp_head==hi_mem_base+3 {head of a temporary list of some kind}
+@d hold_head==hi_mem_base+4 {head of a temporary list of another kind}
+@d adjust_head==hi_mem_base+5 {head of adjustment list returned by |hpack|}
+@d active==hi_mem_base+6 {head of active list in |line_break|, needs two words}
+@d align_head==hi_mem_base+8 {head of preamble list for alignments}
+@d end_span==hi_mem_base+9 {tail of spanned-width lists}
+@d omit_template==hi_mem_base+10 {a constant token list}
+@d null_list==hi_mem_base+11 {permanently empty list}
+@d lig_trick==hi_mem_base+12 {a ligature masquerading as a |char_node|}
+@d garbage==hi_mem_base+12 {used for scrap information}
+@d backup_head==hi_mem_base+13 {head of token list built by |scan_keyword|}
+@d second_mem==hi_mem_base+14 {first dynamically allocatable word in
+ the one-word |mem|}
+@y
+@d lo_mem_stat_max==fil_neg_glue+glue_spec_size-1 {largest statically
+ allocated word in the variable-size |mem|}
+@#
+@d page_ins_head==mem_top {list of insertion data for current page}
+@d contrib_head==mem_top-1 {vlist of items not yet on current page}
+@d page_head==mem_top-2 {vlist for current page}
+@d temp_head==mem_top-3 {head of a temporary list of some kind}
+@d hold_head==mem_top-4 {head of a temporary list of another kind}
+@d adjust_head==mem_top-5 {head of adjustment list returned by |hpack|}
+@d active==mem_top-6 {head of active list in |line_break|, needs two words}
+@d align_head==mem_top-8 {head of preamble list for alignments}
+@d end_span==mem_top-9 {tail of spanned-width lists}
+@d omit_template==mem_top-10 {a constant token list}
+@d null_list==mem_top-11 {permanently empty list}
+@d lig_trick==mem_top-12 {a ligature masquerading as a |char_node|}
+@d garbage==mem_top-12 {used for scrap information}
+@d backup_head==mem_top-13 {head of token list built by |scan_keyword|}
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@z
+@x module 164
+for k:=mem_base+1 to first_mem-1 do mem[k].sc:=0;
+ {all glue dimensions are zeroed}
+@^data structure assumptions@>
+k:=mem_base;@+while k<first_mem do {set first words of glue specifications}
+@y
+for k:=mem_bot+1 to lo_mem_stat_max do mem[k].sc:=0;
+ {all glue dimensions are zeroed}
+@^data structure assumptions@>
+k:=mem_bot;@+while k<=lo_mem_stat_max do
+ {set first words of glue specifications}
+@z
+@x module 164
+rover:=first_mem; link(rover):=empty_flag; {now initialize the dynamic memory}
+node_size(rover):=hi_mem_base-rover; {which is one big available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+link(hi_mem_base):=null; info(hi_mem_base):=null;
+for k:=hi_mem_base+1 to second_mem-1 do
+ mem[k]:=mem[hi_mem_base];{clear list heads}
+@<Initialize the special list heads and constant nodes@>;
+avail:=null; mem_end:=second_mem-1; {initialize the one-word memory}
+var_used:=first_mem-mem_base; dyn_used:=second_mem-hi_mem_base;
+max_var_used:=var_used; {initialize statistics}
+@y
+rover:=lo_mem_stat_max+1;
+link(rover):=empty_flag; {now initialize the dynamic memory}
+node_size(rover):=1000; {which is a 1000-word available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
+for k:=hi_mem_stat_min to mem_top do
+ mem[k]:=mem[lo_mem_max]; {clear list heads}
+@<Initialize the special list heads and constant nodes@>;
+avail:=null; mem_end:=mem_top;
+hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=mem_top+1-hi_mem_stat_min;
+ {initialize statistics}
+@z
+@x module 165
+@t\hskip1em@>@!was_mem_end: pointer; {previous |mem_end|}
+@y
+@t\hskip1em@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
+ {previous |mem_end|, |lo_mem_max|,and |hi_mem_min|}
+@z
+@x module 166
+@!debug was_mem_end:=mem_base; {indicate that everything was previously free}
+@y
+@!debug was_mem_end:=mem_min; {indicate that everything was previously free}
+was_lo_max:=mem_min; was_hi_min:=mem_max;
+@z
+@x module 167
+begin for p:=mem_base to mem_end do free[p]:=false; {you can probably
+ do this faster}
+@y
+begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
+ do this faster}
+for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
+@z
+@x module 167
+for p:=0 to mem_end do was_free[p]:=free[p]; {|was_free:=free| might be faster}
+was_mem_end:=mem_end;
+@y
+for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
+for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
+ {|was_free:=free| might be faster}
+was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
+@z
+@x module 168
+ begin if (p>mem_end)or(p<second_mem) then clobbered:=true
+@y
+ begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
+@z
+@x module 169
+repeat if (p>=hi_mem_base)or(p<first_mem) then clobbered:=true
+ else if (rlink(p)>=hi_mem_base)or(rlink(p)<first_mem) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>hi_mem_base)or@| (llink(rlink(p))<>p) then clobbered:=true;
+@y
+repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
+ else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
+@z
+@x module 170
+p:=mem_base;
+while p<=hi_mem_base do {node |p| should not be empty}
+@y
+p:=mem_min;
+while p<=lo_mem_max do {node |p| should not be empty}
+@z
+@x module 170
+ while (p<=hi_mem_base) and not free[p] do incr(p);
+ while (p<=hi_mem_base) and free[p] do incr(p);
+@y
+ while (p<=lo_mem_max) and not free[p] do incr(p);
+ while (p<=lo_mem_max) and free[p] do incr(p);
+@z
+@x module 171
+for p:=mem_base to mem_end do
+ if not free[p] and ((p>was_mem_end) or was_free[p]) then
+@y
+for p:=mem_min to lo_mem_max do
+ if not free[p] and ((p>was_lo_max) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+for p:=hi_mem_min to mem_end do
+ if not free[p] and
+ ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
+@z
+@x module 172
+begin for q:=mem_base to mem_end do
+@y
+begin for q:=mem_min to lo_mem_max do
+@z
+@x module 172
+@<Search |eqtb| for equivalents equal to |p|@>;
+@y
+for q:=hi_mem_min to mem_end do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+@<Search |eqtb| for equivalents equal to |p|@>;
+@z
+@x module 176
+if (p<hi_mem_base)or(p>mem_end) then print_esc("CLOBBERED.")
+@y
+if (p<hi_mem_min)or(p>mem_end) then print_esc("CLOBBERED.")
+@z
+@x module 178
+begin if (p<mem_base)or(p>=hi_mem_base) then print_char("*")
+@y
+begin if (p<mem_min)or(p>=lo_mem_max) then print_char("*")
+@z
+@x module 293
+if (p<hi_mem_base) or (p>mem_end) then
+@y
+if (p<hi_mem_min) or (p>mem_end) then
+@z
+@x module 639
+ print_int(dyn_used); print("; max so far: ");
+ print_int(max_var_used); print_char("&");
+ print_int(mem_end+1-hi_mem_base); print_ln;
+@y
+ print_int(dyn_used); print("; still untouched: ");
+ print_int(hi_mem_min-lo_mem_max-1); print_ln;
+@z
+@x module 857
+begin save_link:=link(cur_p); {this is OK even if |cur_p=null|}
+link(cur_p):=null; print_nl(""); short_display(link(printed_node));
+link(cur_p):=save_link; printed_node:=cur_p;
+@y
+begin print_nl("");
+if cur_p=null then short_display(link(printed_node))
+else begin save_link:=link(cur_p);
+ link(cur_p):=null; print_nl(""); short_display(link(printed_node));
+ link(cur_p):=save_link;
+ end;
+printed_node:=cur_p;
+@z
+@x module 1249
+if 2*max_halfword<hi_mem_base-mem_base then bad:=41;
+@y
+if 2*max_halfword<mem_top-mem_min then bad:=41;
+@z
+@x module 1307
+dump_int(hi_mem_base);@/
+@y
+dump_int(mem_bot);@/
+dump_int(mem_top);@/
+@z
+@x module 1308
+if x<>hi_mem_base then goto bad_fmt;
+@y
+if x<>mem_bot then goto bad_fmt;
+undump_int(x);
+if x<>mem_top then goto bad_fmt;
+@z
+@x module 1311
+dump_int(rover);
+p:=mem_base; q:=rover; x:=0;
+@y
+dump_int(lo_mem_max); dump_int(rover);
+p:=mem_bot; q:=rover; x:=0;
+@z
+@x module 1311
+var_used:=var_used+hi_mem_base-p; dyn_used:=mem_end+1-hi_mem_base;@/
+dump_int(mem_end); dump_int(avail);
+for k:=p to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-p;
+@y
+var_used:=var_used+lo_mem_max-p; dyn_used:=mem_end+1-hi_mem_min;@/
+for k:=p to lo_mem_max do dump_wd(mem[k]);
+x:=x+lo_mem_max+1-p;
+dump_int(hi_mem_min); dump_int(avail);
+for k:=hi_mem_min to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-hi_mem_min;
+@z
+@x module 1312
+undump(min_halfword)(hi_mem_base)(rover);
+p:=mem_base; q:=rover; x:=0;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>hi_mem_base)or((q>=rlink(q))and(rlink(q)<>rover)) then goto bad_fmt;
+q:=rlink(q);
+until q=rover;
+undump_size(hi_mem_base)(mem_max)('mem max')(mem_end);
+undump(null)(mem_end)(avail);
+for k:=p to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used);
+max_var_used:=var_used
+@y
+undump(lo_mem_stat_max+1000)(hi_mem_stat_min-1)(lo_mem_max);
+undump(lo_mem_stat_max+1)(lo_mem_max)(rover);
+p:=mem_bot; q:=rover; x:=0;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>lo_mem_max)or((q>=rlink(q))and(rlink(q)<>rover)) then goto bad_fmt;
+q:=rlink(q);
+until q=rover;
+for k:=p to lo_mem_max do undump_wd(mem[k]);
+if mem_min<mem_bot-2 then {make more low memory available}
+ begin p:=llink(rover); q:=mem_min+1;
+ link(mem_min):=null; info(mem_min):=null; {we don't use the bottom word}
+ rlink(p):=q; llink(rover):=q;@/
+ rlink(q):=rover; llink(q):=p; link(q):=empty_flag;
+ node_size(q):=mem_bot-q;
+ end;
+undump(lo_mem_max+1)(hi_mem_stat_min)(hi_mem_min);
+undump(null)(mem_top)(avail); mem_end:=mem_top;
+for k:=hi_mem_min to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used)
+@z
+@x module 1323
+undump(min_halfword)(hi_mem_base)(font_glue[k]);
+@y
+undump(min_halfword)(lo_mem_max)(font_glue[k]);
+@z
+@x module 1334
+ wlog_ln(' ',max_var_used:1,'&',mem_end+1-hi_mem_base:1,@|
+ ' words of memory out of ',
+ hi_mem_base-mem_base:1,'&',mem_max+1-hi_mem_base:1);@/
+@y
+ wlog_ln(' ',lo_mem_max-mem_min+mem_end-hi_mem_min+2:1,@|
+ ' words of memory out of ',mem_end+1-mem_min:1);@/
+@z that's the end of change number 300!
+
+301. node_size field could overflow if low mem was too large (Dec 20, 1984)
+[This bug existed from the beginning, if hi_mem_base-first_mem>max_halfword!]
+@x module 111
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@y
+if (mem_min<min_halfword)or(mem_max>=max_halfword)or@|
+ (mem_bot-mem_min>max_halfword+1) then bad:=14;
+@z
+@x module 125
+@!t:pointer; {temporary register}
+@y
+@!t:integer; {temporary register}
+@z
+@x module 125
+if lo_mem_max+2<hi_mem_min then
+@y
+if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_bot+max_halfword then
+@z
+@x module 126
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+@y
+if t>mem_bot+max_halfword then t:=mem_bot+max_halfword;
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+@z
+
+302. Improvement to missing-format-file error (DRF, 5 Jan 85)
+@x module 524
+var j:0..buf_size; {the first space after the file name}
+begin if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then
+ begin loc:=j; goto found;
+ end;@/
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then
+ begin loc:=j; goto found;
+ end;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ end;
+ {now pull out all the stops: try for the system \.{PLAIN} file}
+pack_buffered_name(format_default_length-format_ext_length,1,0);
+if not w_open_in(fmt_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN format file!');
+@.I can't find PLAIN...@>
+@.PLAIN@>
+ open_fmt_file:=false; return;
+ end;
+found:open_fmt_file:=true;
+@y
+var j:0..buf_size; {the first space after the format file name}
+begin j:=loc;
+if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then goto found;
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then goto found;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ update_terminal;
+ end;
+ {now pull out all the stops: try for the system \.{PLAIN} file}
+pack_buffered_name(format_default_length-format_ext_length,1,0);
+if not w_open_in(fmt_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN format file!');
+@.I can't find PLAIN...@>
+@.PLAIN@>
+ open_fmt_file:=false; return;
+ end;
+found:loc:=j; open_fmt_file:=true;
+@z
+
+303. Welcoming message appears sooner (DRF, Jan 7)
+@x module 61
+else begin print(format_ident); print_ln;
+ end;
+@y
+else begin print(format_ident); print_ln;
+ end;
+update_terminal;
+@z
+
+304. Minor change to help message in "confusion" (Jan 23)
+@x module 95
+ help2("One of your earlier faux pas has wounded me deeply,")@/
+ ("so I'm barely conscious. Please fix it and try again.");
+@y
+ help2("One of your faux pas seems to have wounded me deeply...")@/
+ ("in fact, I'm barely conscious. Please fix it and try again.");
+@z
+
+305. Improved logic for `(see the transcript file...)' (Jan 23)
+@x module 245
+if history=spotless then history:=warning_issued;
+if (tracing_online<=0)and(selector=term_and_log) then decr(selector);
+@y
+if (tracing_online<=0)and(selector=term_and_log) then
+ begin decr(selector);
+ if history=spotless then history:=warning_issued;
+ end;
+@z
+
+306. Change 291 violated standard PASCAL (CET1, Feb 18)
+@x module 331
+for first:=0 to buf_size do buffer[first]:=0;
+@y since some procedures `threaten' |first| globally [BS/6192 sec 6.8.3.9]
+first:=buf_size; repeat buffer[first]:=0; decr(first); until first=0;
+@z
+
+307. Nonexistent character could be output via ligature hyphenation (April 11)
+@x module 582 (slight inefficiency removed while correcting this)
+begin if (font_bc[f]<=c)and(font_ec[f]>=c) then
+@y
+begin if font_bc[f]<=c then if font_ec[f]>=c then
+@z
+@x module 892 (likewise)
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@y
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@!hyf_char:integer; {hyphen character of the relevant font}
+@z
+@x module 896 (likewise)
+done2: if hyphen_char[hf]<0 then goto done1;
+if hyphen_char[hf]>255 then goto done1;
+@y
+done2: hyf_char:=hyphen_char[hf];
+if hyf_char<0 then goto done1;
+if hyf_char>255 then goto done1;
+@z
+@x module 912 and another local variable
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@y
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@!hyf_node:pointer; {the hyphen, if it exists}
+@z
+@x module 915 (this is the real bugfix)
+minor_tail:=null; c:=hu[i+1]; hu[i+1]:=hyphen_char[hf];
+repeat l:=reconstitute(l+1,i+1);
+if minor_tail=null then pre_break(r):=link(hold_head)
+else link(minor_tail):=link(hold_head);
+minor_tail:=link(hold_head);
+if link(minor_tail)<>null then minor_tail:=link(minor_tail);
+until l>i;
+hu[i+1]:=c; {restore the character in the hyphen position}
+decr(l); hyf[l]:=0
+@y
+minor_tail:=null; hyf_node:=new_character(hf,hyf_char);
+if hyf_node<>null then
+ begin incr(i); c:=hu[i]; hu[i]:=hyf_char;
+ end;
+repeat l:=reconstitute(l+1,i);
+if minor_tail=null then pre_break(r):=link(hold_head)
+else link(minor_tail):=link(hold_head);
+minor_tail:=link(hold_head);
+if link(minor_tail)<>null then minor_tail:=link(minor_tail);
+until l=i;
+if hyf_node<>null then
+ begin hu[i]:=c; {restore the character in the hyphen position}
+ free_avail(hyf_node); decr(i); l:=i;
+ end;
+hyf[i]:=0
+@z
+@x module 918
+begin r:=new_disc; pre_break(r):=new_character(hf,hyphen_char[hf]);
+@y
+begin r:=new_disc; pre_break(r):=new_character(hf,hyf_char);
+@z
+
+308. Improper memory usage computed by INITEX in change 300. (DRF, April 15)
+@x module 162
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@y
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@d hi_mem_stat_usage=14 {the number of one-word nodes always present}
+@z
+@x module 164 (text macro without parentheses strikes again!)
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=mem_top+1-hi_mem_stat_min;
+@y
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=hi_mem_stat_usage;
+@z
+
+309. A faster |flush_list| routine for the inner loop. (DRF, April 16)
+@x module 123
+var q:pointer; {the successor of node |p|}
+begin while p<>null do
+ begin q:=link(p); free_avail(p); p:=q;
+ end;
+@y
+var @!q,@!r:pointer; {list traversers}
+begin if p<>null then
+ begin r:=p;
+ repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
+ until r=null; {now |q| is the last node on the list}
+ link(q):=avail; avail:=p;
+ end;
+@z
+
+310. Kern for accent positioning could disappear after a line break (April 17)
+(Change 269 fixed only part of the problem!)
+@x module 155
+@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/} and accents}
+@y
+@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/}}
+@d acc_kern=2 {|subtype| of kern nodes from accents}
+@z
+@x module 191
+if subtype(p)<=explicit then
+ begin print_esc("kern");
+ if subtype(p)=explicit then print_char(" ");
+ print_scaled(width(p));
+@y
+if subtype(p)<>mu_glue then
+ begin print_esc("kern");
+ if subtype(p)<>normal then print_char(" ");
+ print_scaled(width(p));
+ if subtype(p)=acc_kern then print(" (for accent)");
+@.for accent@>
+@z
+@x module 837
+ math_node,kern_node: break_width[1]:=break_width[1]-width(s);
+@y
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@z
+@x module 879
+ if non_discardable(q) then goto done1;
+@y
+ if non_discardable(q) then goto done1;
+ if subtype(q)=acc_kern then if type(q)=kern_node then goto done1;
+@z
+@x module 1125
+r:=new_kern(delta); subtype(r):=explicit; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=explicit; link(p):=tail; p:=q;
+@y
+r:=new_kern(delta); subtype(r):=acc_kern; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=acc_kern; link(p):=tail; p:=q;
+@z
+
+311. \lastbox and \unkern must not remove discretionary replacements (April 18)
+(Change 236 introduced this bug!)
+@x module 1079
+label exit;
+var p:pointer; {runs through the current list}
+@y
+label exit, done;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+@z
+@x modules 1080 and 1081
+ @<Apologize for inability to do \.{\\lastbox} now@>
+else begin if not is_char_node(tail) then
+ if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
+ begin p:=head; cur_box:=tail; shift_amount(cur_box):=0;
+ while link(p)<>tail do p:=link(p);
+ tail:=p; link(p):=null;
+ end;
+ end;
+end
+
+@ @<Apologize for inability to do \.{\\lastbox} now@>=
+begin you_cant;
+help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. This \lastbox will therefore be void."); error;
+end
+@y
+ begin you_cant;
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("This \lastbox will therefore be void."); error;
+ end
+else begin if not is_char_node(tail) then
+ if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
+ @<Remove the last box, unless it's part of a discretionary@>;
+ end;
+end
+
+@ @<Remove the last box...@>=
+begin q:=head;
+repeat p:=q;
+if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then goto done;
+ end;
+q:=link(p);
+until q=tail;
+cur_box:=tail; shift_amount(cur_box):=0;
+tail:=p; link(p):=null;
+done: end
+@z
+@x module 1105
+var p:pointer; {runs through the current list}
+@y
+label exit;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+@z
+@x module 1105
+ begin p:=head;
+ while link(p)<>tail do p:=link(p);
+ link(p):=null; flush_node_list(tail); tail:=p;
+ end;
+ end;
+end;
+@y
+ begin q:=head;
+ repeat p:=q;
+ if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then return;
+ end;
+ q:=link(p);
+ until q=tail;
+ link(p):=null; flush_node_list(tail); tail:=p;
+ end;
+ end;
+exit:end;
+@z
+@x module 1106
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("page. Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("page. Perhaps you can make the output routine do it.");
+@y
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("Perhaps you can make the output routine do it.");
+@z
+@x module 1120
+if n<128 then replace_count(tail):=n
+@y
+if n<=max_quarterword then replace_count(tail):=n
+@z
+
+
+*** Note by DEK, 18 Apr 1985
+(The code for version 1.3 has been published by Addison-Wesley under the
+title "TeX: The Program". It was phototypeset on our APS, but does not
+contain changes > 300. Some of those changes have been made in versions
+of TeX called 1.3; but the exact definition of version 1.3 is somewhat
+vague. Version 1.4 was released on April 18, 1985, after
+making change 311. At present I believe Version 1.4 will be identical
+to Version 2.0 except perhaps for things like index entries in the
+documentation and slightly revised help messages. Version 2.0 will
+coincide with the publication of Volume 1 of Computers and Typesetting,
+which will come out when METAFONT and Computer Modern are both finished.)
+ Version 1.5
+312. if nonstandard area specified, don't try TEX_area too. (DRF, 26 Apr 85)
+@x module 537
+ pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+@y
+ if cur_area="" then
+ begin pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@z
+
+313. length of \write need no longer be limited (cf 235). (NBT, 30 Apr 85)
+@x module 1370
+show_token_list(link(def_ref),null,buf_size-10); print_ln;
+@y
+show_token_list(link(def_ref),null,10000000); print_ln;
+@z
+
+314. bug introduced in #300, just lucky it didn't cause trouble (CET1, 8 May 85)
+@x module 162
+@d active==mem_top-6 {head of active list in |line_break|, needs two words}
+@y
+@d active==mem_top-7 {head of active list in |line_break|, needs two words}
+@z
+
+315. Missing eoln after last typeout (Gropp, 11 May 85)
+@x module 1332
+ wterm('Ouch---my internal constants have been',
+@y
+ wterm_ln('Ouch---my internal constants have been',
+@z
+
+316. Redundancy (CET1, 17 May 85)
+[Change 287 was apparently introduced because of a faulty assumption,
+although its change to the calling of "initialize" was valid.]
+@x module 1332
+ begin t_open_out;
+ wterm_ln('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@y
+ begin wterm_ln('Ouch---my internal constants have been clobbered!',
+ '---case ',bad:1);
+@z
+
+317. batchmode to see if log file couldn't be opened (DRF, 22 May 85)
+@x module 92
+if interaction=batch_mode then decr(selector);
+if job_name=0 then open_log_file;
+@y
+if job_name=0 then open_log_file;
+if interaction=batch_mode then decr(selector);
+@z
+
+318. If the string pool overflows while reading the command line... (22 May 85)
+[This bug was first found in METAFONT, when it could occur more easily]
+@x module 525
+begin str_room(name_length);
+for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+make_name_string:=make_string;
+@y
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+@z
+
+319. Bug in \edef\foo{\iffalse\fi\the\toks0} (found by Dan Brotsky, 7 Aug 85)
+@x module 478
+ if cur_cmd<>the then goto done2;
+ q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+@y
+ if cur_cmd<=max_command then goto done2;
+ if cur_cmd<>the then expand
+ else begin q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+ end;
+@z
+
+*** Version 1.5 was released on August 7, 1985.
+
+320. Lowercase `plain' for consistency with the manual
+@x module 521
+TEX_format_default:='TeXformats:PLAIN.fmt';
+@.TeXformats@>
+@.PLAIN@>
+@y
+TEX_format_default:='TeXformats:plain.fmt';
+@.TeXformats@>
+@.plain@>
+@z (index entries also changed to lowercase in modules 524, 1331)
+
+321. Terminal woken up by \show and \showthe (November 27, 1985)
+@x module 1294
+begin get_token; print_nl("> ");
+@y
+begin get_token;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> ");
+@z
+@x module 1297
+begin p:=the_toks; print_nl("> "); token_show(temp_head);
+@y
+begin p:=the_toks;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> "); token_show(temp_head);
+@z
+ Changes subsequent to `Version 2.0' as published in C&T, Volume B:
+
+322. Trivial change to a help message.
+@x module 1283
+ ("Pretend that you're Hercule Poirot, examine all clues,")@/
+@y
+ ("Pretend that you're Hercule Poirot: Examine all clues,")@/
+@z
+
+323. Precaution since |index| can be as high as |max_in_open|. (DRF)
+@x module 14
+if hash_prime>hash_size then bad:=5;
+@y
+if hash_prime>hash_size then bad:=5;
+if max_in_open>=128 then bad:=6;
+@z
+
+324. \kern clobbered at end of pre-break list in discretionary break.
+@x module 881
+ |disc_break:=true|@>;
+ if not is_char_node(q) then
+ if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
+@y
+ |disc_break:=true|@>
+ else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
+@z
+
+325. width after discretionary didn't take account of discarded nodes.
+@x module 830 (slight redefinition)
+@!t:quarterword; {replacement count, if |cur_p| is a discretionary node}
+@y
+@!t:integer; {node count, if |cur_p| is a discretionary node}
+@z
+@x module 837 (slight rearrangement)
+if (break_type=unhyphenated)or(cur_p=null) then
+ begin s:=cur_p;
+ while s<>null do
+ begin if is_char_node(s) then goto done;
+ case type(s) of
+ glue_node:@<Subtract glue from |break_width|@>;
+ penalty_node: do_nothing;
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+ othercases goto done
+ endcases;@/
+ s:=link(s);
+ end;
+ end
+else @<Compute the discretionary |break_width| values@>;
+@y
+s:=cur_p;
+if break_type>unhyphenated then if cur_p<>null then
+ @<Compute the discretionary |break_width| values@>;
+while s<>null do
+ begin if is_char_node(s) then goto done;
+ case type(s) of
+ glue_node:@<Subtract glue from |break_width|@>;
+ penalty_node: do_nothing;
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+ othercases goto done
+ endcases;@/
+ s:=link(s);
+ end;
+@z
+@x module 840 (this is the main change to fix the bug)
+begin t:=replace_count(cur_p); s:=cur_p;
+while t>0 do
+ begin decr(t); s:=link(s);
+ @<Subtract the width of node |s| from |break_width|@>;
+ end;
+s:=post_break(cur_p);
+while s<>null do
+ begin @<Add the width of node |s| to |break_width|@>;
+@y
+begin t:=replace_count(cur_p); v:=cur_p; s:=post_break(cur_p);
+while t>0 do
+ begin decr(t); v:=link(v);
+ @<Subtract the width of node |v| from |break_width|@>;
+ end;
+while s<>null do
+ begin @<Add the width of node |s| to |break_width| and increase |t|,
+ unless it's discardable@>;
+@z
+@x module 840, continued
+break_width[1]:=break_width[1]+disc_width;
+@y
+break_width[1]:=break_width[1]+disc_width;
+if t=0 then s:=link(v); {more nodes may also be discardable after the break}
+@z
+@x module 841 (here we just change |s| to |v|)
+@<Subtract the width of node |s|...@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));@/
+ break_width[1]:=@|break_width[1]-
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(s);
+@y
+@<Subtract the width of node |v|...@>=
+if is_char_node(v) then
+ begin f:=font(v);
+ break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
+ end
+else case type(v) of
+ ligature_node: begin f:=font(lig_char(v));@/
+ break_width[1]:=@|break_width[1]-
+ char_width(f)(char_info(f)(character(lig_char(v))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(v);
+@z
+@x module 842
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases
+@y
+ hlist_node,vlist_node,rule_node:break_width[1]:=break_width[1]+width(s);
+ kern_node: if (t=0)and(subtype(s)<>acc_kern) then t:=-1 {discardable}
+ else break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases;
+incr(t)
+@z
+
+*** Version 2.1 was released on January 26, 1987.
+
+326. Removal of redundant code (found by Pat Monardo, 5 Feb 87)
+@x module 1224 [this change need not be made, since it has no effect]
+ char_def_code: define(p,char_given,cur_val);
+ math_char_def_code: define(p,math_given,cur_val);
+@y the cases cannot occur, so we simply don't list them
+@z
+
+327. More robustness is needed when debugging (Ronaldo Am\'a, 14 Apr 87)
+@x module 174
+begin while p>null do
+@y maybe mem_min>null (but nodes shouldn't be put in location mem_min exactly)
+begin while p>mem_min do
+@z
+@x module 182
+@p procedure show_node_list(@!p:pointer); {prints a node list symbolically}
+@y
+@p procedure show_node_list(@!p:integer); {prints a node list symbolically}
+@z
+@x module 182, continued
+while p>null do
+@y
+while p>mem_min do
+@z
+
+328. Storage allocation can be more elegant and efficient (4/21/87)
+@x module 127
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+@y
+if r=p then if rlink(p)<>p then
+@z
+
+329. Miscalculation of empty-line condition (April 22, 1987)
+@x module 360
+ begin if limit=start then {previous line was empty}
+@y
+ begin if (end_line_char<0)or(end_line_char>127) then incr(limit);
+ if limit=start then {previous line was empty}
+@z
+
+330. A case where we don't have to assume system bookkeeping (April 28, 1987)
+@x module 560
+done: b_close(tfm_file); read_font_info:=g;
+@y [suggested by Jim Sterken]
+done: if file_opened then b_close(tfm_file);
+read_font_info:=g;
+@z [this was not a bug, according to the comment in module 28]
+
+331. jump_out must fix unfinished output (Found by Klaus Gunterman, 3 Aug 87)
+@x module 593
+doing_leaders:=false; dead_cycles:=0;
+@y
+doing_leaders:=false; dead_cycles:=0; cur_s:=-1;
+@z
+@x module 617
+cur_s:=-1; ensure_dvi_open;
+@y
+ensure_dvi_open;
+@z
+@x module 640
+dvi_out(eop); incr(total_pages);
+@y
+dvi_out(eop); incr(total_pages); cur_s:=-1;
+@z
+@x module 642
+if total_pages=0 then print_nl("No pages of output.")
+@y
+while cur_s>-1 do
+ begin if cur_s>0 then dvi_out(pop)
+ else begin dvi_out(eop); incr(total_pages);
+ end;
+ decr(cur_s);
+ end;
+if total_pages=0 then print_nl("No pages of output.")
+@z
+
+332. \hangindent=1pt$$\halign{...\cr\noalign{\hrule}}$$ problem (19 Aug 87)
+@x module 805
+q:=link(head);
+@y
+q:=link(head); s:=head;
+@z
+@x module 805, continued
+ q:=link(q);
+@y
+ s:=q; q:=link(q);
+@z
+@x module 806
+if is_running(depth(q)) then depth(q):=depth(p);
+@y
+if is_running(depth(q)) then depth(q):=depth(p);
+if o<>0 then
+ begin r:=link(q); link(q):=null; q:=hpack(q,natural);
+ shift_amount(q):=o; link(q):=r; link(s):=q;
+ end;
+@z
+
+333. \hskip 0pt plus 1fil\ifdim problem (found by Alan Guth, 20 Aug 87)
+@x module 366
+@!cvl_backup,@!radix_backup:small_number; {to save |cur_val_level| and |radix|}
+@y
+@!cvl_backup,@!radix_backup,@!co_backup:small_number;
+ {to save |cur_val_level|, etc.}
+@z
+@x
+backup_backup:=link(backup_head);
+@y
+co_backup:=cur_order; backup_backup:=link(backup_head);
+@z
+@x
+link(backup_head):=backup_backup;
+@y
+cur_order:=co_backup; link(backup_head):=backup_backup;
+@z
+
+334. leaders too sensitive near exact multiples (M. F. Bridgland, 9 Nov 87)
+@x module 626
+ begin edge:=cur_h+rule_wd; lx:=0;
+@y
+ begin rule_wd:=rule_wd+10; {compensate for floating-point rounding}
+ edge:=cur_h+rule_wd; lx:=0;
+@z
+@x ibid
+ cur_h:=edge; goto next_p;
+@y
+ cur_h:=edge-10; goto next_p;
+@z
+@x module 635
+ begin edge:=cur_v+rule_ht; lx:=0;
+@y
+ begin rule_ht:=rule_ht+10; {compensate for floating-point rounding}
+ edge:=cur_v+rule_ht; lx:=0;
+@z
+@x ibid
+ cur_v:=edge; goto next_p;
+@y
+ cur_v:=edge-10; goto next_p;
+@z
+
+335. Glitch in fixed-point multiplication of negatives (W.G. Sullivan, 17Nov87)
+@x module 572
+begin alpha:=16*z; beta:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; beta:=beta div 2;
+ end;
+@y
+begin alpha:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; alpha:=alpha+alpha;
+ end;
+beta:=256 div alpha; alpha:=alpha*z;
+@z
+
+336. If there are no \patterns and some \lccode is 1 (Breitenlohner, 12Dec87)
+@x module 952
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=0;
+@y
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=min_quarterword;
+@z
+
+337. \csname might encounter undefined_cs in a group (Chris Thompson, 23Dec87)
+@x module 372
+ begin eqtb[cur_cs]:=eqtb[frozen_relax];
+@y
+ begin eq_define(cur_cs,relax,256);
+@z
+
+338. \outer\def\a0{}\a\a shows temp_head list garbage (Silvio Levy, 20Apr88)
+@x module 391
+repeat if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
+ p:=temp_head; link(p):=null; m:=0;
+@y
+repeat link(temp_head):=null;
+if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
+ p:=temp_head; m:=0;
+@z
+
+339. \def\\#1{}\input a\\\z failed (Robert Messer, 24Apr88)
+@x module 259
+var h:integer; {hash code}
+@y
+var h:integer; {hash code}
+@!d:integer; {number of characters in incomplete current string}
+@z
+@x module 260
+str_room(l);
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string;
+@y
+str_room(l); d:=cur_length;
+while pool_ptr>str_start[str_ptr] do
+ begin decr(pool_ptr); str_pool[pool_ptr+l]:=str_pool[pool_ptr];
+ end; {move current string up to make room for another}
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string; pool_ptr:=pool_ptr+d;
+@z
+
+340. Make patterns work when trie_min=0 (Peter Breitenlohner, 10May88)
+@x module 951
+trie_max:=128; trie_min:=128; trie_link(0):=1; trie_taken[0]:=false;
+@y
+trie_max:=128; trie_min:=128; trie_link(0):=1; trie_taken[0]:=false;
+trie_link(trie_size):=0; trie_back(0):=trie_size; {wrap around}
+@z
+@x module 953
+begin c:=trie_c[p]; {we have |c>0|}
+if c<trie_min then trie_min:=c;
+z:=trie_link(trie_min-1); {get the first conceivably good hole}
+@y
+begin c:=trie_c[p];
+if c<trie_min then trie_min:=c;
+if trie_min=0 then z:=trie_link(trie_size)
+else z:=trie_link(trie_min-1); {get the first conceivably good hole}
+@z
+@x module 966
+r:=0; {finally, we will zero out the holes}
+@y
+r:=trie_size; {finally, we will zero out the holes}
+@z
+
+341. Avoid possible trie_pointer out of range (24May88)
+@x module 923
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=256; {insert delimiters}
+@y
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=128; {insert delimiters}
+@z
+
+342. \input a\romannumeral1 etc.; similar to bug 339. (25May88)
+@x module 464
+@p function str_toks:pointer; {changes the current string to a token list}
+@y
+@p function str_toks(@!b:pool_pointer):pointer;
+ {changes the string |str_pool[b..pool_ptr]| to a token list}
+@z
+@x module 464, continued
+p:=temp_head; link(p):=null; k:=str_start[str_ptr];
+@y
+p:=temp_head; link(p):=null; k:=b;
+@z
+@x module 464, concluded
+pool_ptr:=str_start[str_ptr]; str_toks:=p;
+@y
+pool_ptr:=b; str_toks:=p;
+@z
+@x module 465
+begin get_x_token; scan_something_internal(tok_val,false);
+if cur_val_level>=ident_val then @<Copy the token list@>
+else begin old_setting:=selector; selector:=new_string;
+@y
+@!b:pool_pointer; {base of temporary string}
+begin get_x_token; scan_something_internal(tok_val,false);
+if cur_val_level>=ident_val then @<Copy the token list@>
+else begin old_setting:=selector; selector:=new_string; b:=pool_ptr;
+@z
+@x module 465, continued
+ selector:=old_setting; the_toks:=str_toks;
+@y
+ selector:=old_setting; the_toks:=str_toks(b);
+@z
+@x module 470
+begin c:=cur_chr; @<Scan the argument for command |c|@>;
+old_setting:=selector; selector:=new_string;
+@<Print the result of command |c|@>;
+selector:=old_setting; link(garbage):=str_toks; ins_list(link(temp_head));
+@y
+@!b:pool_pointer; {base of temporary string}
+begin c:=cur_chr; @<Scan the argument for command |c|@>;
+old_setting:=selector; selector:=new_string; b:=pool_ptr;
+@<Print the result of command |c|@>;
+selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head));
+@z
+
+343. **\input\romannumeral6 confusion bypassed (25May88)
+@x module 525
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+@y
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+@z
+
+344. Avoid negative dividend rounding upward (Chris Thompson, fixed 19Jun88)
+@x module 126
+else t:=(lo_mem_max+hi_mem_min+2) div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@y
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max)div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@z
+
+345. Better strategy when near memory overflow (Chris Thompson)
+@x module 126
+begin if lo_mem_max+1000<hi_mem_min then t:=lo_mem_max+1000
+@y
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+@z
+
+346. An omission from change 333 (Tsunetoshi Hayashi, reported 20June88)
+@x module 439
+cur_val:=0; cur_val_level:=int_val; radix:=0;
+@y
+cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=0;
+@z
+
+347. Avoid fatal_error after terminal eof (Tim Morgan, reported 25Oct88)
+@x module 71 [serious problem occurred if this was called in open_log_file]
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@y
+if not input_ln(term_in,true) then t_open_in;
+@z
+
+348. Force terminal output when open_log_file aborts (6Nov88)
+@x module 535
+ begin print_err("I can't write on file `");
+@y
+ begin selector:=term_only; print_err("I can't write on file `");
+@z
+
+349. By popular request, undo #347 and fix the bug a more complex way.
+@x module 71 [this undoes change #347]
+if not input_ln(term_in,true) then t_open_in;
+@y
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@z
+@x module 92 [now we get to the new stuff]
+begin if job_name>0 then selector:=term_and_log
+@y
+begin if log_opened then selector:=term_and_log
+@z
+@x module 93
+ error;
+@y
+ if log_opened then error;
+@z
+@x module 527
+@!job_name:str_number; {principal file name}
+@y
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+@z
+@x module 528
+@<Initialize the output...@>=job_name:=0; name_in_progress:=false;
+@y
+@<Initialize the output...@>=
+job_name:=0; name_in_progress:=false; log_opened:=false;
+@z
+@x module 534
+selector:=log_only;
+@y
+selector:=log_only; log_opened:=true;
+@z
+@x module 535
+begin if interaction<scroll_mode then {bypass |fatal_error|}
+ begin selector:=term_only; print_err("I can't write on file `");
+@.I can't write on file x@>
+ print_file_name(cur_name,cur_area,cur_ext); print("'.");@/
+ job_name:=0; history:=fatal_error_stop; jump_out;
+ end; {abort the program without a log file}
+@y
+begin selector:=term_only;
+@z
+@x module 1265 [this change is optional, but it's a slight improvement]
+if job_name<>0 then selector:=selector+2;
+@y
+if log_opened then selector:=selector+2;
+@z
+@x module 1333
+if job_name>0 then
+@y
+if log_opened then
+@z
+@x module 1334
+if job_name>0 then {the log file is open}
+@y
+if log_opened then
+@z
+
+350. Forgotten update in error recovery (Breitenlohner, 20 Jan 89)
+@x module 976
+ glue_ptr(p):=r;
+@y
+ glue_ptr(p):=r; q:=r;
+@z
+@x module 1004
+ glue_ptr(p):=r;
+@y
+ glue_ptr(p):=r; q:=r;
+@z
+
+351. Avoid error \aftergroup\relax\dump (Mittelbach&Schoepf, 17 Feb 89)
+@x module 280
+begin check_full_save_stack;
+save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
+save_index(save_ptr):=t; incr(save_ptr);
+@y
+begin if cur_level>level_one then
+ begin check_full_save_stack;
+ save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
+ save_index(save_ptr):=t; incr(save_ptr);
+ end;
+@z
+
+352. Cosmetic change to validate comment in 115 (Breitenlohner, 20 Mar 89)
+@x module 791
+begin q:=link(cur_align);
+if (cur_align=null)or(q=null) then confusion("endv");
+@y
+begin if cur_align=null then confusion("endv");
+q:=link(cur_align);@+if q=null then confusion("endv");
+@z
+
+353. $$\begingroup\halign{#\cr}$$ caused confusion (Mittelbach, 7 Jun 89)
+@x module 1130
+mmode+halign: if privileged then init_align;
+@y
+mmode+halign: if privileged then
+ if cur_group=math_shift_group then init_align
+ else off_save;
+@z
+
+354. Conflict in global array dig with .5\ifdim.6 (Mittelbach, 20 Jun 89)
+@x module 450
+@!k:small_number; {number of digits in a decimal fraction}
+@y
+@!k,@!kk:small_number; {number of digits in a decimal fraction}
+@!p,@!q:pointer; {top of decimal digit stack}
+@z
+@x module 452
+begin k:=0; get_token; {|point_token| is being re-scanned}
+loop@+ begin get_x_token;
+ if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
+ if k<17 then {digits for |k>=17| cannot affect the result}
+ begin dig[k]:=cur_tok-zero_token; incr(k);
+ end;
+ end;
+done1: f:=round_decimals(k);
+@y
+begin k:=0; p:=null; get_token; {|point_token| is being re-scanned}
+loop@+ begin get_x_token;
+ if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
+ if k<17 then {digits for |k>=17| cannot affect the result}
+ begin q:=get_avail; link(q):=p; info(q):=cur_tok-zero_token;
+ p:=q; incr(k);
+ end;
+ end;
+done1: for kk:=k downto 1 do
+ begin dig[kk-1]:=info(p); q:=p; p:=link(p); free_avail(q);
+ end;
+f:=round_decimals(k);
+@z
+
+355. String startup problems (Wayne Sullivan, 17 Jul 89)
+@x module 31 (Warning: This affects most change files!)
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+@y
+ @<Report overflow of the input buffer, and abort@>;
+@z
+@x module 35
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+@y
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+
+@<Report overflow of the input buffer, and abort@>=
+if format_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ end
+@z
+@x module 1310
+k:=pool_ptr-4; undump_four_ASCII
+@y
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr
+@z
+@x module 1332
+tini@/
+@y
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+@z
+@x module 1332
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+@y
+@z
+
+356. Allow integer products to be 31 bits long (Mittelbach, 16 Aug 89)
+@x module 105
+@p function nx_plus_y(@!n:integer;@!x,@!y:scaled):scaled;
+begin if n<0 then
+ begin negate(x); negate(n);
+ end;
+if n=0 then nx_plus_y:=y
+else if ((x<=(@'7777777777-y) div n)and(-x<=(@'7777777777+y) div n)) then
+ nx_plus_y:=n*x+y
+else begin arith_error:=true; nx_plus_y:=0;
+ end;
+end;
+@y
+@d nx_plus_y(#)==mult_and_add(#,@'7777777777)
+@d mult_integers(#)==mult_and_add(#,0,@'17777777777)
+
+@p function mult_and_add(@!n:integer;@!x,@!y,@!max_answer:scaled):scaled;
+begin if n<0 then
+ begin negate(x); negate(n);
+ end;
+if n=0 then mult_and_add:=y
+else if ((x<=(max_answer-y) div n)and(-x<=(max_answer+y) div n)) then
+ mult_and_add:=n*x+y
+else begin arith_error:=true; mult_and_add:=0;
+ end;
+end;
+@z
+@x module 1240
+ if q=multiply then cur_val:=nx_plus_y(eqtb[l].int,cur_val,0)
+@y
+ if q=multiply then
+ if p=int_val then cur_val:=mult_integers(eqtb[l].int,cur_val)
+ else cur_val:=nx_plus_y(eqtb[l].int,cur_val,0)
+@z
+
+357. Raise the maximum number of tokens normally shown (pointed out by
+John Lavagnino in TeXhax on 19 May 89; corrected on 31 Aug 89).
+@x module 295
+begin if p<>null then show_token_list(link(p),null,1000);
+@y
+begin if p<>null then show_token_list(link(p),null,10000000);
+@z
+@x module 1370 (this part of the change is cosmetic only)
+show_token_list(link(def_ref),null,10000000); print_ln;
+@y
+token_show(def_ref); print_ln;
+@z
+
+358. Like 353, but with $$\begingroup\eqno$$ (Mittelbach, 25 Jul 89)
+@x module 1140
+mmode+eq_no: if privileged then start_eq_no;
+@y
+mmode+eq_no: if privileged then
+ if cur_group=math_shift_group then start_eq_no
+ else off_save;
+@z
+
+359. Major extension to 8-bit character input. (These changes, and a few that
+follow, were discussed during the 10th TUG meeting and installed during
+the week of 10 Sep 1989. They bring the implementation of TeX82 up to date with
+international developments and experience, and I believe they mark the
+final development of this program into a completely stable system.)
+Several changes to the documentation (not the program) are omitted here.
+@x module 18
+@!ASCII_code=0..127; {seven-bit numbers}
+@y
+@!ASCII_code=0..255; {eight-bit numbers}
+@z
+@x module 19
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:0..last_text_char;
+@y
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+@z
+@x module 21
+xchr[0]:=' '; xchr[@'177]:=' ';
+ {ASCII codes 0 and |@'177| do not appear in text}
+@y
+@z
+@x module 23
+for i:=1 to @'37 do xchr[i]:=' ';
+@y changing ' ' to chr(i) here will allow all 8-bit characters to get in
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@z
+@x module 24
+for i:=1 to @'176 do xord[xchr[i]]:=i;
+@y
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+@z
+@x module 38
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of ASCII_code; {the characters}
+@y [OK to make si(#)==#-128 and so(#)==#+128 (without parens) in change files]
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@z
+@x module 42
+begin str_pool[pool_ptr]:=#; incr(pool_ptr);
+@y
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+@z
+@x module 45
+ begin if str_pool[j]<>buffer[k] then
+@y
+ begin if so(str_pool[j])<>buffer[k] then
+@z
+@x module 47
+var k,@!l:0..127; {small indices or counters}
+@y
+var k,@!l:0..255; {small indices or counters}
+@z
+@x module 47
+@<Make the first 128 strings@>;
+@y
+@<Make the first 256 strings@>;
+@z
+@x module 48
+@ @<Make the first 128...@>=
+for k:=0 to 127 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else append_char(k-@'100);
+@y
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0)@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+@z
+@x module 59
+ begin print_char(str_pool[j]); incr(j);
+@y
+ begin print_char(so(str_pool[j])); incr(j);
+@z
+@x modules 59 and 60
+else if s<128 then
+@y
+else if s<256 then
+@z
+@x module 60
+ begin print(str_pool[j]); incr(j);
+@y
+ begin print(so(str_pool[j])); incr(j);
+@z
+@x module 63
+if c>=0 then if c<128 then print(c);
+@y
+if c>=0 then if c<256 then print(c);
+@z
+@x module 68
+@ In certain situations, \TeX\ prints either a standard visible ASCII
+character or its hexadecimal ASCII code.
+
+@p procedure print_ASCII(@!c:integer); {prints a character or its code}
+begin if (c>=0) and (c<=127) then print(c)
+else begin print_char("[");
+ if c<0 then print_int(c)@+else print_hex(c);
+ print_char("]");
+ end;
+end;
+@y
+@ 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\ae ologists.
+
+@d print_ASCII == print
+@z
+@x module 69
+ begin print_char(str_pool[j]); n:=n-v;
+ end;
+ if n<=0 then return; {nonpositive input produces no output}
+ k:=j+2; u:=v div (str_pool[k-1]-"0");
+ if str_pool[k-1]="2" then
+ begin k:=k+2; u:=u div (str_pool[k-1]-"0");
+ end;
+ if n+u>=v then
+ begin print_char(str_pool[k]); n:=n+u;
+ end
+ else begin j:=j+2; v:=v div (str_pool[j-1]-"0");
+@y
+ 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])-"0");
+ if str_pool[k-1]=si("2") then
+ begin k:=k+2; u:=u div (so(str_pool[k-1])-"0");
+ 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])-"0");
+@z
+@x module 70
+ begin print_char(str_pool[j]); incr(j);
+@y
+ begin print_char(so(str_pool[j])); incr(j);
+@z
+@x module 222
+@d single_base=active_base+128 {equivalents of one-letter control sequences}
+@d null_cs=single_base+128 {equivalent of \.{\\csname\\endcsname}}
+@y
+@d single_base=active_base+256 {equivalents of one-character control sequences}
+@d null_cs=single_base+256 {equivalent of \.{\\csname\\endcsname}}
+@z
+@x module 230
+ {table of 128 command codes (the ``catcodes'')}
+@d lc_code_base=cat_code_base+128 {table of 128 lowercase mappings}
+@d uc_code_base=lc_code_base+128 {table of 128 uppercase mappings}
+@d sf_code_base=uc_code_base+128 {table of 128 spacefactor mappings}
+@d math_code_base=sf_code_base+128 {table of 128 math mode mappings}
+@d int_base=math_code_base+128 {beginning of region 5}
+@y
+ {table of 256 command codes (the ``catcodes'')}
+@d lc_code_base=cat_code_base+256 {table of 256 lowercase mappings}
+@d uc_code_base=lc_code_base+256 {table of 256 uppercase mappings}
+@d sf_code_base=uc_code_base+256 {table of 256 spacefactor mappings}
+@d math_code_base=sf_code_base+256 {table of 256 math mode mappings}
+@d int_base=math_code_base+256 {beginning of region 5}
+@z
+@x module 232
+for k:=0 to 127 do
+@y
+for k:=0 to 255 do
+@z
+@x module 236
+@d del_code_base=count_base+256 {128 delimiter code mappings}
+@d dimen_base=del_code_base+128 {beginning of region 6}
+@y
+@d del_code_base=count_base+256 {256 delimiter code mappings}
+@d dimen_base=del_code_base+256 {beginning of region 6}
+@z
+@x module 240
+for k:=0 to 127 do del_code(k):=-1;
+@y
+for k:=0 to 255 do del_code(k):=-1;
+@z
+@x module 264
+begin if s<128 then cur_val:=s+single_base
+@y
+begin if s<256 then cur_val:=s+single_base
+@z
+@x ibid
+ for j:=0 to l-1 do buffer[j]:=str_pool[k+j];
+@y
+ for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+@z
+@x module 289
+@d cs_token_flag==@'10000 {amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256}
+@y
+@d cs_token_flag==@'7777 {amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256, less~1}
+@z
+@x module 293
+ if (info(p)<0)or(c>127) then print_esc("BAD.")
+@y
+ if info(p)<0 then print_esc("BAD.")
+@z
+@x module 341
+@!cat:0..15; {|cat_code(cur_chr)|, usually}
+@y
+@!cat:0..15; {|cat_code(cur_chr)|, usually}
+@!c,@!cc:ASCII_code; {constituents of a possible expanded code}
+@!d:2..3; {number of excess characters in an expanded code}
+@z
+@x module 352
+@<If this |sup_mark| starts a control character...@>=
+begin if (cur_chr=buffer[loc])and(loc<limit) then
+ begin if buffer[loc+1]<@'100 then cur_chr:=buffer[loc+1]+@'100
+ else cur_chr:=buffer[loc+1]-@'100;
+ loc:=loc+2; goto reswitch;
+ end;
+@y
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+
+@d is_hex(#)==(((#>="0")and(#<="9"))or((#>="a")and(#<="f")))
+@d hex_to_cur_chr==
+ if c<="9" then cur_chr:=c-"0" @+else cur_chr:=c-"a"+10;
+ if cc<="9" then cur_chr:=16*cur_chr+cc-"0"
+ else cur_chr:=16*cur_chr+cc-"a"+10
+
+@<If this |sup_mark| starts an expanded character...@>=
+begin if cur_chr=buffer[loc] then if loc<limit then
+ begin c:=buffer[loc+1]; @+if c<@'200 then {yes we have an expanded char}
+ begin loc:=loc+2;
+ if is_hex(c) then if loc<=limit then
+ begin cc:=buffer[loc]; @+if is_hex(cc) then
+ begin incr(loc); hex_to_cur_chr; goto reswitch;
+ end;
+ end;
+ if c<@'100 then cur_chr:=c+@'100 @+else cur_chr:=c-@'100;
+ goto reswitch;
+ end;
+ end;
+@z
+@x module 355
+ begin cur_chr:=buffer[k+1];
+ if cur_chr<@'100 then buffer[k-1]:=cur_chr+@'100
+ else buffer[k-1]:=cur_chr-@'100;
+ limit:=limit-2; first:=first-2;
+ while k<=limit do
+ begin buffer[k]:=buffer[k+2]; incr(k);
+ end;
+ goto start_cs;
+ end;
+@y
+begin c:=buffer[k+1]; @+if c<@'200 then {yes, one is indeed present}
+ begin d:=2;
+ if is_hex(c) then @+if k+2<=limit then
+ begin cc:=buffer[k+2]; @+if is_hex(cc) then incr(d);
+ end;
+ if d>2 then
+ begin hex_to_cur_chr; buffer[k-1]:=cur_chr;
+ end
+ else if c<@'100 then buffer[k-1]:=c+@'100
+ else buffer[k-1]:=c-@'100;
+ limit:=limit-d; first:=first-d;
+ while k<=limit do
+ begin buffer[k]:=buffer[k+d]; incr(k);
+ end;
+ goto start_cs;
+ end;
+ end;
+@z
+@x module 360
+There is one more branch.
+@y
+There is one more branch.
+
+@d end_line_char_inactive == (end_line_char<0)or(end_line_char>255)
+@z
+@x ibid
+ if (end_line_char<0)or(end_line_char>127) then decr(limit)
+@y
+ if end_line_char_inactive then decr(limit)
+@z
+@x modules 362 and 483 and 538 and 1337
+if (end_line_char<0)or(end_line_char>127) then decr(limit)
+@y
+if end_line_char_inactive then decr(limit)
+@z
+@x module 391
+if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+@y
+if (info(r)>match_token+255)or(info(r)<match_token) then s:=null
+@z
+@x module 407
+ ((cur_chr=str_pool[k])or(cur_chr=str_pool[k]-"a"+"A")) then
+@y
+ ((cur_chr=so(str_pool[k]))or(cur_chr=so(str_pool[k])-"a"+"A")) then
+@z
+@x module 414
+begin scan_seven_bit_int;
+@y
+begin scan_char_num;
+@z
+@x module 432
+|scan_something_internal|:
+
+@<Declare procedures that scan restricted classes of integers@>=
+procedure scan_seven_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>127) then
+ begin print_err("Bad character code");
+@.Bad character code@>
+ help2("The numeric code for a character must be between 0 and 127.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+@y
+|scan_something_internal|.
+@z (and I'll interchange modules 434 and 435)
+@x module 442
+if cur_val>127 then
+@y
+if cur_val>255 then
+@z
+@x module 464
+ begin t:=str_pool[k];
+@y
+ begin t:=so(str_pool[k]);
+@z
+@x module 506 (correction needed twice)
+if (cur_cmd>active_char)or(cur_chr>127) then
+@y
+if (cur_cmd>active_char)or(cur_chr>255) then
+@z
+@x module 519
+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]);
+@y
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+@z
+@x module 526
+if (cur_cmd>active_char)or(cur_chr>127) then
+@y
+if (cur_cmd>active_char)or(cur_chr>255) then
+@z
+@x module 603 (change to be made twice)
+ dvi_out(str_pool[k]);
+@y
+ dvi_out(so(str_pool[k]));
+@z
+@x module 617
+ for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(str_pool[s]);
+@y
+ for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[s]));
+@z
+@x module 766
+ begin case str_pool[r_type*8+t+magic_offset] of
+@y
+ begin case so(str_pool[r_type*8+t+magic_offset]) of
+@z
+@x module 780
+@d span_code=128 {distinct from any character}
+@d cr_code=129 {distinct from |span_code| and from any character}
+@y
+@d span_code=256 {distinct from any character}
+@d cr_code=257 {distinct from |span_code| and from any character}
+@z
+@x module 815
+label done,done1,done2,done3,done4;
+@y
+label done,done1,done2,done3,done4,continue;
+@z
+@x module 896
+ else if (type(s)=kern_node)and(subtype(s)=normal) then c:=128
+ else if type(s)=whatsit_node then c:=128
+ else goto done1;
+ if c<128 then if lc_code(c)<>0 then
+ if (lc_code(c)=c)or(uc_hyph>0) then goto done2
+ else goto done1;
+ s:=link(s);
+@y
+ else if (type(s)=kern_node)and(subtype(s)=normal) then goto continue
+ else if type(s)=whatsit_node then goto continue
+ else goto done1;
+ if lc_code(c)<>0 then
+ if (lc_code(c)=c)or(uc_hyph>0) then goto done2
+ else goto done1;
+continue: s:=link(s);
+@z
+@x module 897
+ if c>=128 then goto done3;
+ if (lc_code(c)=0)or(hn=63) then goto done3;
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c)-1;
+@y
+ if lc_code(c)=0 then goto done3;
+ if hn=63 then goto done3;
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c);
+@z
+@x module 898
+if c>=128 then goto done3;
+if (lc_code(c)=0)or(j=63) then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c)-1;@/
+@y
+if lc_code(c)=0 then goto done3;
+if j=63 then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+@z
+@x module 923
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=128; {insert delimiters}
+@y
+hc[0]:=0; hc[hn+1]:=0; hc[hn+2]:=256; {insert delimiters}
+@z
+@x ibid
+ while hc[l]=trie_char(z) do
+@y
+ while hc[l]=qo(trie_char(z)) do
+@z
+@x module 931
+ repeat if str_pool[u]<hc[j] then goto not_found;
+ if str_pool[u]>hc[j] then goto done;
+@y
+ repeat if so(str_pool[u])<hc[j] then goto not_found;
+ if so(str_pool[u])>hc[j] then goto done;
+@z
+@x module 937
+else begin if (cur_chr>127)or(lc_code(cur_chr)=0) then
+@y
+else begin if lc_code(cur_chr)=0 then
+@z
+@x ibid
+ begin incr(n); hc[n]:=lc_code(cur_chr)-1;
+@y
+ begin incr(n); hc[n]:=lc_code(cur_chr);
+@z
+@x module 945
+@!init @!trie_c:packed array[trie_pointer] of ASCII_code; {characters to match}
+@y
+@!init @!trie_c:packed array[trie_pointer] of packed_ASCII_code;
+ {characters to match}
+@z
+% In modules 951--954, change "127" to "255" and "128" to "256"
+@x module 952
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=min_quarterword;
+@y
+trie_link(0):=0; trie_char(0):=min_quarterword; trie_op(0):=min_quarterword;
+@z
+@x module 953
+begin c:=trie_c[p];
+@y
+begin c:=so(trie_c[p]);
+@z
+@x module 955
+ begin if trie_link(h+trie_c[q])=0 then goto not_found;
+@y
+ begin if trie_link(h+so(trie_c[q]))=0 then goto not_found;
+@z
+@x module 956
+repeat z:=h+trie_c[q]; trie_back(trie_link(z)):=trie_back(z);
+@y
+repeat z:=h+so(trie_c[q]); trie_back(trie_link(z)):=trie_back(z);
+@z
+@x module 959
+ begin q:=trie_l[p]; c:=trie_c[p];
+ trie_link(z+c):=trie_ref[q]; trie_char(z+c):=c; trie_op(z+c):=trie_o[p];
+@y
+ begin q:=trie_l[p]; c:=so(trie_c[p]);
+ trie_link(z+c):=trie_ref[q]; trie_char(z+c):=qi(c); trie_op(z+c):=trie_o[p];
+@z
+@x module 962
+ begin if cur_chr="." then cur_chr:=128 {edge-of-word delimiter}
+ else begin cur_chr:=lc_code(cur_chr);
+ if cur_chr=0 then
+ begin print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)"); error; cur_chr:=128;
+ end;
+ end;
+ if k<63 then
+ begin incr(k); hc[k]:=cur_chr-1; hyf[k]:=0; digit_sensed:=false;
+ end;
+ end
+else begin hyf[k]:=cur_chr-"0";
+ if k<63 then digit_sensed:=true;
+ end
+@y
+ begin if cur_chr="." then cur_chr:=0 {edge-of-word delimiter}
+ else begin cur_chr:=lc_code(cur_chr);
+ if cur_chr=0 then
+ begin print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)"); error;
+ end;
+ end;
+ if k<63 then
+ begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digit_sensed:=false;
+ end;
+ end
+ else if k<63 then
+ begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
+ end;
+ end
+@z
+@x module 963
+ while (p>0)and(c>trie_c[p]) do
+@y
+ while (p>0)and(c>so(trie_c[p])) do
+@z
+@x ibid
+ if (p=0)or(c<trie_c[p]) then
+@y
+ if (p=0)or(c<so(trie_c[p])) then
+@z
+@x module 964
+trie_c[p]:=c; trie_o[p]:=min_quarterword;
+@y
+trie_c[p]:=si(c); trie_o[p]:=min_quarterword;
+@z
+@x module 965
+if hc[1]=127 then hyf[0]:=0;
+if hc[k]=127 then hyf[k]:=0;
+@y
+if hc[1]=0 then hyf[0]:=0;
+if hc[k]=0 then hyf[k]:=0;
+@z
+@x module 966
+h.rh:=0; h.b0:=min_quarterword; h.b1:=0; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=0|}
+@y
+h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
+@z
+@x module 1034
+if c<128 then
+@y
+@z
+@x ibid
+else space_factor:=1000
+@y
+@z
+@x module 1151
+letter,other_char,char_given: if cur_chr>=128 then c:=cur_chr
+ else begin c:=ho(math_code(cur_chr));
+@y
+letter,other_char,char_given: begin c:=ho(math_code(cur_chr));
+@z
+@x module 1154
+mmode+letter,mmode+other_char,mmode+char_given: if cur_chr<128 then
+ set_math_char(ho(math_code(cur_chr)))
+ else set_math_char(cur_chr);
+mmode+char_num: begin scan_char_num; cur_chr:=cur_val;
+ if cur_chr<128 then set_math_char(ho(math_code(cur_chr)))
+ else set_math_char(cur_chr);
+@y
+mmode+letter,mmode+other_char,mmode+char_given:
+ set_math_char(ho(math_code(cur_chr)));
+mmode+char_num: begin scan_char_num; cur_chr:=cur_val;
+ set_math_char(ho(math_code(cur_chr)));
+@z
+@x module 1232
+ p:=cur_chr; scan_seven_bit_int; p:=p+cur_val; scan_optional_equals;
+@y
+ p:=cur_chr; scan_char_num; p:=p+cur_val; scan_optional_equals;
+@z
+@x module 1233
+else n:=127
+@y
+else n:=255
+@z
+@x module 1289
+ begin if t>=cs_token_flag then t:=t-active_base;
+ c:=t mod 256;
+ if c<128 then if equiv(b+c)<>0 then t:=256*(t div 256)+equiv(b+c);
+ if t>=cs_token_flag then info(p):=t+active_base
+ else info(p):=t;
+@y
+ begin c:=t mod 256;
+ if equiv(b+c)<>0 then info(p):=t-c+equiv(b+c);
+@z
+@x module 1309
+ w.b0:=str_pool[k]; w.b1:=str_pool[k+1];
+ w.b2:=str_pool[k+2]; w.b3:=str_pool[k+3];
+@y [often qi(so(x))=x, but not e.g. when "quarterwords" are two bytes]
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+@z
+@x module 1310
+ str_pool[k]:=w.b0; str_pool[k+1]:=w.b1;
+ str_pool[k+2]:=w.b2; str_pool[k+3]:=w.b3
+@y
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+@z
+@x module 1368
+for k:=str_start[str_ptr] to pool_ptr-1 do dvi_out(str_pool[k]);
+@y
+for k:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[k]));
+@z
+
+360. Major change to allow multiple hyphenation tables.
+I've combined this with the code for a minor change:
+361. New parameters \lefthyphenmin and \righthyphenmin.
+@x module 11 (affects most change files!)
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@y
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@z
+@x module 212
+known as |space_factor|; it holds the current space factor used in spacing
+calculations. In math mode, |aux| is also known as |incompleat_noad|; if
+@y
+known as |space_factor| and |clang|; it holds the current space factor used in
+spacing calculations, and the current language used for hyphenation.
+(The value of |clang| is undefined in restricted horizontal mode.)
+In math mode, |aux| is also known as |incompleat_noad|; if
+@z
+@x ibid
+ @!pg_field,@!aux_field,@!ml_field: integer;
+@y
+ @!pg_field,@!ml_field: integer;
+ @!aux_field: memory_word;
+@z
+@x module 213
+@d prev_depth==aux {the name of |aux| in vertical mode}
+@d space_factor==aux {the name of |aux| in horizontal mode}
+@d incompleat_noad==aux {the name of |aux| in math mode}
+@y
+@d prev_depth==aux.sc {the name of |aux| in vertical mode}
+@d space_factor==aux.hh.lh {part of |aux| in horizontal mode}
+@d clang==aux.hh.rh {the other part of |aux| in horizontal mode}
+@d incompleat_noad==aux.int {the name of |aux| in math mode}
+@z
+@x module 218
+@!a:integer; {auxiliary}
+@y
+@!a:memory_word; {auxiliary}
+@z
+@x module 219
+ if a<=ignore_depth then print("ignored")
+ else print_scaled(a);
+ if nest[p].pg_field<>0 then
+ begin print(", prevgraf ");
+ print_int(nest[p].pg_field); print(" line");
+ if nest[p].pg_field<>1 then print_char("s");
+ end;
+ end;
+1: begin print_nl("spacefactor "); print_int(a);
+ end;
+2: if a<>null then
+ begin print("this will be denominator of:"); show_box(a);
+@y
+ if a.sc<=ignore_depth then print("ignored")
+ else print_scaled(a.sc);
+ if nest[p].pg_field<>0 then
+ begin print(", prevgraf ");
+ print_int(nest[p].pg_field); print(" line");
+ if nest[p].pg_field<>1 then print_char("s");
+ end;
+ end;
+1: begin print_nl("spacefactor "); print_int(a.hh.lh);
+ if m>0 then if a.hh.rh>0 then
+ begin print(", current language "); print_int(a.hh.rh);
+ end;
+ end;
+2: if a.int<>null then
+ begin print("this will be denominator of:"); show_box(a.int);
+@z
+@x modules 236--238
+@d int_pars=50 {total number of integer parameters}
+@y
+@d language_code=50 {current hyphenation table}
+@d left_hyphen_min_code=51 {minimum left hyphenation fragment size}
+@d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
+@d int_pars=53 {total number of integer parameters}
+@z and define/print these new parameters appropriately (mimicking new_line_char)
+@x module 418
+else begin cur_val:=aux;
+ if m=vmode then cur_val_level:=dimen_val@+else cur_val_level:=int_val;
+ end
+@y
+else if m=vmode then
+ begin cur_val:=prev_depth; cur_val_level:=dimen_val;
+ end
+else begin cur_val:=space_factor; cur_val_level:=int_val;
+ end
+@z
+@x module 775
+ begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field;
+@y
+ begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field.sc;
+@z
+@x module 786
+begin push_nest; mode:=(-hmode-vmode)-mode; aux:=0;
+@y
+begin push_nest; mode:=(-hmode-vmode)-mode;
+if mode=-hmode then space_factor:=0 @+else prev_depth:=0;
+@z
+@x module 863
+loop@+ begin @<Create an active breakpoint representing the beginning of
+ the paragraph@>;
+@y
+loop@+ begin if threshold>inf_bad then threshold:=inf_bad;
+ if second_pass then @<Initialize for hyphenating a paragraph@>;
+ @<Create an active breakpoint representing the beginning of the paragraph@>;
+@z
+@x module 891
+passes on only about 5 per cent of the paragraphs.
+@y
+passes on only about 5 per cent of the paragraphs.
+
+@<Initialize for hyphenating...@>=
+begin @!init if trie_not_ready then init_trie; @+tini@;@/
+l_hyf:=left_hyphen_min-1;@+if l_hyf<0 then l_hyf:=0;
+r_hyf:=right_hyphen_min-1;@+if r_hyf<0 then r_hyf:=0;
+min_hyf:=l_hyf+r_hyf+2; cur_lang:=0;
+end
+@z
+@x module 892
+@!hyf_char:integer; {hyphen character of the relevant font}
+@y
+@!hyf_char:integer; {hyphen character of the relevant font}
+@!cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf,@!min_hyf:integer; {limits on fragment sizes}
+@z
+@x module 894
+begin s:=link(cur_p);
+@y
+begin if min_hyf>63 then goto done1;
+s:=link(cur_p);
+@z
+@x module 896
+ else if type(s)=whatsit_node then goto continue
+@y
+ else if type(s)=whatsit_node then
+ begin @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
+ goto continue;
+ end
+@z
+@x module 899 (title of this module also changes appropriately)
+if hn<5 then goto done1;
+@y
+if hn<min_hyf then goto done1;
+@z
+@x module 902
+for j:=2 to hn-3 do if odd(hyf[j]) then goto found1;
+@y
+for j:=l_hyf+1 to hn-r_hyf-1 do if odd(hyf[j]) then goto found1;
+@z
+@x module 920
+$p_1\ldots p_k$ by setting |@t$z_1$@>:=@t$p_1$@>| and then, for |1<i<=k|,
+@y
+$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language index
+and then, for |1<=i<=k|,
+@z
+@x ibid
+the letters in |hc[(l-k+1)..l@,]|, we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v:=trie_op(@t$z_k$@>)|. Then set |hyf[l-hyf_distance[v]]:=@tmax@>(
+hyf[l-hyf_distance[v]], hyf_num[v])|, and |v:=hyf_next[v]|; repeat, if
+necessary, until |v=min_quarterword|.
+@y
+the letters in |hc[(l-k+1)..l@,]| of language |t|,
+we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v:=trie_op(@t$z_k$@>)|. Then set |v:=v+op_start[t]|,
+|hyf[l-hyf_distance[v]]:=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
+and |v:=hyf_next[v]|; repeat, if necessary, until |v=min_quarterword|.
+@z
+@x module 921
+@!hyf_distance:array[quarterword] of small_number; {position |k-j| of $n_j$}
+@!hyf_num:array[quarterword] of small_number; {value of $n_j$}
+@!hyf_next:array[quarterword] of quarterword; {continuation of this |trie_op|}
+@y
+@!hyf_distance:array[1..trie_op_size] of small_number; {position |k-j| of $n_j$}
+@!hyf_num:array[1..trie_op_size] of small_number; {value of $n_j$}
+@!hyf_next:array[1..trie_op_size] of quarterword; {continuation code}
+@!op_start:array[ASCII_code] of 0..trie_op_size; {offset for current language}
+@z
+@x module 922
+@!v:quarterword; {an index into |hyf_distance|, etc.}
+@y
+@!v:integer; {an index into |hyf_distance|, etc.}
+@z
+@x module 923
+for j:=0 to hn-2 do
+ begin z:=hc[j]; l:=j;
+@y
+if trie_char(cur_lang+1)<>qi(cur_lang) then return; {no patterns for |cur_lang|}
+for j:=0 to hn-r_hyf do
+ begin z:=trie_link(cur_lang+1)+hc[j]; l:=j;
+@z
+@x ibid
+found: hyf[1]:=0; hyf[hn-2]:=0; hyf[hn-1]:=0; hyf[hn]:=0
+@y
+found: for j:=0 to l_hyf do hyf[j]:=0;
+for j:=0 to r_hyf do hyf[hn-j]:=0
+@z
+@x module 924
+repeat i:=l-hyf_distance[v];
+@y
+repeat v:=v+op_start[cur_lang]; i:=l-hyf_distance[v];
+@z
+@x module 930
+h:=hc[1];
+@y
+h:=hc[1]; incr(hn); hc[hn]:=cur_lang;
+@z
+@x ibid
+not_found:
+@y
+not_found: decr(hn)
+@z
+@x module 931
+ goto found;
+@y
+ decr(hn); goto found;
+@z
+@x module 934
+@p procedure new_hyph_exceptions; {enters new exceptions}
+label reswitch, exit, found, not_found, done;
+@y
+@d set_cur_lang==if language<=0 then cur_lang:=0
+ else if language>255 then cur_lang:=0
+ else cur_lang:=language
+
+@p procedure new_hyph_exceptions; {enters new exceptions}
+label reswitch, exit, found, not_found;
+@z
+@x ibid
+begin scan_left_brace; {a left brace must follow \.{\\hyphenation}}
+@y
+begin scan_left_brace; {a left brace must follow \.{\\hyphenation}}
+set_cur_lang;
+@z
+@x module 935
+ spacer,right_brace: begin if n>4 then @<Enter a hyphenation exception@>;
+@y
+ spacer,right_brace: begin if n>1 then @<Enter a hyphenation exception@>;
+@z
+@x module 938
+begin if n>1 then
+@y
+begin if n<63 then
+@z
+@x module 939
+begin str_room(n); h:=0;
+@y
+begin incr(n); hc[n]:=cur_lang; str_room(n); h:=0;
+@z
+@x ibid
+loop@+ begin if p=null then goto done;
+ if info(p)<n-2 then goto done;
+ q:=link(p); free_avail(p); p:=q; {eliminate hyphens that \TeX\ doesn't like}
+ end;
+done: @<Insert the \(p)pair |(s,p)| into the exception table@>;
+@y
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+@z
+@x module 942
+@p@!init @<Declare procedures for preprocessing hyphenation patterns@>@;
+@y
+@<Declare subprocedures for |line_break|@>=
+@!init @<Declare procedures for preprocessing hyphenation patterns@>@;
+@z
+@x module 943
+@d quarterword_diff=max_quarterword-min_quarterword
+@d trie_op_hash_size=quarterword_diff+quarterword_diff {double}
+
+@<Glob...@>=
+@!init@! trie_op_hash:array[0..trie_op_hash_size] of quarterword;
+ {trie op codes for triples}
+tini@;@/
+@t\hskip1em@>@!trie_op_ptr:quarterword; {highest |trie_op| assigned}
+@y
+@<Glob...@>=
+@!init@! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size;
+ {trie op codes for quadruples}
+@!trie_used:array[ASCII_code] of quarterword;
+ {largest opcode used so far for this language}
+@!trie_op_lang:array[1..trie_op_size] of ASCII_code;
+ {language part of a hashed quadruple}
+@!trie_op_val:array[1..trie_op_size] of quarterword;
+ {opcode corresponding to a hashed quadruple}
+@!trie_op_ptr:0..trie_op_size; {number of stored ops so far}
+tini
+@z
+@x module 944 should be entirely replaced
+@y by the following code:
+@ It's tempting to remove the |overflow| stops in the following procedure;
+|new_trie_op| could return |min_quarterword| (thereby simply ignoring
+part of a hyphenation pattern) instead of aborting the job. However, that would
+lead to different hyphenation results on different installations of \TeX\
+using the same patterns. The |overflow| stops are necessary for portability
+of patterns.
+
+@<Declare procedures for preprocessing hyph...@>=
+function new_trie_op(@!d,@!n:small_number;@!v:quarterword):quarterword;
+label exit;
+var h:-trie_op_size..trie_op_size; {trial hash location}
+@!u:quarterword; {trial op code}
+@!l:0..trie_op_size; {pointer to stored data}
+begin h:=abs(n+313*d+361*v+1009*cur_lang) mod (trie_op_size+trie_op_size)
+ - trie_op_size;
+loop@+ begin l:=trie_op_hash[h];
+ if l=0 then {empty position found for a new op}
+ begin if trie_op_ptr=trie_op_size then
+ overflow("pattern memory ops",trie_op_size);
+ u:=trie_used[cur_lang];
+ if u=max_quarterword then
+ overflow("pattern memory ops per language",
+ max_quarterword-min_quarterword);
+ incr(trie_op_ptr); incr(u); trie_used[cur_lang]:=u;
+ hyf_distance[trie_op_ptr]:=d;
+ hyf_num[trie_op_ptr]:=n; hyf_next[trie_op_ptr]:=v;
+ trie_op_lang[trie_op_ptr]:=cur_lang; trie_op_hash[h]:=trie_op_ptr;
+ trie_op_val[trie_op_ptr]:=u; new_trie_op:=u; return;
+ end;
+ if (hyf_distance[l]=d)and(hyf_num[l]=n)and(hyf_next[l]=v)
+ and(trie_op_lang[l]=cur_lang) then
+ begin new_trie_op:=trie_op_val[l]; return;
+ end;
+ if h>-trie_op_size then decr(h)@+else h:=trie_op_size;
+ end;
+exit:end;
+@z
+@x module 945: move the code from this module into 946, and introduce new code:
+@y
+@ After |new_trie_op| has compressed the necessary opcode information,
+plenty of information is available to unscramble the data into the
+final form needed by our hyphenation algorithm.
+
+@<Sort the hyphenation op tables into proper order@>=
+op_start[0]:=-min_quarterword;
+for j:=1 to 255 do op_start[j]:=op_start[j-1]+qo(trie_used[j-1]);
+for j:=1 to trie_op_ptr do
+ trie_op_hash[j]:=op_start[trie_op_lang[j]]+trie_op_val[j]; {destination}
+for j:=1 to trie_op_ptr do while trie_op_hash[j]>j do
+ begin k:=trie_op_hash[j];@/
+ t:=hyf_distance[k]; hyf_distance[k]:=hyf_distance[j]; hyf_distance[j]:=t;@/
+ t:=hyf_num[k]; hyf_num[k]:=hyf_num[j]; hyf_num[j]:=t;@/
+ t:=hyf_next[k]; hyf_next[k]:=hyf_next[j]; hyf_next[j]:=t;@/
+ trie_op_hash[j]:=trie_op_hash[k]; trie_op_hash[k]:=k;
+ end
+@z
+@x module 949: Move this code to just after the new module 945!
+mentioned so far, let's write a procedure that does the initialization.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure init_pattern_memory; {gets ready to build a linked trie}
+var h:0..trie_op_hash_size; {an index into |trie_op_hash|}
+@!p:trie_pointer; {an index into |trie_hash|}
+begin for h:=0 to trie_op_hash_size do trie_op_hash[h]:=min_quarterword;
+trie_op_ptr:=min_quarterword; trie_root:=0; trie_c[0]:=0; trie_ptr:=0;
+for p:=0 to trie_size do trie_hash[p]:=0;
+end;
+@y
+mentioned so far, let's write down the code that gets them started.
+
+@<Initialize table entries...@>=
+for k:=-trie_op_size to trie_op_size do trie_op_hash[k]:=0;
+for k:=0 to 255 do trie_used[k]:=min_quarterword;
+trie_op_ptr:=0;
+@z
+@x module 950
+@t\hskip1em@>@!trie_min:trie_pointer;
+ {all locations |<=trie_min| are vacant in |trie|}
+tini@;@/
+@t\hskip1em@>@!trie_max:trie_pointer; {largest location used in |trie|}
+@y
+@t\hskip10pt@>@!trie_min:array[ASCII_code] of trie_pointer;
+ {the first possible slot for each character}
+@t\hskip10pt@>@!trie_max:trie_pointer; {largest location used in |trie|}
+@t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?}
+tini
+@z
+@x modules 951 and 952
+@ Here is how these data structures are initialized.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure init_trie_memory; {gets ready to pack into |trie|}
+var p:trie_pointer; {index into |trie_ref|, |trie|, |trie_taken|}
+begin for p:=0 to trie_ptr do trie_ref[p]:=0;
+trie_max:=256; trie_min:=256; trie_link(0):=1; trie_taken[0]:=false;
+trie_link(trie_size):=0; trie_back(0):=trie_size; {wrap around}
+for p:=1 to 256 do
+ begin trie_back(p):=p-1; trie_link(p):=p+1; trie_taken[p]:=false;
+ end;
+end;
+
+@ Each time \.{\\patterns} appears, it overrides any patterns that were
+entered earlier, so the arrays are not initialized until \TeX\ sees
+\.{\\patterns}. However, some of the global variables must be
+initialized when \.{INITEX} is loaded, in case the user never mentions
+any \.{\\patterns}.
+
+@<Initialize table entries...@>=
+trie_op_ptr:=min_quarterword;@/
+trie_link(0):=0; trie_char(0):=min_quarterword; trie_op(0):=min_quarterword;
+for k:=1 to 255 do trie[k]:=trie[0];
+trie_max:=255;
+@y
+@ Each time \.{\\patterns} appears, it contributes further patterns to
+the future trie, which will be built only when hyphenation is attempted or
+when a format file is dumped. The boolean variable |trie_not_ready|
+will change to |false| when the trie is compressed; this will disable
+further patterns.
+
+@<Initialize table entries...@>=
+trie_not_ready:=true; trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0;
+
+@ Here is how the trie-compression data structures are initialized.
+If storage is tight, it would be possible to overlap |trie_op_hash|,
+|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
+because we finish with the former just before we need the latter.
+
+@<Get ready to compress the trie@>=
+@<Sort the hyphenation...@>;
+for p:=0 to trie_size do trie_hash[p]:=0;
+trie_root:=compress_trie(trie_root); {identify equivalent subtries}
+for p:=0 to trie_ptr do trie_ref[p]:=0;
+for p:=0 to 255 do trie_min[p]:=p+1;
+trie_link(0):=1; trie_max:=0
+@z
+@x module 953
+begin c:=so(trie_c[p]);
+if c<trie_min then trie_min:=c;
+if trie_min=0 then z:=trie_link(trie_size)
+else z:=trie_link(trie_min-1); {get the first conceivably good hole}
+loop@+ begin if z<c then goto not_found;
+ h:=z-c;@/
+@y
+@!l,@!r:trie_pointer; {left and right neighbors}
+@!ll:1..256; {upper limit of |trie_min| updating}
+begin c:=so(trie_c[p]);
+z:=trie_min[c]; {get the first conceivably good hole}
+loop@+ begin h:=z-c;@/
+@z
+@x module 956
+repeat z:=h+so(trie_c[q]); trie_back(trie_link(z)):=trie_back(z);
+trie_link(trie_back(z)):=trie_link(z); trie_link(z):=0; q:=trie_r[q];
+@y
+repeat z:=h+so(trie_c[q]); l:=trie_back(z); r:=trie_link(z);
+trie_back(r):=l; trie_link(l):=r; trie_link(z):=0;
+if l<256 then
+ begin if z<256 then ll:=z @+else ll:=256;
+ repeat trie_min[l]:=r; incr(l);
+ until l=ll;
+ end;
+q:=trie_r[q];
+@z
+@x module 958 is entirely replaced
+@y by the following new code:
+@ When the whole trie has been allocated into the sequential table, we
+must go through it once again so that |trie| contains the correct
+information. Null pointers in the linked trie will be represented by the
+value~0, which properly implements an ``empty'' family.
+
+@<Move the data into |trie|@>=
+h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
+if trie_root=0 then {no patterns were given}
+ begin for r:=0 to 256 do trie[r]:=h;
+ trie_max:=256;
+ end
+else begin trie_fix(trie_root); {this fixes the non-holes in |trie|}
+ r:=0; {now we will zero out all the holes}
+ repeat s:=trie_link(r); trie[r]:=h; r:=s;
+ until r>trie_max;
+ end;
+trie_char(0):=qi("?"); {make |trie_char(c)<>c| for all |c|}
+@z
+@x module 960
+@!r,@!s:trie_pointer; {used to clean up the packed |trie|}
+@!h:two_halves; {template used to zero out |trie|'s holes}
+begin scan_left_brace; {a left brace must follow \.{\\patterns}}
+init_pattern_memory;@/
+@<Enter all of the patterns into a linked trie, until coming to a right
+ brace@>;
+trie_root:=compress_trie(trie_root); {compress the trie}
+@<Pack the trie@>;
+end;
+@y
+begin if trie_not_ready then
+ begin set_cur_lang; scan_left_brace; {a left brace must follow \.{\\patterns}}
+ @<Enter all of the patterns into a linked trie, until coming to a right
+ brace@>;
+ end
+else begin print_err("Too late for "); print_esc("patterns");
+ help1("All patterns must be given before typesetting begins.");
+ error; link(garbage):=scan_toks(false,false); flush_list(def_ref);
+ end;
+end;
+@z
+@x module 963
+q:=0;
+while l<k do
+ begin incr(l); c:=hc[l]; p:=trie_l[q]; first_child:=true;
+@y
+q:=0; hc[0]:=cur_lang;
+while l<=k do
+ begin c:=hc[l]; incr(l); p:=trie_l[q]; first_child:=true;
+@z
+@x module 966 now says that the first call ``takes'' location 1,
+@y and the Pascal code in this module is entirely replaced by the following:
+@<Declare procedures for preprocessing hyphenation patterns@>=
+procedure init_trie;
+var @!p:trie_pointer; {pointer for initialization}
+@!j,@!k,@!t:integer; {all-purpose registers for initialization}
+@!r,@!s:trie_pointer; {used to clean up the packed |trie|}
+@!h:two_halves; {template used to zero out |trie|'s holes}
+begin @<Get ready to compress the trie@>;
+if trie_root<>0 then
+ begin first_fit(trie_root); trie_pack(trie_root);
+ end;
+@<Move the data into |trie|@>;
+trie_not_ready:=false;
+end;
+@z
+@x module 1033 gets new code before main_loop_1
+@y
+if mode>0 then if language<>clang then fix_language;
+@z
+@x modules 1091 and 1200
+push_nest; mode:=hmode; space_factor:=1000;
+@y
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@z
+@x module 1324
+for k:=0 to trie_max do dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for k:=min_quarterword+1 to trie_op_ptr do
+ begin dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ end;
+print_ln; print_int(hyph_count); print(" hyphenation exception");
+if hyph_count<>1 then print_char("s");
+print_nl("Hyphenation trie of length "); print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has "); print_int(qo(trie_op_ptr)); print(" op");
+if trie_op_ptr<>min_quarterword+1 then print_char("s")
+@y
+print_ln; print_int(hyph_count); print(" hyphenation exception");
+if hyph_count<>1 then print_char("s");
+if trie_not_ready then init_trie;
+dump_int(trie_max);
+for k:=0 to trie_max do dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for k:=1 to trie_op_ptr do
+ begin dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ end;
+print_nl("Hyphenation trie of length "); print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has "); print_int(trie_op_ptr); print(" op");
+if trie_op_ptr<>1 then print_char("s");
+print(" out of "); print_int(trie_op_size);
+for k:=255 downto 0 do if trie_used[k]>min_quarterword then
+ begin print_nl(" "); print_int(qo(trie_used[k]));
+ print(" for language "); print_int(k);
+ dump_int(k); dump_int(qo(trie_used[k]));
+ end
+@z
+@x module 1325
+undump_size(0)(trie_size)('trie size')(trie_max);
+for k:=0 to trie_max do undump_hh(trie[k]);
+undump(min_quarterword)(max_quarterword)(trie_op_ptr);
+for k:=min_quarterword+1 to trie_op_ptr do
+ begin undump(0)(63)(hyf_distance[k]); {a |small_number|}
+ undump(0)(63)(hyf_num[k]);
+ undump(min_quarterword)(max_quarterword)(hyf_next[k]);
+ end
+@y
+undump_size(0)(trie_size)('trie size')(j); {|trie_max|}
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); {|trie_op_ptr|}
+for k:=1 to j do
+ begin undump(0)(63)(hyf_distance[k]); {a |small_number|}
+ undump(0)(63)(hyf_num[k]);
+ undump(min_quarterword)(max_quarterword)(hyf_next[k]);
+ end;
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x); j:=j-x; op_start[k]:=qo(j);
+ end;
+@!init trie_not_ready:=false @+tini
+@z
+@x module 1341 gets two new definitions
+@y
+@d language_node=4 {|subtype| in whatsits that change the current language}
+@d stored_language(#)==mem[#+1].int {language number, in the range |0..255|}
+@z
+@x module 1344 gets a new definition and a new Pascal statement
+@y
+@d set_language_code=5 {command modifier for \.{\\setlanguage}}
+primitive("setlanguage",extension,set_language_code);@/
+@!@:set_language_}{\.{\\setlanguage} primitive@>
+@z
+@x module 1346 gets a new case
+@y
+ set_language_code:print_esc("setlanguage");
+@z
+@x module 1348 gets a new case
+@y
+set_language_code:@<Implement \.{\\setlanguage}@>;
+@z
+@x module 1356 gets a new case
+@y
+language_node:begin print_esc("setlanguage");
+ print_int(stored_language(p));
+ end;
+@z
+% in modules 1357 and 1358, change "close_node" to "close_node,language_node".
+@x module 1362 becomes two modules
+@ @<Advance \(p)past a whatsit node in the |line_break| loop@>=do_nothing
+@y
+@ @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=
+if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=
+if subtype(s)=language_node then cur_lang:=stored_language(s)
+@z and old module 1367 is moved to just before the old module 1378
+@x module 1373 gets a new case
+@y
+language_node:do_nothing;
+@z
+@x new modules before the system-dependent changes (i.e. before the old 1376)
+@y
+@ @<Implement \.{\\setlanguage}@>=
+if abs(mode)<>hmode then report_illegal_case
+else begin new_whatsit(language_node,small_node_size);
+ scan_int;
+ if cur_val<=0 then clang:=0
+ else if cur_val>255 then clang:=0
+ else clang:=cur_val;
+ stored_language(tail):=clang;
+ end
+
+@ Finally, we need a subroutine that comes into play when a character of
+a non-|clang| language is being appended to the current paragraph.
+
+@<Declare action...@>=
+procedure fix_language;
+var @!l:ASCII_code; {the new current language}
+begin if language<=0 then l:=0
+else if language>255 then l:=0
+else l:=language;
+if l<>clang then
+ begin new_whatsit(language_node,small_node_size);
+ stored_language(tail):=l; clang:=l;
+ end;
+end;
+@z
+
+362. Major extension to ligature capability.
+@x module 143
+a linked list of character nodes for those characters.
+@y
+a linked list of character nodes for all original characters that have been
+deleted. (This list might be empty if the characters that generated the
+ligature were retained in other nodes.)
+
+The |subtype| field is 0, plus 2 and/or 1 if the original source of the
+ligature included implicit left and/or right boundaries.
+@z
+@x in module 144 add the following procedure to the existing code
+@y
+function new_lig_item(@!c:quarterword):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); character(p):=c; lig_ptr(p):=null;
+new_lig_item:=p;
+end;
+@z
+@x module 193
+font_in_short_display:=font(lig_char(p));
+short_display(lig_ptr(p)); print_char(")");
+@y
+if subtype(p)>1 then print_char("|");
+font_in_short_display:=font(lig_char(p)); short_display(lig_ptr(p));
+if odd(subtype(p)) then print_char("|");
+print_char(")");
+end
+@z
+@x modules 208 and 209
+@d radical=65 {square root and similar signs ( \.{\\radical} )}
+@y
+@d no_boundary=65 {suppress boundary ligatures ( \.{\\noboundary} )}
+@d radical=66 {square root and similar signs ( \.{\\radical} )}
+@z and so on, adding 1 to each definition until getting to max_command=100
+@x module 265 gets a new statement
+@y
+primitive("noboundary",no_boundary,0);@/
+@!@:no_boundary_}{\.{\\noboundary} primitive@>
+@z
+@x module 266 gets a new case
+@y
+no_boundary:print_esc("noboundary");
+@z
+@x module 545 is entirely replaced
+@y by the following new specifications:
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must have
+|256*op_byte+remainder<nl|. If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=qi(128) {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=qi(128) {op code for a kern step}
+@d skip_byte(#)==#.b0
+@d next_char(#)==#.b1
+@d op_byte(#)==#.b2
+@d rem_byte(#)==#.b3
+@z
+@x module 549
+@<Glob...@>=
+@y
+@d non_char==qi(256) {a |halfword| code that can't match a real character}
+@d non_address==font_mem_size {a spurious |font_index|}
+
+@<Glob...@>=
+@z
+@x module 548 gets a new type definition
+@y
+@!font_index=0..font_mem_size;
+@z
+@x module 549
+ {current \.{\\skewchar} values}
+@y
+ {current \.{\\skewchar} values}
+@!bchar_label:array[internal_font_number] of font_index;
+ {start of |lig_kern| program for left boundary character,
+ |non_address| if there is none}
+@!font_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {right boundary character, |non_char| if there is none}
+@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+@z
+@x module 557
+@d char_kern_end(#)==rem_byte(#)].sc
+@y NOTE: Optimize kern_base_offset in your change file! It's a constant.
+@d char_kern_end(#)==256*op_byte(#)+rem_byte(#)].sc
+@d kern_base_offset==256*(kern_flag)
+@d lig_kern_restart_end(#)==256*op_byte(#)+rem_byte(#)+32768-kern_base_offset
+@d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end
+@z
+@x module 560
+@!z:scaled; {the design size or the ``at'' size}
+@y
+@!bch_label:integer; {left boundary start location, or infinity}
+@!bchar:0..256; {right boundary character, or 256}
+@!z:scaled; {the design size or the ``at'' size}
+@z
+@x module 566
+kern_base[f]:=lig_kern_base[f]+nl;
+exten_base[f]:=kern_base[f]+nk;
+@y
+kern_base[f]:=lig_kern_base[f]+nl-kern_base_offset;
+exten_base[f]:=kern_base[f]+kern_base_offset+nk;
+@z
+@x module 573: The entire module is replaced
+@y by the following code.
+@ @d check_existence(#)==@t@>@;@/
+ begin check_byte_range(#);
+ qw:=char_info(f)(#); {N.B.: not |qi(#)|}
+ if not char_exists(qw) then abort;
+ end
+
+@<Read ligature/kern program@>=
+bch_label:=@'77777; bchar:=256;
+if nl>0 then
+ begin for k:=lig_kern_base[f] to kern_base[f]+kern_base_offset-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if a>128 then
+ begin if 256*c+d>=nl then abort;
+ if a=255 then if k=lig_kern_base[f] then bchar:=b;
+ end
+ else begin if b<>bchar then check_existence(b);
+ if c<128 then check_existence(d) {check ligature}
+ else if 256*(c-128)+d>=nk then abort; {check kern}
+ if a<128 then if k-lig_kern_base[f]+a+1>=nl then abort;
+ end;
+ end;
+ if a=255 then bch_label:=256*c+d;
+ end;
+for k:=kern_base[f]+kern_base_offset to exten_base[f]-1 do
+ store_scaled(font_info[k].sc);
+@z
+@x module 574 (actually a bugfix)
+ if a<>0 then check_byte_range(a);
+ if b<>0 then check_byte_range(b);
+ if c<>0 then check_byte_range(c);
+ check_byte_range(d);
+@y
+ if a<>0 then check_existence(a);
+ if b<>0 then check_existence(b);
+ if c<>0 then check_existence(c);
+ check_existence(d);
+@z
+@x module 576
+font_name[f]:=nom;
+@y
+if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f]
+else bchar_label[f]:=non_address;
+font_bchar[f]:=qi(bchar);
+font_false_bchar[f]:=qi(bchar);
+if bchar<=ec then if bchar>=bc then
+ begin qw:=char_info(f)(bchar); {N.B.: not |qi(bchar)|}
+ if char_exists(qw) then font_false_bchar[f]:=non_char;
+ end;
+font_name[f]:=nom;
+@z
+@x module 708 (slight but optional optimization)
+continue: if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
+ begin q:=char_info(g)(y);
+@y
+if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
+ begin continue: q:=char_info(g)(y);
+@z
+@x module 740 (another bugfix)
+ i:=char_info(f)(y);
+@y
+ i:=char_info(f)(y);
+ if not char_exists(i) then goto done;
+@z
+@x module 741
+ repeat cur_i:=font_info[a].qqqq;
+ if qo(next_char(cur_i))=skew_char[cur_f] then
+ begin if op_bit(cur_i)>=kern_flag then
+ s:=char_kern(cur_f)(cur_i);
+ goto done1;
+ end;
+ incr(a);
+ until stop_bit(cur_i)>=stop_flag;
+@y
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=256*(qo(op_byte(cur_i)))+rem_byte(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop begin if qo(next_char(cur_i))=skew_char[cur_f] then
+ begin if op_byte(cur_i)>=kern_flag then
+ if skip_byte(cur_i)<=stop_flag then s:=char_kern(cur_f)(cur_i);
+ goto done1;
+ end;
+ if skip_byte(cur_i)>=stop_flag then goto done1
+ else a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+@z
+@x module 749 (yet another bugfix of the same type!)
+@!p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@y
+@!p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@!c:quarterword;@+@!i:four_quarters; {registers for character examination}
+@z
+@x ibid
+ begin cur_c:=rem_byte(cur_i); character(nucleus(q)):=cur_c;
+ cur_i:=char_info(cur_f)(cur_c);
+@y
+ begin c:=rem_byte(cur_i); i:=char_info(cur_f)(c);
+ if char_exists(i) then
+ begin cur_c:=c; cur_i:=i; character(nucleus(q)):=c;
+ end;
+@z
+@x module 752
+@!p:pointer; {temporary register for list manipulation}
+@y
+@!p,@!r:pointer; {temporary registers for list manipulation}
+@z
+@x ibid
+ repeat cur_i:=font_info[a].qqqq;@/
+ @<If instruction |cur_i| is a kern with |cur_c|,
+ attach the kern after |q| and |return|;
+ or if it is a ligature with |cur_c|, combine
+ noads |q| and |p| and |goto restart|@>;
+ incr(a);
+ until stop_bit(cur_i)>=stop_flag;
+@y
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=lig_kern_restart(cur_f)(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop@+ begin @<If instruction |cur_i| is a kern with |cur_c|, attach
+ the kern after~|q|; or if it is a ligature with |cur_c|, combine
+ noads |q| and~|p| appropriately; then |return| if the cursor has
+ moved past a noad, or |goto restart|@>;
+ if skip_byte(cur_i)>=stop_flag then return;
+ a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+@z
+@x The entire code of module 753 is revised
+@y and should be replaced by the following:
+@ Note that a ligature between an |ord_noad| and another kind of noad
+is replaced by an |ord_noad|, when the two noads collapse into one.
+But we could make a parenthesis (say) change shape when it follows
+certain letters. Presumably a font designer will define such
+ligatures only when this convention makes sense.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<If instruction |cur_i| is a kern with |cur_c|, ...@>=
+if next_char(cur_i)=cur_c then if skip_byte(cur_i)<=stop_flag then
+ if op_byte(cur_i)>=kern_flag then
+ begin p:=new_kern(char_kern(cur_f)(cur_i));
+ link(p):=link(q); link(q):=p; return;
+ end
+ else begin check_interrupt; {allow a way out of infinite ligature loop}
+ case op_byte(cur_i) of
+ qi(1),qi(5): character(nucleus(q)):=rem_byte(cur_i); {\.{=:\?}, \.{=:\?>}}
+ qi(2),qi(6): character(nucleus(p)):=rem_byte(cur_i); {\.{\?=:}, \.{\?=:>}}
+ qi(3),qi(7),qi(11):begin r:=new_noad; {\.{\?=:\?}, \.{\?=:\?>}, \.{\?=:\?>>}}
+ character(nucleus(r)):=rem_byte(cur_i);
+ fam(nucleus(r)):=fam(nucleus(q));@/
+ link(q):=r; link(r):=p;
+ if op_byte(cur_i)<qi(11) then math_type(nucleus(r)):=math_char
+ else math_type(nucleus(r)):=math_text_char; {prevent combination}
+ end;
+ othercases begin link(q):=link(p);
+ character(nucleus(q)):=rem_byte(cur_i); {\.{=:}}
+ mem[subscr(q)]:=mem[subscr(p)]; mem[supscr(q)]:=mem[supscr(p)];@/
+ free_node(p,noad_size);
+ end
+ endcases;
+ if op_byte(cur_i)>qi(3) then return;
+ math_type(nucleus(q)):=math_char; goto restart;
+ end
+@z
+@x module 862
+@!q,@!r,@!s:pointer; {miscellaneous nodes of temporary interest}
+@y
+@!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
+@z
+@x module 892
+nodes $p_a$ and~$p_b$ in the description above are placed into variables
+@y
+nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
+@z
+@x ibid
+@!hc:array[0..65] of halfword; {word to be hyphenated}
+@y
+@!hc:array[0..65] of 0..256; {word to be hyphenated}
+@z
+@x ibid
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@y
+@!hu:array[0..63] of 0..256; {like |hc|, before conversion to lowercase}
+@z
+@x module 894
+s:=link(cur_p);
+@y
+prev_s:=cur_p; s:=link(prev_s);
+@z
+@x module 895
+label done,found,not_found,found1,exit;
+@y
+label common_ending,done,found,found1,not_found,not_found+1,exit;
+@z
+@x module 896
+ else if type(s)=ligature_node then
+ begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
+ end
+@y
+ else if type(s)=ligature_node then
+ if lig_ptr(s)=null then goto continue
+ else begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
+ end
+@z
+@x ibid
+continue: s:=link(s);
+@y
+continue: prev_s:=s; s:=link(prev_s);
+@z
+@x ibid
+ha:=s
+@y
+ha:=prev_s
+@z
+@x module 898
+begin j:=hn; q:=lig_ptr(s);
+if font(q)<>hf then goto done3;
+repeat c:=qo(character(q));
+if lc_code(c)=0 then goto done3;
+if j=63 then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+q:=link(q);
+until q=null;
+@y
+begin if font(lig_char(s))<>hf then goto done3;
+j:=hn; q:=lig_ptr(s);
+while q>null do
+ begin c:=qo(character(q));
+ if lc_code(c)=0 then goto done3;
+ if j=63 then goto done3;
+ incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+ q:=link(q);
+ end;
+@z
+@x module 900 gets three new global variables
+@y
+@!init_list:pointer; {list of punctuation characters preceding the word}
+@!init_lig:boolean; {does |init_list| represent a ligature?}
+@!init_lft:boolean; {if so, did the ligature involve a left boundary?}
+@z
+@x module 901
+@!q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@y
+@!p,@!q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@!bchar:halfword; {right boundary character of hyphenated word, or |non_char|}
+@z
+@x module 903 should be entirely replaced
+@y by the following:
+@ If hyphens are in fact going to be inserted, \TeX\ first deletes the
+subsequence of nodes between |ha| and~|hb|. An attempt is made to
+preserve the effect that implicit boundary characters and punctuation marks
+had on ligatures inside the hyphenated word, by storing a left boundary or
+preceding character in |hu[0]| and by storing a possible right boundary
+in |bchar|. We set |j:=0| if |hu[0]| is to be part of the reconstruction;
+otherwise |j:=1|.
+The variable |s| will point to the tail of the current hlist, and
+|q| will point to the node following |hb|, so that
+things can be hooked up after we reconstitute the hyphenated word.
+
+@<Replace nodes |ha..hb| by a sequence of nodes...@>=
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=non_char;
+if type(hb)=ligature_node then if odd(subtype(hb)) then
+ bchar:=font_bchar[hf];
+if is_char_node(ha) then
+ begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else goto not_found+1; {no punctuation found}
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+not_found+1: j:=1; s:=ha; init_list:=null;
+if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then
+ begin j:=0; hu[0]:=256; init_lig:=false;
+ end;
+common_ending: flush_node_list(r);
+@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
+flush_list(init_list)
+@z
+@x modules 905--911 are entirely replaced
+@y by the following new code:
+Still further complications arise in the presence of ligatures that do not
+delete the original characters. When punctuation precedes the word being
+hyphenated, \TeX's method is not perfect under all possible scenarios,
+because punctuation marks and letters can propagate information back and forth.
+For example, suppose the original pre-hyphenation pair
+\.{*a} changes to \.{*y} via a \.{\?=:} ligature, which changes to \.{xy}
+via a \.{=:\?} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
+procedure isn't smart enough to obtain \.{xy} again. In such cases the
+font designer should include a ligature that goes from \.{xa} to \.{xy}.
+
+@ The processing is facilitated by a subroutine called |reconstitute|. Given
+a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
+such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
+has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
+where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
+kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
+For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
+ligature and a kern between `fl' and `y', then $m=2$, $y=2$, and $y_1$ will
+be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
+In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
+simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
+$x_{m+1}\ldots x_n$ until the entire translation has been found.
+
+The |reconstitute| function returns the integer $m$ and puts the nodes
+$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
+getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
+we consider $x_j$ to be an implicit left boundary character; in this
+case |j| must be strictly less than~|n|. There is a
+parameter |bchar|, which is either 256 or an implicit right boundary character
+assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
+explicitly examined, but the algorithm imagines that |bchar| is there.)
+
+If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
+is odd and such that the result of reconstitute would have been different
+if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
+to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
+
+A special convention is used in the case |j=0|: Then we assume that the
+translation of |hu[0]| appears in a special list of charnodes starting at
+|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
+a ligature character, involving a left boundary if |init_lft| is |true|.
+This facility is provided for cases when a hyphenated
+word is preceded by punctuation (like single or double quotes) that might
+affect the translation of the beginning of the word.
+
+@<Glob...@>=
+@!hyphen_passed:small_number; {first hyphen in a ligature, if any}
+
+@ @<Declare the function called |reconstitute|@>=
+function reconstitute(@!j,@!n:small_number;@!bchar,@!hchar:halfword):
+ small_number;
+label continue,done;
+var @!p:pointer; {temporary register for list manipulation}
+@!t:pointer; {a node being appended to}
+@!q:four_quarters; {character information or a lig/kern instruction}
+@!cur_rh:halfword; {hyphen character for ligature testing}
+@!test_char:halfword; {hyphen or other character for ligature testing}
+@!w:scaled; {amount of kerning}
+@!k:font_index; {position of current lig/kern instruction}
+begin hyphen_passed:=0; t:=hold_head; w:=0; link(hold_head):=null;
+ {at this point |ligature_present=lft_hit=rt_hit=false|}
+@<Set up data structures with the cursor following position |j|@>;
+continue:@<If there's a ligature or kern at the cursor position, update the data
+ structures, possibly advancing~|j|; continue until the cursor moves@>;
+@<Append a ligature and/or kern to the translation;
+ |goto continue| if the stack of inserted ligatures is nonempty@>;
+reconstitute:=j;
+end;
+
+@ The reconstitution procedure shares many of the global data structures
+by which \TeX\ has processed the words before they were hyphenated.
+There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
+these characters will be tested for possible ligature activity. If
+|ligature_present| then |cur_l| is a ligature character formed from the
+original characters following |cur_q| in the current translation list.
+There is a ``ligature stack'' between the cursor and character |j+1|,
+consisting of pseudo-ligature nodes linked together by their |link| fields.
+This stack is normally empty unless a ligature command has created a new
+character that will need to be processed later. A pseudo-ligature is
+a special node having a |character| field that represents a potential
+ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
+We have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |qi(hu[j+1])|,&if |lig_stack=null| and |j<n|;\cr
+ bchar,&if |lig_stack=null| and |j=n|.\cr}$$
+
+@<Glob...@>=
+@!cur_l,@!cur_r:halfword; {characters before and after the cursor}
+@!cur_q:pointer; {where a ligature should be detached}
+@!lig_stack:pointer; {unfinished business to the right of the cursor}
+@!ligature_present:boolean; {should a ligature node be made for |cur_l|?}
+@!lft_hit,@!rt_hit:boolean; {did we hit a ligature with a boundary character?}
+
+@ @d append_charnode_to_t(#)== begin link(t):=get_avail; t:=link(t);
+ font(t):=hf; character(t):=#;
+ end
+@d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar;
+ if odd(hyf[j]) then cur_rh:=hchar@+else cur_rh:=non_char;
+ end
+
+@<Set up data structures with the cursor following position |j|@>=
+cur_l:=qi(hu[j]); cur_q:=t;
+if j=0 then
+ begin ligature_present:=init_lig; p:=init_list;
+ if ligature_present then lft_hit:=init_lft;
+ while p>null do
+ begin append_charnode_to_t(character(p)); p:=link(p);
+ end;
+ end
+else if cur_l<non_char then append_charnode_to_t(cur_l);
+lig_stack:=null; set_cur_r
+
+@ We may want to look at the lig/kern program twice, once for a hyphen
+and once for a normal letter. (The hyphen might appear after the letter
+in the program, so we'd better not try to look for both at once.)
+
+@<If there's a ligature or kern at the cursor position, update...@>=
+if cur_l=non_char then
+ begin k:=bchar_label[hf];
+ if k=non_address then goto done@+else q:=font_info[k].qqqq;
+ end
+else begin q:=char_info(hf)(cur_l);
+ if char_tag(q)<>lig_tag then goto done;
+ k:=lig_kern_start(hf)(q); q:=font_info[k].qqqq;
+ if skip_byte(q)>stop_flag then
+ begin k:=lig_kern_restart(hf)(q); q:=font_info[k].qqqq;
+ end;
+ end; {now |k| is the starting address of the lig/kern program}
+if cur_rh<non_char then test_char:=cur_rh@+else test_char:=cur_r;
+loop@+begin if next_char(q)=test_char then if skip_byte(q)<=stop_flag then
+ if cur_rh<non_char then
+ begin hyphen_passed:=j; hchar:=non_char; cur_rh:=non_char;
+ goto continue;
+ end
+ else begin if hchar<non_char then if odd(hyf[j]) then
+ begin hyphen_passed:=j; hchar:=non_char;
+ end;
+ if op_byte(q)<kern_flag then
+ @<Carry out a ligature replacement, updating the cursor structure
+ and possibly advancing~|j|; |goto continue| if the cursor doesn't
+ advance, otherwise |goto done|@>;
+ w:=char_kern(hf)(q); goto done; {this kern will be inserted below}
+ end;
+ if skip_byte(q)>=stop_flag then
+ if cur_rh=non_char then goto done
+ else begin cur_rh:=non_char; goto continue;
+ end;
+ k:=k+qo(skip_byte(q))+1; q:=font_info[k].qqqq;
+ end;
+done:
+
+@ @d wrap_lig(#)==if ligature_present then
+ begin p:=new_ligature(hf,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(p)); rt_hit:=false;
+ end;
+ link(cur_q):=p; t:=p; ligature_present:=false;
+ end
+@d pop_lig_stack==begin if lig_ptr(lig_stack)>null then
+ begin link(t):=lig_ptr(lig_stack); {this is a charnode for |hu[j+1]|}
+ t:=link(t); incr(j);
+ end;
+ p:=lig_stack; lig_stack:=link(p); free_node(p,small_node_size);
+ if lig_stack=null then set_cur_r@+else cur_r:=character(lig_stack);
+ end {if |lig_stack| isn't |null| we have |cur_rh=non_char|}
+
+@<Append a ligature and/or kern to the translation...@>=
+wrap_lig(rt_hit);
+if w<>0 then
+ begin link(t):=new_kern(w); t:=link(t); w:=0;
+ end;
+if lig_stack>null then
+ begin cur_q:=t; cur_l:=character(lig_stack); ligature_present:=true;
+ pop_lig_stack; goto continue;
+ end
+
+@ @<Carry out a ligature replacement, updating the cursor structure...@>=
+begin if cur_l=non_char then lft_hit:=true;
+if j=n then if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(q) of
+qi(1),qi(5):begin cur_l:=rem_byte(q); {\.{=:\?}, \.{=:\?>}}
+ ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(q); {\.{\?=:}. \.{\?=:>}}
+ if lig_stack>null then character(lig_stack):=cur_r
+ else begin lig_stack:=new_lig_item(cur_r);
+ if j=n then bchar:=non_char
+ else begin p:=get_avail; lig_ptr(lig_stack):=p;
+ character(p):=qi(hu[j+1]); font(p):=hf;
+ end;
+ end;
+ end;
+qi(3):begin cur_r:=rem_byte(q); {\.{\?=:\?}}
+ p:=lig_stack; lig_stack:=new_lig_item(cur_r); link(lig_stack):=p;
+ end;
+qi(7),qi(11):begin wrap_lig(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=t; cur_l:=rem_byte(q); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(q); ligature_present:=true; {\.{=:}}
+ if lig_stack>null then pop_lig_stack
+ else if j=n then goto done
+ else begin append_charnode_to_t(cur_r); incr(j); set_cur_r;
+ end;
+ end
+endcases;
+if op_byte(q)>qi(4) then if op_byte(q)<>qi(7) then goto done;
+goto continue;
+end
+@z
+@x module 912
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@y
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@!c_loc:0..63; {where that character came from}
+@!r_count:integer; {replacement count for discretionary}
+@z
+@x modules 913--918 are to be completely replaced
+@y by the following code:
+@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
+
+@<Reconstitute nodes for the hyphenated word...@>=
+repeat l:=j; j:=reconstitute(j,hn,bchar,qi(hyf_char))+1;
+if hyphen_passed=0 then
+ begin link(s):=link(hold_head);
+ while link(s)>null do s:=link(s);
+ if odd(hyf[j-1]) then
+ begin l:=j; hyphen_passed:=j-1; link(hold_head):=null;
+ end;
+ end;
+if hyphen_passed>0 then
+ @<Create and append a discretionary node as an alternative to the
+ unhyphenated word, and continue to develop both branches until they
+ become equivalent@>;
+until j>hn;
+link(s):=q
+
+@ @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+begin r:=get_node(small_node_size);
+link(r):=link(hold_head); type(r):=disc_node;
+major_tail:=r; r_count:=0;
+while link(major_tail)>null do advance_major_tail;
+i:=hyphen_passed;
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|, appending to this
+ list and to |major_tail| until synchronization has been achieved@>;
+@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
+ appropriately@>;
+end
+
+@ The new hyphen might combine with the previous character via ligature
+or kern. At this point we have |l-1<=i<=j| and |i<hn|.
+
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
+minor_tail:=null; pre_break(r):=null; hyf_node:=new_character(hf,hyf_char);
+if hyf_node<>null then
+ begin incr(i); c:=hu[i]; hu[i]:=hyf_char; free_avail(hyf_node);
+ end;
+while l<=i do
+ begin l:=reconstitute(l,i,font_bchar[hf],non_char)+1;
+ if link(hold_head)>null then
+ begin if minor_tail=null then pre_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ end;
+if hyf_node<>null then
+ begin hu[i]:=c; {restore the character in the hyphen position}
+ l:=i; decr(i);
+ end
+
+@ The synchronization algorithm begins with |l=i+1<=j|.
+
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|...@>=
+minor_tail:=null; post_break(r):=null; c_loc:=0;
+if bchar_label[hf]<non_address then {put left boundary at beginning of new line}
+ begin decr(l); c:=hu[l]; c_loc:=l; hu[l]:=256;
+ end;
+while l<j do
+ begin repeat l:=reconstitute(l,hn,bchar,non_char)+1;
+ if c_loc>0 then
+ begin hu[c_loc]:=c; c_loc:=0;
+ end;
+ if link(hold_head)>null then
+ begin if minor_tail=null then post_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ until l>=j;
+ while l>j do
+ @<Append characters of |hu[j..@,]| to |major_tail|, advancing~|j|@>;
+ end
+
+@ @<Append characters of |hu[j..@,]|...@>=
+begin j:=reconstitute(j,hn,bchar,non_char)+1;
+link(major_tail):=link(hold_head);
+while link(major_tail)>null do advance_major_tail;
+end
+
+@ Ligature insertion can cause a word to grow exponentially in size. Therefore
+we must test the size of |r_count| here, even though the hyphenated text
+was at most 63 characters long.
+
+@<Move pointer |s| to the end of the current list...@>=
+if r_count>127 then {we have to forget the discretionary hyphen}
+ begin link(s):=link(r); link(r):=null; flush_node_list(r);
+ end
+else begin link(s):=r; replace_count(r):=r_count;
+ end;
+s:=major_tail
+@z
+@x module 1030
+@d main_loop=70 {go here to typeset |cur_chr| in the current font}
+@d main_loop_1=71 {like |main_loop|, but |(f,c)| = current font and char}
+@d main_loop_2=72 {like |main_loop_1|, but |c| is known to be in range}
+@d main_loop_3=73 {like |main_loop_2|, but several variables are set up}
+@d append_normal_space=74 {go here to append a normal space between words}
+@y
+@d main_loop=70 {go here to typeset a string of consecutive characters}
+@d main_loop_wrapup=80 {go here to finish a character or ligature}
+@d main_loop_move=90 {go here to advance the ligature cursor}
+@d main_loop_move_lig=95 {same, when advancing past a generated ligature}
+@d main_loop_lookahead=100 {go here to bring in another character, if any}
+@d main_lig_loop=110 {go here to check for ligatures or kerning}
+@d append_normal_space=120 {go here to append a normal space between words}
+@z
+@x ibid
+label big_switch,reswitch,main_loop,main_loop_1,main_loop_2,main_loop_3,
+@y
+label big_switch,reswitch,main_loop,main_loop_wrapup,
+ main_loop_move,main_loop_move+1,main_loop_move+2,main_loop_move_lig,
+ main_loop_lookahead,main_loop_lookahead+1,
+ main_lig_loop,main_lig_loop+1,main_lig_loop+2,
+@z
+@x ibid
+@<Local variables for the inner loop of |main_control|@>@;
+@y
+@z
+@x ibid
+hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;
+ end;
+@y
+hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;@+end;
+hmode+no_boundary: begin get_x_token;
+ if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given)or
+ (cur_cmd=char_num) then cancel_boundary:=true;
+ goto reswitch;
+ end;
+@z
+@x modules 1032--1040 are all to be replaced
+@y by the following code:
+@ The following part of the program was first written in a structured
+manner, according to the philosophy that ``premature optimization is
+the root of all evil.'' Then it was rearranged into pieces of
+spaghetti so that the most common actions could proceed with little or
+no redundancy.
+
+The original unoptimized form of this algorithm resembles the
+|reconstitute| procedure, which was described earlier in connection with
+hyphenation. Again we have an implied ``cursor`` between characters
+|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now
+contain a charnode as well as pseudo-ligatures; that stack is now
+usually nonempty, because the next character of input (if any) has been
+appended to it. In |main_control| we have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |font_bchar[cur_font]|,&otherwise.\cr}$$
+Several additional global variables are needed.
+
+@<Glob...@>=
+@!main_f:internal_font_number; {the current font}
+@!main_i:four_quarters; {character information bytes for |cur_l|}
+@!main_j:four_quarters; {ligature/kern command}
+@!main_k:font_index; {index into |font_info|}
+@!main_p:pointer; {temporary register for list manipulation}
+@!main_s:integer; {space factor value}
+@!bchar:halfword; {right boundary character of current font, or |non_char|}
+@!false_bchar:halfword; {nonexistent character matching |bchar|, or |non_char|}
+@!cancel_boundary:boolean; {should the left boundary be ignored?}
+@!ins_disc:boolean; {should we insert a discretionary node?}
+
+@ The boolean variables of the main loop are normally false, and always reset
+to false before the loop is left. That saves us the extra work of initializing
+each time.
+
+@<Set init...@>=
+ligature_present:=false; cancel_boundary:=false; lft_hit:=false; rt_hit:=false;
+ins_disc:=false;
+
+@ We leave |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we
+set it to |sf_code(cur_chr)|, except that the space factor never changes
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)=1000|, so we want that case to be fast.
+
+The overall structure of the main loop is presented here. Some program labels
+are inside the individual sections.
+
+@d adjust_space_factor==@t@>@;@/
+ main_s:=sf_code(cur_chr);
+ if main_s=1000 then space_factor:=1000
+ else if main_s<1000 then
+ begin if main_s>0 then space_factor:=main_s;
+ end
+ else if space_factor<1000 then space_factor:=1000
+ else space_factor:=main_s
+
+@<Append character |cur_chr|...@>=
+adjust_space_factor;@/
+main_f:=cur_font;
+bchar:=font_bchar[main_f]; false_bchar:=font_false_bchar[main_f];
+if mode>0 then if language<>clang then fix_language;
+fast_get_avail(lig_stack); font(lig_stack):=main_f; cur_l:=qi(cur_chr);
+character(lig_stack):=cur_l;@/
+cur_q:=tail;
+if cancel_boundary then
+ begin cancel_boundary:=false; main_k:=non_address;
+ end
+else main_k:=bchar_label[main_f];
+if main_k=non_address then goto main_loop_move+2; {no left boundary processing}
+cur_r:=cur_l; cur_l:=non_char;
+goto main_lig_loop+1; {begin with cursor after left boundary}
+@#
+main_loop_wrapup:@<Make a ligature node, if |ligature_present|;
+ insert a null discretionary, if appropriate@>;
+main_loop_move:@<If the cursor is immediately followed by the right boundary,
+ |goto reswitch|; if it's followed by an invalid character, |goto big_switch|;
+ otherwise move the cursor one step to the right and |goto main_lig_loop|@>;
+main_loop_lookahead:@<Look ahead for another character, or leave |lig_stack|
+ empty if there's none there@>;
+main_lig_loop:@<If there's a ligature/kern command relevant to |cur_l| and
+ |cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>;
+main_loop_move_lig:@<Move the cursor past a pseudo-ligature, then
+ |goto main_loop_lookahead| or |main_lig_loop|@>
+
+@ If the current horizontal list is empty, the reference to |character(tail)|
+here is not strictly legal, since |tail| will be a node freshly returned by
+|get_avail|. But this should cause no problem on most implementations, and we
+do want the inner loop to be fast.
+@^dirty Pascal@>
+
+A discretionary break is not inserted for an explicit hyphen when we are in
+restricted horizontal mode. In particular, this avoids putting discretionary
+nodes inside of other discretionaries.
+
+@d pack_lig(#)== {the parameter is either |rt_hit| or |false|}
+ begin main_p:=new_ligature(main_f,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(main_p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(main_p)); rt_hit:=false;
+ end;
+ link(cur_q):=main_p; tail:=main_p; ligature_present:=false;
+ end
+
+@d wrapup(#)==if cur_l<non_char then
+ begin if character(tail)=qi(hyphen_char[main_f]) then if link(cur_q)>null then
+ ins_disc:=true;
+ if ligature_present then pack_lig(#);
+ if ins_disc then
+ begin ins_disc:=false;
+ if mode>0 then tail_append(new_disc);
+ end;
+ end
+
+@<Make a ligature node, if |ligature_present|;...@>=
+wrapup(rt_hit)
+
+@ @<If the cursor is immediately followed by the right boundary...@>=
+if lig_stack=null then goto reswitch;
+cur_q:=tail; cur_l:=cur_r; {or |character(lig_stack)|}
+main_loop_move+1:if not is_char_node(lig_stack) then goto main_loop_move_lig;
+main_loop_move+2:if(cur_chr<font_bc[main_f])or(cur_chr>font_ec[main_f]) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+main_i:=char_info(main_f)(cur_l);
+if not char_exists(main_i) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+tail_append(lig_stack) {|main_loop_lookahead| is next}
+
+@ Here we are at |main_loop_move_lig|.
+When we begin this code we have |cur_l=character(lig_stack)| and |cur_q=tail|.
+
+@<Move the cursor past a pseudo-ligature...@>=
+main_p:=lig_ptr(lig_stack);
+if main_p>null then tail_append(main_p);
+temp_ptr:=lig_stack; lig_stack:=link(temp_ptr);
+free_node(temp_ptr,small_node_size);
+main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+if lig_stack=null then
+ if main_p>null then goto main_loop_lookahead
+ else cur_r:=bchar
+else cur_r:=character(lig_stack);
+goto main_lig_loop
+
+@ The result of \.{\\char} can participate in a ligature or kern, so we must
+look ahead for it.
+
+@<Look ahead for another character...@>=
+get_next; {set only |cur_cmd| and |cur_chr|, for speed}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+x_token; {now expand and set |cur_cmd|, |cur_chr|, |cur_tok|}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+if cur_cmd=char_num then
+ begin scan_char_num; cur_chr:=cur_val; goto main_loop_lookahead+1;
+ end;
+if cur_cmd=no_boundary then bchar:=non_char;
+cur_r:=bchar; lig_stack:=null; goto main_lig_loop;
+main_loop_lookahead+1: adjust_space_factor;
+fast_get_avail(lig_stack); font(lig_stack):=main_f;
+cur_r:=qi(cur_chr); character(lig_stack):=cur_r;
+if cur_r=false_bchar then cur_r:=non_char {this prevents spurious ligatures}
+
+@ Even though comparatively few characters have a lig/kern program, several
+of the instructions here count as part of \TeX's inner loop, since a
+potentially long sequential search must be performed. For example, tests with
+Computer Modern Roman showed that about 40 per cent of all characters
+actually encountered in practice had a lig/kern program, and that about four
+lig/kern commands were investigated for every such character.
+
+At the beginning of this code we have |main_i=char_info(main_f)(cur_l)|.
+
+@<If there's a ligature/kern command...@>=
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+main_k:=lig_kern_start(main_f)(main_i); main_j:=font_info[main_k].qqqq;
+if skip_byte(main_j)<=stop_flag then goto main_lig_loop+2;
+main_k:=lig_kern_restart(main_f)(main_j);
+main_lig_loop+1:main_j:=font_info[main_k].qqqq;
+main_lig_loop+2:if next_char(main_j)=cur_r then
+ if skip_byte(main_j)<=stop_flag then
+ @<Do ligature or kern command, returning to |main_lig_loop|
+ or |main_loop_wrapup| or |main_loop_move|@>;
+if skip_byte(main_j)=qi(0) then incr(main_k)
+else begin if skip_byte(main_j)>=stop_flag then goto main_loop_wrapup;
+ main_k:=main_k+qo(skip_byte(main_j))+1;
+ end;
+goto main_lig_loop+1
+
+@ When a ligature or kern instruction matches a character, we know from
+|read_font_info| that the character exists in the font, even though we
+haven't verified its existence in the normal way.
+
+This section could be made into a subroutine, if the code inside
+|main_control| needs to be shortened.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<Do ligature or kern command...@>=
+begin if op_byte(main_j)>=kern_flag then
+ begin wrapup(rt_hit);
+ tail_append(new_kern(char_kern(main_f)(main_j))); goto main_loop_move;
+ end;
+if cur_l=non_char then lft_hit:=true
+else if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(main_j) of
+qi(1),qi(5):begin cur_l:=rem_byte(main_j); {\.{=:\?}, \.{=:\?>}}
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(main_j); {\.{\?=:}, \.{\?=:>}}
+ if lig_stack=null then {right boundary character is being consumed}
+ begin lig_stack:=new_lig_item(cur_r); bchar:=non_char;
+ end
+ else if is_char_node(lig_stack) then {|link(lig_stack)=null|}
+ begin main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ lig_ptr(lig_stack):=main_p;
+ end
+ else character(lig_stack):=cur_r;
+ end;
+qi(3):begin cur_r:=rem_byte(main_j); {\.{\?=:\?}}
+ main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ link(lig_stack):=main_p;
+ end;
+qi(7),qi(11):begin wrapup(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=tail; cur_l:=rem_byte(main_j);
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(main_j); ligature_present:=true; {\.{=:}}
+ if lig_stack=null then goto main_loop_wrapup
+ else goto main_loop_move+1;
+ end
+endcases;
+if op_byte(main_j)>qi(4) then
+ if op_byte(main_j)<>qi(7) then goto main_loop_wrapup;
+if cur_l<non_char then goto main_lig_loop;
+main_k:=bchar_label[main_f]; goto main_lig_loop+1;
+end
+@z
+% HINTS for making main_control shorter if you need to conserve space:
+% (1) make pack_lig(#) a procedure call instead of a macro call
+% (2) make the code of @<Do ligature...@> a function that returns one of
+% four values; you goto a label based on the value returned.
+@x module 1042
+begin p:=font_glue[cur_font];
+if p=null then
+ begin f:=cur_font; p:=new_spec(zero_glue); k:=param_base[f]+space_code;
+ width(p):=font_info[k].sc; {that's |space(f)|}
+ stretch(p):=font_info[k+1].sc; {and |space_stretch(f)|}
+ shrink(p):=font_info[k+2].sc; {and |space_shrink(f)|}
+ font_glue[f]:=p;
+@y
+begin main_p:=font_glue[cur_font];
+if main_p=null then
+ begin main_p:=new_spec(zero_glue); main_k:=param_base[cur_font]+space_code;
+ width(main_p):=font_info[main_k].sc; {that's |space(cur_font)|}
+ stretch(main_p):=font_info[main_k+1].sc; {and |space_stretch(cur_font)|}
+ shrink(main_p):=font_info[main_k+2].sc; {and |space_shrink(cur_font)|}
+ font_glue[cur_font]:=main_p;
+@z
+@x module 1045
+any_mode(relax),vmode+spacer,mmode+spacer,mmode+no_boundary:do_nothing;
+@y
+any_mode(relax),vmode+spacer,mmode+spacer:do_nothing;
+@z
+@x module 1090
+ vmode+ex_space:@t@>@;@/
+@y
+ vmode+ex_space,vmode+no_boundary:@t@>@;@/
+@z
+@x module 1322
+dump_int(font_glue[k]);@/
+@y
+dump_int(font_glue[k]);@/
+dump_int(bchar_label[k]);
+dump_int(font_bchar[k]);
+dump_int(font_false_bchar[k]);@/
+@z
+@x module 1323
+undump(min_halfword)(lo_mem_max)(font_glue[k]);@/
+@y
+undump(min_halfword)(lo_mem_max)(font_glue[k]);@/
+undump(0)(font_mem_size)(bchar_label[k]);
+undump(min_quarterword)(non_char)(font_bchar[k]);
+undump(min_quarterword)(non_char)(font_false_bchar[k]);
+@z
+
+363. New \inputlineno feature desired by Spivak
+@x module 416 gets a new definition and a new statement
+@y
+@d input_line_no_code=glue_val+1 {code for \.{\\inputlineno}}
+primitive("inputlineno",last_item,input_line_no_code);
+@!@:input_line_no_}{\.{\\inputlineno} primitive@>
+@z and module 417 changes in the obvious way
+@x module 424 gets new code at the beginning
+@y
+if cur_chr>glue_val then
+ begin cur_val:=line; cur_val_level:=int_val;
+ end
+else
+@z
+
+364. New feature \holdinginserts suggested by Mittelbach.
+@x modules 236--238 get a new integer parameter
+@d int_pars=53 {total number of integer parameters}
+@y
+@d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
+@d int_pars=54 {total number of integer parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 1014
+@<Prepare all the boxes involved in insertions to act as queues@>;
+q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
+while p<>best_page_break do
+ begin if type(p)=ins_node then @<Either insert the material
+ specified by node |p| into the appropriate box, or
+ hold it for the next page; also delete node |p| from
+ the current page@>
+@y
+if holding_inserts<=0 then
+ @<Prepare all the boxes involved in insertions to act as queues@>;
+q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
+while p<>best_page_break do
+ begin if type(p)=ins_node then
+ begin if holding_inserts<=0 then
+ @<Either insert the material specified by node |p| into the
+ appropriate box, or hold it for the next page;
+ also delete node |p| from the current page@>;
+ end
+@z
+% also insert begin...end around the Pascal code of module 1018
+
+365. New \badness feature (which I'd been resisting for years)
+@x module 416 gets a new definition and a new statement
+@y
+@d badness_code=glue_val+2 {code for \.{\\badness}}
+primitive("badness",last_item,badness_code);
+@!@:badness_}{\.{\\badness} primitive@>
+@z and module 417 changes in the obvious way
+@x module 424 (the new code just added in #363)
+ begin cur_val:=line; cur_val_level:=int_val;
+@y
+ begin if cur_chr=input_line_no_code then cur_val:=line
+ else cur_val:=last_badness; {|cur_chr=badness_code|}
+ cur_val_level:=int_val;
+@z
+@x module 646 gets a new global variable
+@y
+@!last_badness:integer; {badness of the most recently packaged box}
+@z
+@x module 648
+@ @<Set init...@>=adjust_tail:=null;
+@y
+@ @<Set init...@>=adjust_tail:=null; last_badness:=0;
+@z
+@x modules 649 and 668
+@!b:integer; {badness of the new box}
+begin r:=get_node(box_node_size); type(r):=hlist_node;
+@y (except it's vlist_node in 668)
+begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
+@z (except it's vlist_node in 668)
+% change b to last_badness in modules 660&674 (4 times), 667&678 (3 times)
+@x modules 658 and 673
+if (hbadness<inf_bad)and(o=normal)and(list_ptr(r)<>null) then
+@y (except it's vbadness in 673)
+if o=normal then if list_ptr(r)<>null then
+@z
+@x modules 664 and 676
+ begin set_glue_ratio_one(glue_set(r)); {this is the maximum shrinkage}
+@y
+ begin last_badness:=1000000;
+ set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
+@z
+@x ibid
+else if (hbadness<100)and(o=normal)and(list_ptr(r)<>null) then
+@y (except it's vbadness in 676)
+else if o=normal then if list_ptr(r)<>null then
+@z
+
+366. New \emergencystretch feature
+@x modules 247--248 get a new dimen parameter
+@d dimen_pars=20 {total number of dimension parameters}
+@y
+@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking}
+@d dimen_pars=21 {total number of dimension parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 828
+@!second_pass:boolean; {is this our second attempt to break this paragraph?}
+@y
+@!second_pass:boolean; {is this our second attempt to break this paragraph?}
+@!final_pass:boolean; {is this our final attempt to break this paragraph?}
+@z
+% change second_pass to final_pass in modules 854 and 873
+@x module 863
+ second_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+@y
+ second_pass:=false; final_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+ final_pass:=(emergency_stretch<=0);
+@z
+@x ibid
+ @!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
+ threshold:=tolerance; second_pass:=true; {if at first you don't
+ succeed, \dots}
+@y
+ if not second_pass then
+ begin@!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
+ threshold:=tolerance; second_pass:=true; final_pass:=(emergency_stretch<=0);
+ end {if at first you don't succeed, \dots}
+ else begin @!stat if tracing_paragraphs>0 then
+ print_nl("@@finalpass");@;@+tats@/
+ background[2]:=background[2]+emergency_stretch; final_pass:=true;
+ end;
+@z
+
+367. New \errorcontextlines feature suggested by a TUG participant
+@x modules 236--238 get a new integer parameter
+@d int_pars=54 {total number of integer parameters}
+@y
+@d error_context_lines_code=54 {maximum intermediate line pairs shown}
+@d int_pars=55 {total number of integer parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 311
+@<Local variables for formatting calculations@>@/
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+ {store current state}
+loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
+ @<Display the current context@>;
+ if (state<>token_list) then
+ if (name>17) or (base_ptr=0) then goto done;
+@y
+@!nn:integer; {number of contexts shown so far, less one}
+@!bottom_line:boolean; {have we reached the final context to be shown?}
+@<Local variables for formatting calculations@>@/
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+ {store current state}
+nn:=-1; bottom_line:=false;
+loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
+ if (state<>token_list) then
+ if (name>17) or (base_ptr=0) then bottom_line:=true;
+ if (base_ptr=input_ptr)or bottom_line or(nn<error_context_lines) then
+ @<Display the current context@>
+ else if nn=error_context_lines then
+ begin print_nl("..."); incr(nn); {omitted if |error_context_lines<0|}
+ end;
+ if bottom_line then goto done;
+@z
+% also insert begin...end around the Pascal code of module 312
+% and add the statement "incr(nn)" to that module
+
+368. char_warning inside hyphenation could clobber old_setting
+@x module 863
+done: @!stat if tracing_paragraphs>0 then end_diagnostic(true);@;@+tats@/
+@y
+done: @!stat if tracing_paragraphs>0 then
+ begin end_diagnostic(true); normalize_selector;
+ end;@+tats@/
+@z
+
+369. Make ".fmt" more easily switchable (Don Hosek).
+@x module 520 gets a new definition
+@y
+@d format_extension=".fmt" {the extension, as a \.{WEB} constant}
+@z now replace ".fmt" by format_extension in modules 529 and 1328.
+
+370. Possible range check on weird nullfont (Breitenlohner, 16 Oct 89).
+@x module 565
+if (bc>ec+1)or(ec>255) then abort;
+@y
+if (bc>ec+1)or(ec>255) then abort;
+if bc>255 then {|bc=256| and |ec=255|}
+ begin bc:=1; ec:=0;
+ end;
+@z
+
+371. Prevent save_stack conflicts, e.g. in {\hbox\expandafter{\csname
+ \endcsname}} (found by Sullivan).
+@x module 645
+@p procedure scan_spec; {scans a box specification and left brace}
+label found;
+begin if scan_keyword("to") then saved(0):=exactly
+@.to@>
+else if scan_keyword("spread") then saved(0):=additional
+@.spread@>
+else begin saved(0):=additional; saved(1):=0;
+ goto found;
+ end;
+scan_normal_dimen; saved(1):=cur_val;
+found: save_ptr:=save_ptr+2; scan_left_brace;
+@y
+@p procedure scan_spec(@!c:group_code;@!three_codes:boolean);
+ {scans a box specification and left brace}
+label found;
+var @!s:integer; {temporarily saved value}
+@!spec_code:exactly..additional;
+begin if three_codes then s:=saved(0);
+if scan_keyword("to") then spec_code:=exactly
+@.to@>
+else if scan_keyword("spread") then spec_code:=additional
+@.spread@>
+else begin spec_code:=additional; cur_val:=0;
+ goto found;
+ end;
+scan_normal_dimen;
+found: if three_codes then
+ begin saved(0):=s; incr(save_ptr);
+ end;
+saved(0):=spec_code; saved(1):=cur_val; save_ptr:=save_ptr+2;
+new_save_level(c); scan_left_brace;
+@z
+@x module 774
+scan_spec; new_save_level(align_group);@/
+@y
+scan_spec(align_group,false);@/
+@z
+@x module 1073
+ if t=0 then saved(0):=cur_val@+else saved(0):=-cur_val;
+ scan_box;
+ end;
+any_mode(leader_ship): begin saved(0):=leader_flag-a_leaders+cur_chr; scan_box;
+ end;
+any_mode(make_box): begin saved(0):=0; begin_box;
+ end;
+@y
+ if t=0 then scan_box(cur_val)@+else scan_box(-cur_val);
+ end;
+any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);
+any_mode(make_box): begin_box(0);
+@z
+@x module 1075
+procedure box_end;
+var p:pointer; {|ord_noad| for new box in math mode}
+begin if saved(0)<box_flag then @<Append box |cur_box| to the current list,
+ shifted by |saved(0)|@>
+else if saved(0)<ship_out_flag then @<Store \(c)|cur_box| in a box register@>
+else if cur_box<>null then
+ if saved(0)>ship_out_flag then @<Append a new leader node that
+ uses |cur_box|@>
+@y
+procedure box_end(@!box_context:integer);
+var p:pointer; {|ord_noad| for new box in math mode}
+begin if box_context<box_flag then @<Append box |cur_box| to the current list,
+ shifted by |box_context|@>
+else if box_context<ship_out_flag then @<Store \(c)|cur_box| in a box register@>
+else if cur_box<>null then
+ if box_context>ship_out_flag then @<Append a new leader node that
+ uses |cur_box|@>
+@z
+@x module 1076
+ begin shift_amount(cur_box):=saved(0);
+@y
+ begin shift_amount(cur_box):=box_context;
+@z
+@x module 1077
+if saved(0)<box_flag+256 then
+ eq_define(box_base-box_flag+saved(0),box_ref,cur_box)
+else geq_define(box_base-box_flag-256+saved(0),box_ref,cur_box)
+@y
+if box_context<box_flag+256 then
+ eq_define(box_base-box_flag+box_context,box_ref,cur_box)
+else geq_define(box_base-box_flag-256+box_context,box_ref,cur_box)
+@z
+@x module 1078
+ begin append_glue; subtype(tail):=saved(0)-(leader_flag-a_leaders);
+@y
+ begin append_glue; subtype(tail):=box_context-(leader_flag-a_leaders);
+@z
+@x module 1079
+procedure begin_box;
+@y
+procedure begin_box(@!box_context:integer);
+@z
+@x ibid
+box_end; {in simple cases, we use the box immediately}
+@y
+box_end(box_context); {in simple cases, we use the box immediately}
+@z
+@x module 1083
+incr(save_ptr); scan_spec;
+if k=hmode then
+ if (saved(-3)<box_flag)and(abs(mode)=vmode) then
+ new_save_level(adjusted_hbox_group)
+ else new_save_level(hbox_group)
+else begin if k=vmode then new_save_level(vbox_group)
+ else begin new_save_level(vtop_group); k:=vmode;
+@y
+saved(0):=box_context;
+if k=hmode then
+ if (box_context<box_flag)and(abs(mode)=vmode) then
+ scan_spec(adjusted_hbox_group,true)
+ else scan_spec(hbox_group,true)
+else begin if k=vmode then scan_spec(vbox_group,true)
+ else begin scan_spec(vtop_group,true); k:=vmode;
+@z
+@x module 1084
+procedure scan_box; {the next input should specify a box or perhaps a rule}
+begin @<Get the next non-blank non-relax...@>;
+if cur_cmd=make_box then begin_box
+else if (saved(0)>=leader_flag)and((cur_cmd=hrule)or(cur_cmd=vrule)) then
+ begin cur_box:=scan_rule_spec; box_end;
+@y
+procedure scan_box(@!box_context:integer);
+ {the next input should specify a box or perhaps a rule}
+begin @<Get the next non-blank non-relax...@>;
+if cur_cmd=make_box then begin_box(box_context)
+else if (box_context>=leader_flag)and((cur_cmd=hrule)or(cur_cmd=vrule)) then
+ begin cur_box:=scan_rule_spec; box_end(box_context);
+@z
+@x module 1086
+pop_nest; box_end;
+@y
+pop_nest; box_end(saved(0));
+@z
+@x module 1117
+else begin incr(save_ptr); saved(-1):=0; scan_left_brace;
+ new_save_level(disc_group); push_nest; mode:=-hmode; space_factor:=1000;
+@y
+else begin incr(save_ptr); saved(-1):=0; new_save_level(disc_group);
+ scan_left_brace; push_nest; mode:=-hmode; space_factor:=1000;
+@z
+@x module 1119
+incr(saved(-1)); scan_left_brace; new_save_level(disc_group);
+@y
+incr(saved(-1)); new_save_level(disc_group); scan_left_brace;
+@z
+@x module 1167
+mmode+vcenter: begin scan_spec; new_save_level(vcenter_group); normal_paragraph;
+@y
+mmode+vcenter: begin scan_spec(vcenter_group,false); normal_paragraph;
+@z
+@x module 1172
+scan_left_brace; push_math(math_choice_group);
+@y
+push_math(math_choice_group); scan_left_brace;
+@z
+@x module 1174
+incr(saved(-1)); scan_left_brace; push_math(math_choice_group);
+@y
+incr(saved(-1)); push_math(math_choice_group); scan_left_brace;
+@z
+@x module 1241
+ if global then saved(0):=box_flag+256+cur_val
+ else saved(0):=box_flag+cur_val;
+ scan_optional_equals; scan_box;
+@y
+ if global then n:=256+cur_val@+else n:=cur_val;
+ scan_optional_equals; scan_box(box_flag+n);
+@z
+
+372. Bugfix 339 didn't go far enough (found by Sch\"opf and Mittelbach).
+@x module 516 [NOTE: THIS AFFECTS ALMOST ALL CHANGE FILES!]
+@ 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}
+@y
+@ And here's the second. The string pool might change as the file name is
+being scanned, since a new \.{\\csname} might be entered; therefore we keep
+|area_delimiter| and |ext_delimiter| relative to the beginning of the current
+string, instead of assigning an absolute address like |pool_ptr| to them.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin str_room(1); append_char(c); {contribute |c| to the current string}
+ if (c=">")or(c=":") then
+ begin area_delimiter:=cur_length; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=cur_length;
+@z
+@x module 517 [NOTE: THIS DOES TOO, AND SO DOES THE NEXT!]
+else begin cur_area:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=area_delimiter+1;
+@y
+else begin cur_area:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
+@z
+@x ibid
+else begin cur_name:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
+@y
+else begin cur_name:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
+ incr(str_ptr); cur_ext:=make_string;
+@z
+
+373. Allow multiple hyphenmins in the same paragraph (Mike Ferguson).
+@x module 212, two new fields for list_state_record
+@y
+ @!lhm_field,@!rhm_field: quarterword;
+@z
+@x module 213, two new macros to access those fields
+@y
+@d lhmin==cur_list.lhm_field {\.{\\lefthyphenmin} at start of paragraph}
+@d rhmin==cur_list.rhm_field {\.{\\righthyphenmin} at start of paragraph}
+@z
+@x module 215, two new initializations
+@y
+lhmin:=0; rhmin:=0;
+@z
+@x module 218
+ if nest[p].ml_field<0 then print(" (\output routine)");
+@y
+ if m=hmode then@+if(nest[p].lhm_field<>2)or(nest[p].rhm_field<>3)then
+ begin print(" (hyphenmin "); print_int(nest[p].lhm_field); print_char(",");
+ print_int(nest[p].rhm_field); print_char(")");
+ end;
+ if nest[p].ml_field<0 then print(" (\output routine)");
+@z
+@x module 891
+l_hyf:=left_hyphen_min-1;@+if l_hyf<0 then l_hyf:=0;
+r_hyf:=right_hyphen_min-1;@+if r_hyf<0 then r_hyf:=0;
+min_hyf:=l_hyf+r_hyf+2; cur_lang:=0;
+@y
+l_hyf:=lhmin; r_hyf:=rhmin; cur_lang:=0;
+@z
+@x module 892
+@!l_hyf,@!r_hyf,@!min_hyf:integer; {limits on fragment sizes}
+@y
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@z
+@x module 894
+begin if min_hyf>63 then goto done1;
+prev_s:=cur_p; s:=link(prev_s);
+if s<>null then
+ begin @<Skip to node |ha|, or |goto done1| if no hyphenation
+ should be attempted@>;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+ |min_hyf| letters have been found, otherwise |goto done1|@>;
+@y
+begin prev_s:=cur_p; s:=link(prev_s);
+if s<>null then
+ begin @<Skip to node |ha|, or |goto done1| if no hyphenation
+ should be attempted@>;
+ if l_hyf+r_hyf>63 then goto done1;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+ |l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
+@z
+@x module 899
+if hn<min_hyf then goto done1;
+@y
+if hn<l_hyf+r_hyf then goto done1;
+@z
+@x module 902
+for j:=l_hyf+1 to hn-r_hyf-1 do if odd(hyf[j]) then goto found1;
+@y
+for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1;
+@z
+@x module 923
+for j:=0 to hn-r_hyf do
+@y
+for j:=0 to hn-r_hyf+1 do
+@z
+@x ibid
+found: for j:=0 to l_hyf do hyf[j]:=0;
+for j:=0 to r_hyf do hyf[hn-j]:=0
+@y
+found: for j:=0 to l_hyf-1 do hyf[j]:=0;
+for j:=0 to r_hyf-1 do hyf[hn-j]:=0
+@z
+@x module 1091 begins with a new subroutine
+@y
+function norm_min(@!h:integer):small_number;
+begin if h<=0 then norm_min:=1@+else if h>=63 then norm_min:=63@+
+else norm_min:=h;
+end;
+@z
+@x module 1091 then uses the new subroutine
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@y
+lhmin:=norm_min(left_hyphen_min); rhmin:=norm_min(right_hyphen_min);
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@z
+@x module 1341
+@d stored_language(#)==mem[#+1].int {language number, in the range |0..255|}
+@y
+@d what_lang(#)==link(#+1) {language number, in the range |0..255|}
+@d what_lhm(#)==type(#+1) {minimum left fragment, in the range |1..63|}
+@d what_rhm(#)==subtype(#+1) {minimum right fragment, in the range |1..63|}
+@z
+@x module 1356
+ print_int(stored_language(p));
+@y
+ print_int(what_lang(p)); print(" (hyphenmin ");
+ print_int(what_lhm(p)); print_char(",");
+ print_int(what_rhm(p)); print_char(")");
+@z
+@x modules 1362 and 1363
+@ @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=
+if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=
+if subtype(s)=language_node then cur_lang:=stored_language(s)
+@y
+@ @d adv_past(#)==@+if subtype(#)=language_node then
+ begin cur_lang:=what_lang(#); l_hyf:=what_lhm(#); r_hyf:=what_rhm(#);@+end
+
+@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+
+adv_past(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+
+adv_past(s)
+@z
+@x module 1376
+ stored_language(tail):=l; clang:=l;
+@y
+ what_lang(tail):=l; clang:=l;@/
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+@z
+@x module 1377
+ stored_language(tail):=clang;
+@y
+ what_lang(tail):=clang;
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+@z
+
+374. Make \par and end_template definitely non-character (Marc van Leeuwen)
+@x module 14 gets a new line
+@y
+if mem_top<256+11 then bad:=7; {we will want |null_list>255|}
+@z
+@x module 334
+primitive("par",par_end,0); par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
+@y
+primitive("par",par_end,256); {cf. |scan_file_name|}
+par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
+@z
+
+375. Alignments must be more robust to prevent crashes (Marc van Leeuwen)
+@x module 324
+else if token_type=u_template then align_state:=0;
+@y
+else if token_type=u_template then
+ if align_state>500000 then align_state:=0
+ else fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+@z
+@x module 782
+if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
+@y
+if cur_cmd=endv then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
+@z
+@x module 791
+p:=link(q);
+@y
+if align_state<500000 then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+p:=link(q);
+@z
+
+376. Avoid kern removal in discretionary breaks (Marc van Leeuwen)
+@x module 815
+label done,done1,done2,done3,done4,continue;
+@y
+label done,done1,done2,done3,done4,done5,continue;
+@z
+@x module 866
+glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|@>;
+ @<Update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+@y
+glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|;
+ then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+@z
+@x ibid
+disc_node: @<Try to break after a discretionary fragment@>;
+@y
+disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
+@z
+@x ibid
+end
+@y
+done5:end
+@z
+Combine modules 868 and 869 into a single module, with a semicolon between.
+@x module 870 (which becomes module 869) gets ... in its title
+@y and the following new code just before its final "end":
+r:=replace_count(cur_p); s:=link(cur_p);
+while r>0 do
+ begin @<Add the width of node |s| to |act_width|@>;
+ decr(r); s:=link(s);
+ end;
+prev_p:=cur_p; cur_p:=s; goto done5;
+@z
+@x Now module 871 becomes 870, and we add a new (very similar) module 871:
+@y
+@ @<Add the width of node |s| to |act_width|@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ act_width:=act_width+char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+ act_width:=act_width+
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ act_width:=act_width+width(s);
+ othercases confusion("disc4")
+@:this can't happen disc4}{\quad disc4@>
+ endcases
+@z
+@x module 877
+@!disc_break:boolean; {was the current break at a discretionary node?}
+@y
+@!disc_break:boolean; {was the current break at a discretionary node?}
+@!post_disc_break:boolean; {and did it have a nonempty post-break part?}
+@z
+@x ibid
+if cur_p<>null then @<Prune unwanted nodes at the beginning of the next line@>;
+@y
+if cur_p<>null then if not post_disc_break then
+ @<Prune unwanted nodes at the beginning of the next line@>;
+@z
+@x module 881
+q:=cur_break(cur_p); disc_break:=false;
+@y
+q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false;
+@z
+@x module 883
+ if not is_char_node(s) then if next_break(cur_p)<>null then
+ if cur_break(next_break(cur_p))=s then s:=r;
+@y that subtle bug is no longer possible so we can remove the code
+@z
+@x module 884
+link(s):=r; r:=post_break(q); post_break(q):=null;
+@y
+link(s):=r; r:=post_break(q); post_break(q):=null; post_disc_break:=true;
+@z
+
+377. Pick up a few discretionary hyphens that were lost because of
+the new (cautious) algorithm.
+@x module 914
+@ @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+begin r:=get_node(small_node_size);
+@y
+@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
+odd, after both branches of the previous discretionary end at position |j-1|.
+Strictly speaking, we aren't justified in doing this, because we don't know
+that a hyphen after |j-1| is truly independent of those branches. But in almost
+all applications we would rather not lose a potentially valuable hyphenation
+point. (Consider the word `difficult', where the letter `c' is in position |j|.)
+
+@d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+repeat r:=get_node(small_node_size);
+@z
+@x ibid
+i:=hyphen_passed;
+@y
+i:=hyphen_passed; hyf[i]:=0;
+@z
+@x ibid
+end
+@y
+hyphen_passed:=j-1; link(hold_head):=null;
+until not odd(hyf[j-1])
+@z
+
+378. Undumped trie must also be dumpable again (Breitenlohner, 11 Dec 89)
+@x module 1325
+undump_size(0)(trie_size)('trie size')(j); {|trie_max|}
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); {|trie_op_ptr|}
+@y
+undump_size(0)(trie_size)('trie size')(j); @+init trie_max:=j;@+tini
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); @+init trie_op_ptr:=j;@+tini
+@z
+@x ibid
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x); j:=j-x; op_start[k]:=qo(j);
+@y
+init for k:=0 to 255 do trie_used[k]:=min_quarterword;@+tini
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x);@+init trie_used[k]:=qi(x);@+tini
+ j:=j-x; op_start[k]:=qo(j);
+@z
+
+379. Allow output routine to access page totals. (Suggested by Frank
+Mittelbach and Chris Rowley, December 1989)
+@x module 421
+begin if page_contents=empty then
+@y
+begin if (page_contents=empty) and (not output_active) then
+@z
+
+380. Slightly more robust recovery and detection of \output anomalies.
+(Suggested by Chris Thompson, provoked by George Russell, January 1990)
+@x module 1026
+begin if loc<>null then @<Recover from an unbalanced output routine@>;
+@y
+begin if (loc<>null) or
+ ((token_type<>output_text)and(token_type<>backed_up)) then
+ @<Recover from an unbalanced output routine@>;
+@z
+@x module 1027
+help2("Your sneaky output routine has fewer real {'s than }'s.")@/
+@y
+help2("Your sneaky output routine has problematic {'s and/or }'s.")@/
+@z
+
+381. \eqno\aftergroup*$$ should yield * after the $$ (Michael Downes, 29 Jan 90)
+@x module 1194
+ begin cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
+@y
+ begin @<Check that another \.\$ follows@>;
+ cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
+@z
+@x ibid
+else begin @<Check that another \.\$ follows@>;
+@y
+else begin if a=null then @<Check that another \.\$ follows@>;
+@z
+
+382. Missed a case in the 8-bit change (Wayne Sullivan, 1 Feb 1990)
+@x module 360
+ begin if (end_line_char<0)or(end_line_char>127) then incr(limit);
+@y
+ begin if end_line_char_inactive then incr(limit);
+@z
+
+383. Linebreaking needs to be more robust when total demerits get
+very high (Frank Mittelbach, 22 Feb 1990)
+@x module 830
+@!artificial_badness:boolean; {has |b| been forced to zero?}
+@y
+@!artificial_demerits:boolean; {has |d| been forced to zero?}
+@z
+@x module 833 (this change is cosmetic only)
+@!minimal_demerits:array[very_loose_fit..tight_fit] of scaled; {best total
+ demerits known for current line class and position, given the fitness}
+@!minimum_demerits:scaled; {best total demerits known for current line class
+@y
+@!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
+ demerits known for current line class and position, given the fitness}
+@!minimum_demerits:integer; {best total demerits known for current line class
+@z
+@x module 836 here we want to ensure that total_demerits < awful_bad
+minimum_demerits:=minimum_demerits+abs(adj_demerits);
+@y
+if abs(adj_demerits)>=awful_bad-minimum_demerits then
+ minimum_demerits:=awful_bad-1
+else minimum_demerits:=minimum_demerits+abs(adj_demerits);
+@z
+@x module 851
+begin @!stat artificial_badness:=false;@+tats@/
+@y
+begin artificial_demerits:=false;@/
+@z
+@x module 854
+ begin b:=0; {set badness zero, this break is forced}
+ @!stat artificial_badness:=true;@+tats
+ end
+@y
+ artificial_demerits:=true {set demerits zero, this break is forced}
+@z
+@x module 855
+@<Compute the demerits, |d|, from |r| to |cur_p|@>;
+@y
+if artificial_demerits then d:=0
+else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
+@z
+@x module 856
+if artificial_badness then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi);
+print(" d="); print_int(d);
+@y
+if b>inf_bad then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi); print(" d=");
+if artificial_demerits then print_char("*")@+else print_int(d);
+@z
+@x module 859
+d:=line_penalty+b; d:=d*d;
+@y
+begin d:=line_penalty+b;
+if abs(d)>=10000 then d:=100000000@+else d:=d*d;
+@z
+@x ibid
+if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits
+@y
+if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
+end
+@z
+
+384. Math fonts may disappear outside of \eqno (Marc van Leeuwen).
+@x module 1194
+ if danger then flush_math;
+@y
+ danger:=false;
+ @<Check that the necessary fonts for math symbols are present;
+ if not, flush the current math lists and set |danger:=true|@>;
+@z
+
+385. Forgot to rule out charnode when testing for node type (Marc van Leeuwen).
+@x module 805
+ begin if type(q)=unset_node then
+ @<Set the unset box |q| and the unset boxes in it@>
+ else if type(q)=rule_node then
+ @<Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment@>;
+@y
+ begin if not is_char_node(q) then
+ if type(q)=unset_node then
+ @<Set the unset box |q| and the unset boxes in it@>
+ else if type(q)=rule_node then
+ @<Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment@>;
+@z
+@x module 903
+if type(hb)=ligature_node then if odd(subtype(hb)) then
+@y
+if not is_char_node(hb) then
+ if type(hb)=ligature_node then if odd(subtype(hb)) then
+@z
+@x module 1202
+ if p<>null then if type(p)=glue_node then d:=0;
+@y
+ if p<>null then if not is_char_node(p) then if type(p)=glue_node then d:=0;
+@z
+
+386. Don't change the font of punctuation before a hyphenated word
+ (Scott Allendorf, reported 7 Mar 90)
+@x module 895
+label common_ending,done,found,found1,not_found,not_found+1,exit;
+@y
+label common_ending,done,found,found1,found2,not_found,exit;
+@z
+@x module 903
+if is_char_node(ha) then
+ begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else goto not_found+1; {no punctuation found}
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+not_found+1: j:=1; s:=ha; init_list:=null;
+if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then
+ begin j:=0; hu[0]:=256; init_lig:=false;
+ end;
+@y
+if is_char_node(ha) then
+ if font(ha)<>hf then goto found2
+ else begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ if font(lig_char(ha))<>hf then goto found2
+ else begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else begin {no punctuation found; look for left boundary}
+ if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then goto found2;
+ j:=1; s:=ha; init_list:=null; goto common_ending;
+ end;
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+found2: s:=ha; j:=0; hu[0]:=256; init_lig:=false; init_list:=null;
+@z
+
+387. Balance the parens showing on the terminal (for Lispers).
+@x module 304
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@y
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@z
+@x module 331
+in_open:=0; max_buf_stack:=0;
+@y
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+@z
+@x module 362
+ begin print_char(")"); force_eof:=false;
+ update_terminal; {show user that file has been read}
+@y
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+@z
+@x module 537
+print_char("("); print(name); update_terminal; state:=new_line;
+@y
+print_char("("); incr(open_parens); print(name); update_terminal;
+state:=new_line;
+@z
+@x module 1334
+if job_name=0 then open_log_file;
+@y
+if job_name=0 then open_log_file;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+@z
+
+388. Optimize \ifx\p\q after \let\p=\q (Marc van Leeuwen says that
+ AmS-TeX uses this a lot).
+@x module 508
+ while (p<>null)and(q<>null) do
+ if info(p)<>info(q) then p:=null
+ else begin p:=link(p); q:=link(q);
+ end;
+b:=((p=null)and(q=null));
+@y
+if p=q then b:=true
+else begin while (p<>null)and(q<>null) do
+ if info(p)<>info(q) then p:=null
+ else begin p:=link(p); q:=link(q);
+ end;
+ b:=((p=null)and(q=null));
+ end;
+@z
+@x module 538 (while we're at it, might as well optimize this too)
+begin if not input_ln(cur_file,false) then do_nothing;
+@y
+begin if input_ln(cur_file,false) then do_nothing;
+@z
+@x module 1020 (and this)
+else begin wait:=false; s:=ins_ptr(p);
+ link(last_ins_ptr(r)):=s; s:=last_ins_ptr(r);
+@y
+else begin wait:=false; s:=last_ins_ptr(r); link(s):=ins_ptr(p);
+@z
+
+389. Treat migration properly in displays (Marc van Leeuwen).
+@x module 1199 (a bad bug that really has bitten)
+adjust_tail:=adjust_head; b:=hpack(p,natural);
+@y
+adjust_tail:=adjust_head; b:=hpack(p,natural); p:=list_ptr(b);
+@z
+@x module 1204
+shift_amount(b):=s+d; append_to_vlist(b);
+if t<>adjust_head then
+ begin link(tail):=link(adjust_head); tail:=t;
+ end
+@y
+shift_amount(b):=s+d; append_to_vlist(b)
+@z
+@x module 1205
+ tail_append(new_penalty(post_display_penalty));
+ end
+else begin tail_append(new_penalty(post_display_penalty));
+ tail_append(new_param_glue(g2));
+ end
+@y
+ g2:=0;
+ end;
+if t<>adjust_head then {migrating material comes after equation number}
+ begin link(tail):=link(adjust_head); tail:=t;
+ end;
+tail_append(new_penalty(post_display_penalty));
+if g2>0 then tail_append(new_param_glue(g2))
+@z
+
+-----------Here I draw the line with respect to further changes
+
+390. Uninitialized nullfont parameters (found by Lance Carnes, 11 May 90).
+@x module 552
+hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
+@y
+hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
+bchar_label[null_font]:=non_address;
+font_bchar[null_font]:=non_char; font_false_bchar[null_font]:=non_char;
+@z
+
+391. Disable \write{\the\prevgraf} (B. Jackowski, July 1990).
+@x module 422
+begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+while abs(nest[p].mode_field)<>vmode do decr(p);
+scanned_result(nest[p].pg_field)(int_val);
+end
+@y
+if mode=0 then scanned_result(0)(int_val) {|prev_graf=0| within \.{\\write}}
+else begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scanned_result(nest[p].pg_field)(int_val);
+ end
+@z
+
+392. Report correct line number when buffer overflows (George Russell).
+@x module 538
+begin if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start; line:=1;
+@y
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start;
+@z
+
+-------------
+Note: When making change 376, I forgot to delete the redundant
+code in module 883, and I should also have changed the name of that
+module. These cosmetic changes (and some changes to the comments)
+were made in version 3.14, in addition to the following two changes.
+-------------
+
+393. Show unprintable characters in font id's (Wayne Sullivan, Dec 1990)
+@x module 63
+print(s);
+@y
+slow_print(s);
+@z
+@x module 262 can now be spruced up
+else begin print_esc(""); slow_print(text(p));
+@y
+else begin print_esc(text(p));
+@z
+@x and module 263 likewise
+else begin print_esc(""); slow_print(text(p));
+ end;
+@y
+else print_esc(text(p));
+@z
+
+394. Avoid range check if total_pages>65535 (Eberhard Mattes, Dec 1990)
+@x module 642
+ dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@y
+ dvi_out((total_pages div 256) mod 256); dvi_out(total_pages mod 256);@/
+@z
+
+395. Less confusing error message (18 Sep 91)
+@x module 436
+ begin print_err("Bad math code");
+@.Bad math code@>
+ help2("A numeric math code must be between 0 and 32767.")@/
+@y (because a \mathcode can be 32768, but not a \mathchar)
+ begin print_err("Bad mathchar");
+@.Bad mathchar@>
+ help2("A mathchar number must be between 0 and 32767.")@/
+@z
+
+396. Internal strings should not be expanded even if unprintable;
+i.e., selector=new_string => print(c)==print_char(c), print(s)==slow_print(s).
+This change completes what I began with number 393 (18 Sep 91).
+@x modules 59 and 60 (same change to both)
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@y
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@z
+@x module 61
+else begin print(format_ident); print_ln;
+@y
+else begin slow_print(format_ident); print_ln;
+@z
+@x module 84
+ print(input_stack[base_ptr].name_field);
+@y
+ slow_print(input_stack[base_ptr].name_field);
+@z
+@x module 518
+begin print(a); print(n); print(e);
+@y
+begin slow_print(a); slow_print(n); slow_print(e);
+@z
+@x module 536
+print(format_ident); print(" ");
+@y
+slow_print(format_ident); print(" ");
+@z
+@x module 537
+print_char("("); incr(open_parens); print(name); update_terminal;
+@y
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+@z
+@x module 581
+ print(font_name[f]); print_char("!"); end_diagnostic(false);
+@y
+ slow_print(font_name[f]); print_char("!"); end_diagnostic(false);
+@z
+@x module 642
+ print_nl("Output written on "); print(output_file_name);
+@y
+ print_nl("Output written on "); slow_print(output_file_name);
+@z
+@x module 1261
+set_font:begin print("select font "); print(font_name[chr_code]);
+@y
+set_font:begin print("select font "); slow_print(font_name[chr_code]);
+@z
+@x module 1280
+print(s); update_terminal;
+@y
+slow_print(s); update_terminal;
+@z
+@x module 1283
+begin print_err(s);
+@y
+begin print_err(""); slow_print(s);
+@z
+@x module 1328
+print(w_make_name_string(fmt_file)); flush_string;
+print_nl(format_ident)
+@y
+slow_print(w_make_name_string(fmt_file)); flush_string;
+print_nl(""); slow_print(format_ident)
+@z
+@x module 1333
+ print(log_name); print_char(".");
+@y
+ slow_print(log_name); print_char(".");
+@z
+@x module 1339
+10: print(n);
+@y
+10: slow_print(n);
+@z
+
+397. Retain more right context for reconstructing ligatures (19 Sep 91)
+@x module 892
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@y
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@!hyf_bchar:halfword; {boundary character after $c_n$}
+@z
+@x module 897
+ c:=qo(character(s));
+@y
+ hyf_bchar:=character(s); c:=qo(hyf_bchar);
+@z
+@x module 897 again
+ s:=link(s);
+@y
+ s:=link(s); hyf_bchar:=non_char;
+@z
+@x module 898
+j:=hn; q:=lig_ptr(s);
+@y
+j:=hn; q:=lig_ptr(s);@+if q>null then hyf_bchar:=character(q);
+@z
+@x module 903
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=non_char;
+@y
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=hyf_bchar;
+@z
+
+398. Patch the previous code, which could cause double kerning (CET1, 29 Sep 91)
+@x module 897
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c);
+ end
+ else if type(s)=ligature_node then
+ @<Move the characters of a ligature node to |hu| and |hc|;
+ but |goto done3| if they are not all letters@>
+ else if (type(s)<>kern_node)or(subtype(s)<>normal) then goto done3;
+ s:=link(s); hyf_bchar:=non_char;
+@y
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c); hyf_bchar:=non_char;
+ end
+ else if type(s)=ligature_node then
+ @<Move the characters of a ligature node to |hu| and |hc|;
+ but |goto done3| if they are not all letters@>
+ else if (type(s)=kern_node)and(subtype(s)=normal) then hb:=s
+ else goto done3;
+ s:=link(s);
+@z
+@x module 898
+hb:=s; hn:=j;
+@y
+hb:=s; hn:=j;
+if odd(subtype(s)) then hyf_bchar:=font_bchar[hf]@+else hyf_bchar:=non_char;
+@z
+@x module 903
+if not is_char_node(hb) then
+ if type(hb)=ligature_node then if odd(subtype(hb)) then
+ bchar:=font_bchar[hf];
+@y
+@z
+
+399. Disallow \setbox in do_assignments (problem found by Robert Hunt, Nov 91)
+@x module 76
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?}
+@y
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?}
+@!set_box_allowed:boolean; {is it safe to do a \.{\\setbox} assignment?}
+@z
+@x module 77
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+@y
+deletions_allowed:=true; set_box_allowed:=true;
+error_count:=0; {|history| is initialized elsewhere}
+@z
+@x module 1241
+ scan_optional_equals; scan_box(box_flag+n);
+@y
+ if set_box_allowed then scan_box(box_flag+n)
+ else begin print_err("Improper "); print_esc("setbox");
+@.Improper \\setbox@>
+ help2("Sorry, \setbox is not allowed after \halign in a display,")@/
+ ("or between \accent and an accented character."); error;
+ end;
+@z
+@x module 1270
+ prefixed_command;
+@y
+ set_box_allowed:=false; prefixed_command; set_box_allowed:=true;
+@z
+
+400. Defend against negative cur_mu (Wayne Sullivan, Nov 91)
+@x module 716
+begin n:=x_over_n(m,@'200000); f:=remainder;@/
+@y
+begin n:=x_over_n(m,@'200000); f:=remainder;@/
+if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+@z
+@x module 717
+ begin n:=x_over_n(m,@'200000); f:=remainder;@/
+@y
+ begin n:=x_over_n(m,@'200000); f:=remainder;@/
+ if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+@z
+
+401. Defend against `|}{|' in |\read| (Michael Downes).
+@x module 483
+ store_new_token(cur_tok);
+@y
+ if align_state<1000000 then {unmatched `\.\}' aborts the line}
+ begin repeat get_token; until cur_tok=0;
+ align_state:=1000000; goto done;
+ end;
+ store_new_token(cur_tok);
+@z
+
+402. Economize string storage under multiple \font defs (B. Jackowski)
+@x module 1257
+@!old_setting:0..max_selector; {holds |selector| setting}
+@y
+@!old_setting:0..max_selector; {holds |selector| setting}
+@!flushable_string:str_number; {string not yet referenced}
+@z
+@x module 1260
+for f:=font_base+1 to font_ptr do
+ if str_eq_str(font_name[f],cur_name)and str_eq_str(font_area[f],cur_area) then
+ begin if s>0 then
+@y
+flushable_string:=str_ptr-1;
+for f:=font_base+1 to font_ptr do
+ if str_eq_str(font_name[f],cur_name)and str_eq_str(font_area[f],cur_area) then
+ begin if cur_name=flushable_string then
+ begin flush_string; cur_name:=font_name[f];
+ end;
+ if s>0 then
+@z
+
+403. Don't look for \newlinechar within unprintable chars (B. Raichle, Nov 91)
+@x module 59
+var j:pool_pointer; {current character code position}
+@y
+var j:pool_pointer; {current character code position}
+@!nl:integer; {new-line character to restore}
+@z
+@x
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@y
+ 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@>) 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;
+@z
+@x module 60
+label exit;
+var j:pool_pointer; {current character code position}
+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 if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+exit:end;
+label exit;
+var j:pool_pointer; {current character code position}
+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 if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+exit:end;
+@y
+var j:pool_pointer; {current character code position}
+begin if (s>=str_ptr) or (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;
+@z
+
+404. False boundary character logic was incorrect (found by Eberhard
+ Mattes and Bernd Raichle, 6 Feb 92)
+@x module 1036
+cur_q:=tail; cur_l:=cur_r; {or |character(lig_stack)|}
+@y the following is needed since cur_r might have been changed to non_char
+cur_q:=tail; cur_l:=character(lig_stack);
+@z
+
+405. New paragraph and resume-after-display should continue current
+ \language (Sch\"opf and Thompson, 17 July 92)
+@@@ Undo the changes to modules 212, 213, 215 in change number 373.
+@x module 218
+ if m=hmode then@+if(nest[p].lhm_field<>2)or(nest[p].rhm_field<>3)then
+ begin print(" (hyphenmin "); print_int(nest[p].lhm_field); print_char(",");
+ print_int(nest[p].rhm_field); print_char(")");
+ end;
+@y
+ if m=hmode then if (nest[p].pg_field <> @'40600000) then
+ begin print(" (language"); print_int(nest[p].pg_field mod @'200000);
+ print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000);
+ print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100);
+ print_char(")");
+ end;
+@z
+@x module 816
+pop_nest;
+@y prev_graf now carries the language parameters at start of horizontal mode
+init_cur_lang:=prev_graf mod @'200000;
+init_l_hyf:=prev_graf div @'20000000;
+init_r_hyf:=(prev_graf div @'200000) mod @'100;
+pop_nest;
+@z
+@x module 891
+l_hyf:=lhmin; r_hyf:=rhmin; cur_lang:=0;
+@y
+cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf;
+@z
+@x module 892
+@!cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@y
+@!cur_lang,@!init_cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf,@!init_l_hyf,@!init_r_hyf:integer; {limits on fragment sizes}
+@z
+@x module 1091
+lhmin:=norm_min(left_hyphen_min); rhmin:=norm_min(right_hyphen_min);
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;@/
+@y
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+@z
+@x module 1200
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@y
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+@z
+
+406. Avoid (harmless) range errors (Philip Taylor and CET1, 17 Dec 92).
+@x module 934
+var n:small_number; {length of current word}
+@!j:small_number; {an index into |hc|}
+@y
+var n:0..64; {length of current word; not always a |small_number|}
+@!j:0..64; {an index into |hc|}
+@z
+@x module 960
+var k,@!l:small_number; {indices into |hc| and |hyf|}
+@y
+var k,@!l:0..64; {indices into |hc| and |hyf|;
+ not always in |small_number| range}
+@z
+
+407. Kerns inserted via boundary chars should not disappear (William E. Baxter,
+ 25 Feb 93)
+@x module 837
+Kern nodes for accents are treated specially: They do not
+disappear at a line break.
+@y
+Kern nodes do not disappear at a line break unless they are |explicit|.
+@z
+@x ibid
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@y
+ math_node: break_width[1]:=break_width[1]-width(s);
+ kern_node: if subtype(s)<>explicit then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@z
+@x module 839
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$,
+minus the length of nodes that will be discarded after the discretionary break.
+@y (this change and the next two should have been made long ago, with #376!)
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~|q|; in that unusual case we subtract the length of~|q| and any
+other nodes that will be discarded after the discretionary break.
+@z
+@x module 840
+ begin @<Add the width of node |s| to |break_width| and increase |t|,
+ unless it's discardable@>;
+ s:=link(s);
+ end;
+break_width[1]:=break_width[1]+disc_width;
+if t=0 then s:=link(v); {more nodes may also be discardable after the break}
+@y
+ begin @<Add the width of node |s| to |break_width|@>;
+ s:=link(s);
+ end;
+break_width[1]:=break_width[1]+disc_width;
+if post_break(cur_p)=null then s:=link(v);
+ {nodes may be discardable after the break}
+@z
+@x module 842 (which now has a new title)
+ hlist_node,vlist_node,rule_node:break_width[1]:=break_width[1]+width(s);
+ kern_node: if (t=0)and(subtype(s)<>acc_kern) then t:=-1 {discardable}
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases;
+incr(t)
+@y
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases
+@z
+@x module 866
+kern_node: kern_break;
+@y
+kern_node: if subtype(cur_p)=explicit then kern_break
+ else act_width:=act_width+width(cur_p);
+@z
+@x module 868
+ else if precedes_break(prev_p) then try_break(0,unhyphenated);
+@y
+ else if precedes_break(prev_p) then try_break(0,unhyphenated)
+ else if (type(prev_p)=kern_node)and(subtype(prev_p)<>explicit) then
+ try_break(0,unhyphenated);
+@z
+@x module 879
+ if subtype(q)=acc_kern then if type(q)=kern_node then goto done1;
+@y
+ if type(q)=kern_node then if subtype(q)<>explicit then goto done1;
+@z
+
+408. Prevent boundary kern from disappearing after hyphenation (25 Feb 93)
+@x module 897
+ else if (type(s)=kern_node)and(subtype(s)=normal) then hb:=s
+@y
+ else if (type(s)=kern_node)and(subtype(s)=normal) then
+ begin hb:=s;
+ hyf_bchar:=font_bchar[hf];
+ end
+@z
+
+409. Avoid potential future bug [P Breitenlohner] (26 Jun 93)
+@x module 628 [shift_amount(leader_box)=0 now but maybe not in extensions]
+dvi_v:=save_v; dvi_h:=save_h; cur_v:=save_v;
+@y
+dvi_v:=save_v; dvi_h:=save_h; cur_v:=base_line;
+@z
+@x module 637
+dvi_v:=save_v; dvi_h:=save_h; cur_h:=save_h;
+@y
+dvi_v:=save_v; dvi_h:=save_h; cur_h:=left_edge;
+@z
+
+410. Mustn't let |non-address| depend on |font_mem_size|
+@x module 549
+@d non_address==font_mem_size {a spurious |font_index|}
+@y
+@d non_address=0 {a spurious |bchar_label|}
+@z
+@x module 916
+if bchar_label[hf]<non_address then {put left boundary at beginning of new line}
+@y
+if bchar_label[hf]<>non_address then {put left boundary at beginning of new line}
+@z
+@x module 1323, this will detect obsolete format files
+undump(0)(font_mem_size)(bchar_label[k]);
+@y
+undump(0)(fmem_ptr-1)(bchar_label[k]);
+@z
+
+411. Allow large |font_mem_size|, many font parameters.
+@x module 549
+@!font_params:array[internal_font_number] of halfword; {how many font
+@y
+@!font_params:array[internal_font_number] of font_index; {how many font
+@z
+
+412. Math kerns are explicit; this matters, after change 405 (W Carlip, Sep 94)
+@x module 717
+ width(p):=mu_mult(width(p)); subtype(p):=normal;
+@y
+ width(p):=mu_mult(width(p)); subtype(p):=explicit;
+@z
+
+413. Avoid overflow on real-to-integer conversion
+@x module 619
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
+@y
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
+@!glue_temp:real; {glue value before rounding}
+@z
+@x module 625
+@ @<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ rule_wd:=rule_wd+round(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ end
+ else begin if shrink_order(g)=g_order then
+ rule_wd:=rule_wd-round(float(glue_set(this_box))*shrink(g));
+@y
+@ @d vet_glue(#)== glue_temp:=#;
+ if glue_temp>float_constant(1000000000) then
+ glue_temp:=float_constant(1000000000)
+ else if glue_temp<-float_constant(1000000000) then
+ glue_temp:=-float_constant(1000000000)
+
+@<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_wd:=rule_wd+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_wd:=rule_wd-round(glue_temp);
+@z
+@x module 629
+@!edge:scaled; {bottom boundary of leader space}
+@y
+@!edge:scaled; {bottom boundary of leader space}
+@!glue_temp:real; {glue value before rounding}
+@z
+@x module 634
+ rule_ht:=rule_ht+round(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ end
+ else begin if shrink_order(g)=g_order then
+ rule_ht:=rule_ht-round(float(glue_set(this_box))*shrink(g));
+@y
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_ht:=rule_ht+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_ht:=rule_ht-round(glue_temp);
+@z
+
+414. final cleanup should not retain spurious reference counts (20 Mar 95)
+@x module 1335
+while open_parens>0 do
+@y
+while input_ptr>0 do
+ if state=token_list then end_token_list@+else end_file_reading;
+while open_parens>0 do
+@z
+@x module 1335, continued
+ cur_if:=subtype(cond_ptr); cond_ptr:=link(cond_ptr);
+@y
+ cur_if:=subtype(cond_ptr); temp_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(temp_ptr,if_node_size);
+@z
+@x module 1335, concluded
+ begin @!init store_fmt_file; return;@+tini@/
+@y
+ 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@/
+@z
+
+415. we need not raise hassles about two-digit years (23 Nov 98)
+@x module 1328
+print_int(year mod 100); print_char(".");
+@y
+print_int(year); print_char(".");
+@z
+
+416. \xleaders often drops the final box (Hiroshi Nakashima, 12 July 99)
+@x modules 627 and 636
+ else begin lx:=(2*lr+lq+1) div (2*lq+2); {round|(lr/(lq+1))|}
+@y
+ else begin lx:=lr div (lq+1);
+@z
+
+417. disallow 100000 unbalanced }s outside of alignment (Ralf Roth, 30 Aug 01)
+@x module 789
+to another alignment is being scanned.
+
+@<Insert the \(v)\<v_j>...@>=
+begin if scanner_status=aligning then
+@y
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@<Insert the \(v)\<v_j>...@>=
+begin if (scanner_status=aligning) or (cur_align=null) then
+@z
+
+418. don't allow end-template except at end of template (Roth, 30 Aug 01)
+@x module 325
+while (state=token_list)and(loc=null) do end_token_list; {conserve stack space}
+@y
+while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+@z
+@x module 390
+while (state=token_list)and(loc=null) do end_token_list; {conserve stack space}
+@y
+while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+@z
+@x module 1131
+during an entire alignment, until |fin_align| removes it.
+
+@<Declare act...@>=
+procedure do_endv;
+begin if cur_group=align_group then
+@y
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@<Declare act...@>=
+procedure do_endv;
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+while (input_stack[base_ptr].index_field<>v_template) and
+ (input_stack[base_ptr].loc_field=null) and
+ (input_stack[base_ptr].state_field=token_list) do decr(base_ptr);
+if (input_stack[base_ptr].index_field<>v_template) or
+ (input_stack[base_ptr].loc_field<>null) or
+ (input_stack[base_ptr].state_field<>token_list) then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+ if cur_group=align_group then
+@z
+
+419. Improve rounding of glue during output.
+@x module 3
+@!@^dirty \PASCAL@>
+@y
+@!@^dirty \PASCAL@>
+
+Incidentally, \PASCAL's standard |round| function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+@z
+@x module 619
+begin this_box:=temp_ptr; g_order:=glue_order(this_box);
+@y
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+@z
+@x module 625
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_wd:=rule_wd+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_wd:=rule_wd-round(glue_temp);
+ end;
+ end;
+@y
+begin g:=glue_ptr(p); rule_wd:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_wd:=rule_wd+cur_g;
+@z
+@x module 629
+begin this_box:=temp_ptr; g_order:=glue_order(this_box);
+@y
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+@z
+@x module 634
+begin g:=glue_ptr(p); rule_ht:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_ht:=rule_ht+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_ht:=rule_ht-round(glue_temp);
+ end;
+ end;
+@y
+begin g:=glue_ptr(p); rule_ht:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_ht:=rule_ht+cur_g;
+@z
+
+420. Error recovery hindered by missing goto statement (reported by
+David Fuchs, September 2007)
+
+@x module 395
+incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
+@y
+incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
+goto continue;
+@z
+
+421. Slight improvement to inner loop speed also eliminates a case
+of "dirty Pascal"
+
+@x module 1035
+ begin if character(tail)=qi(hyphen_char[main_f]) then if link(cur_q)>null then
+@y
+ begin if link(cur_q)>null then
+ if character(tail)=qi(hyphen_char[main_f]) then ins_disc:=true;
+@z
+
+422. Amendment to bugfix 414 (reported by David Fuchs, September 2007)
+
+@x module 1335
+ store_fmt_file; return;@+tini@/
+@y
+ if last_glue<>max_halfword then delete_glue_ref(last_glue);
+ store_fmt_file; return;@+tini@/
+@z
+
+423. Remove leaking glue reference after error (DRF, September 2007)
+
+@x module 1236
+ help2("I can't carry out that multiplication or division,")@/
+ ("since the result is out of range.");
+@y
+ help2("I can't carry out that multiplication or division,")@/
+ ("since the result is out of range.");
+ if p>=glue_val then delete_glue_ref(cur_val);
+@z
+
+424. Leaders with muglue is henceforth disallowed (indeed, David Fuchs
+observed that it has ever worked, except in 180pt fonts!)
+
+@x module 1078
+if ((cur_cmd=hskip)and(abs(mode)<>vmode))or@|
+ ((cur_cmd=vskip)and(abs(mode)=vmode))or@|
+ ((cur_cmd=mskip)and(abs(mode)=mmode)) then
+@y
+if ((cur_cmd=hskip)and(abs(mode)<>vmode))or@|
+ ((cur_cmd=vskip)and(abs(mode)=vmode)) then
+@z
+
+425. An optimization suggested by Fuchs that I couldn't resist, because
+it improves the inner loop whenever the last letter of a word
+has a lig/kern program. Also another twiddle for the inner loop.
+
+@x module 1039
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+@y
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+if cur_r=non_char then goto main_loop_wrapup;
+@z
+@x module 1036
+tail_append(lig_stack) {|main_loop_lookahead| is next}
+@y
+link(tail):=lig_stack; tail:=lig_stack {|main_loop_lookahead| is next}
+@z
+
+426. For robustness, enforce a restriction checked by TFtoPL (suggested
+by DRF)
+
+@x module 365
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
+@y
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
+if (nw=0)or(nh=0)or(nd=0)or(ni=0) then abort;
+@z
+
+427. Tell more precisely the effective size of 1:1 insertions (DEK, 27 Feb 08)
+
+@x module 986
+ t:=x_over_n(height(r),1000)*count(t); print_scaled(t);
+@y
+ if count(t)=1000 then t:=height(r)
+ else t:=x_over_n(height(r),1000)*count(t);
+ print_scaled(t);
+@z
+
+-----------
+999. The absolutely final change (to be made after my death)
+@x module 2
+@d banner=='This is TeX, Version 3.141592' {printed when \TeX\ starts}
+@y
+@d banner=='This is TeX, Version $\pi$' {printed when \TeX\ starts}
+@z
+When this change is made, the corresponding line should be changed in
+Volume B, and also on page 23 of The TeXbook.
+My last will and testament for TeX is that no further changes be made
+under any circumstances. Improved systems should not be called simply `TeX';
+that name, unqualified, should refer only to the program for which I have
+taken personal responsibility. -- Don Knuth
+
+ * Possibly nice ideas that will not be implemented
+
+. classes of marks analogous to classes of insertions
+
+. \showcontext to show the current location without stopping for error
+
+. \show commands to be less like errors
+
+. \everyeof to insert tokens before an \input file ends
+ (strange example: \everyeof{\noexpand} will allow things
+ like \xdef\a{\input foo}!)
+
+. generalize \leftskip and \rightskip to token lists (problems with
+ displayed math then)
+
+. generalize \widowline and \clubline to go further into a paragraph
+
+. \lastbox to remove and box a charnode if one is there
+
+. \lastbox to set a new quantity \lastboxshift, to preserve info now lost
+
+. \posttolerance for third pass of line breaking
+
+* Design errors that are too late to fix
+
+. additional parameters should be in symbol fonts to govern the space between
+ rules and text in \over, \sqrt, etc.
+
+. multilingual typesetting doesn't work properly when the \lccode changes
+ within a paragraph
+
+. large accents cannot be brought close to small letters in math mode
+
+. \hyphenation is not sensitive to \language
+
+* Bad ideas that will not be implemented
+
+. several people want to be able to remove arbitrary elements of lists,
+ but that must never be done because some of those elements (e.g. kerns
+ for accents) depend on floating point arithmetic
+
+. if anybody wants letter spacing desperately they should put it in their own
+ private version (e.g. generalize the hpack routine) and NOT call it TeX.