---
 texk/dvipdfm-x/ChangeLog                 |    6 
 texk/dvipdfm-x/dvi.c                     |  266 +++++-
 texk/dvipdfm-x/dvicodes.h                |    1 
 texk/dvipdfm-x/vf.c                      |    8 
 texk/web2c/synctexdir/ChangeLog          |    6 
 texk/web2c/synctexdir/am/synctex.am      |    3 
 texk/web2c/synctexdir/synctex-xe-rec.ch0 |  109 ++
 texk/web2c/xetexdir/ChangeLog            |    7 
 texk/web2c/xetexdir/tex.ch0              |    2 
 texk/web2c/xetexdir/xetex.ch             |    2 
 texk/web2c/xetexdir/xetex.web            | 1259 +++++++------------------------
 11 files changed, 664 insertions(+), 1005 deletions(-)

--- texlive-bin.orig/texk/dvipdfm-x/ChangeLog
+++ texlive-bin/texk/dvipdfm-x/ChangeLog
@@ -1,3 +1,9 @@
+2014-07-22  Khaled Hosny  <khaledhosny@eglug.org>
+
+	* src/dvi.c, src/dvicodes.h: Support DVI-IVD inspired text reflection
+	for XeTeX.
+	* src/vf.c: Adapt
+
 2014-05-22  Akira Kakuto  <kakuto@fuk.kindai.ac.jp>
 
 	* dvipdfmx.c: disable warnings in the case of -q option.
--- texlive-bin.orig/texk/dvipdfm-x/dvi.c
+++ texlive-bin/texk/dvipdfm-x/dvi.c
@@ -2,6 +2,8 @@
 
     Copyright (C) 2002-2014 by Jin-Hwan Cho and Shunsaku Hirata,
     the dvipdfmx project team.
+
+    Copyright (C) 2012-2014 by Khaled Hosny <khaledhosny@eglug.org>
     
     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
 
@@ -104,6 +106,25 @@
   return x ? dev_origin_x : dev_origin_y;
 }
 
+#ifdef XETEX
+#define LTYPESETTING	0 /* typesetting from left to right */
+#define RTYPESETTING	1 /* typesetting from right to left */
+#define SKIMMING	2 /* skimming through reflected segment measuring its width */
+#define REVERSE(MODE)	(LTYPESETTING + RTYPESETTING - MODE)
+
+struct dvi_lr
+{
+  int state, font;
+  unsigned long buf_index;
+};
+
+static struct dvi_lr lr_state;                            /* state at start of current skimming  */
+static int           lr_mode;                             /* current direction or skimming depth */
+static SIGNED_QUAD   lr_width;                            /* total width of reflected segment    */
+static SIGNED_QUAD   lr_width_stack[DVI_STACK_DEPTH_MAX];
+static unsigned      lr_width_stack_depth = 0;
+#endif
+
 #define PHYSICAL 1
 #define VIRTUAL  2
 #define SUBFONT  3
@@ -1074,8 +1095,19 @@
   dvi_state.v = y;
 }
 
+/* FIXME: dvi_forward() might be a better name */
 void dvi_right (SIGNED_QUAD x)
 {
+#ifdef XETEX
+  if (lr_mode >= SKIMMING) {
+    lr_width += x;
+    return;
+  }
+
+  if (lr_mode == RTYPESETTING)
+    x = -x;
+#endif
+
   switch (dvi_state.d) {
   case 0:
     dvi_state.h += x; break;
@@ -1088,16 +1120,23 @@
 
 void dvi_down (SIGNED_QUAD y)
 {
-  switch (dvi_state.d) {
-  case 0:
-    dvi_state.v += y; break;
-  case 1:
-    dvi_state.h -= y; break;
-  case 3:
-    dvi_state.h += y; break;
+#ifdef XETEX
+  if (lr_mode < SKIMMING) {
+#endif
+    switch (dvi_state.d) {
+    case 0:
+      dvi_state.v += y; break;
+    case 1:
+      dvi_state.h -= y; break;
+    case 3:
+      dvi_state.h += y; break;
+    }
+#ifdef XETEX
   }
+#endif
 }
 
+#if 0
 /* Please remove this.
  * Optimization for 8-bit encodings.
  */
@@ -1158,6 +1197,7 @@
     dvi_state.v -= width; break;
   }
 }
+#endif
 
 /* _FIXME_
  * CMap decoder wants multibyte strings as input but
@@ -1186,6 +1226,16 @@
   width = tfm_get_fw_width(font->tfm_id, ch);
   width = sqxfw(font->size, width);
 
+#ifdef XETEX
+  if (lr_mode >= SKIMMING) {
+    lr_width += width;
+    return;
+  }
+
+  if (lr_mode == RTYPESETTING)
+    dvi_right(width);
+#endif
+
   switch (font->type) {
   case  PHYSICAL:
     if (!is_xetex && ch > 65535) { /* _FIXME_ */
@@ -1234,14 +1284,20 @@
     vf_set_char(ch, font->font_id); /* push/pop invoked */
     break;
   }
-  switch (dvi_state.d) {
-  case 0:
-    dvi_state.h += width; break;
-  case 1:
-    dvi_state.v += width; break;
-  case 3:
-    dvi_state.v -= width; break;
+#ifdef XETEX
+  if (lr_mode == LTYPESETTING) {
+#endif
+    switch (dvi_state.d) {
+    case 0:
+      dvi_state.h += width; break;
+    case 1:
+      dvi_state.v += width; break;
+    case 3:
+      dvi_state.v -= width; break;
+    }
+#ifdef XETEX
   }
+#endif
 }
 
 void
