% This is TEX--XET.CHANGE in text format, as of October 9, 1992. TEX--XET.CHANGE - WEB change file for TEX 3.141 TeX--XeT Copyright (C) 1992 by Dante e.V. Contains code for mixed direction text (please note that the name is TeX--XeT and not TeX-XeT). % A preliminary version was released in April 1992. % Version 1.0 was released in June 1992. % Version 1.1 prevents arith overflow in glue computation (October 1992). This change file is free software; you can redistribute and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. In order to implement TeX--XeT for a particular system these generic, i.e., system-independent changes for TeX--XeT must be merged into the system-dependent changes for TeX. For that purpose the GNU General Public License is to be interpreted as follows: 1. In the banner line (displayed on the terminal and written to the transcript file) 'TeX' must be replaced by 'TeX--XeT' and the TeX--XeT version (currently '--1.1') be appended to the TeX version (currently '3.141'). 2. If the implementation is distributed under the terms of the GNU General Public License or similar (in particular if the source is available) then the change file must include the TeX--XeT copyright notice above. Moreover if the program displays its own copyright message then the TeX--XeT copyright message must be displayed as well. 3. If the source of the implementation is not available then the program must display the TeX--XeT copyright message. Moreover the distribution of the implementation must make clear that it uses this change file and that this change file is available under the terms of the GNU General Public License. 4. The distributor of the implementation is obliged to make this change file available on request (paragraph 3 of the GNU General Public License). % All line numbers refer to TEX.WEB 3.141 as of March 16, 1992. @x limbo l.59 - TeX--XeT \let\mc=\ninerm % medium caps for names like SAIL @y \let\mc=\ninerm % medium caps for names like SAIL \font\revrm=xbmc10 % for right-to-left text % to generate xbmc10 (i.e., reflected cmbx10) use a file % xbmc10.mf containing: %+++++++++++++++++++++++++++++++++++++++++++++++++ % if unknown cmbase: input cmbase fi % extra_endchar := extra_endchar & % "currentpicture:=currentpicture " & % "reflectedabout((.5[l,r],0),(.5[l,r],1));"; % input cmbx10 %+++++++++++++++++++++++++++++++++++++++++++++++++ \ifx\beginL\undefined % this is TeX \def\XeT{X\kern-.125em\lower.5ex\hbox{E}\kern-.1667emT} \def\TeXeT{\TeX-\hbox{\revrm \XeT}} % for TeX-XeT \def\TeXXeT{\TeX-\hbox{\revrm -\XeT}} % for TeX--XeT \else % this is TeX--XeT (or TeX-XeT) \def\TeXeT{\TeX-{\revrm\beginR\TeX\endR}} % for TeX-XeT \def\TeXXeT{\TeX-{\revrm\beginR\TeX-\endR}} % for TeX--XeT \fi @z %--------------------------------------- @x [1] m.1 l.122 - TeX--XeT @:TeXbook}{\sl The \TeX book@> @y @:TeXbook}{\sl The \TeX book@> @d TeX_XeT_version=='--1.1' {identifies the current TeX--XeT version} @d TeX_XeT_copyright=='TeX--XeT Copyright (C) 1992 by Dante e.V.' @z %--------------------------------------- @x [1] m.2 l.177 - TeX--XeT If this program is changed, the resulting system should not be called `\TeX'; the official name `\TeX' by itself is reserved for software systems that are fully compatible with each other. A special test suite called the ``\.{TRIP} test'' is available for helping to determine whether a particular implementation deserves to be known as `\TeX' [cf.~Stanford Computer Science report CS1027, November 1984]. @y This program contains code for mixed left-to-right and right-to-left typesetting. This code is inspired by but different from \TeXeT\ as presented by Donald~E. Knuth and Pierre MacKay in {\sl TUGboat\/} @^Knuth, Donald Ervin@> @^MacKay, Pierre@> {\bf 8}, 14--25, 1987. Since the original program is changed, the resulting system is not called `\TeX'; the official name `\TeX' by itself is reserved for software systems that are fully compatible with each other. In order to avoid confusion with \TeXeT\ the present implementation of mixed direction typesetting is called \TeXXeT. It differs from \TeXeT\ in several important aspects: (1)~Right-to-left text is reversed explicitely by the |ship_out| routine and is written to a normal \.{DVI} file without any |begin_reflect| or |end_reflect| commands; (2)~a |math_node| is (ab)used instead of a |whatsit_node| to record the \.{\\beginL}, \.{\\endL}, \.{\\beginR}, and \.{\\endR} text direction primitives in order not to influence the line breaking algorithm for pure left-to-right text; (3)~therefore \TeXXeT\ is designed to be used instead of and not in addition to \TeX\ and consequently the pool file name is not changed; (4)~right-to-left text interrupted by a displayed equation is automatically resumed after that equation; and (5)~the |valign| command code with a non-zero command modifier is (ab)used for the text direction primitives. (6) \TeXXeT\ includes code to prevent arithmetic overflow during |ship_out| caused by constructions such as $$\vbox{\halign{\.{#}\hfil\cr {}\\vbox to \\vsize\{\cr \hskip 25pt \\vskip 0pt plus 0.0001fil\cr \hskip 25pt ...\cr \hskip 25pt \\vfil\\penalty-200\\vfilneg\cr \hskip 25pt ...\}\cr}}$$ The relevant stretch or shrink components of consecutive glue nodes are added before they are converted to \.{DVI} units; during this process glue nodes are converted into kern nodes and glue specifications may be discarded. A special test suite called the ``\.{TRIP} test'' is available for helping to determine whether a particular implementation deserves to be known as `\TeX' [cf.~Stanford Computer Science report CS1027, November 1984]. As a consequence of points~(1), (2), and~(6) above \TeXXeT\ passes the \.{TRIP} test with the following modifications: there are four additional `multiletter control sequences' (one line in both \.{tripin.log} and \.{trip.log}), one \.{\\mathon} discarded by the line breaking algorithm is reinserted at the beginning of a line (four lines in \.{trip.log}), and the `Memory usage before' value for three pages is reduced because some glue nodes are discarded before the memory usage statistic is printed (three more lines in \.{trip.log}). @z %--------------------------------------- @x [1] m.3 l.216 - TeX--XeT @!@^dirty \PASCAL@> @y @!@^dirty \PASCAL@> @d TeX_XeT_banner=='This is TeX--XeT', ', Version 3.141' (here we should use a substring of banner) ,TeX_XeT_version {printed when \TeX\ starts} @z %--------------------------------------- @x [5] m.61 l.1554 - TeX--XeT wterm(banner); @y wterm(TeX_XeT_banner); @z %--------------------------------------- @x [5] m.61 l.1558 - TeX--XeT update_terminal; @y wterm_ln(TeX_XeT_copyright); {may be omitted under certain circumstances} update_terminal; @z %--------------------------------------- @x [10] m.147 l.3069 - TeX--XeT @d math_node=9 {|type| of a math node} @d before=0 {|subtype| for math node that introduces a formula} @d after=1 {|subtype| for math node that winds up a formula} @y In addition a |math_node| with |subtype>after| and |width=0| will be (ab)used to record one of the text direction primitives ( \.{\\beginL}, \.{\\endL}, \.{\\beginR}, \.{\\endR} ). @d math_node=9 {|type| of a math node} @d before=0 {|subtype| for math node that introduces a formula} @d after=1 {|subtype| for math node that winds up a formula} @# @d L_code=2 @d begin_L_code=L_code+before {|subtype| for \.{\\beginL} `math node'} @d end_L_code=L_code+after {|subtype| for \.{\\endL} `math node'} @d R_code=4 @d begin_R_code=R_code+before {|subtype| for \.{\\beginR} `math node'} @d end_R_code=R_code+after {|subtype| for \.{\\endR} `math node'} @# @d end_LR(#)==odd(subtype(#)) @d end_LR_type(#)==(subtype(#)+after-before) @d begin_LR_type(#)==(info(#)-after+before) @z %--------------------------------------- @x [12] m.175 l.3544 - TeX--XeT math_node: print_char("$"); @y math_node: if subtype(p)>after then print("[]") else print_char("$"); @z %--------------------------------------- @x [12] m.192 l.3809 - TeX--XeT begin print_esc("math"); @y if subtype(p)>after then begin if odd(subtype(p)) then print_esc("end") else print_esc("begin"); if subtype(p)<=end_L_code then print_char("L") else print_char("R"); end else begin print_esc("math"); @z %--------------------------------------- @x [15] m.208 l.4089 - TeX--XeT @d valign=33 {vertical table alignment ( \.{\\valign} )} @y @d valign=33 {vertical table alignment ( \.{\\valign} ) or text direction ( \.{\\beginL}, \.{\\endL}, \.{\\beginR}, \.{\\endR} )} @z %--------------------------------------- @x [16] m.212 l.4289 - TeX--XeT Seventh and eighth quantities, |lhmin| and |rhmin|, affect hyphenation. @y Seventh and eighth quantities, |lhmin| and |rhmin|, affect hyphenation. A nineth quantity, |LR_save|, holds the LR stack when a paragraph is interrupted by a displayed formula. @z %--------------------------------------- @x [16] m.212 l.4305 - TeX--XeT @!lhm_field,@!rhm_field: quarterword; @y @!lhm_field,@!rhm_field: quarterword; @!LRs_field: halfword; @z %--------------------------------------- @x [16] m.213 l.4319 - TeX--XeT @d rhmin==cur_list.rhm_field {\.{\\righthyphenmin} at start of paragraph} @y @d rhmin==cur_list.rhm_field {\.{\\righthyphenmin} at start of paragraph} @d LR_save==cur_list.LRs_field {LR stack when a paragraph is interrupted} @z %--------------------------------------- @x [16] m.215 l.4345 - TeX--XeT lhmin:=0; rhmin:=0; @y lhmin:=0; rhmin:=0; LR_save:=null; @z %--------------------------------------- @x [16] m.216 l.4360 push_nest - TeX--XeT incr(nest_ptr); head:=get_avail; tail:=head; prev_graf:=0; mode_line:=line; @y incr(nest_ptr); head:=get_avail; tail:=head; prev_graf:=0; mode_line:=line; LR_save:=null; @z %--------------------------------------- @x [18] m.265 l.5712 - TeX--XeT primitive("valign",valign,0);@/ @!@:valign_}{\.{\\valign} primitive@> @y primitive("valign",valign,0);@/ @!@:valign_}{\.{\\valign} primitive@> primitive("beginL",valign,begin_L_code);@/ @!@:beginL_}{\.{\\beginL} primitive@> primitive("endL",valign,end_L_code);@/ @!@:endL_}{\.{\\endL} primitive@> primitive("beginR",valign,begin_R_code);@/ @!@:beginR_}{\.{\\beginR} primitive@> primitive("endR",valign,end_R_code);@/ @!@:endR_}{\.{\\endR} primitive@> @z %--------------------------------------- @x [18] m.266 l.5766 - TeX--XeT valign: print_esc("valign"); @y valign: if chr_code<>0 then case chr_code of begin_L_code: print_esc("beginL"); end_L_code: print_esc("endL"); begin_R_code: print_esc("beginR"); othercases print_esc("endR") endcases else print_esc("valign"); @z %--------------------------------------- @x [29] m.536 l.10324 - TeX--XeT begin wlog(banner); @y begin wlog(TeX_XeT_banner); @z %--------------------------------------- @x [32] m.616 l.12238 - TeX--XeT this is essentially the depth of |push| commands in the \.{DVI} output. @y this is essentially the depth of |push| commands in the \.{DVI} output. For mixed direction text (\TeXXeT) the current text direction is called |cur_dir|. As the box being shipped out will never be used again and soon be recycled, we can simply reverse any R-text (i.e., right-to-left) segments of hlist nodes as well as complete hlist nodes embedded in such segments. Moreover this can be done iteratively rather than recursively. There are, however, two complications related to leaders which require some additional bookkeeping: (1)~One and the same hlist node might be used more than once (but never inside both L- and R-text); and (2)~leader boxes inside hlists must be aligned with respect to the left edge of the original hlist. A math node is changed into a kern node whenever the text direction remains the same, it is replaced by an |edge_node| if the text direction changes; an |hlist_node| inside R-text is changed to an |R_node| once its hlist has been reversed. @!@^data structure assumptions@> @z %--------------------------------------- @x [32] m.616 l.12240 - TeX--XeT @d synch_h==if cur_h<>dvi_h then @y @d R_node=unset_node {an |unset_node| does not occur during |ship_out|} @d left_to_right=0 @d right_to_left=1 @d reflected==1-cur_dir {the opposite of |cur_dir|} @# @d synch_h==if cur_h<>dvi_h then @z %--------------------------------------- @x [32] m.616 l.12251 - TeX--XeT @!cur_s:integer; {current depth of output box nesting, initially $-1$} @y @!cur_s:integer; {current depth of output box nesting, initially $-1$} @!cur_dir:small_number; {current text direction} @!LR_ptr:pointer; {stack of LR codes} @z %--------------------------------------- @x [32] m.619 l.12300 hlist_out - TeX--XeT @!g_order: glue_ord; {applicable order of infinity for glue} @y @z %--------------------------------------- @x [32] m.619 l.12308 hlist_out - TeX--XeT @!edge:scaled; {left edge of sub-box, or right edge of leader space} begin this_box:=temp_ptr; g_order:=glue_order(this_box); @y @!edge:scaled; {right edge of sub-box or leader space} @!prev_p:pointer; {one step behind |p|} begin this_box:=temp_ptr; @z %--------------------------------------- @x [32] m.619 l.12314 hlist_out - TeX--XeT save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v; left_edge:=cur_h; @y save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v; left_edge:=cur_h; prev_p:=this_box+list_offset; if cur_dir=right_to_left then if type(this_box)=hlist_node then @; @z %--------------------------------------- @x [32] m.620 l.12336 - TeX--XeT p:=link(p); @y prev_p:=p; p:=link(p); @z %--------------------------------------- @x [32] m.622 l.12355 - TeX--XeT hlist_node,vlist_node:@; @y hlist_node,vlist_node,R_node:@; @z %--------------------------------------- @x [32] m.622 l.12361 - TeX--XeT kern_node,math_node:cur_h:=cur_h+width(p); @y kern_node:cur_h:=cur_h+width(p); math_node: begin @; cur_h:=cur_h+width(p); end; @z %--------------------------------------- @x [32] m.622 l.12363 - TeX--XeT othercases do_nothing @y @@; othercases do_nothing @z %--------------------------------------- @x [32] m.622 l.12368 - TeX--XeT next_p:p:=link(p); @y next_p:prev_p:=p; p:=link(p); @z %--------------------------------------- @x [32] m.623 l.12375 - TeX--XeT temp_ptr:=p; edge:=cur_h; @y temp_ptr:=p; edge:=cur_h+width(p); if cur_dir=right_to_left then cur_h:=edge; @z %--------------------------------------- @x [32] m.623 l.12378 - TeX--XeT cur_h:=edge+width(p); cur_v:=base_line; @y cur_h:=edge; cur_v:=base_line; @z %--------------------------------------- @x [32] m.625 l.12394 - TeX--XeT 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)); end; end; @y add_glue(rule_wd); @z %--------------------------------------- @x [32] m.626 l.12418 - TeX--XeT edge:=cur_h+rule_wd; lx:=0; @y if cur_dir=right_to_left then cur_h:=cur_h-10; edge:=cur_h+rule_wd; lx:=0; @z %--------------------------------------- @x [32] m.626 l.12424 - TeX--XeT cur_h:=edge-10; goto next_p; @y if cur_dir=right_to_left then cur_h:=edge else cur_h:=edge-10; goto next_p; @z %--------------------------------------- @x [32] m.628 l.12463 - TeX--XeT synch_h; save_h:=dvi_h; temp_ptr:=leader_box; @y synch_h; save_h:=dvi_h; temp_ptr:=leader_box; if cur_dir=right_to_left then cur_h:=cur_h+leader_wd; @z %--------------------------------------- @x [32] m.629 l.12479 vlist_out - TeX--XeT @!g_order: glue_ord; {applicable order of infinity for glue} @y @z %--------------------------------------- @x [32] m.629 l.12488 vlist_out - TeX--XeT begin this_box:=temp_ptr; g_order:=glue_order(this_box); @y begin this_box:=temp_ptr; @z %--------------------------------------- @x [32] m.631 l.12511 - TeX--XeT hlist_node,vlist_node:@; @y hlist_node,vlist_node,R_node:@; @z %--------------------------------------- @x [32] m.632 l.12533 - TeX--XeT cur_h:=left_edge+shift_amount(p); {shift the box right} @y if cur_dir=right_to_left then cur_h:=left_edge-shift_amount(p) else cur_h:=left_edge+shift_amount(p); {shift the box right} @z %--------------------------------------- @x [32] m.633 l.12545 - TeX--XeT begin synch_h; synch_v; dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd); @y begin if cur_dir=right_to_left then cur_h:=cur_h-rule_wd; synch_h; synch_v; dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd); cur_h:=left_edge; @z %--------------------------------------- @x [32] m.634 l.12553 - TeX--XeT begin if g_sign=stretching then begin if stretch_order(g)=g_order then 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)); end; end; @y add_glue(rule_ht); @z %--------------------------------------- @x [32] m.638 l.12642 ship_out - TeX--XeT @; @y cur_dir:=left_to_right; {L-text at the outer level} LR_ptr:=get_avail; info(LR_ptr):=before; {this will never match} @; if info(LR_ptr)<>before then LR_confusion; free_avail(LR_ptr); LR_ptr:=null; @z %--------------------------------------- @x [33] m.649 l.12848 - TeX--XeT @p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; @y @p function safe_info(@!p:pointer): halfword; begin if p=null then safe_info:=0@+else safe_info:=info(p); end; @# function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer; @z %--------------------------------------- @x [33] m.649 l.12859 hpack - TeX--XeT begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node; @y @!LR_ptr:pointer; {stack of LR codes} @!LR_problems:integer; {counts missing begins and ends} begin LR_ptr:=null; LR_problems:=0; last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node; @z %--------------------------------------- @x [33] m.649 l.12872 hpack - TeX--XeT exit: hpack:=r; @y exit: @; hpack:=r; @z %--------------------------------------- @x [33] m.651 l.12896 - TeX--XeT kern_node,math_node: x:=x+width(p); @y kern_node,math_node: begin x:=x+width(p); @; end; @z %--------------------------------------- @x [39] m.866 l.17013 - TeX--XeT math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break; @y math_node: begin auto_breaking:=(subtype(cur_p)<>before); kern_break; @z %--------------------------------------- @x [39] m.877 l.17197 post_line_break - TeX--XeT begin @; @z %--------------------------------------- @x [39] m.880 l.17257 - TeX--XeT @; @y @; @; @; @z %--------------------------------------- @x [39] m.881 l.17280 - TeX--XeT else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0; @y else if (type(q)=math_node)or(type(q)=kern_node) then begin width(q):=0; @; end; @z %--------------------------------------- @x [47] m.1096 l.21095 - TeX--XeT else line_break(widow_penalty); @y else line_break(widow_penalty); if LR_save<>null then begin flush_list(LR_save); LR_save:=null; end; @z %--------------------------------------- @x [47] m.1130 l.21538 - TeX--XeT vmode+halign,hmode+valign:init_align; @y vmode+halign,hmode+valign: if cur_chr>0 then tail_append(new_math(0,cur_chr)) else init_align; @z %--------------------------------------- @x [54] m.1379 l.24872 additions - TeX--XeT This section should be replaced, if necessary, by any special modifications of the program that are necessary to make \TeX\ work at a particular installation. It is usually best to design your change file so that all changes to previous sections preserve the section numbering; then everybody's version will be consistent with the published program. More extensive changes, which introduce new sections, can be inserted here; then only the index itself will get a new section number. @^system dependencies@> @y The following sections, as recommended, contain the more extensive modifications of the program that are necessary to make \TeX\ work at a particular installation. @^system dependencies@> @ Insert system dependent changes here. @ Last not least we do the main work required for mixed-direction texts. A number of routines are based on a stack of one-word nodes whose |info| fields contain |after|, |end_L_code|, or |end_R_code|. The top of the stack is pointed to by |LR_ptr|. When the stack manipulation macros of this section are used below, variable |LR_ptr| might be the global variable declared for |ship_out|, or might be local to |hpack| or |post_line_break|. @d push_LR(#)==begin temp_ptr:=get_avail; info(temp_ptr):=end_LR_type(#); link(temp_ptr):=LR_ptr; LR_ptr:=temp_ptr; end @d pop_LR==begin temp_ptr:=LR_ptr; LR_ptr:=link(temp_ptr); free_avail(temp_ptr); end @= q:=link(temp_head); if LR_ptr<>null then begin temp_ptr:=LR_ptr; r:=q; repeat s:=new_math(0,begin_LR_type(temp_ptr)); link(s):=r; r:=s; temp_ptr:=link(temp_ptr); until temp_ptr=null; link(temp_head):=r; end; while q<>cur_break(cur_p) do begin if not is_char_node(q) then @; q:=link(q); end @ @= if type(q)=math_node then if end_LR(q) then begin if LR_ptr<>null then if info(LR_ptr)=subtype(q) then pop_LR; end else push_LR(q) @ We use the fact that |q| now points to the node with \.{\\rightskip} glue. @= if LR_ptr<>null then begin s:=temp_head; r:=link(s); while r<>q do begin s:=r; r:=link(s); end; r:=LR_ptr; while r<>null do begin temp_ptr:=new_math(0,info(r)); link(s):=temp_ptr; s:=temp_ptr; r:=link(r); end; link(s):=q; end @ @= if type(p)=math_node then if end_LR(p) then if safe_info(LR_ptr)=subtype(p) then pop_LR else begin incr(LR_problems); while link(q)<>p do q:=link(q); link(q):=link(p); free_node(p,small_node_size); p:=q; end else push_LR(p) @ @= if LR_ptr<>null then begin while link(q)<>null do q:=link(q); repeat temp_ptr:=q; q:=new_math(0,info(LR_ptr)); link(temp_ptr):=q; LR_problems:=LR_problems+10000; pop_LR; until LR_ptr=null; end; if LR_problems>0 then begin print_ln; print_nl("\endL or \endR problem (");@/ print_int(LR_problems div 10000); print(" missing, ");@/ print_int(LR_problems mod 10000); print(" extra");@/ LR_problems:=0; goto common_ending; end @ @= procedure LR_confusion; begin confusion("LR"); @:this can't happen LR}{\quad LR@> end; @ @d LR_dir(#)==(subtype(#) div 4) {text direction of a `math node'} @= if end_LR(p) then begin if info(LR_ptr)<>subtype(p) then LR_confusion; pop_LR; type(p):=kern_node; end else begin push_LR(p); type(p):=kern_node; if LR_dir(p)<>cur_dir then begin @; goto reswitch; end; type(p):=kern_node; end @ @d edge_node=style_node {a |style_node| does not occur during |ship_out|} @d edge_node_size=style_node_size {number of words in an edge node} @d edge_dist(#)==depth(#) {new |left_edge| position relative to |cur_h| (after |width| has been taken into account)} @= function new_edge(@!s:small_number;@!w:scaled):pointer; {create an edge node} var p:pointer; {the new node} begin p:=get_node(edge_node_size); type(p):=edge_node; subtype(p):=s; width(p):=w; {the |edge_dist| field will be set later} new_edge:=p; end; @ @= edge_node: begin cur_h:=cur_h+width(p); left_edge:=cur_h+edge_dist(p); cur_dir:=subtype(p); end; @ We detach the hlist, start a new one consisting of just one kern node, append the reversed list, and set the width of the kern node. @= begin save_h:=cur_h; temp_ptr:=p; p:=new_kern(0); link(prev_p):=p; cur_h:=0; link(p):=reverse(this_box,null); width(p):=-cur_h; cur_h:=save_h; type(this_box):=R_node; end @ We detach the remainder of the hlist, replace the math node by an edge node, and append the reversed hlist segment to it; the tail of the reversed segment is another edge node and the remainder of the original list is attached to it. @= begin save_h:=cur_h; temp_ptr:=link(p); rule_wd:=width(p); free_node(p,small_node_size); cur_dir:=reflected; p:=new_edge(cur_dir,rule_wd); link(prev_p):=p; cur_h:=cur_h-left_edge+rule_wd; link(p):=reverse(this_box,new_edge(reflected,0)); edge_dist(p):=cur_h; cur_dir:=reflected; cur_h:=save_h; end @ In constructions such as $$\vbox{\halign{\.{#}\hfil\cr {}\\vbox to \\vsize\{\cr \hskip 25pt \\vskip 0pt plus 0.0001fil\cr \hskip 25pt ...\cr \hskip 25pt \\vfil\\penalty-200\\vfilneg\cr \hskip 25pt ...\}\cr}}$$ the stretch components of \.{\\vfil} and \.{\\vfilneg} compensate; nevertheless they may cause arithmetic overflow during |ship_out| when each of them is multiplied by a large |glue_set| value. In order to avoid such unnecessary arithmetic overflow the |do_glue| function adds up the relevant stretch (or shrink) components of consecutive glue nodes and converts the glue nodes them into equivalent kern nodes; the accumulated stretch or shrink is then multiplied by |glue_set(this_box)| and returned as result. Since one and the same box may be used several times inside leaders the result is also added to the width of the first or only kern node; the subtype of the glue node(s) remains unchanged. The consecutive glue nodes may be separated by insert, mark, adjust, kern, and penalty nodes. @d add_glue(#)==#:=#+do_glue(this_box,p) @# @d add_stretch_shrink== {accumulate stretch or shrink amount} if g_sign=stretching then begin if stretch_order(g)=g_order then s:=s+stretch(g); end else begin if shrink_order(g)=g_order then s:=s-shrink(g); end @= function do_glue(@!this_box,@!p:pointer):scaled; label continue, next_p, done; var q:pointer; {list traverser} @!g_order: glue_ord; {applicable order of infinity for glue} @!g_sign: normal..shrinking; {selects type of glue} @!s:scaled; {accumulated stretch or shrink} @!r:real; {|s| multiplied by |glue_set(this_box)|} begin g_order:=glue_order(this_box); g_sign:=glue_sign(this_box); s:=0; add_stretch_shrink; if subtype(p)>=a_leaders then goto done; q:=p; continue: type(q):=kern_node; width(q):=width(g); fast_delete_glue_ref(g); next_p: q:=link(q); if (q<>null) and not is_char_node(q) then case type(q) of ins_node,mark_node,adjust_node,kern_node,penalty_node: goto next_p; glue_node: if subtype(q)0 then begin r:=float(glue_set(this_box))*s; @^real multiplication@> if abs(r)>=max_dimen.5 then begin print_err("Glue stretch or shrink too big"); @.Glue stretch... too big@> help2("I can't work with sizes bigger than about 19 feet.")@/ ("Continue and I'll use the largest value I can.");@/ error; if r>0.0 then s:=max_dimen @+ else s:=-max_dimen; end else s:=round(r); if type(p)=kern_node then width(p):=width(p)+s; end; do_glue:=s; end; @ The |reverse| function defined here is responsible to reverse the nodes of an hlist (segment). The first parameter |this_box| is the enclosing hlist node, the second parameter |t| is to become the tail of the reversed list, and the global variable |temp_ptr| is the head of the list to be reversed. We remove nodes from the original list and add them to the head of the new one. @= function reverse(@!this_box,@!t:pointer):pointer; label reswitch, next_p, done; var l:pointer; {the new list} @!p:pointer; {the current node} @!q:pointer; {the next node} @!g_sign: normal..shrinking; {selects type of glue} @!m,@!n:halfword; {number of unmatched math nodes} begin g_sign:=glue_sign(this_box); l:=t; p:=temp_ptr; m:=min_halfword; n:=min_halfword; while p<>null do @; if (t<>null)or(m<>min_halfword)or(n<>min_halfword) then LR_confusion; done:reverse:=l; end; @ @= reswitch: if is_char_node(p) then repeat f:=font(p); c:=character(p); cur_h:=cur_h+char_width(f)(char_info(f)(c)); q:=link(p); link(p):=l; l:=p; p:=q; until not is_char_node(p) else @ @ @= begin q:=link(p); rule_wd:=width(p); {the width is needed in most cases} case type(p) of hlist_node,vlist_node,rule_node,kern_node: do_nothing; @t\4@>@@; R_node,edge_node: LR_confusion; othercases goto next_p endcases;@/ cur_h:=cur_h+rule_wd; next_p: if (type(p)=kern_node)and((rule_wd=0)or(l=null)) then free_node(p,small_node_size) else begin link(p):=l; l:=p; end; p:=q; end @ Here we have to remember that |add_glue| may have converted the glue node into a kern node. If this is not the case we try to covert the glue node into a rule node. @= glue_node: begin g:=glue_ptr(p); rule_wd:=width(g); if g_sign<>normal then add_glue(rule_wd); if subtype(p)>=a_leaders then begin temp_ptr:=leader_ptr(p); if type(temp_ptr)=rule_node then begin delete_glue_ref(g); free_node(p,small_node_size); p:=temp_ptr; width(p):=rule_wd; end; end; end; @ A ligature node is replaced by a char node. @= ligature_node: begin flush_node_list(lig_ptr(p)); temp_ptr:=p; p:=get_avail; mem[p]:=mem[lig_char(temp_ptr)]; link(p):=q; free_node(temp_ptr,small_node_size); goto reswitch; end; @ Math nodes in an inner reflected segment are modified, those at the outer level are changed into kern nodes. @= math_node: if end_LR(p) then if n>min_halfword then begin decr(n); decr(subtype(p)); {change |after| into |before|} end else begin if info(LR_ptr)<>subtype(p) then LR_confusion; pop_LR; if m=min_halfword then @; decr(m); type(p):=kern_node; end else if (n>min_halfword)or(LR_dir(p)<>cur_dir) then begin incr(n); incr(subtype(p)); {change |before| into |after|} end else begin push_LR(p); incr(m); type(p):=kern_node; end; @ Finally we have found the end of the hlist segment to be reversed; the terminating math node is released and the remaining list is attached to the edge node at the end of the reversed list. @= begin if t=null then LR_confusion; free_node(p,small_node_size); link(t):=q; width(t):=rule_wd; edge_dist(t):=-cur_h-rule_wd; goto done; end @z %---------------------------------------