From cfd5aee575d84f09834ffcf92e5ad51c7cac38ca Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Sat, 23 May 2009 00:17:44 +0000 Subject: move tex-refs and knuth out of texmf-doc git-svn-id: svn://tug.org/texlive/trunk@13409 c570f23f-e606-0410-a88d-b1316a301751 --- .../texmf-dist/doc/generic/knuth/errata/tex82.bug | 9094 ++++++++++++++++++++ 1 file changed, 9094 insertions(+) create mode 100644 Master/texmf-dist/doc/generic/knuth/errata/tex82.bug (limited to 'Master/texmf-dist/doc/generic/knuth/errata/tex82.bug') 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_style0 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:@; +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@> + +@= +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 . +f. Delete the primitive ":" in modules 250 and 251. +g. Add to module 376: +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: ; +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 +k. Module 389 becomes + = + 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: + = + procedure scan_font_ident; + var f:internal_font_number; + begin ; + 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. + = +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 + ; + u:=cs_ptr; scan_optional_equals; scan_file_name; + ; + ; + 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. + = + for f:=font_base+1 to font_ptr do + if [the test previously in module 1174] then goto common_ending +cc. New module 1173: + = + 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: + = + 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: + ; + define(u,set_font,undefined_font); + scan_optional_equals; scan_file_name; + ; + ; + 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. + = + get_token; + if cs_ptr0 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+= + let: begin get_token; + if cs_ptr<>0 then + 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: + = + 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("") + else print_nl(" "); + inserted: print_nl(" "); +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 "" 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: + ; +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. + +@= +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 +"" +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 "" 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 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) + 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 ; goto restart; + end; +In module 1064, the body of the procedure becomes +begin if c>='4000 then else ; end +And there's a new module: += +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 = + 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= + 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: + @ @= + 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 interaction0 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 \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: += +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 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=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 @; +Then define @ 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 replaced by \ifodd (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: +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 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. 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 (Jun 14) +The optional space will now be analogous to that after . + +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 then . + +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. = 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] +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 @;@+save_cond_ptr:=cond_ptr;@/ +@y +@!this_if:small_number; {type of this conditional} +begin @;@+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@> + +@= +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 +@ @= +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; + @; + while cur_h+leader_wd<=edge do + @; +@y + begin edge:=cur_h+rule_wd; lx:=0; + @; + while cur_h+leader_wd<=edge do + @; +@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; + @; + while cur_v+leader_ht<=edge do + @; +@y + begin edge:=cur_v+rule_ht; lx:=0; + @; + while cur_v+leader_ht<=edge do + @; +@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 +@ @= +@y +@ @= +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: @; +@y +if_void_code, if_hbox_code, if_vbox_code: @; +@z +@x module 505 +@ @= +begin scan_eight_bit_int; b:=(box(cur_val)=null); +end +@y +@ @= +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 then + if selector +@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: @; +@y +last_item: @; +@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@> + +@= +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. + +@= +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 +@; +@y +@; +@z +@x module 996 +@ @= +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 +@ @= +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); +@; {this sets |last_page_glue:=max_halfword|} +@y +if last_glue<>max_halfword then delete_glue_ref(last_glue); +@; {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 + @ +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 + @ +else begin if not is_char_node(tail) then if type(tail)=cur_chr then +@z +@x module 1106 +@ @= +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 +@ @= +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} +@; +define(u,set_font,null_font); scan_optional_equals; scan_file_name; +@; +@; +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; +@; +@; +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}. + +@= +get_r_token; +if cs_ptr + 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} + +@= +@!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} + +@= +@!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; + @; + if cur_cmd<>left_brace then @; + back_input; q:=scan_toks(false,false); +@y + @; + if cur_cmd<>left_brace then @; + 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 jbuffer[k] then goto not_found; +@y +begin j:=str_start[s]; +while jbuffer[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 +if cur_cmd<=max_command then goto done; +if cur_cmd>=call then + if cur_cmd0 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 @; +@z +@x module 176 (removing a reference to |font_ident|) + else sprint_cs(font_ident[font(p)]); +@y + else @; +@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_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; +@; +@; +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; +@; +@; +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): @; +@y +any_state_plus(invalid_char): @; +@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 +@; +@y +if name=str_ptr-1 then {we can conserve string pool space now} + begin flush_string; name:=cur_name; + end; +@; +@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}funity 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 @; +@y + begin deletions_allowed:=false; + @; +@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} +@@; +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 +@@; +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 interaction0 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 + +@= +procedure show_whatever; +var p:pointer; {tail of a token list to show} +begin case cur_chr of +show_code: @; +show_box_code: @; +show_the_code: @; +othercases @ +endcases; +end; +@y +@ @= +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_code: @; +othercases @ +endcases;@/ +@; +common_ending: if interaction0 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 +@ @= +begin get_token; print_nl("> "); +if cur_cs<>0 then + begin sprint_cs(cur_cs); print_char("="); + end; +print_meaning; show_error; +@y +@ @= +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 +@ @= +begin begin_diagnostic; show_activities; end_diagnostic(true); +print_err("OK"); +show_error; +@.OK@> +end +@y +@ @= +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; +@; +@y +loop@+ begin if p=null then goto done; + if info(p); +@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_basemem_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_max0)or(max_quarterword<127) then bad:=11; +@z +@x module 111 +if (mem_base=max_halfword) then bad:=14; +@y +if (mem_min=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 + 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; +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. + +@= +q:=p+node_size(p); {find the physical successor} +@^inner loop@> +while is_empty(q) do @; +r:=q-s; +if r>p+1 then @; +if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then + @; +node_size(p):=q-p {reset the size in case it grew} + +@ @= +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@> + +@= +begin if lo_mem_max+1000p+1| about 95\% of the time. + +@= +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 @; +if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then + @; +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 +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} +@; +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} +@; +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(pmem_end)or(p=hi_mem_base)or(p=hi_mem_base)or(rlink(p)hi_mem_base)or@| (llink(rlink(p))<>p) then clobbered:=true; +@y +repeat if (p>=lo_mem_max)or(p=lo_mem_max)or(rlink(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 + ((pwas_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 +@; +@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; +@; +@z +@x module 176 +if (pmem_end) then print_esc("CLOBBERED.") +@y +if (pmem_end) then print_esc("CLOBBERED.") +@z +@x module 178 +begin if (p=hi_mem_base) then print_char("*") +@y +begin if (p=lo_mem_max) then print_char("*") +@z +@x module 293 +if (pmem_end) then +@y +if (pmem_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_halfwordhi_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_minmax_halfword!] +@x module 111 +if (mem_min=max_halfword) then bad:=14; +@y +if (mem_min=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+2mem_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 + @ +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 + +@ @= +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 + @; + end; +end + +@ @= +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:@; + 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 @; +@y +s:=cur_p; +if break_type>unhyphenated then if cur_p<>null then + @; +while s<>null do + begin if is_char_node(s) then goto done; + case type(s) of + glue_node:@; + 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); + @; + end; +s:=post_break(cur_p); +while s<>null do + begin @; +@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); + @; + end; +while s<>null do + begin @; +@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|) +@= +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 +@= +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+127)or(info(r)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=ident_val then @ +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 @ +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; @; +old_setting:=selector; selector:=new_string; +@; +selector:=old_setting; link(garbage):=str_toks; ins_list(link(temp_head)); +@y +@!b:pool_pointer; {base of temporary string} +begin c:=cur_chr; @; +old_setting:=selector; selector:=new_string; b:=pool_ptr; +@; +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=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 +@=job_name:=0; name_in_progress:=false; +@y +@= +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 + 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=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=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 + @; +@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. + +@= +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|} + +@= +@!i:0..last_text_char; +@y +@d last_text_char=255 {ordinal number of the largest element of |text_char|} + +@= +@!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 +@= +@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|} +@!str_number = 0..max_strings; {for variables that point into |str_start|} + +@ @= +@!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|} + +@= +@!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} + +@ @= +@!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 +@; +@y +@; +@z +@x module 48 +@ @= +for k:=0 to 127 do + begin if (@) 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") + +@= +for k:=0 to 255 do + begin if (@) 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 +@= +begin if (cur_chr=buffer[loc])and(loc="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 + +@= +begin if cur_chr=buffer[loc] then if loc2 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+255)or(info(r)= +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 done; +@y + repeat 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=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 @; +@y +loop@+ begin if threshold>inf_bad then threshold:=inf_bad; + if second_pass then @; + @; +@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. + +@= +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 @; + goto continue; + end +@z +@x module 899 (title of this module also changes appropriately) +if hn<5 then goto done1; +@y +if hn:=@t$p_1$@>| and then, for |1)|. 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 @; +@y + spacer,right_brace: begin if n>1 then @; +@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); +@y +@; +@z +@x module 942 +@p@!init @@; +@y +@= +@!init @@; +@z +@x module 943 +@d quarterword_diff=max_quarterword-min_quarterword +@d trie_op_hash_size=quarterword_diff+quarterword_diff {double} + +@= +@!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 +@= +@!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. + +@= +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. + +@= +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. + +@= +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. + +@= +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. + +@= +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}. + +@= +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. + +@= +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. + +@= +@; +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= +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;@/ +@; +trie_root:=compress_trie(trie_root); {compress the trie} +@; +end; +@y +begin if trie_not_ready then + begin set_cur_lang; scan_left_brace; {a left brace must follow \.{\\patterns}} + @; + 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= +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 @; +if trie_root<>0 then + begin first_fit(trie_root); trie_pack(trie_root); + end; +@; +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:@; +@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 +@ @=do_nothing +@y +@ @= +if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p) + +@ @= +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 +@ @= +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. + +@= +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= +@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|} + +@= +@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 + +@= +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=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;@/ + @; + 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 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 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(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. + +@= +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); +@; +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= +@!hyphen_passed:small_number; {first hyphen in a ligature, if any} + +@ @= +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|} +@; +continue:@; +@; +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= +@!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= +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= +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; + 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|} + +@= +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 + +@ @= +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. + +@= +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 + @; +until j>hn; +link(s):=q + +@ @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count); + end + +@= +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; +@; +@; +@; +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= +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|. + +@= +minor_tail:=null; post_break(r):=null; c_loc:=0; +if bchar_label[hf]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 + @; + end + +@ @= +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. + +@= +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 +@@; +@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. + +@= +@!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. + +@= +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 + +@= +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:@; +main_loop_move:@; +main_loop_lookahead:@; +main_lig_loop:@; +main_loop_move_lig:@ + +@ 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_lnull 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 + +@= +wrapup(rt_hit) + +@ @= +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_chrfont_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|. + +@= +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. + +@= +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 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 + @; +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 + +@= +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 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 +@; +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 @ +@y +if holding_inserts<=0 then + @; +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 + @; + 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 +@ @=adjust_tail:=null; +@y +@ @=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 (hbadnessnull) 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 +@@/ +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} + @; + 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?} +@@/ +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 + 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) +else if saved(0) +else if cur_box<>null then + if saved(0)>ship_out_flag then @ +@y +procedure box_end(@!box_context:integer); +var p:pointer; {|ord_noad| for new box in math mode} +begin if box_context +else if box_context +else if cur_box<>null then + if box_context>ship_out_flag then @ +@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); +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 @; +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 @; + @; + @; +@y +begin prev_s:=cur_p; s:=link(prev_s); +if s<>null then + begin @; + if l_hyf+r_hyf>63 then goto done1; + @; + @; +@z +@x module 899 +if hn=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 +@ @= +if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p) + +@ @= +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 + +@=@+ +adv_past(cur_p) + +@ @=@+ +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 @; + @; +@y +glue_node: begin @; +@z +@x ibid +disc_node: @; +@y +disc_node: @; +@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 @; + 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 +@ @= +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 @; +@y +if cur_p<>null then if not post_disc_break then + @; +@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 + +@= +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 + +@= +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 @; +@y +begin if (loc<>null) or + ((token_type<>output_text)and(token_type<>backed_up)) then + @; +@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 @; + cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false; +@z +@x ibid +else begin @; +@y +else begin if a=null then @; +@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 +@; +@y +if artificial_demerits then d:=0 +else @; +@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; + @; +@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 + @ + else if type(q)=rule_node then + @; +@y + begin if not is_char_node(q) then + if type(q)=unset_node then + @ + else if type(q)=rule_node then + @; +@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 (@) then + if selectorpseudo then + begin print_char(s); return; {internal strings are not expanded} + end + else if (@) then + if selectornull 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 + @ + 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 + @ + 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 (@) then + if selectorpseudo then + begin print_char(s); return; {internal strings are not expanded} + end; + if (@) then + if selector=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 (@) then + if selector=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 (@) then + if selector=str_ptr) or (s<256) then print(s) +else begin j:=str_start[s]; + while j2)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 @; + 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 @; + 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} +@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 +@ @= +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) + +@= +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. + +@...@>= +begin if scanner_status=aligning then +@y +to another alignment is being scanned, or when no alignment preamble is active. + +@...@>= +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. + +@= +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. + +@= +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. -- cgit v1.2.3