@@ -1321,18 +1377,20 @@
 void
 dvi_rule (SIGNED_QUAD width, SIGNED_QUAD height)
 {
-  do_moveto(dvi_state.h, dvi_state.v);
+  if (width > 0 && height > 0) {
+    do_moveto(dvi_state.h, dvi_state.v);
 
-  switch (dvi_state.d) {
-  case 0:
-    pdf_dev_set_rule(dvi_state.h, -dvi_state.v,  width, height);
-    break;
-  case 1:
-    pdf_dev_set_rule(dvi_state.h, -dvi_state.v - width, height, width);
-    break;
-  case 3: 
-    pdf_dev_set_rule(dvi_state.h - height, -dvi_state.v , height, width);
-    break;
+    switch (dvi_state.d) {
+    case 0:
+      pdf_dev_set_rule(dvi_state.h, -dvi_state.v,  width, height);
+      break;
+    case 1:
+      pdf_dev_set_rule(dvi_state.h, -dvi_state.v - width, height, width);
+      break;
+    case 3: 
+      pdf_dev_set_rule(dvi_state.h - height, -dvi_state.v , height, width);
+      break;
+    }
   }
 }
 
@@ -1370,10 +1428,24 @@
 
   height = get_buffered_signed_quad();
   width  = get_buffered_signed_quad();
-  if (width > 0 && height > 0) {
+#ifdef XETEX
+  switch (lr_mode) {
+  case LTYPESETTING:
     dvi_rule(width, height);
+    dvi_right(width);
+    break;
+  case RTYPESETTING:
+    dvi_right(width);
+    dvi_rule(width, height);
+    break;
+  default:
+    lr_width += width;
+    break;
   }
+#else
+  dvi_rule(width, height);
   dvi_right(width);
+#endif
 }
 
 static void
@@ -1383,9 +1455,22 @@
 
   height = get_buffered_signed_quad ();
   width  = get_buffered_signed_quad ();
-  if (width > 0 && height > 0) {
+#ifdef XETEX
+  switch (lr_mode) {
+  case LTYPESETTING:
     dvi_rule(width, height);
+    break;
+  case RTYPESETTING:
+    dvi_right(width);
+    dvi_rule(width, height);
+    dvi_right(-width);
+    break;
+  default:
+    break;
   }
+#else
+  dvi_rule(width, height);
+#endif
 }
 
 static void
@@ -1759,7 +1844,10 @@
 static void
 do_xxx (UNSIGNED_QUAD size) 
 {
-  dvi_do_special(dvi_page_buffer + dvi_page_buf_index, size);
+#ifdef XETEX
+  if (lr_mode < SKIMMING)
+#endif
+    dvi_do_special(dvi_page_buffer + dvi_page_buf_index, size);
   dvi_page_buf_index += size;
 }
 
@@ -1848,6 +1936,71 @@
 
 #ifdef XETEX
 static void
+lr_width_push (void)
+{
+  if (lr_width_stack_depth >= DVI_STACK_DEPTH_MAX)
+    ERROR("Segment width stack exceeded limit.");
+
+  lr_width_stack[lr_width_stack_depth++] = lr_width;
+}
+
+static void
+lr_width_pop (void)
+{
+  if (lr_width_stack_depth <= 0)
+    ERROR("Tried to pop an empty segment width stack.");
+
+  lr_width = lr_width_stack[--lr_width_stack_depth];
+}
+
+static void
+dvi_begin_reflect (void)
+{
+  if (lr_mode >= SKIMMING) {
+    ++lr_mode;
+  } else {
+    lr_state.buf_index = dvi_page_buf_index;
+    lr_state.font = current_font;
+    lr_state.state = lr_mode;
+    lr_mode = SKIMMING;
+    lr_width = 0;
+  }
+}
+
+static void
+dvi_end_reflect (void)
+{
+  switch (lr_mode) {
+  case SKIMMING:
+    current_font = lr_state.font;
+    dvi_page_buf_index = lr_state.buf_index;
+    lr_mode = REVERSE(lr_state.state); /* must precede dvi_right */
+    dvi_right(-lr_width);
+    lr_width_push();
+    break;
+  case LTYPESETTING:
+  case RTYPESETTING:
+    lr_width_pop();
+    dvi_right(-lr_width);
+    lr_mode = REVERSE(lr_mode);
+    break;
+  default:                     /* lr_mode > SKIMMING */
+    lr_mode--;
+  }
+}
+
+static void
+do_reflect (void)
+{
+  UNSIGNED_BYTE value;
+  value = get_buffered_unsigned_byte();
+  if (value == 0)
+    dvi_begin_reflect();
+  else
+    dvi_end_reflect();
+}
+
+static void
 do_native_font_def (int scanning)
 {
   if (scanning) {
@@ -1879,6 +2032,19 @@
 }
 
 static void
+skip_glyph_array_glyphs (int yLocsPresent)
+{
+  unsigned int i, slen = 0;
+  slen = (unsigned int) get_buffered_unsigned_pair();
+  for (i = 0; i < slen; i++) {
+    get_buffered_signed_quad();
+    if (yLocsPresent)
+      get_buffered_signed_quad();
+    get_buffered_unsigned_pair();
+  }
+}
+
+static void
 do_glyph_array (int yLocsPresent)
 {
   struct loaded_font *font;
@@ -1893,6 +2059,15 @@
 
   width = get_buffered_signed_quad();
 
+  if (lr_mode >= SKIMMING) {
+    lr_width += width;
+    skip_glyph_array_glyphs(yLocsPresent);
+    return;
+  }
+
+  if (lr_mode == RTYPESETTING)
+    dvi_right(width);
+
   slen = (unsigned int) get_buffered_unsigned_pair();
   xloc = NEW(slen, spt_t);
   yloc = NEW(slen, spt_t);
@@ -1947,10 +2122,12 @@
   RELEASE(xloc);
   RELEASE(yloc);
 
-  if (!dvi_state.d) {
-    dvi_state.h += width;
-  } else {
-    dvi_state.v += width;
+  if (lr_mode == LTYPESETTING) {
+    if (!dvi_state.d) {
+      dvi_state.h += width;
+    } else {
+      dvi_state.v += width;
+    }
   }
 
   return;
@@ -2025,8 +2202,10 @@
              double hmargin,     double vmargin)
 {
   unsigned char opcode;
+#if 0
   unsigned char sbuf[SBUF_SIZE];
   unsigned int  slen = 0;
+#endif
 
   /* before this is called, we have scanned the page for papersize specials
      and the complete DVI data is now in dvi_page_buffer */
@@ -2038,6 +2217,7 @@
 
   dvi_stack_depth = 0;
   for (;;) {
+#if 0
     /* The most likely opcodes are individual setchars.
      * These are buffered for speed. */
     slen  = 0;
@@ -2050,6 +2230,13 @@
     }
     if (slen == SBUF_SIZE)
       continue;
+#endif
+    opcode = get_buffered_unsigned_byte();
+
+    if (opcode <= SET_CHAR_127) {
+      dvi_set(opcode);
+      continue;
+    }
 
     /* If we are here, we have an opcode that is something
      * other than SET_CHAR.
@@ -2096,6 +2283,10 @@
 
     case PUSH:
       dvi_push();
+#ifdef XETEX
+      if (lr_mode >= SKIMMING)
+        lr_width_push();
+#endif
       /* The following line needs to go here instead of in
        * dvi_push() since logical structure of document is
        * oblivous to virtual fonts. For example the last line on a
@@ -2108,6 +2299,10 @@
       break;
     case POP:
       dvi_pop();
+#ifdef XETEX
+      if (lr_mode >= SKIMMING)
+        lr_width_pop();
+#endif
       /* Above explanation holds for following line too */
       dvi_mark_depth();
       break;
@@ -2171,6 +2366,9 @@
 
 #ifdef XETEX
     /* XeTeX extension */
+    case XDV_REFLECT:
+      do_reflect();
+      break;
     case XDV_GLYPH_STRING:
       do_glyph_array(0);
       break;
@@ -2674,6 +2872,10 @@
     case FNT_DEF4: do_fntdef4(1); break;
 
 #ifdef XETEX
+    case XDV_REFLECT:
+      get_and_buffer_unsigned_byte(fp);
+      break;
+
     case XDV_GLYPH_STRING:
     {
       UNSIGNED_PAIR count;
--- texlive-bin.orig/texk/dvipdfm-x/dvicodes.h
+++ texlive-bin/texk/dvipdfm-x/dvicodes.h
@@ -118,6 +118,7 @@
 #define PTEXDIR 255 /* Ascii pTeX DIR command */
 
                     /* XeTeX ".xdv" codes */
+#define XDV_REFLECT         250 /* begin/end (possiply recursive) reflected segment */
 #define XDV_PIC_FILE        251 /* include graphic or PDF file */
 #define XDV_NATIVE_FONT_DEF 252 /* fontdef for native platform font */
 #define XDV_GLYPH_ARRAY     253 /* string of glyph IDs with X and Y positions */
--- texlive-bin.orig/texk/dvipdfm-x/vf.c
+++ texlive-bin/texk/dvipdfm-x/vf.c
@@ -465,9 +465,7 @@
   SIGNED_QUAD width, height;
   height = signed_quad (start, end);
   width = signed_quad (start, end);
-  if (width > 0 && height > 0) {
-    dvi_rule (sqxfw(ptsize,width), sqxfw(ptsize, height));
-  }
+  dvi_rule (sqxfw(ptsize,width), sqxfw(ptsize, height));
   return;
 }
 
@@ -477,9 +475,7 @@
   height = signed_quad (start, end);
   width = signed_quad (start, end);
   s_width = sqxfw(ptsize, width);
-  if (width > 0 && height > 0) {
-    dvi_rule (s_width, sqxfw(ptsize, height));
-  }
+  dvi_rule (s_width, sqxfw(ptsize, height));
   dvi_right (s_width);
   return;
 }
--- texlive-bin.orig/texk/web2c/synctexdir/ChangeLog
+++ texlive-bin/texk/web2c/synctexdir/ChangeLog
@@ -1,3 +1,9 @@
+2014-07-22  Khaled Hosny  <khaledhosny@eglug.org>
+
+	* am/synctex.am: Don't apply synctex-e-mem.ch0 to XeTeX, and replace
+	synctex-e-rec.ch0 with synctex-xe-rec.ch0, after TeX--XeT removal.
+	* synctex-xe-rec.ch0: New file (adapted from synctex-e-rec.ch0).
+
 2014-01-14  Sebastian Ramacher  <sramacher@debian.org>
 
 	* synctex_main.c: Bug fix (segfault).
--- texlive-bin.orig/texk/web2c/synctexdir/am/synctex.am
+++ texlive-bin/texk/web2c/synctexdir/am/synctex.am
@@ -321,10 +321,9 @@
 xetex_ch_synctex = \
 	synctexdir/synctex-xe-def.ch0 \
 	synctexdir/synctex-mem.ch0 \
-	synctexdir/synctex-e-mem.ch0 \
 	synctexdir/synctex-e-mem.ch1 \
 	synctexdir/synctex-rec.ch0 \
-	synctexdir/synctex-e-rec.ch0
+	synctexdir/synctex-xe-rec.ch0
 
 xetex_post_ch_synctex = \
 	synctexdir/synctex-xe-rec.ch3
--- /dev/null
+++ texlive-bin/texk/web2c/synctexdir/synctex-xe-rec.ch0
@@ -0,0 +1,109 @@
+Copyright (c) 2008-2011 jerome DOT laurens AT u-bourgogne DOT fr
+
+This file is part of the SyncTeX package.
+
+Latest Revision: Wed Jul  1 11:17:29 UTC 2009
+
+License:
+--------
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE
+
+Except as contained in this notice, the name of the copyright holder  
+shall not be used in advertising or otherwise to promote the sale,  
+use or other dealings in this Software without prior written  
+authorization from the copyright holder.
+
+Acknowledgments:
+----------------
+The author received useful remarks from the pdfTeX developers, especially Hahn The Thanh,
+and significant help from XeTeX developer Jonathan Kew
+
+Nota Bene:
+----------
+If you include or use a significant part of the synctex package into a software,
+I would appreciate to be listed as contributor and see "SyncTeX" highlighted.
+
+Version 1
+Thu Jun 19 09:39:21 UTC 2008
+
+Notice:
+-------
+Description forthcoming
+
+@x srcdir/etex.ch l.536
+@/@<Cases for |print_param|@>@/
+othercases print("[unknown integer parameter!]")
+@y
+@/@<synctex case for |print_param|@>@/
+@/@<Cases for |print_param|@>@/
+othercases print("[unknown integer parameter!]")
+@z
+
+@x etex.ch l.1568
+kern_node,math_node:cur_h:=cur_h+width(p);
+@y
+kern_node: begin
+  @<Record |kern_node| {\sl Sync\TeX} information@>;
+  cur_h:=cur_h+width(p);
+end;
+math_node: begin
+  @<Record |math_node| {\sl Sync\TeX} information@>;
+  cur_h:=cur_h+width(p);
+end;
+@z
+
+@x etex.ch l.4459
+  name:=19; print("( "); incr(open_parens); update_terminal;
+  end
+else name:=18
+@y
+  name:=19; print("( "); incr(open_parens); update_terminal;
+  end
+else begin
+    name:=18;
+    @<Prepare pseudo file {\sl Sync\TeX} information@>;
+end
+@z
+
+@x synctex-mem.ch0 l.236
+@ @<Put each...@>=
+primitive("synctex",assign_int,int_base+synctex_code);@/
+@!@:synctex_}{\.{\\synctex} primitive@>
+@y
+@ @<Put each...@>=
+primitive("synctex",assign_int,int_base+synctex_code);@/
+@!@:synctex_}{\.{\\synctex} primitive@>
+
+@ @<synctex case for |print_param|@>=
+synctex_code:    print_esc("synctex");
+@z
+
+@x synctex-rec.ch0 l.267
+@ @<Prepare terminal input {\sl Sync\TeX} information@>=
+synctex_tag:=0;
+@y
+@ @<Prepare terminal input {\sl Sync\TeX} information@>=
+synctex_tag:=0;
+
+@ @<Prepare pseudo file {\sl Sync\TeX} information@>=
+synctex_tag:=0;
+@z
--- texlive-bin.orig/texk/web2c/xetexdir/ChangeLog
+++ texlive-bin/texk/web2c/xetexdir/ChangeLog
@@ -1,3 +1,10 @@
+2014-07-22  Khaled Hosny  <khaledhosny@eglug.org>
+
+	* xetex.web, xetex.ch, tex.ch0: Switch right-to-left support from
+	e-TeX's TeX--XeT to Knuth's original TeX-XeT code (with some fixes and
+	slight modification to the DVI opcode) to fix TeX--XeT paired specials
+	issue.
+
 2014-05-10  Akira Kakuto  <kakuto@fuk.kindai.ac.jp>
 
 	* image/bmpimage.c: Error exit instead of crashing for unsupported
--- texlive-bin.orig/texk/web2c/xetexdir/tex.ch0
+++ texlive-bin/texk/web2c/xetexdir/tex.ch0
@@ -12,7 +12,7 @@
 @z
 
 @x [15.209] l.4165 - additional XeTeX commands
-@d shorthand_def=97 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
+@d shorthand_def=98 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
 @y
 @d shorthand_def=95 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
 @z
--- texlive-bin.orig/texk/web2c/xetexdir/xetex.ch
+++ texlive-bin/texk/web2c/xetexdir/xetex.ch
@@ -141,7 +141,7 @@
 @x [15.209] l.4165
 @d shorthand_def=95 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
 @y
-@d shorthand_def=97 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
+@d shorthand_def=98 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
 @z
 
 @x [17.222] l.4523 - frozen_special, for source specials.
--- texlive-bin.orig/texk/web2c/xetexdir/xetex.web
+++ texlive-bin/texk/web2c/xetexdir/xetex.web
@@ -315,6 +315,12 @@
 known as `\TeX' [cf.~Stanford Computer Science report CS1027,
 November 1984].
 
+This program contains code for mixed left-to-right and right-to-left
+typesetting, based on \TeXeT\ as presented by Donald~E. Knuth and Pierre
+MacKay in {\sl TUGboat\/} {\bf 8}, 14--25, 1987.
+@^Knuth, Donald Ervin@>
+@^MacKay, Pierre@>
+
 A similar test suite called the ``\.{e-TRIP} test'' is available for
 helping to determine whether a particular implementation deserves to be
 known as `\eTeX'.
@@ -334,7 +340,7 @@
 @#
 @d TEX==XETEX {change program name into |XETEX|}
 @#
-@d TeXXeT_code=0 {the \TeXXeT\ feature is optional}
+@d TeXXeT_code=0 {for backward compatibility, ignored in \XeTeX}
 @#
 @d XeTeX_dash_break_code=1 {non-zero to enable breaks after en- and em-dashes}
 @#
@@ -375,7 +381,6 @@
 into other high-level languages. For example, the `\&{with}' and `\\{new}'
 features are not used, nor are pointer types, set types, or enumerated
 scalar types; there are no `\&{var}' parameters, except in the case of files
---- \eTeX, however, does use `\&{var}' parameters for the |reverse| function;
 there are no tag fields on variant records; there are no assignments
 |real:=integer|; no procedures are declared local to other procedures.)
 
@@ -3119,8 +3124,7 @@
 |stretching| or |shrinking| or |normal| depending on whether or not the
 glue should stretch or shrink or remain rigid; and |glue_order(p)|
 specifies the order of infinity to which glue setting applies (|normal|,
-|fil|, |fill|, or |filll|). The |subtype| field is not used in \TeX.
-In \eTeX\ the |subtype| field records the box direction mode |box_lr|.
+|fil|, |fill|, or |filll|). The |subtype| field is not used.
 
 @d hlist_node=0 {|type| of hlist nodes}
 @d box_node_size=7 {number of words to allocate for a box node}
@@ -3397,28 +3401,9 @@
 formula and |after| after it. There is a |width| field, which represents
 the amount of surrounding space inserted by \.{\\mathsurround}.
 
-In addition a |math_node| with |subtype>after| and |width=0| will be
-(ab)used to record a regular |math_node| reinserted after being
-discarded at a line break or one of the text direction primitives (
-\.{\\beginL}, \.{\\endL}, \.{\\beginR}, and \.{\\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 M_code=2
-@d begin_M_code=M_code+before {|subtype| for \.{\\beginM} node}
-@d end_M_code=M_code+after {|subtype| for \.{\\endM} node}
-@d L_code=4
-@d begin_L_code=L_code+begin_M_code {|subtype| for \.{\\beginL} node}
-@d end_L_code=L_code+end_M_code {|subtype| for \.{\\endL} node}
-@d R_code=L_code+L_code
-@d begin_R_code=R_code+begin_M_code {|subtype| for \.{\\beginR} node}
-@d end_R_code=R_code+end_M_code {|subtype| for \.{\\endR} node}
-@#
-@d end_LR(#)==odd(subtype(#))
-@d end_LR_type(#)==(L_code*(subtype(#) div L_code)+end_M_code)
-@d begin_LR_type(#)==(#-after+before)
 
 @p function new_math(@!w:scaled;@!s:small_number):pointer;
 var p:pointer; {the new node}
@@ -3655,6 +3640,7 @@
 the \.{WEB} macro definitions above, so that format changes will leave
 \TeX's other algorithms intact.
 @^system dependencies@>
+@p@<Declare functions needed for special kinds of nodes@>
 
 @* \[11] Memory layout.
 Some areas of |mem| are dedicated to fixed usage, since static allocation is
@@ -3983,8 +3969,7 @@
   endcases;
 rule_node: print_char("|");
 glue_node: if glue_ptr(p)<>zero_glue then print_char(" ");
-math_node: if subtype(p)>=L_code then print("[]")
-  else print_char("$");
+math_node: print_char("$");
 ligature_node: short_display(lig_ptr(p));
 disc_node: begin short_display(pre_break(p));
   short_display(post_break(p));@/
@@ -4162,7 +4147,6 @@
   if shift_amount(p)<>0 then
     begin print(", shifted "); print_scaled(shift_amount(p));
     end;
-  if eTeX_ex then @<Display if this box is never to be reversed@>;
   end;
 node_list_display(list_ptr(p)); {recursive call}
 end
@@ -4259,13 +4243,6 @@
   end
 
 @ @<Display math node |p|@>=
-if subtype(p)>after then
-  begin if end_LR(p) then print_esc("end")
-  else print_esc("begin");
-  if subtype(p)>R_code then print_char("R")
-  else if subtype(p)>L_code then print_char("L")
-  else print_char("M");
-  end else
 begin print_esc("math");
 if subtype(p)=before then print("on")
 else print("off");
@@ -4568,7 +4545,6 @@
 @d leader_ship=31 {use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)}
 @d halign=32 {horizontal table alignment ( \.{\\halign} )}
 @d valign=33 {vertical table alignment ( \.{\\valign} )}
-  {or text direction directives ( \.{\\beginL}, etc.~)}
 @d no_align=34 {temporary escape from alignment ( \.{\\noalign} )}
 @d vrule=35 {vertical rule ( \.{\\vrule} )}
 @d hrule=36 {horizontal rule ( \.{\\hrule} )}
@@ -4610,52 +4586,53 @@
 @d XeTeX_math_given=70 {extended math code defined by \.{\\Umathchardef}}
 @d last_item=71 {most recent item ( \.{\\lastpenalty},
   \.{\\lastkern}, \.{\\lastskip} )}
-@d max_non_prefixed_command=71 {largest command code that can't be \.{\\global}}
+@d LR=72 {text direction ( \.{\\beginL}, \.{\\beginR}, \.{\\endL}, \.{\\endR} )}
+@d max_non_prefixed_command=72 {largest command code that can't be \.{\\global}}
 
 @ The next codes are special; they all relate to mode-independent
 assignment of values to \TeX's internal registers or tables.
 Codes that are |max_internal| or less represent internal quantities
 that might be expanded by `\.{\\the}'.
 
-@d toks_register=72 {token list register ( \.{\\toks} )}
-@d assign_toks=73 {special token list ( \.{\\output}, \.{\\everypar}, etc.~)}
-@d assign_int=74 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)}
-@d assign_dimen=75 {user-defined length ( \.{\\hsize}, etc.~)}
-@d assign_glue=76 {user-defined glue ( \.{\\baselineskip}, etc.~)}
-@d assign_mu_glue=77 {user-defined muglue ( \.{\\thinmuskip}, etc.~)}
-@d assign_font_dimen=78 {user-defined font dimension ( \.{\\fontdimen} )}
-@d assign_font_int=79 {user-defined font integer ( \.{\\hyphenchar},
+@d toks_register=73 {token list register ( \.{\\toks} )}
+@d assign_toks=74 {special token list ( \.{\\output}, \.{\\everypar}, etc.~)}
+@d assign_int=75 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)}
+@d assign_dimen=76 {user-defined length ( \.{\\hsize}, etc.~)}
+@d assign_glue=77 {user-defined glue ( \.{\\baselineskip}, etc.~)}
+@d assign_mu_glue=78 {user-defined muglue ( \.{\\thinmuskip}, etc.~)}
+@d assign_font_dimen=79 {user-defined font dimension ( \.{\\fontdimen} )}
+@d assign_font_int=80 {user-defined font integer ( \.{\\hyphenchar},
   \.{\\skewchar} )}
-@d set_aux=80 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )}
-@d set_prev_graf=81 {specify state info ( \.{\\prevgraf} )}
-@d set_page_dimen=82 {specify state info ( \.{\\pagegoal}, etc.~)}
-@d set_page_int=83 {specify state info ( \.{\\deadcycles},
+@d set_aux=81 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )}
+@d set_prev_graf=82 {specify state info ( \.{\\prevgraf} )}
+@d set_page_dimen=83 {specify state info ( \.{\\pagegoal}, etc.~)}
+@d set_page_int=84 {specify state info ( \.{\\deadcycles},
   \.{\\insertpenalties} )}
   {( or \.{\\interactionmode} )}
-@d set_box_dimen=84 {change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )}
-@d set_shape=85 {specify fancy paragraph shape ( \.{\\parshape} )}
+@d set_box_dimen=85 {change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )}
+@d set_shape=86 {specify fancy paragraph shape ( \.{\\parshape} )}
   {(or \.{\\interlinepenalties}, etc.~)}
-@d def_code=86 {define a character code ( \.{\\catcode}, etc.~)}
-@d XeTeX_def_code=87 {\.{\\Umathcode}, \.{\\Udelcode}}
-@d def_family=88 {declare math fonts ( \.{\\textfont}, etc.~)}
-@d set_font=89 {set current font ( font identifiers )}
-@d def_font=90 {define a font file ( \.{\\font} )}
-@d register=91 {internal register ( \.{\\count}, \.{\\dimen}, etc.~)}
-@d max_internal=91 {the largest code that can follow \.{\\the}}
-@d advance=92 {advance a register or parameter ( \.{\\advance} )}
-@d multiply=93 {multiply a register or parameter ( \.{\\multiply} )}
-@d divide=94 {divide a register or parameter ( \.{\\divide} )}
-@d prefix=95 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )}
+@d def_code=87 {define a character code ( \.{\\catcode}, etc.~)}
+@d XeTeX_def_code=88 {\.{\\Umathcode}, \.{\\Udelcode}}
+@d def_family=89 {declare math fonts ( \.{\\textfont}, etc.~)}
+@d set_font=90 {set current font ( font identifiers )}
+@d def_font=91 {define a font file ( \.{\\font} )}
+@d register=92 {internal register ( \.{\\count}, \.{\\dimen}, etc.~)}
+@d max_internal=92 {the largest code that can follow \.{\\the}}
+@d advance=93 {advance a register or parameter ( \.{\\advance} )}
+@d multiply=94 {multiply a register or parameter ( \.{\\multiply} )}
+@d divide=95 {divide a register or parameter ( \.{\\divide} )}
+@d prefix=96 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )}
   {( or \.{\\protected} )}
-@d let=96 {assign a command code ( \.{\\let}, \.{\\futurelet} )}
-@d shorthand_def=97 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
-@d read_to_cs=98 {read into a control sequence ( \.{\\read} )}
+@d let=97 {assign a command code ( \.{\\let}, \.{\\futurelet} )}
+@d shorthand_def=98 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
+@d read_to_cs=99 {read into a control sequence ( \.{\\read} )}
   {( or \.{\\readline} )}
-@d def=99 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )}
-@d set_box=100 {set a box ( \.{\\setbox} )}
-@d hyph_data=101 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )}
-@d set_interaction=102 {define level of interaction ( \.{\\batchmode}, etc.~)}
-@d max_command=102 {the largest command code seen at |big_switch|}
+@d def=100 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )}
+@d set_box=101 {set a box ( \.{\\setbox} )}
+@d hyph_data=102 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )}
+@d set_interaction=103 {define level of interaction ( \.{\\batchmode}, etc.~)}
+@d max_command=103 {the largest command code seen at |big_switch|}
 
 @ The remaining command codes are extra special, since they cannot get through
 \TeX's scanner to the main control routine. They have been given values higher
@@ -6488,8 +6465,7 @@
   @<Cases of |the| for |print_cmd_chr|@>;
 toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>;
 vadjust: print_esc("vadjust");
-valign: if chr_code=0 then print_esc("valign")@/
-  @<Cases of |valign| for |print_cmd_chr|@>;
+valign: print_esc("valign");
 vcenter: print_esc("vcenter");
 vrule: print_esc("vrule");
 
@@ -9312,7 +9288,6 @@
 var m:halfword; {|chr_code| part of the operand token}
     n, k, kk: integer; {accumulators}
 @!q,@!r:pointer; {general purpose indices}
-@!tx:pointer; {effective tail node}
 @!i:four_quarters; {character info}
 @!p:0..nest_size; {index into |nest|}
 begin m:=cur_chr;
@@ -9614,21 +9589,6 @@
 We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
 legal in similar contexts.
 
-The macro |find_effective_tail_eTeX| sets |tx| to the last non-\.{\\endM}
-node of the current list.
-
-@d find_effective_tail_eTeX==
-tx:=tail;
-if not is_char_node(tx) then
-  if (type(tx)=math_node)and(subtype(tx)=end_M_code) then
-    begin r:=head;
-    repeat q:=r; r:=link(q);
-    until r=tx;
-    tx:=q;
-    end
-@#
-@d find_effective_tail==find_effective_tail_eTeX
-
 @<Fetch an item in the current node...@>=
 if m>=input_line_no_code then
  if m>=eTeX_glue then @<Process an expression and |return|@>@;
@@ -9654,24 +9614,23 @@
   cur_val_level:=int_val;
   end
 else begin if cur_chr=glue_val then cur_val:=zero_glue@+else cur_val:=0;
-  find_effective_tail;
   if cur_chr=last_node_type_code then
     begin cur_val_level:=int_val;
-    if (tx=head)or(mode=0) then cur_val:=-1;
+    if (tail=head)or(mode=0) then cur_val:=-1;
     end
   else cur_val_level:=cur_chr;
-  if not is_char_node(tx)and(mode<>0) then
+  if not is_char_node(tail)and(mode<>0) then
     case cur_chr of
-    int_val: if type(tx)=penalty_node then cur_val:=penalty(tx);
-    dimen_val: if type(tx)=kern_node then cur_val:=width(tx);
-    glue_val: if type(tx)=glue_node then
-      begin cur_val:=glue_ptr(tx);
-      if subtype(tx)=mu_glue then cur_val_level:=mu_val;
+    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;
-    last_node_type_code: if type(tx)<=unset_node then cur_val:=type(tx)+1
+    last_node_type_code: if type(tail)<=unset_node then cur_val:=type(tail)+1
       else cur_val:=unset_node+2;
     end {there are no other cases}
-  else if (mode=vmode)and(tx=head) then
+  else if (mode=vmode)and(tail=head) then
     case cur_chr of
     int_val: cur_val:=last_penalty;
     dimen_val: cur_val:=last_kern;
@@ -12770,6 +12729,9 @@
 $-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
 more than one byte position appear in BigEndian order.
 
+\XeTeX\ extends the format of \.{DVI} with its own commands, and thus produced
+``extended device independent'' (\.{XDV}) files.
+
 A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
 or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
 |pre| command, with its parameters that define the dimensions used in the
@@ -12823,7 +12785,7 @@
 its \.{DVI} output is in sp units, i.e., scaled points, in agreement with
 all the |scaled| dimensions in \TeX's data structures.
 
-@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+@ Here is a list of all the commands that may appear in a \.{XDV} file. Each
 command is specified by its symbolic name (e.g., |bop|), its opcode byte
 (e.g., 139), and its parameters (if any). The parameters are followed
 by a bracketed number telling how many bytes they occupy; for example,
@@ -13058,6 +13020,12 @@
 
 \yskip\hang|post_post| 249. Ending of the postamble, see below.
 
+\yskip\noindent Commands 250--255 are undefined in normal \.{DVI} files, but
+the following commands are used in \.{XDV} files.
+
+\yskip\hang|reflect| 250 |b[1]|. Begin (|b=0|) or end (|b=1|) a (possibly
+recursive) reflected segment.
+
 \yskip\hang|pic_file| 251 |flags[1]| |t[4][6]| |p[2]| |len[2]| |path[l]|.
     |flags=0| for raster image, and |1| for \.{PDF}.
     |t| is transform matrix.
@@ -13068,17 +13036,17 @@
 \yskip\hang\vbox{\halign{#&#\hfil\cr
 |define_native_font| 252 & |k[4]| |s[4]| |flags[2]| |lenps[1]| |lenfam[1]| |lensty[1]| |ps[lenps]| |fam[lenfam]| |sty[lensty]|\cr
 & |if (flags and COLORED) then| |rgba[4]|\cr
+& |if (flags and EXTEND) then| |extend[4]|\cr
+& |if (flags and SLANT) then| |slant[4]|\cr
+& |if (flags and EMBOLDEN) then| |embolden[4]|\cr
 & |if (flags and VARIATIONS) then| |numvars[2]| |axes[4nv]| |values[4nv]|\cr
-& |if (flags and MATRIX) then| |ta[4]| |tb[4]| |tc[4]| |td[4]| |tx[4]| |ty[4]|\cr
 }}
 
 \yskip\hang|set_glyph_array| 253 |w[4]| |k[2]| |xy[8k]| |g[2k]|.
 
 \yskip\hang|set_glyph_string| 254 |w[4]| |k[2]| |x[4k]| |g[2k]|.
 
-\yskip\noindent Commands 250--255 are undefined in normal \.{DVI} files,
-but 251--254 are permitted in the extended `\.{XDV}' files
-produced by \XeTeX. p\TeX\ uses 255.
+\yskip\noindent Command 255 is undefined in normal \.{XDV} files.
 
 @ @d set_char_0=0 {typeset character 0 and move right}
 @d set1=128 {typeset a character and move right}
@@ -13108,6 +13076,7 @@
 @d post=248 {postamble beginning}
 @d post_post=249 {postamble ending}
 
+@d reflect=250 {begin or end a reflected segment}
 @d pic_file=251 {embed picture or PDF}
 @d define_native_font=252 {define native font}
 @d set_glyph_array=253 {sequence of glyphs with individual x-y coordinates}
@@ -13748,33 +13717,6 @@
 The depth of nesting of |hlist_out| and |vlist_out| is called |cur_s|;
 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 that 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; the subtype of an an |hlist_node| inside R-text is changed to
-|reversed| once its hlist has been reversed.
-@!@^data structure assumptions@>
-
-@d reversed=1 {subtype for an |hlist_node| whose hlist has been reversed}
-@d dlist=2 {subtype for an |hlist_node| from display math mode}
-@d box_lr(#) == (qo(subtype(#))) {direction mode of a box}
-@d set_box_lr(#) ==  subtype(#):=set_box_lr_end
-@d set_box_lr_end(#) == qi(#)
-@#
-@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
     begin movement(cur_h-dvi_h,right1); dvi_h:=cur_h;
     end
@@ -13847,7 +13789,7 @@
 @!leader_wd:scaled; {width of leader box being replicated}
 @!lx:scaled; {extra space between leader boxes}
 @!outer_doing_leaders:boolean; {were we doing leaders?}
-@!edge:scaled; {right edge of sub-box or leader space}
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
 @!prev_p:pointer; {one step behind |p|}
 @!len: integer; {length of scratch string for native word output}
 @!q,@!r: pointer;
@@ -13863,13 +13805,9 @@
 incr(cur_s);
 if cur_s>0 then dvi_out(push);
 if cur_s>max_push then max_push:=cur_s;
-save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v;
-prev_p:=this_box+list_offset;
-@<Initialize |hlist_out| for mixed direction typesetting@>;
-left_edge:=cur_h;
+save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v; left_edge:=cur_h;
 while p<>null do @<Output node |p| for |hlist_out| and move to the next node,
   maintaining the condition |cur_v=base_line|@>;
-@<Finish |hlist_out| for mixed direction typesetting@>;
 prune_movements(save_loc);
 if cur_s>0 then dvi_pop(save_loc);
 decr(cur_s);
@@ -13982,7 +13920,6 @@
   if c>=qi(128) then dvi_out(set1);
   dvi_out(qo(c));@/
   cur_h:=cur_h+char_width(f)(char_info(f)(c));
-  prev_p:=link(prev_p); {N.B.: not |prev_p:=p|, |p| might be |lig_trick|}
   p:=link(p);
   until not is_char_node(p);
   dvi_h:=cur_h;
@@ -14011,27 +13948,24 @@
 margin_kern_node: begin
   cur_h:=cur_h+width(p);
 end;
-kern_node:cur_h:=cur_h+width(p);
-math_node: @<Handle a math node in |hlist_out|@>;
+kern_node,math_node:cur_h:=cur_h+width(p);
 ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>;
-@/@<Cases of |hlist_out| that arise in mixed direction text only@>@;
 othercases do_nothing
 endcases;@/
 goto next_p;
 fin_rule: @<Output a rule in an hlist@>;
 move_past: cur_h:=cur_h+rule_wd;
-next_p:prev_p:=p; p:=link(p);
+next_p:p:=link(p);
 end
 
 @ @<Output a box in an hlist@>=
 if list_ptr(p)=null then cur_h:=cur_h+width(p)
 else  begin save_h:=dvi_h; save_v:=dvi_v;
   cur_v:=base_line+shift_amount(p); {shift the box down}
-  temp_ptr:=p; edge:=cur_h+width(p);
-  if cur_dir=right_to_left then cur_h:=edge;
+  temp_ptr:=p; edge:=cur_h;
   if type(p)=vlist_node then vlist_out@+else hlist_out;
   dvi_h:=save_h; dvi_v:=save_v;
-  cur_h:=edge; cur_v:=base_line;
+  cur_h:=edge+width(p); cur_v:=base_line;
   end
 
 @ @<Output a rule in an hlist@>=
@@ -14050,8 +13984,9 @@
            glue_temp:=billion
   else if glue_temp<-billion then
            glue_temp:=-billion
-@#
-@d round_glue==g:=glue_ptr(p); rule_wd:=width(g)-cur_g;
+
+@<Move right or output leaders@>=
+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
@@ -14067,11 +14002,7 @@
       cur_g:=round(glue_temp);
       end;
   end;
-rule_wd:=rule_wd+cur_g
-
-@<Move right or output leaders@>=
-begin round_glue;
-if eTeX_ex then @<Handle a glue node for mixed direction typesetting@>;
+rule_wd:=rule_wd+cur_g;
 if subtype(p)>=a_leaders then
   @<Output leaders in an hlist, |goto fin_rule| if a rule
     or to |next_p| if done@>;
@@ -14087,16 +14018,13 @@
 leader_wd:=width(leader_box);
 if (leader_wd>0)and(rule_wd>0) then
   begin rule_wd:=rule_wd+10; {compensate for floating-point rounding}
-  if cur_dir=right_to_left then cur_h:=cur_h-10;
   edge:=cur_h+rule_wd; lx:=0;
   @<Let |cur_h| be the position of the first box, and set |leader_wd+lx|
     to the spacing between corresponding parts of boxes@>;
   while cur_h+leader_wd<=edge do
     @<Output a leader box at |cur_h|,
       then advance |cur_h| by |leader_wd+lx|@>;
-  if cur_dir=right_to_left then cur_h:=edge
-  else cur_h:=edge-10;
-  goto next_p;
+  cur_h:=edge-10; goto next_p;
   end;
 end
 
@@ -14136,7 +14064,6 @@
 @<Output a leader box at |cur_h|, ...@>=
 begin cur_v:=base_line+shift_amount(leader_box); synch_v; save_v:=dvi_v;@/
 synch_h; save_h:=dvi_h; temp_ptr:=leader_box;
-if cur_dir=right_to_left then cur_h:=cur_h+leader_wd;
 outer_doing_leaders:=doing_leaders; doing_leaders:=true;
 if type(leader_box)=vlist_node then vlist_out@+else hlist_out;
 doing_leaders:=outer_doing_leaders;
@@ -14213,8 +14140,7 @@
 if list_ptr(p)=null then cur_v:=cur_v+height(p)+depth(p)
 else  begin if upwards then cur_v:=cur_v-depth(p) else cur_v:=cur_v+height(p); synch_v;
   save_h:=dvi_h; save_v:=dvi_v;
-  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}
+  cur_h:=left_edge+shift_amount(p); {shift the box right}
   temp_ptr:=p;
   if type(p)=vlist_node then vlist_out@+else hlist_out;
   dvi_h:=save_h; dvi_v:=save_v;
@@ -14226,10 +14152,8 @@
 rule_ht:=rule_ht+rule_dp; {this is the rule thickness}
 if upwards then cur_v:=cur_v-rule_ht else cur_v:=cur_v+rule_ht;
 if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules}
-  begin if cur_dir=right_to_left then cur_h:=cur_h-rule_wd;
-  synch_h; synch_v;
+  begin synch_h; synch_v;
   dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd);
-  cur_h:=left_edge;
   end;
 goto next_p
 
@@ -14294,10 +14218,7 @@
 leader box, not its baseline.
 
 @<Output a leader box at |cur_v|, ...@>=
-begin if cur_dir=right_to_left then
-  cur_h:=left_edge-shift_amount(leader_box)
-  else cur_h:=left_edge+shift_amount(leader_box);
-synch_h; save_h:=dvi_h;@/
+begin cur_h:=left_edge+shift_amount(leader_box); synch_h; save_h:=dvi_h;@/
 cur_v:=cur_v+height(leader_box); synch_v; save_v:=dvi_v;
 temp_ptr:=leader_box;
 outer_doing_leaders:=doing_leaders; doing_leaders:=true;
@@ -14318,6 +14239,7 @@
 @!old_setting:0..max_selector; {saved |selector| setting}
 begin
 if job_name=0 then open_log_file;
+LR_ptr:=get_avail; info(LR_ptr):=0; {|begin_L_code| at outer level}
 if tracing_output>0 then
   begin print_nl(""); print_ln;
   print("Completed box being shipped out");
@@ -14337,7 +14259,6 @@
   begin_diagnostic; show_box(p); end_diagnostic(true);
   end;
 @<Ship box |p| out@>;
-if eTeX_ex then @<Check for LR anomalies at the end of |ship_out|@>;
 if tracing_output<=0 then print_char("]");
 dead_cycles:=0;
 update_terminal; {progress report}
@@ -14352,7 +14273,7 @@
   print_int(dyn_used); print_char(";");
   end;
 tats@/
-flush_node_list(p);
+flush_node_list(p); @<Flush the LR stack@>;
 @!stat if tracing_stats>1 then
   begin print(" after: ");
   print_int(var_used); print_char("&");
@@ -14640,11 +14561,13 @@
 @!hd:eight_bits; {height and depth indices for a character}
 @!pp,@!ppp: pointer;
 @!total_chars, @!k: integer;
-begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
+@!LR_ptr,@!LR_tmp:pointer; {for LR stack maintenance}
+@!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;
 subtype(r):=min_quarterword; shift_amount(r):=0;
 q:=r+list_offset; link(q):=p;@/
 h:=0; @<Clear dimensions to zero@>;
-if TeXXeT_en then @<Initialize the LR stack@>;
 while p<>null do @<Examine node |p| in the hlist, taking account of its effect
   on the dimensions of the new box, or moving it to the adjustment list;
   then advance |p| to the next node@>;
@@ -14655,7 +14578,7 @@
   then |return| or |goto common_ending|@>;
 common_ending: @<Finish issuing a diagnostic message
       for an overfull or underfull hbox@>;
-exit: if TeXXeT_en then @<Check for LR anomalies at the end of |hpack|@>;
+exit: @<Check for LR anomalies at the end of |hpack|@>;
 hpack:=r;
 end;
 
@@ -14680,11 +14603,8 @@
     @<Transfer node |p| to the adjustment list@>;
   whatsit_node:@<Incorporate a whatsit node into an hbox@>;
   glue_node:@<Incorporate glue into the horizontal totals@>;
-  kern_node: x:=x+width(p);
+  kern_node,math_node: x:=x+width(p);
   margin_kern_node: x:=x+width(p);
-  math_node: begin x:=x+width(p);
-    if TeXXeT_en then @<Adjust \(t)the LR stack for the |hpack| routine@>;
-    end;
   ligature_node: @<Make node |p| look like a |char_node|
     and |goto reswitch|@>;
   othercases do_nothing
@@ -18589,7 +18509,6 @@
 @<Set the unset box |q| and the unset boxes in it@>=
 begin if mode=-vmode then
   begin type(q):=hlist_node; width(q):=width(p);
-  if nest[nest_ptr-1].mode_field=mmode then set_box_lr(q)(dlist); {for |ship_out|}
   end
 else  begin type(q):=vlist_node; height(q):=height(p);
   end;
@@ -18608,7 +18527,6 @@
 
 @<Set the glue in node |r|...@>=
 n:=span_count(r); t:=width(s); w:=t; u:=hold_head;
-set_box_lr(r)(0); {for |ship_out|}
 while n>min_quarterword do
   begin decr(n);
   @<Append tabskip glue and an empty box to list |u|,
@@ -19973,8 +19891,7 @@
   act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
   end;
 disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
-math_node: begin if subtype(cur_p)<L_code then auto_breaking:=odd(subtype(cur_p));
-  kern_break;
+math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
   end;
 penalty_node: try_break(penalty(cur_p),unhyphenated);
 mark_node,ins_node,adjust_node: do_nothing;
@@ -20180,7 +20097,7 @@
 @!t:quarterword; {used for replacement counts in discretionary nodes}
 @!pen:integer; {use when calculating penalties between lines}
 @!cur_line: halfword; {the current line number being justified}
-@!LR_ptr:pointer; {stack of LR codes}
+@!LR_ptr,@!LR_tmp:pointer; {for LR stack maintenance}
 begin LR_ptr:=LR_save;
 @<Reverse the links of the relevant passive nodes, setting |cur_p| to the
   first breakpoint@>;
@@ -20227,8 +20144,6 @@
   if non_discardable(q) then goto done1;
   if type(q)=kern_node then if subtype(q)<>explicit then goto done1;
   r:=q; {now |type(q)=glue_node|, |kern_node|, |math_node| or |penalty_node|}
-  if type(q)=math_node then if TeXXeT_en then
-    @<Adjust \(t)the LR stack for the |post_line_break| routine@>;
   end;
 done1: if r<>temp_head then
   begin link(r):=null; flush_node_list(link(temp_head));
@@ -20245,12 +20160,11 @@
 the |left_skip| glue at the left of the line, unless it is zero.
 
 @<Justify the line ending at breakpoint |cur_p|, and append it...@>=
-if TeXXeT_en then
-  @<Insert LR nodes at the beginning of the current line and adjust
-    the LR stack based on LR nodes in this line@>;
+@<Insert LR nodes at the beginning of the current line@>;
+@<Adjust the LR stack based on LR nodes in this line@>;
 @<Modify the end of the line to reflect the nature of the break and to include
   \.{\\rightskip}; also set the proper value of |disc_break|@>;
-if TeXXeT_en then @<Insert LR nodes at the end of the current line@>;
+@<Insert LR nodes at the end of the current line@>;
 @<Put the \(l)\.{\\leftskip} glue at the left and detach this line@>;
 @<Call the packaging subroutine, setting |just_box| to the justified box@>;
 @<Append the new box to the current vertical list, followed by the list of
@@ -20274,11 +20188,7 @@
   else  begin if type(q)=disc_node then
       @<Change discretionary to compulsory and set
         |disc_break:=true|@>
-    else if type(q)=kern_node then width(q):=0
-    else if type(q)=math_node then
-      begin width(q):=0;
-      if TeXXeT_en then @<Adjust \(t)the LR stack for the |p...@>;
-      end;
+    else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
     end
 else  begin q:=temp_head;
   while link(q)<>null do q:=link(q);
@@ -20464,8 +20374,7 @@
 belongs to a ``potentially hyphenatable part'' of the current paragraph.
 This is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node,
 $p_1\ldots p_{m-1}$ are either character or ligature or whatsit or
-implicit kern or text direction nodes, and $p_m$ is a glue or penalty or
-insertion or adjust
+implicit kern nodes, and $p_m$ is a glue or penalty or insertion or adjust
 or mark or whatsit or explicit kern node.  (Therefore hyphenation is
 disabled by boxes, math formulas, and discretionary nodes already inserted
 by the user.) The ligature nodes among $p_1\ldots p_{m-1}$ are effectively
@@ -20651,7 +20560,6 @@
     else begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
       end
   else if (type(s)=kern_node)and(subtype(s)=normal) then goto continue
-  else if (type(s)=math_node)and(subtype(s)>=L_code) then goto continue
   else if type(s)=whatsit_node then
     begin
       if subtype(s) = native_word_node then begin
@@ -20735,7 +20643,6 @@
     kern_node: if subtype(s)<>normal then goto done4;
     whatsit_node,glue_node,penalty_node,ins_node,adjust_node,mark_node:
       goto done4;
-    math_node: if subtype(s)>=L_code then goto done4@+else goto done1;
     othercases goto done1
     endcases;
   s:=link(s);
@@ -24439,9 +24346,6 @@
 procedure begin_box(@!box_context:integer);
 label exit, done;
 var @!p,@!q:pointer; {run through the current list}
-@!r:pointer; {running behind |p|}
-@!fm:boolean; {a final \.{\\beginM} \.{\\endM} node pair?}
-@!tx:pointer; {effective tail node}
 @!m:quarterword; {the length of a replacement list}
 @!k:halfword; {0 or |vmode| or |hmode|}
 @!n:halfword; {a box number}
@@ -24462,28 +24366,6 @@
 @ Note that the condition |not is_char_node(tail)| implies that |head<>tail|,
 since |head| is a one-word node.
 
-@d fetch_effective_tail_eTeX(#)== {extract |tx|,
-  drop \.{\\beginM} \.{\\endM} pair}
-q:=head; p:=null;
-repeat r:=p; p:=q; fm:=false;
-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=tx then #;
-    end
-  else if (type(q)=math_node)and(subtype(q)=begin_M_code) then fm:=true;
-q:=link(p);
-until q=tx; {found |r|$\to$|p|$\to$|q=tx|}
-q:=link(tx); link(p):=q; link(tx):=null;
-if q=null then if fm then confusion("tail1")
-@:this can't happen tail1}{\quad tail1@>
-  else tail:=p
-else if fm then {|r|$\to$|p=begin_M|$\to$|q=end_M|}
-  begin tail:=r; link(r):=null; flush_node_list(p);@+end
-@#
-@d check_effective_tail(#)==find_effective_tail_eTeX
-@d fetch_effective_tail==fetch_effective_tail_eTeX
-
 @<If the current list ends with a box node, delete it...@>=
 begin cur_box:=null;
 if abs(mode)=mmode then
@@ -24494,17 +24376,24 @@
   help2("Sorry...I usually can't take things from the current page.")@/
     ("This \lastbox will therefore be void."); error;
   end
-else  begin check_effective_tail(goto done);
-  if not is_char_node(tx) then
-    if (type(tx)=hlist_node)or(type(tx)=vlist_node) then
+else  begin if not is_char_node(tail) then
+    if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
       @<Remove the last box, unless it's part of a discretionary@>;
-  done:end;
+  end;
 end
 
 @ @<Remove the last box...@>=
-begin fetch_effective_tail(goto done);
-cur_box:=tx; shift_amount(cur_box):=0;
-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
 
 @ Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
 
@@ -24623,7 +24512,7 @@
 vmode+letter,vmode+other_char,vmode+char_num,vmode+char_given,
    vmode+math_shift,vmode+un_hbox,vmode+vrule,
    vmode+accent,vmode+discretionary,vmode+hskip,vmode+valign,
-   vmode+ex_space,vmode+no_boundary:@t@>@;@/
+   vmode+ex_space,vmode+no_boundary,vmode+LR:@t@>@;@/
   begin back_input; new_graf(true);
   end;
 
@@ -24697,7 +24586,7 @@
 procedure end_graf;
 begin if mode=hmode then
   begin if head=tail then pop_nest {null paragraphs are ignored}
-  else line_break(false);
+  else line_break(widow_penalty);
   if LR_save<>null then
     begin flush_list(LR_save); LR_save:=null;
     end;
@@ -24800,17 +24689,20 @@
 procedure delete_last;
 label exit;
 var @!p,@!q:pointer; {run through the current list}
-@!r:pointer; {running behind |p|}
-@!fm:boolean; {a final \.{\\beginM} \.{\\endM} node pair?}
-@!tx:pointer; {effective tail node}
 @!m:quarterword; {the length of a replacement list}
 begin if (mode=vmode)and(tail=head) then
   @<Apologize for inability to do the operation now,
     unless \.{\\unskip} follows non-glue@>
-else  begin check_effective_tail(return);
-  if not is_char_node(tx) then if type(tx)=cur_chr then
-    begin fetch_effective_tail(return);
-    flush_node_list(tx);
+else  begin if not is_char_node(tail) then if type(tail)=cur_chr then
+    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;
@@ -25185,8 +25077,7 @@
 Let's take a look at what happens when they are used correctly.
 
 @<Cases of |main_control| that build...@>=
-vmode+halign:init_align;
-hmode+valign:@<Cases of |main_control| for |hmode+valign|@>@; init_align;
+vmode+halign,hmode+valign:init_align;
 mmode+halign: if privileged then
   if cur_group=math_shift_group then init_align
   else off_save;
@@ -25265,12 +25156,9 @@
 hmode+math_shift:init_math;
 
 @ @<Declare act...@>=
-@t\4@>@<Declare subprocedures for |init_math|@>@;
 procedure init_math;
 label reswitch,found,not_found,done;
 var w:scaled; {new or partial |pre_display_size|}
-@!j:pointer; {prototype box for display}
-@!x:integer; {new |pre_display_direction|}
 @!l:scaled; {new |display_width|}
 @!s:scaled; {new |display_indent|}
 @!p:pointer; {current node when calculating |pre_display_size|}
@@ -25326,10 +25214,10 @@
 |display_indent| and |pre_display_size|.
 
 @<Go into display math mode@>=
-begin j:=null; w:=-max_dimen;
-if head=tail then {`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'}
-  @<Prepare for display after an empty paragraph@>
-else  begin line_break(true);@/
+begin if head=tail then {`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'}
+  begin pop_nest; w:=-max_dimen;
+  end
+else  begin line_break(display_widow_penalty);@/
   @<Calculate the natural width, |w|, by which the characters of the
     final line extend to the right of the reference point,
     plus two ems; or set |w:=max_dimen| if the non-blank information
@@ -25340,8 +25228,6 @@
 push_math(math_shift_group); mode:=mmode;
 eq_word_define(int_base+cur_fam_code,-1);@/
 eq_word_define(dimen_base+pre_display_size_code,w);
-LR_box:=j;
-if eTeX_ex then eq_word_define(int_base+pre_display_direction_code,x);
 eq_word_define(dimen_base+display_width_code,l);
 eq_word_define(dimen_base+display_indent_code,s);
 if every_display<>null then begin_token_list(every_display,every_display_text);
@@ -25349,7 +25235,8 @@
 end
 
 @ @<Calculate the natural width, |w|, by which...@>=
-@<Prepare for display after a non-empty paragraph@>;
+v:=shift_amount(just_box)+2*quad(cur_font); w:=-max_dimen;
+p:=list_ptr(just_box);
 while p<>null do
   begin @<Let |d| be the natural width of node |p|;
     if the node is ``visible,'' |goto found|;
@@ -25364,7 +25251,6 @@
   not_found: p:=link(p);
   end;
 done:
-@<Finish the natural width computation@>
 
 @ @<Let |d| be the natural width of node |p|...@>=
 reswitch: if is_char_node(p) then
@@ -25375,9 +25261,8 @@
 hlist_node,vlist_node,rule_node: begin d:=width(p); goto found;
   end;
 ligature_node:@<Make node |p| look like a |char_node|...@>;
-kern_node: d:=width(p);
+kern_node,math_node: d:=width(p);
 margin_kern_node: d:=width(p);
-@t\4@>@<Cases of `Let |d| be the natural width' that need special treatment@>@;
 glue_node:@<Let |d| be the natural width of this glue; if stretching
   or shrinking, set |v:=max_dimen|; |goto found| in the case of leaders@>;
 whatsit_node: @<Let |d| be the width of the whatsit |p|, and |goto found| if ``visible''@>;
@@ -26091,7 +25976,6 @@
   else off_save;
 
 @ @<Declare act...@>=
-@t\4@>@<Declare subprocedures for |after_math|@>@;
 procedure after_math;
 var l:boolean; {`\.{\\leqno}' instead of `\.{\\eqno}'}
 @!danger:boolean; {not enough symbol fonts are present}
@@ -26100,7 +25984,6 @@
 @!a:pointer; {box containing equation number}
 @<Local variables for finishing a displayed formula@>@;
 begin danger:=false;
-@<Retrieve the prototype box@>;
 @<Check that the necessary fonts for math symbols are present;
   if not, flush the current math lists and set |danger:=true|@>;
 m:=mode; l:=false; p:=fin_mlist(null); {this pops the nest}
@@ -26108,11 +25991,9 @@
   begin @<Check that another \.\$ follows@>;
   cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
   mlist_to_hlist; a:=hpack(link(temp_head),natural);
-  set_box_lr(a)(dlist);
   unsave; decr(save_ptr); {now |cur_group=math_shift_group|}
   if saved(0)=1 then l:=true;
   danger:=false;
-  @<Retrieve the prototype box@>;
   @<Check that the necessary fonts for math symbols are present;
     if not, flush the current math lists and set |danger:=true|@>;
   m:=mode; p:=fin_mlist(null);
@@ -26159,9 +26040,11 @@
 
 @<Finish math in text@>=
 begin tail_append(new_math(math_surround,before));
+@<Append a |begin_L| to the tail of the current list@>;
 cur_mlist:=p; cur_style:=text_style; mlist_penalties:=(mode>0); mlist_to_hlist;
 link(tail):=link(temp_head);
 while link(tail)<>null do tail:=link(tail);
+@<Append an |end_L| to the tail of the current list@>;
 tail_append(new_math(math_surround,after));
 space_factor:=1000; unsave;
 end
@@ -26209,7 +26092,6 @@
 t:=adjust_tail; adjust_tail:=null;@/
 pre_t:=pre_adjust_tail; pre_adjust_tail:=null;@/
 w:=width(b); z:=display_width; s:=display_indent;
-if pre_display_direction<0 then s:=-s-z;
 if (a=null)or danger then
   begin e:=0; q:=0;
   end
@@ -26224,7 +26106,6 @@
 @<Append the glue or equation number preceding the display@>;
 @<Append the display and perhaps also the equation number@>;
 @<Append the glue or equation number following the display@>;
-@<Flush the prototype box@>;
 resume_after_display
 
 @ @<Declare act...@>=
@@ -26267,7 +26148,6 @@
 user put glue there to control the spacing precisely.
 
 @<Determine the displacement, |d|, of the left edge of the equation...@>=
-set_box_lr(b)(dlist);
 d:=half(z-w);
 if (e>0)and(d<2*e) then {too close}
   begin d:=half(z-w-e);
@@ -26289,7 +26169,7 @@
   g2:=below_display_short_skip_code;
   end;
 if l and(e=0) then {it follows that |type(a)=hlist_node|}
-  begin app_display(j,a,0);
+  begin shift_amount(a):=s; append_to_vlist(a);
   tail_append(new_penalty(inf_penalty));
   end
 else tail_append(new_param_glue(g1))
@@ -26304,12 +26184,13 @@
     end;
   b:=hpack(b,natural);
   end;
-app_display(j,b,d)
+shift_amount(b):=s+d; append_to_vlist(b)
 
 @ @<Append the glue or equation number following the display@>=
 if (a<>null)and(e=0)and not l then
   begin tail_append(new_penalty(inf_penalty));
-  app_display(j,a,z-width(a));
+  shift_amount(a):=s+z-width(a);
+  append_to_vlist(a);
   g2:=0;
   end;
 if t<>adjust_head then {migrating material comes after equation number}
@@ -26330,7 +26211,6 @@
 begin do_assignments;
 if cur_cmd<>math_shift then @<Pontificate about improper alignment in display@>
 else @<Check that another \.\$ follows@>;
-flush_node_list(LR_box);
 pop_nest;
 tail_append(new_penalty(pre_display_penalty));
 tail_append(new_param_glue(above_display_skip_code));
@@ -28538,7 +28418,9 @@
 @d write_node=1 {|subtype| in whatsits that represent things to \.{\\write}}
 @d close_node=2 {|subtype| in whatsits that represent streams to \.{\\closeout}}
 @d special_node=3 {|subtype| in whatsits that represent \.{\\special} things}
-@d language_node=4 {|subtype| in whatsits that change the current language}
+@d LR_node=4 {|subtype| in whatsits that represent \.{\\beginL}, etc.}
+@d LR_type(#)==mem[#+1].int {the sub-subtype}
+@d language_node=5 {|subtype| in whatsits that change the current language}
 @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|}
@@ -28568,6 +28450,13 @@
 @d immediate_code=4 {command modifier for \.{\\immediate}}
 @d set_language_code=5 {command modifier for \.{\\setlanguage}}
 
+@d begin_L_code=0 {command modifier for \.{\\beginL}}
+@d begin_R_code=1 {command modifier for \.{\\beginR}}
+@d end_L_code=2 {command modifier for \.{\\endL}}
+@d end_R_code=3 {command modifier for \.{\\endR}}
+@d begin_LR(#)==(LR_type(#)<end_L_code)
+@d begin_LR_type(#)==(LR_type(#)-end_L_code)
+
 @d pdftex_first_extension_code = 6
 @d pdf_save_pos_node           == pdftex_first_extension_code + 0
 
@@ -28580,6 +28469,14 @@
 @d XeTeX_linebreak_locale_extension_code=46
 
 @<Put each...@>=
+primitive("beginL",LR,begin_L_code);@/
+@!@:begin_L_}{\.{\\beginL} primitive@>
+primitive("beginR",LR,begin_R_code);@/
+@!@:begin_R_}{\.{\\beginR} primitive@>
+primitive("endL",LR,end_L_code);@/
+@!@:end_L_}{\.{\\endL} primitive@>
+primitive("endR",LR,end_R_code);@/
+@!@:end_R_}{\.{\\endR} primitive@>
 primitive("openout",extension,open_node);@/
 @!@:open_out_}{\.{\\openout} primitive@>
 primitive("write",extension,write_node); write_loc:=cur_val;@/
@@ -28617,6 +28514,12 @@
 @!write_loc:pointer; {|eqtb| address of \.{\\write}}
 
 @ @<Cases of |print_cmd_chr|...@>=
+LR: case chr_code of
+begin_L_code: print_esc("beginL");
+begin_R_code: print_esc("beginR");
+end_L_code: print_esc("endL");
+othercases print_esc("endR")
+endcases;
 extension: case chr_code of
   open_node:print_esc("openout");
   write_node:print_esc("write");
@@ -28847,6 +28750,12 @@
     print("""");
   end;
 pdf_save_pos_node: print_esc("pdfsavepos");
+LR_node: case LR_type(p) of
+begin_L_code: print_esc("beginL");
+begin_R_code: print_esc("beginR");
+end_L_code: print_esc("endL");
+othercases print_esc("endR")
+endcases;
 othercases print("whatsit?")
 endcases
 
@@ -28860,7 +28769,7 @@
 write_node,special_node: begin r:=get_node(write_node_size);
   add_token_ref(write_tokens(p)); words:=write_node_size;
   end;
-close_node,language_node: begin r:=get_node(small_node_size);
+close_node,language_node,LR_node: begin r:=get_node(small_node_size);
   words:=small_node_size;
   end;
 native_word_node: begin words:=native_size(p);
@@ -28888,7 +28797,7 @@
 write_node,special_node: begin delete_token_ref(write_tokens(p));
   free_node(p,write_node_size); goto done;
   end;
-close_node,language_node: free_node(p,small_node_size);
+close_node,language_node,LR_node: free_node(p,small_node_size);
 native_word_node: begin free_native_glyph_info(p); free_node(p,native_size(p)); end;
 glyph_node: free_node(p,glyph_node_size);
 pic_node,pdf_node: free_node(p,total_pic_node_size(p));
@@ -28999,6 +28908,8 @@
             x := x + width(p);
         end;
 
+    LR_node: @<Adjust the LR stack for the |hpack| routine@>;
+
     othercases
         do_nothing
 
@@ -29091,6 +29002,8 @@
     pdf_save_pos_node:
         @<Save current position to |pdf_last_x_pos|, |pdf_last_y_pos|@>;
 
+    LR_node: @<Output a reflection instruction if the direction has changed@>;
+
     othercases
         out_what(p)
 
@@ -29153,7 +29066,6 @@
         save_h:=dvi_h; save_v:=dvi_v;
         cur_v:=base_line;
         edge:=cur_h+width(p);
-        if cur_dir=right_to_left then cur_h:=edge;
         pic_out(p, subtype(p) = pdf_node);
         dvi_h:=save_h; dvi_v:=save_v;
         cur_h:=edge; cur_v:=base_line;
@@ -29162,6 +29074,8 @@
     pdf_save_pos_node:
         @<Save current position to |pdf_last_x_pos|, |pdf_last_y_pos|@>;
 
+    LR_node: @<Output a reflection instruction if the direction has changed@>;
+
     othercases
         out_what(p)
 
@@ -30735,81 +30649,7 @@
 @ @<Cases of |left_right| for |print_cmd_chr|@>=
 else if chr_code=middle_noad then print_esc("middle")
 
-@ In constructions such as
-$$\vbox{\halign{\.{#}\hfil\cr
-{}\\hbox to \\hsize\{\cr
-\hskip 25pt \\hskip 0pt plus 0.0001fil\cr
-\hskip 25pt ...\cr
-\hskip 25pt \\hfil\\penalty-200\\hfilneg\cr
-\hskip 25pt ...\}\cr}}$$
-the stretch components of \.{\\hfil} and \.{\\hfilneg} compensate; they may,
-however, get modified in order to prevent arithmetic overflow during
-|hlist_out| when each of them is multiplied by a large |glue_set| value.
-
-Since this ``glue rounding'' depends on state variables |cur_g| and
-|cur_glue| and \TeXXeT\ is supposed to emulate the behaviour of \TeXeT\
-(plus a suitable postprocessor) as close as possible the glue rounding
-cannot be postponed until (segments of) an hlist has been reversed.
-
-The code below is invoked after the effective width, |rule_wd|, of a glue
-node has been computed. The glue node is either converted into a kern node
-or, for leaders, the glue specification is replaced by an equivalent rigid
-one; the subtype of the glue node remains unchanged.
-
-@<Handle a glue node for mixed...@>=
-if (((g_sign=stretching) and (stretch_order(g)=g_order)) or
-    ((g_sign=shrinking) and (shrink_order(g)=g_order))) then
-  begin fast_delete_glue_ref(g);
-  if subtype(p)<a_leaders then
-    begin type(p):=kern_node; width(p):=rule_wd;
-    end
-  else  begin g:=get_node(glue_spec_size);@/
-    stretch_order(g):=filll+1; shrink_order(g):=filll+1; {will never match}
-    width(g):=rule_wd; stretch(g):=0; shrink(g):=0; glue_ptr(p):=g;
-    end;
-  end
-
-@ The optional |TeXXeT| feature of \eTeX\ contains the 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\/} {\bf 8}, 14--25, 1987.
-@^Knuth, Donald Ervin@>
-@^MacKay, Pierre@>
-
-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
-explicitly 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 to keep the influence on the line breaking algorithm
-for pure left-to-right text as small as possible; (3)~right-to-left text
-interrupted by a displayed equation is automatically resumed after that
-equation; and (4)~the |valign| command code with a non-zero command
-modifier is (ab)used for the text direction primitives.
-
-Nevertheless there is a subtle difference between \TeX\ and \TeXXeT\
-that may influence the line breaking algorithm for pure left-to-right
-text.  When a paragraph containing math mode material is broken into
-lines \TeX\ may generate lines where math mode material is not enclosed
-by properly nested \.{\\mathon} and \.{\\mathoff} nodes.  Unboxing such
-lines as part of a new paragraph may have the effect that hyphenation is
-attempted for `words' originating from math mode or that hyphenation is
-inhibited for words originating from horizontal mode.
-
-In \TeXXeT\ additional \.{\\beginM}, resp.\ \.{\\endM} math nodes are
-supplied at the start, resp.\ end of lines such that math mode material
-inside a horizontal list always starts with either \.{\\mathon} or
-\.{\\beginM} and ends with \.{\\mathoff} or \.{\\endM}.  These
-additional nodes are transparent to operations such as \.{\\unskip},
-\.{\\lastpenalty}, or \.{\\lastbox} but they do have the effect that
-hyphenation is never attempted for `words' originating from math mode
-and is never inhibited for words originating from horizontal mode.
-
-@d TeXXeT_state==eTeX_state(TeXXeT_code)
-@d TeXXeT_en==(TeXXeT_state>0) {is \TeXXeT\ enabled?}
-
+@ @d TeXXeT_state==eTeX_state(TeXXeT_code)
 @d XeTeX_upwards_state==eTeX_state(XeTeX_upwards_code)
 @d XeTeX_upwards==(XeTeX_upwards_state>0)
 
@@ -30859,631 +30699,6 @@
 
 primitive("XeTeXinputencoding",extension,XeTeX_input_encoding_extension_code);
 primitive("XeTeXdefaultencoding",extension,XeTeX_default_encoding_extension_code);
-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@>
-
-@ @<Cases of |valign| for |print_cmd_chr|@>=
-else 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
-
-@ @<Cases of |main_control| for |hmode+valign|@>=
-if cur_chr>0 then
-  begin if eTeX_enabled(TeXXeT_en,cur_cmd,cur_chr) then
-@.Improper \\beginL@>
-@.Improper \\endL@>
-@.Improper \\beginR@>
-@.Improper \\endR@>
-    tail_append(new_math(0,cur_chr));
-  end
-else
-
-@ An hbox with subtype dlist will never be reversed, even when embedded
-in right-to-left text.
-
-@<Display if this box is never to be reversed@>=
-if (type(p)=hlist_node)and(box_lr(p)=dlist) then print(", display")
-
-@ A number of routines are based on a stack of one-word nodes whose
-|info| fields contain |end_M_code|, |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 here for |hpack|
-and |ship_out|, or might be local to |post_line_break|.
-
-@d put_LR(#)==begin temp_ptr:=get_avail; info(temp_ptr):=#;
-  link(temp_ptr):=LR_ptr; LR_ptr:=temp_ptr;
-  end
-@#
-@d push_LR(#)==put_LR(end_LR_type(#))
-@#
-@d pop_LR==begin temp_ptr:=LR_ptr; LR_ptr:=link(temp_ptr);
-  free_avail(temp_ptr);
-  end
-
-@<Glob...@>=
-@!LR_ptr:pointer; {stack of LR codes for |hpack|, |ship_out|, and |init_math|}
-@!LR_problems:integer; {counts missing begins and ends}
-@!cur_dir:small_number; {current text direction}
-
-@ @<Set init...@>=
-LR_ptr:=null; LR_problems:=0; cur_dir:=left_to_right;
-
-@ @<Insert LR nodes at the beg...@>=
-begin q:=link(temp_head);
-if LR_ptr<>null then
-  begin temp_ptr:=LR_ptr; r:=q;
-  repeat s:=new_math(0,begin_LR_type(info(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
-    if type(q)=math_node then @<Adjust \(t)the LR stack for the |p...@>;
-  q:=link(q);
-  end;
-end
-
-@ @<Adjust \(t)the LR stack for the |p...@>=
-if end_LR(q) then
-  begin if LR_ptr<>null then if info(LR_ptr)=end_LR_type(q) then pop_LR;
-  end
-else push_LR(q)
-
-@ We use the fact that |q| now points to the node with \.{\\rightskip} glue.
-
-@<Insert LR nodes at the end...@>=
-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
-
-@ @<Initialize the LR stack@>=
-put_LR(before) {this will never match}
-
-@ @<Adjust \(t)the LR stack for the |hp...@>=
-if end_LR(p) then
-  if info(LR_ptr)=end_LR_type(p) then pop_LR
-  else  begin incr(LR_problems); type(p):=kern_node; subtype(p):=explicit;
-    end
-else push_LR(p)
-
-@ @<Check for LR anomalies at the end of |hp...@>=
-begin if info(LR_ptr)<>before 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 info(LR_ptr)=before;
-  end;
-if LR_problems>0 then
-  begin @<Report LR problems@>; goto common_ending;
-  end;
-pop_LR;
-if LR_ptr<>null then confusion("LR1");
-@:this can't happen LR1}{\quad LR1@>
-end
-
-@ @<Report LR problems@>=
-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;
-end
-
-@ @<Initialize |hlist_out| for mixed...@>=
-if eTeX_ex then
-  begin @<Initialize the LR stack@>;
-  if box_lr(this_box)=dlist then
-    if cur_dir=right_to_left then
-      begin cur_dir:=left_to_right; cur_h:=cur_h-width(this_box);
-      end
-    else set_box_lr(this_box)(0);
-  if (cur_dir=right_to_left)and(box_lr(this_box)<>reversed) then
-    @<Reverse the complete hlist and set the subtype to |reversed|@>;
-  end
-
-@ @<Finish |hlist_out| for mixed...@>=
-if eTeX_ex then
-  begin @<Check for LR anomalies at the end of |hlist_out|@>;
-  if box_lr(this_box)=dlist then cur_dir:=right_to_left;
-  end
-
-@ @<Handle a math node in |hlist_out|@>=
-begin if eTeX_ex then
-    @<Adjust \(t)the LR stack for the |hlist_out| routine; if necessary
-      reverse an hlist segment and |goto reswitch|@>;
-  cur_h:=cur_h+width(p);
-  end
-
-@ Breaking a paragraph into lines while \TeXXeT\ is disabled may result
-in lines whith unpaired math nodes.  Such hlists are silently accepted
-in the absence of text direction directives.
-
-@d LR_dir(#)==(subtype(#) div R_code) {text direction of a `math node'}
-
-@<Adjust \(t)the LR stack for the |hl...@>=
-begin if end_LR(p) then
-  if info(LR_ptr)=end_LR_type(p) then pop_LR
-  else  begin if subtype(p)>L_code then incr(LR_problems);
-    end
-else  begin push_LR(p);
-  if LR_dir(p)<>cur_dir then
-    @<Reverse an hlist segment and |goto reswitch|@>;
-  end;
-type(p):=kern_node;
-end
-
-@ @<Check for LR anomalies at the end of |hl...@>=
-begin while info(LR_ptr)<>before do
-  begin if info(LR_ptr)>L_code then LR_problems:=LR_problems+10000;
-  pop_LR;
-  end;
-pop_LR;
-end
-
-@ @d edge_node=style_node {a |style_node| does not occur in hlists}
-@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)}
-
-@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
-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; edge_dist(p):=0; {the |edge_dist| field will be set later}
-new_edge:=p;
-end;
-
-@ @<Cases of |hlist_out| that arise...@>=
-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.
-
-@<Reverse the complete hlist...@>=
-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,cur_g,cur_glue); width(p):=-cur_h;
-cur_h:=save_h; set_box_lr(this_box)(reversed);
-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.
-
-@<Reverse an hlist segment...@>=
-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),cur_g,cur_glue);
-edge_dist(p):=cur_h; cur_dir:=reflected; cur_h:=save_h;
-goto reswitch;
-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. Finally |cur_g| and |cur_glue| are the current glue rounding state
-variables, to be updated by this function. We remove nodes from the original
-list and add them to the head of the new one.
-
-@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
-function reverse(@!this_box,@!t:pointer; var cur_g:scaled;
-  var cur_glue:real):pointer;
-label reswitch,next_p,done;
-var l:pointer; {the new list}
-@!p:pointer; {the current node}
-@!q:pointer; {the next node}
-@!g_order: glue_ord; {applicable order of infinity for glue}
-@!g_sign: normal..shrinking; {selects type of glue}
-@!glue_temp:real; {glue value before rounding}
-@!m,@!n:halfword; {count of unmatched math nodes}
-begin g_order:=glue_order(this_box); g_sign:=glue_sign(this_box);
-l:=t; p:=temp_ptr; m:=min_halfword; n:=min_halfword;
-loop@+  begin while p<>null do
-    @<Move node |p| to the new list and go to the next node;
-    or |goto done| if the end of the reflected segment has been reached@>;
-  if (t=null)and(m=min_halfword)and(n=min_halfword) then goto done;
-  p:=new_math(0,info(LR_ptr)); LR_problems:=LR_problems+10000;
-    {manufacture one missing math node}
-  end;
-done:reverse:=l;
-end;
-
-@ @<Move node |p| to the new list...@>=
-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 @<Move the non-|char_node| |p| to the new list@>
-
-@ @<Move the non-|char_node| |p| to the new list@>=
-begin q:=link(p);
-case type(p) of
-hlist_node,vlist_node,rule_node,kern_node: rule_wd:=width(p);
-@t\4@>@<Cases of |reverse| that need special treatment@>@;
-edge_node: confusion("LR2");
-@:this can't happen LR2}{\quad LR2@>
-othercases goto next_p
-endcases;@/
-cur_h:=cur_h+rule_wd;
-next_p: link(p):=l;
-if type(p)=kern_node then if (rule_wd=0)or(l=null) then
-  begin free_node(p,small_node_size); p:=l;
-  end;
-l:=p; p:=q;
-end
-
-@ Need to measure |native_word| and picture nodes when reversing!
-@<Cases of |reverse|...@>=
-whatsit_node:
-  if (subtype(p)=native_word_node)
-  or (subtype(p)=glyph_node)
-  or (subtype(p)=pic_node)
-  or (subtype(p)=pdf_node)
-  then
-    rule_wd:=width(p)
-  else
-    goto next_p;
-
-@ Here we compute the effective width of a glue node as in |hlist_out|.
-
-@<Cases of |reverse|...@>=
-glue_node: begin round_glue;
-  @<Handle a glue node for mixed...@>;
-  end;
-
-@ A ligature node is replaced by a char node.
-
-@<Cases of |reverse|...@>=
-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.
-
-@<Cases of |reverse|...@>=
-math_node: begin rule_wd:=width(p);
-if end_LR(p) then
-  if info(LR_ptr)<>end_LR_type(p) then
-    begin type(p):=kern_node; incr(LR_problems);
-    end
-  else  begin pop_LR;
-    if n>min_halfword then
-      begin decr(n); decr(subtype(p)); {change |after| into |before|}
-      end
-    else  begin type(p):=kern_node;
-      if m>min_halfword then decr(m)
-      else @<Finish the reversed hlist segment and |goto done|@>;
-      end;
-    end
-else  begin push_LR(p);
-  if (n>min_halfword)or(LR_dir(p)<>cur_dir) then
-    begin incr(n); incr(subtype(p)); {change |before| into |after|}
-    end
-  else  begin type(p):=kern_node; incr(m);
-    end;
-  end;
-end;
-
-@ Finally we have found the end of the hlist segment to be reversed; the
-final math node is released and the remaining list attached to the
-edge node terminating the reversed segment.
-
-@<Finish the reversed...@>=
-begin free_node(p,small_node_size);
-link(t):=q; width(t):=rule_wd; edge_dist(t):=-cur_h-rule_wd; goto done;
-end
-
-@ @<Check for LR anomalies at the end of |s...@>=
-begin if LR_problems>0 then
-  begin @<Report LR problems@>; print_char(")"); print_ln;
-  end;
-if (LR_ptr<>null)or(cur_dir<>left_to_right) then confusion("LR3");
-@:this can't happen LR3}{\quad LR3@>
-end
-
-@ Some special actions are required for displayed equation in paragraphs
-with mixed direction texts.  First of all we have to set the text
-direction preceding the display.
-
-@<Set the value of |x| to the text direction before the display@>=
-if LR_save=null then x:=0
-else if info(LR_save)>=R_code then x:=-1@+else x:=1
-
-@ @<Prepare for display after an empty...@>=
-begin pop_nest; @<Set the value of |x|...@>;
-end
-
-@ When calculating the natural width, |w|, of the final line preceding
-the display, we may have to copy all or part of its hlist.  We copy,
-however, only those parts of the original list that are relevant for the
-computation of |pre_display_size|.
-@^data structure assumptions@>
-
-@<Declare subprocedures for |init_math|@>=
-procedure just_copy(@!p,@!h,@!t:pointer);
-label found,not_found;
-var @!r:pointer; {current node being fabricated for new list}
-@!words:0..5; {number of words remaining to be copied}
-begin while p<>null do
-  begin words:=1; {this setting occurs in more branches than any other}
-  if is_char_node(p) then r:=get_avail
-  else case type(p) of
-  hlist_node,vlist_node: begin r:=get_node(box_node_size);
-    mem[r+6]:=mem[p+6]; mem[r+5]:=mem[p+5]; {copy the last two words}
-    words:=5; list_ptr(r):=null; {this affects |mem[r+5]|}
-    end;
-  rule_node: begin r:=get_node(rule_node_size); words:=rule_node_size;
-    end;
-  ligature_node: begin r:=get_avail; {only |font| and |character| are needed}
-    mem[r]:=mem[lig_char(p)]; goto found;
-    end;
-  kern_node,math_node: begin r:=get_node(small_node_size);
-    words:=small_node_size;
-    end;
-  glue_node: begin r:=get_node(small_node_size); add_glue_ref(glue_ptr(p));
-    glue_ptr(r):=glue_ptr(p); leader_ptr(r):=null;
-    end;
-  whatsit_node:@<Make a partial copy of the whatsit...@>;
-  othercases goto not_found
-  endcases;
-  while words>0 do
-    begin decr(words); mem[r+words]:=mem[p+words];
-    end;
-  found: link(h):=r; h:=r;
-  not_found: p:=link(p);
-  end;
-link(h):=t;
-end;
-
-@ When the final line ends with R-text, the value |w| refers to the line
-reflected with respect to the left edge of the enclosing vertical list.
-
-@<Prepare for display after a non-empty...@>=
-if eTeX_ex then @<Let |j| be the prototype box for the display@>;
-v:=shift_amount(just_box);
-@<Set the value of |x|...@>;
-if x>=0 then
-  begin p:=list_ptr(just_box); link(temp_head):=null;
-  end
-else  begin v:=-v-width(just_box);
-  p:=new_math(0,begin_L_code); link(temp_head):=p;
-  just_copy(list_ptr(just_box),p,new_math(0,end_L_code));
-  cur_dir:=right_to_left;
-  end;
-v:=v+2*quad(cur_font);
-if TeXXeT_en then @<Initialize the LR stack@>
-
-@ @<Finish the natural width computation@>=
-if TeXXeT_en then
-  begin while LR_ptr<>null do pop_LR;
-  if LR_problems<>0 then
-    begin w:=max_dimen; LR_problems:=0;
-    end;
-  end;
-cur_dir:=left_to_right; flush_node_list(link(temp_head))
-
-@ In the presence of text direction directives we assume that any LR
-problems have been fixed by the |hpack| routine.  If the final line
-contains, however, text direction directives while \TeXXeT\ is disabled,
-then we set |w:=max_dimen|.
-
-@<Cases of `Let |d| be the natural...@>=
-math_node: begin d:=width(p);
-  if TeXXeT_en then @<Adjust \(t)the LR stack for the |init_math| routine@>
-  else if subtype(p)>=L_code then
-    begin w:=max_dimen; goto done;
-    end;
-  end;
-edge_node: begin d:=width(p); cur_dir:=subtype(p);
-  end;
-
-@ @<Adjust \(t)the LR stack for the |i...@>=
-if end_LR(p) then
-  begin if info(LR_ptr)=end_LR_type(p) then pop_LR
-  else if subtype(p)>L_code then
-    begin w:=max_dimen; goto done;
-    end
-  end
-else  begin push_LR(p);
-  if LR_dir(p)<>cur_dir then
-    begin just_reverse(p); p:=temp_head;
-    end;
-  end
-
-@ @<Declare subprocedures for |init_math|@>=
-procedure just_reverse(@!p:pointer);
-label found,done;
-var l:pointer; {the new list}
-@!t:pointer; {tail of reversed segment}
-@!q:pointer; {the next node}
-@!m,@!n:halfword; {count of unmatched math nodes}
-begin m:=min_halfword; n:=min_halfword;
-if link(temp_head)=null then
-  begin just_copy(link(p),temp_head,null); q:=link(temp_head);
-  end
-else  begin q:=link(p); link(p):=null; flush_node_list(link(temp_head));
-  end;
-t:=new_edge(cur_dir,0); l:=t; cur_dir:=reflected;
-while q<>null do
-  if is_char_node(q) then
-    repeat p:=q; q:=link(p); link(p):=l; l:=p;
-    until not is_char_node(q)
-  else  begin p:=q; q:=link(p);
-    if type(p)=math_node then
-      @<Adjust \(t)the LR stack for the |just_reverse| routine@>;
-    link(p):=l; l:=p;
-    end;
-goto done;
-found:width(t):=width(p); link(t):=q; free_node(p,small_node_size);
-done:link(temp_head):=l;
-end;
-
-@ @<Adjust \(t)the LR stack for the |j...@>=
-if end_LR(p) then
-  if info(LR_ptr)<>end_LR_type(p) then
-    begin type(p):=kern_node; incr(LR_problems);
-    end
-  else  begin pop_LR;
-    if n>min_halfword then
-      begin decr(n); decr(subtype(p)); {change |after| into |before|}
-      end
-    else  begin if m>min_halfword then decr(m)@+else goto found;
-      type(p):=kern_node;
-      end;
-    end
-else  begin push_LR(p);
-  if (n>min_halfword)or(LR_dir(p)<>cur_dir) then
-    begin incr(n); incr(subtype(p)); {change |before| into |after|}
-    end
-  else  begin type(p):=kern_node; incr(m);
-    end;
-  end
-
-@ The prototype box is an hlist node with the width, glue set, and shift
-amount of |just_box|, i.e., the last line preceding the display.  Its
-hlist reflects the current \.{\\leftskip} and \.{\\rightskip}.
-
-@<Let |j| be the prototype box for the display@>=
-begin if right_skip=zero_glue then j:=new_kern(0)
-else j:=new_param_glue(right_skip_code);
-if left_skip=zero_glue then p:=new_kern(0)
-else p:=new_param_glue(left_skip_code);
-link(p):=j; j:=new_null_box; width(j):=width(just_box);
-shift_amount(j):=shift_amount(just_box); list_ptr(j):=p;
-glue_order(j):=glue_order(just_box); glue_sign(j):=glue_sign(just_box);
-glue_set(j):=glue_set(just_box);
-end
-
-@ At the end of a displayed equation we retrieve the prototype box.
-
-@<Local variables for finishing...@>=
-@!j:pointer; {prototype box}
-
-@ @<Retrieve the prototype box@>=
-if mode=mmode then j:=LR_box
-
-@ @<Flush the prototype box@>=
-flush_node_list(j)
-
-@ The |app_display| procedure used to append the displayed equation
-and\slash or equation number to the current vertical list has three
-parameters:  the prototype box, the hbox to be appended, and the
-displacement of the hbox in the display line.
-
-@<Declare subprocedures for |after_math|@>=
-procedure app_display(@!j,@!b:pointer;@!d:scaled);
-var z:scaled; {width of the line}
-@!s:scaled; {move the line right this much}
-@!e:scaled; {distance from right edge of box to end of line}
-@!x:integer; {|pre_display_direction|}
-@!p,@!q,@!r,@!t,@!u:pointer; {for list manipulation}
-begin s:=display_indent; x:=pre_display_direction;
-if x=0 then shift_amount(b):=s+d
-else  begin z:=display_width; p:=b;
-  @<Set up the hlist for the display line@>;
-  @<Package the display line@>;
-  end;
-append_to_vlist(b);
-end;
-
-@ Here we construct the hlist for the display, starting with node |p|
-and ending with node |q|. We also set |d| and |e| to the amount of
-kerning to be added before and after the hlist (adjusted for the
-prototype box).
-
-@<Set up the hlist for the display line@>=
-if x>0 then e:=z-d-width(p)
-else  begin e:=d; d:=z-e-width(p);
-  end;
-if j<>null then
-  begin b:=copy_node_list(j); height(b):=height(p); depth(b):=depth(p);
-  s:=s-shift_amount(b); d:=d+s; e:=e+width(b)-z-s;
-  end;
-if box_lr(p)=dlist then q:=p {display or equation number}
-else  begin {display and equation number}
-  r:=list_ptr(p); free_node(p,box_node_size);
-  if r=null then confusion("LR4");
-  if x>0 then
-    begin p:=r;
-    repeat q:=r; r:=link(r); {find tail of list}
-    until r=null;
-    end
-  else  begin p:=null; q:=r;
-    repeat t:=link(r); link(r):=p; p:=r; r:=t; {reverse list}
-    until r=null;
-    end;
-  end
-
-@ In the presence of a prototype box we use its shift amount and width
-to adjust the values of kerning and add these values to the glue nodes
-inserted to cancel the \.{\\leftskip} and \.{\\rightskip}.  If there is
-no prototype box (because the display is preceded by an empty
-paragraph), or if the skip parameters are zero, we just add kerns.
-
-The |cancel_glue| macro creates and links a glue node that is, together
-with another glue node, equivalent to a given amount of kerning.  We can
-use |j| as temporary pointer, since all we need is |j<>null|.
-
-@d cancel_glue(#)==j:=new_skip_param(#); cancel_glue_cont
-@d cancel_glue_cont(#)==link(#):=j; cancel_glue_cont_cont
-@d cancel_glue_cont_cont(#)==link(j):=#; cancel_glue_end
-@d cancel_glue_end(#)==j:=glue_ptr(#); cancel_glue_end_end
-@d cancel_glue_end_end(#)==
-stretch_order(temp_ptr):=stretch_order(j);
-shrink_order(temp_ptr):=shrink_order(j); width(temp_ptr):=#-width(j);
-stretch(temp_ptr):=-stretch(j); shrink(temp_ptr):=-shrink(j)
-
-@<Package the display line@>=
-if j=null then
-  begin r:=new_kern(0); t:=new_kern(0); {the widths will be set later}
-  end
-else  begin r:=list_ptr(b); t:=link(r);
-  end;
-u:=new_math(0,end_M_code);
-if type(t)=glue_node then {|t| is \.{\\rightskip} glue}
-  begin cancel_glue(right_skip_code)(q)(u)(t)(e); link(u):=t;
-  end
-else  begin width(t):=e; link(t):=u; link(q):=t;
-  end;
-u:=new_math(0,begin_M_code);
-if type(r)=glue_node then {|r| is \.{\\leftskip} glue}
-  begin cancel_glue(left_skip_code)(u)(p)(r)(d); link(r):=u;
-  end
-else  begin width(r):=d; link(r):=p; link(u):=r;
-  if j=null then
-    begin b:=hpack(u,natural); shift_amount(b):=s;
-    end
-  else list_ptr(b):=u;
-  end
 
 @ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens}
 primitive.
@@ -33300,6 +32515,124 @@
   end;
 end
 
+@ Now we do the main work required for mixed-direction texts.
+
+@<Cases of |main_control| that build...@>=
+hmode+LR,mmode+LR: begin new_whatsit(LR_node,small_node_size); LR_type(tail):=cur_chr;
+  end;
+
+@ A number of routines are based on a stack of one-word nodes whose |info|
+fields contain either |begin_L_code| or |begin_R_code|. The top of the
+stack is pointed to by |LR_ptr|, and an auxiliary variable |LR_tmp| is
+available for stack manipulation.
+
+@<Glob...@>=
+@!LR_ptr,@!LR_tmp:pointer; {stack of LR codes and temp for manipulation}
+
+@ @<Declare functions needed for special kinds of nodes@>=
+function new_LR(@!s:small_number): pointer;
+var p: pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=whatsit_node; subtype(p):=LR_node;
+LR_type(p):=s; new_LR:=p;
+end;
+
+@ @<Declare functions needed for special kinds of nodes@>=
+function safe_info(@!p:pointer): integer;
+begin if p=null then safe_info:=-1@+else safe_info:=info(p);
+end;
+
+@ @<Append a |begin_L| to the tail of the current list@>=
+tail_append(new_LR(begin_L_code))
+
+@ @<Append an |end_L| to the tail of the current list@>=
+tail_append(new_LR(end_L_code))
+
+@ When the stack-manipulation macros of this section are used below, variables
+|LR_ptr| and |LR_tmp| might be the global variables declared above, or they
+might be local to |hpack| or |post_line_break|.
+
+@d push_LR(#)==begin LR_tmp:=get_avail; info(LR_tmp):=LR_type(#);
+  link(LR_tmp):=LR_ptr; LR_ptr:=LR_tmp;
+  end
+@d pop_LR==begin LR_tmp:=LR_ptr; LR_ptr:=link(LR_tmp); free_avail(LR_tmp);
+  end
+
+@<Flush the LR stack@>=while LR_ptr<>null do pop_LR
+
+@ @<Insert LR nodes at the beginning of the current line@>=
+while LR_ptr<>null do
+  begin LR_tmp:=new_LR(info(LR_ptr)); link(LR_tmp):=link(temp_head);
+  link(temp_head):=LR_tmp; pop_LR;
+  end
+
+@ @<Adjust the LR stack based on LR nodes in this line@>=
+q:=link(temp_head);
+while q<>cur_break(cur_p) do
+  begin if not is_char_node(q) then if type(q)=whatsit_node then
+   if subtype(q)=LR_node then
+    if begin_LR(q) then push_LR(q)
+    else if LR_ptr<>null then
+     if info(LR_ptr)=begin_LR_type(q) then pop_LR;
+  q:=link(q);
+  end
+
+@ We use the fact that |q| now points to the node with \.{\\rightskip} glue.
+
+@<Insert LR nodes at the end of the current line@>=
+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 LR_tmp:=new_LR(info(r)+end_L_code);
+    link(s):=LR_tmp; s:=LR_tmp; r:=link(r);
+    end;
+  link(s):=q;
+  end
+
+@ @<Adjust the LR stack for the |hpack| routine@>=
+if begin_LR(p) then push_LR(p)
+else if safe_info(LR_ptr)=begin_LR_type(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
+
+@ @<Check for LR anomalies at the end of |hpack|@>=
+if LR_ptr<>null then
+  begin while link(q)<>null do q:=link(q);
+  repeat link(q):=new_LR(info(LR_ptr)+end_L_code); q:=link(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
+
+@ @d begin_reflect=0 {begin a reflcted segment value of |reflect| \.{XDV} command}
+@d end_reflect=1 {end a reflcted segment value of |reflect| \.{XDV} command}
+
+@<Output a reflection instruction if the direction has changed@>=
+if begin_LR(p) then
+  begin if safe_info(LR_ptr)<>LR_type(p) then
+    begin synch_h; synch_v; dvi_out(reflect); dvi_out(begin_reflect);
+    end;
+  push_LR(p);
+  end
+else if safe_info(LR_ptr)=begin_LR_type(p) then
+  begin pop_LR;
+  if info(LR_ptr)+end_L_code<>LR_type(p) then
+    begin synch_h; synch_v; dvi_out(reflect); dvi_out(end_reflect);
+    end;
+  end
+else confusion("LR")
+@:this can't happen LR}{\quad LR@>
+
 @* \[54] System-dependent changes.
 This section should be replaced, if necessary, by any special
 modifications of the program
